@guanlin4924/devflow-pi 0.1.0
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 +164 -0
- package/docs/OMP-NATIVE-ARCHITECTURE.md +104 -0
- package/package.json +40 -0
- package/plugins/devflow-core/extensions/devflow-core.ts +1642 -0
- package/plugins/devflow-core/package.json +14 -0
- package/plugins/devflow-core/runtime/hooks/context_sync_gate.py +174 -0
- package/plugins/devflow-core/runtime/hooks/hook_common.py +696 -0
- package/plugins/devflow-core/runtime/hooks/index_gate.py +63 -0
- package/plugins/devflow-core/runtime/hooks/injection_code_specification.py +7 -0
- package/plugins/devflow-core/runtime/hooks/post_tool.py +44 -0
- package/plugins/devflow-core/runtime/hooks/start.py +288 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +265 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/agents/openai.yaml +4 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/advance_state.rb +520 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/control_io.rb +42 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_state.rb +138 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_workflow.rb +121 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/inspect_state.rb +553 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/manage_lessons.rb +219 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/sync_artifacts.rb +168 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/update_triage_route.rb +110 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +565 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +783 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/uat-remediation.yaml +143 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/workflows-README.md +10 -0
- package/plugins/devflow-core/skills/workflow-pipeline/SKILL.md +117 -0
- package/plugins/devflow-core/skills/workflow-pipeline/references/pipeline-flow.md +167 -0
- package/plugins/devflow-core/skills/workflow-pipeline/references/slice-safety-guide.md +125 -0
- package/plugins/devflow-core/skills/workflow-project-context/SKILL.md +819 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/agents-md-template.md +25 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-backend-spec-template.md +68 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-frontend-spec-template.md +64 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-navigation-template.md +168 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/context-domain-index-template.md +43 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/context-spec-index-template.md +45 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/execution-contract-template.md +57 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/navigation-index-template.md +40 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/project-initialization-template.md +109 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/rules-index-template.md +80 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/run-manifest-template.json +40 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/directory-structure.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/engine.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/errors.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/locator.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/model.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/repository.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/router.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/scheduler.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/schema.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/service.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/backend/testing.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/api-client.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/case-editor.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/components.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/directory-structure.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/forms.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/routing.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/state.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/spec-templates/frontend/testing.md +46 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/stack-spec-template.md +106 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/state-template.md +77 -0
- package/plugins/devflow-core/skills/workflow-project-context/assets/toolchain-template.md +37 -0
- package/plugins/devflow-core/skills/workflow-project-context/references/navigation-guide.md +262 -0
- package/plugins/devflow-core/skills/workflow-project-context/scripts/validate_initialization.py +545 -0
- package/plugins/devflow-design/agents/contract-auditor.md +18 -0
- package/plugins/devflow-design/extensions/devflow-design.ts +47 -0
- package/plugins/devflow-design/package.json +14 -0
- package/plugins/devflow-design/skills/workflow-api-call-guide/SKILL.md +142 -0
- package/plugins/devflow-design/skills/workflow-api-contract/SKILL.md +150 -0
- package/plugins/devflow-design/skills/workflow-api-supplement/SKILL.md +121 -0
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/SKILL.md +257 -0
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/assets/adr-template.md +57 -0
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/assets/backend-spec-template.md +205 -0
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/affected-module-exploration.md +59 -0
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/backend-conventions.md +30 -0
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/code-explore-guide.md +40 -0
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/project-rule-contract.md +66 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/SKILL.md +91 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/index.md +71 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/accessibility.md +31 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/component-library.md +31 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/design-systems.md +47 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/responsive-grid.md +29 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/stack-guidelines.md +31 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/ux-patterns.md +40 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tokens/project-tokens.md +19 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/SKILL.md +377 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/charts.csv +26 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/colors.csv +97 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/icons.csv +101 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/landing.csv +31 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/products.csv +97 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/styles.csv +68 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/typography.csv +58 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/scripts/core.py +253 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/scripts/search.py +114 -0
- package/plugins/devflow-design/skills/workflow-design-rule-pack/scripts/install.py +67 -0
- package/plugins/devflow-design/skills/workflow-feature-breakdown/SKILL.md +370 -0
- package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/admin-system-template.md +57 -0
- package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/api-service-template.md +42 -0
- package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/c-end-system-template.md +25 -0
- package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/output-template.md +139 -0
- package/plugins/devflow-design/skills/workflow-feature-breakdown/references/bug-rules.md +12 -0
- package/plugins/devflow-design/skills/workflow-feature-breakdown/references/exception-checklist.md +31 -0
- package/plugins/devflow-design/skills/workflow-feature-breakdown/references/trace-integrity-guide.md +153 -0
- package/plugins/devflow-design/skills/workflow-feature-breakdown/references/writing-guide.md +92 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/SKILL.md +223 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/assets/frontend-spec-template.md +204 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/affected-module-exploration.md +58 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/component-patterns.md +42 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/frontend-conventions.md +28 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/frontend-stack-slots/common-web.md +53 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/frontend-stack-slots/next.md +40 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/frontend-stack-slots/react.md +41 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/frontend-stack-slots/vue.md +41 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/project-rule-contract.md +95 -0
- package/plugins/devflow-design/skills/workflow-integration-verify/SKILL.md +172 -0
- package/plugins/devflow-design/skills/workflow-ui-spec/SKILL.md +302 -0
- package/plugins/devflow-design/skills/workflow-ui-spec/assets/ui-spec-template.md +244 -0
- package/plugins/devflow-design/skills/workflow-ui-spec/references/affected-module-exploration.md +55 -0
- package/plugins/devflow-implementation/agents/implementation-worker.md +20 -0
- package/plugins/devflow-implementation/package.json +6 -0
- package/plugins/devflow-implementation/skills/workflow-bug-fix/SKILL.md +124 -0
- package/plugins/devflow-implementation/skills/workflow-bug-fix/references/INDEX.md +13 -0
- package/plugins/devflow-implementation/skills/workflow-bug-fix/references/adversarial-review.md +35 -0
- package/plugins/devflow-implementation/skills/workflow-bug-fix/references/first-principles.md +26 -0
- package/plugins/devflow-implementation/skills/workflow-bug-fix/references/lesson-format.md +28 -0
- package/plugins/devflow-implementation/skills/workflow-bug-fix/references/root-cause-ladder.md +51 -0
- package/plugins/devflow-implementation/skills/workflow-bug-fix/references/seam-analysis.md +30 -0
- package/plugins/devflow-implementation/skills/workflow-change-plan/SKILL.md +160 -0
- package/plugins/devflow-implementation/skills/workflow-change-plan/references/code-conventions.md +33 -0
- package/plugins/devflow-implementation/skills/workflow-change-plan/references/ddl-guidelines.md +60 -0
- package/plugins/devflow-implementation/skills/workflow-change-plan/references/output-template.md +217 -0
- package/plugins/devflow-implementation/skills/workflow-change-plan/references/project-rule-contract.md +55 -0
- package/plugins/devflow-implementation/skills/workflow-execute-phase/SKILL.md +271 -0
- package/plugins/devflow-implementation/skills/workflow-execute-phase/references/execute-phase.md +1251 -0
- package/plugins/devflow-implementation/skills/workflow-execute-phase/references/ui-brand.md +160 -0
- package/plugins/devflow-intel/agents/project-scout.md +27 -0
- package/plugins/devflow-intel/extensions/devflow-intel.ts +104 -0
- package/plugins/devflow-intel/package.json +14 -0
- package/plugins/devflow-intel/skills/code-index-query/SKILL.md +37 -0
- package/plugins/devflow-intel/skills/code-index-query/references/query-patterns.md +27 -0
- package/plugins/devflow-intel/skills/workflow-code-index/SKILL.md +68 -0
- package/plugins/devflow-intel/skills/workflow-code-index/references/adapter-contract.md +38 -0
- package/plugins/devflow-intel/skills/workflow-code-index/references/audit-checklist.md +10 -0
- package/plugins/devflow-intel/skills/workflow-code-index/references/confidence-rules.md +43 -0
- package/plugins/devflow-intel/skills/workflow-code-index/references/data-model.md +74 -0
- package/plugins/devflow-intel/skills/workflow-code-index/references/discovery.md +24 -0
- package/plugins/devflow-intel/skills/workflow-code-index/references/query-guide.md +20 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/adapters/__init__.py +1 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/adapters/base.py +30 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/adapters/generic_text.py +287 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/adapters/registry.py +17 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/code_index.py +437 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/__init__.py +1 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/discovery.py +139 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/hashing.py +11 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/scanner.py +115 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/schema.py +123 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/__init__.py +1 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/project_binding_check.py +66 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/relation_check.py +36 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/schema_validate.py +11 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/stale_check.py +42 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/writers/__init__.py +1 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/writers/jsonl_writer.py +25 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/writers/markdown_writer.py +106 -0
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/writers/sqlite_writer.py +143 -0
- package/plugins/devflow-intel/skills/workflow-context-sync/SKILL.md +381 -0
- package/plugins/devflow-intel/skills/workflow-context-sync/agents/openai.yaml +4 -0
- package/plugins/devflow-intel/skills/workflow-context-sync/scripts/context_sync_cursor.py +241 -0
- package/plugins/devflow-intel/skills/workflow-update-navigation/SKILL.md +186 -0
- package/plugins/devflow-intel/skills/workflow-update-navigation/references/incremental-update-guide.md +47 -0
- package/plugins/devflow-quality/agents/code-reviewer.md +18 -0
- package/plugins/devflow-quality/agents/verification-runner.md +21 -0
- package/plugins/devflow-quality/extensions/devflow-quality.ts +59 -0
- package/plugins/devflow-quality/package.json +14 -0
- package/plugins/devflow-quality/skills/code-review/SKILL.md +288 -0
- package/plugins/devflow-quality/skills/code-review/references/cross-layer-checklist.md +70 -0
- package/plugins/devflow-quality/skills/code-review/references/dao-checklist.md +70 -0
- package/plugins/devflow-quality/skills/code-review/references/handler-checklist.md +48 -0
- package/plugins/devflow-quality/skills/code-review/references/repository-checklist.md +44 -0
- package/plugins/devflow-quality/skills/code-review/references/service-checklist.md +57 -0
- package/plugins/devflow-quality/skills/code-review/references/spec-checklist.md +34 -0
- package/plugins/devflow-quality/skills/code-review/references/vo-checklist.md +26 -0
- package/plugins/devflow-quality/skills/workflow-audit-milestone/SKILL.md +31 -0
- package/plugins/devflow-quality/skills/workflow-audit-milestone/references/audit-milestone.md +328 -0
- package/plugins/devflow-quality/skills/workflow-complete-milestone/SKILL.md +132 -0
- package/plugins/devflow-quality/skills/workflow-complete-milestone/assets/milestone-archive.md +123 -0
- package/plugins/devflow-quality/skills/workflow-complete-milestone/references/complete-milestone.md +775 -0
- package/plugins/devflow-quality/skills/workflow-complete-milestone/references/milestone-archive.md +123 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/SKILL.md +126 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/api-mainflow-rules.md +27 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/codebase-discovery.md +54 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/intermediate-format.md +244 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/mainflow-selection.md +36 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/project-dsl-contract.md +190 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/ui-mainflow-rules.md +110 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/compile_dsl.py +583 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/discover_codebases.py +199 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/normalize_case_payloads.py +177 -0
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/validate_case_payloads.py +370 -0
- package/plugins/devflow-quality/skills/workflow-verify-work/SKILL.md +106 -0
- package/plugins/devflow-quality/skills/workflow-verify-work/assets/UAT.md +265 -0
- package/plugins/devflow-quality/skills/workflow-verify-work/references/verify-work.md +759 -0
- package/plugins/devflow-requirements/agents/requirements-auditor.md +20 -0
- package/plugins/devflow-requirements/package.json +6 -0
- package/plugins/devflow-requirements/skills/requirement-feasibility-review/SKILL.md +219 -0
- package/plugins/devflow-requirements/skills/requirement-feasibility-review/agents/openai.yaml +4 -0
- package/plugins/devflow-requirements/skills/requirement-feasibility-review/references/review-method.md +257 -0
- package/plugins/devflow-requirements/skills/requirement-feasibility-review/references/review-template.md +80 -0
- package/plugins/devflow-requirements/skills/workflow-demand-clarify/SKILL.md +325 -0
- package/plugins/devflow-requirements/skills/workflow-domain-modeling/SKILL.md +163 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/SKILL.md +748 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/assets/baseline-template.md +281 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/antipatterns.md +69 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/challenge-review-guide.md +158 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/decomposition-guide.md +115 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/project-context-guide.md +119 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/questioning.md +76 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/requirement-review-checklist.md +115 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-triage/SKILL.md +204 -0
- package/plugins/devflow-requirements/skills/workflow-wayfinder/SKILL.md +183 -0
- package/src/cli.ts +76 -0
- package/src/constants.ts +15 -0
- package/src/doctor.ts +66 -0
- package/src/paths.ts +37 -0
- package/src/platform-extension.ts +46 -0
- package/src/project-context.ts +165 -0
- package/src/runtime.ts +167 -0
- package/src/usage-stats.ts +101 -0
- package/templates/commands/start.md +58 -0
- package/templates/omp/AGENTS.md +36 -0
- package/templates/omp/config.yml +112 -0
- package/templates/omp/hook-config.json +18 -0
- package/templates/omp/index-policy.json +57 -0
package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/compile_dsl.py
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Compile AI-friendly case descriptions into strict DSL JSON.
|
|
3
|
+
|
|
4
|
+
AI 产出简化的用例描述(locator 用 "type:value" 字符串格式),
|
|
5
|
+
本脚本负责编译成符合系统 DslTarget 严格格式的 DSL JSON。
|
|
6
|
+
这是硬约束层 —— 所有 target key、strategy、assertion 由脚本决定,AI 无法编造。
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import re
|
|
14
|
+
from copy import deepcopy
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# 硬约束常量 — 唯一权威定义
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
VALID_TARGET_KEYS = {"css", "xpath", "label", "placeholder", "role", "name", "near_text", "exact"}
|
|
24
|
+
EXACT_LOCATOR_KEYS = {"css", "xpath", "role"} # 精确匹配,必须有 source evidence
|
|
25
|
+
SEMANTIC_LOCATOR_KEYS = {"label", "placeholder", "near_text"} # 语义匹配,需检查唯一性
|
|
26
|
+
|
|
27
|
+
# 高风险 label — 表格行常见操作文本,用 label 定位时几乎一定不唯一
|
|
28
|
+
HIGH_RISK_LABELS = {
|
|
29
|
+
"编辑", "删除", "查看", "详情", "启用", "禁用", "停用",
|
|
30
|
+
"新增", "添加", "修改", "复制", "移动", "导出", "导入", "下载",
|
|
31
|
+
"上传", "设置", "配置", "操作", "更多", "取消", "确定", "保存",
|
|
32
|
+
"提交", "重置", "刷新", "搜索", "查询", "筛选", "排序",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
MIN_CSS_LENGTH = 10
|
|
36
|
+
|
|
37
|
+
DEFAULT_UI_STRATEGY = {"mode": "structured_first", "fallback": "midscene"}
|
|
38
|
+
AI_ONLY_STRATEGY = {"mode": "ai_only"}
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
# Locator 字符串解析器
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
LOCATOR_RE = re.compile(
|
|
45
|
+
r"^(?P<type>text|placeholder|css|xpath|role|near)"
|
|
46
|
+
r":(?P<value>.+)$"
|
|
47
|
+
)
|
|
48
|
+
ROLE_LOCATOR_RE = re.compile(r"^role:(?P<role>[^:]+):(?P<name>.+)$")
|
|
49
|
+
SCOPE_RE = re.compile(r"^row:(?P<row>\d+)$")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def parse_locator(raw: str) -> dict[str, str]:
|
|
53
|
+
"""Parse 'type:value' string into flat target dict.
|
|
54
|
+
|
|
55
|
+
Returns {'label': '...'} for text:, {'placeholder': '...'} for placeholder:, etc.
|
|
56
|
+
Raises ValueError on unrecognized format.
|
|
57
|
+
"""
|
|
58
|
+
raw = raw.strip()
|
|
59
|
+
|
|
60
|
+
# role:button:确定
|
|
61
|
+
m = ROLE_LOCATOR_RE.match(raw)
|
|
62
|
+
if m:
|
|
63
|
+
return {"role": m.group("role"), "name": m.group("name")}
|
|
64
|
+
|
|
65
|
+
m = LOCATOR_RE.match(raw)
|
|
66
|
+
if not m:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"Invalid locator format: '{raw}'. "
|
|
69
|
+
f"Expected: text:<text> | placeholder:<text> | css:<selector> | "
|
|
70
|
+
f"xpath:<expr> | role:<role>:<name> | near:<text>"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
loc_type = m.group("type")
|
|
74
|
+
value = m.group("value").strip()
|
|
75
|
+
|
|
76
|
+
if not value:
|
|
77
|
+
raise ValueError(f"Locator '{raw}' has empty value.")
|
|
78
|
+
|
|
79
|
+
if loc_type == "text":
|
|
80
|
+
return {"label": value}
|
|
81
|
+
if loc_type == "placeholder":
|
|
82
|
+
return {"placeholder": value}
|
|
83
|
+
if loc_type == "css":
|
|
84
|
+
return {"css": value}
|
|
85
|
+
if loc_type == "xpath":
|
|
86
|
+
return {"xpath": value}
|
|
87
|
+
if loc_type == "near":
|
|
88
|
+
return {"near_text": value}
|
|
89
|
+
|
|
90
|
+
raise ValueError(f"Unknown locator type: {loc_type}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# CSS 编造检测 — 零容忍
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
# 明显是 AI 编造的 CSS class 特征
|
|
98
|
+
HALLUCINATED_CSS_PATTERNS: list[tuple[str, str]] = [
|
|
99
|
+
# 包含 % 的伪 Tailwind class(w-100%, h-50% 等)
|
|
100
|
+
(r"\b[a-z]+-\d+%", "contains invented utility class with % (e.g. w-100%)"),
|
|
101
|
+
# 裸的百分比值
|
|
102
|
+
(r"%", "contains '%' — not valid in CSS selector without escaping"),
|
|
103
|
+
# 纯表单容器通用类,不指定具体字段
|
|
104
|
+
(r"^\.el-form-item(__content)?$", "matches all form items, not a specific field"),
|
|
105
|
+
# 明显编造的随机 class 名模式
|
|
106
|
+
(r"\b[a-z]{1,3}-\d{3,}\b", "looks like a made-up class (random letters+numbers)"),
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
# fill/select 动作在非 placeholder 情况下,CSS 禁止匹配纯容器类
|
|
110
|
+
GENERIC_FORM_CLASSES = [
|
|
111
|
+
".el-form-item",
|
|
112
|
+
".el-form-item__content",
|
|
113
|
+
".el-form-item__label",
|
|
114
|
+
".ant-form-item",
|
|
115
|
+
".ant-form-item-control",
|
|
116
|
+
".form-item",
|
|
117
|
+
".form-group",
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _validate_css_not_hallucinated(css_value: str, step_name: str) -> None:
|
|
122
|
+
"""Reject CSS selectors that show signs of AI hallucination. Hard gate."""
|
|
123
|
+
errors: list[str] = []
|
|
124
|
+
|
|
125
|
+
# Check against hallucination patterns
|
|
126
|
+
for pattern, description in HALLUCINATED_CSS_PATTERNS:
|
|
127
|
+
if re.search(pattern, css_value):
|
|
128
|
+
errors.append(
|
|
129
|
+
f"CSS selector '{css_value}' rejected: {description}. "
|
|
130
|
+
f"Step: {step_name}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Check if CSS is purely generic form classes (no specific target)
|
|
134
|
+
stripped = css_value.strip()
|
|
135
|
+
parts = [p.strip() for p in stripped.split() if p.strip()]
|
|
136
|
+
if parts and all(
|
|
137
|
+
any(p == g or p.startswith(g) for g in GENERIC_FORM_CLASSES)
|
|
138
|
+
for p in parts
|
|
139
|
+
):
|
|
140
|
+
errors.append(
|
|
141
|
+
f"CSS selector '{css_value}' only matches generic form containers, "
|
|
142
|
+
f"not a specific field. Use 'near:<label文本>' instead. "
|
|
143
|
+
f"Step: {step_name}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Too short = probably made up
|
|
147
|
+
if len(css_value) < MIN_CSS_LENGTH:
|
|
148
|
+
errors.append(
|
|
149
|
+
f"CSS selector '{css_value}' is too generic ({len(css_value)} chars, min {MIN_CSS_LENGTH}). "
|
|
150
|
+
f"Step: {step_name}"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if errors:
|
|
154
|
+
raise ValueError("CSS HALLUCINATION DETECTED:\n" + "\n".join(f" - {e}" for e in errors))
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# ---------------------------------------------------------------------------
|
|
158
|
+
# Fill 动作 locator 硬决策链
|
|
159
|
+
# ---------------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
FILL_ALLOWED_LOCATORS = {"placeholder", "near"} # fill 只能用这两个
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _validate_fill_locator(step_name: str, locator_raw: str) -> None:
|
|
165
|
+
"""Hard decision chain for fill actions: placeholder → near → ai_only. CSS/xpath forbidden."""
|
|
166
|
+
if not locator_raw:
|
|
167
|
+
raise ValueError(
|
|
168
|
+
f"Fill step '{step_name}' has no locator. "
|
|
169
|
+
f"Use 'placeholder:<text>', 'near:<label文本>', or change action to 'act' with ai_only."
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
loc_type = locator_raw.split(":", 1)[0].strip()
|
|
173
|
+
if loc_type not in FILL_ALLOWED_LOCATORS:
|
|
174
|
+
raise ValueError(
|
|
175
|
+
f"Fill step '{step_name}' uses locator type '{loc_type}', which is forbidden for fill actions. "
|
|
176
|
+
f"Fill hard chain: placeholder → near → ai_only. "
|
|
177
|
+
f"Got '{locator_raw}'. "
|
|
178
|
+
f"Use 'placeholder:<text>' if the field has a placeholder, "
|
|
179
|
+
f"'near:<label文本>' if there's a label next to the field, "
|
|
180
|
+
f"or change action to 'act' with ai_only strategy."
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ---------------------------------------------------------------------------
|
|
185
|
+
# Target 编译 — 硬约束规则
|
|
186
|
+
# ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
def compile_target(
|
|
189
|
+
locator_raw: str,
|
|
190
|
+
scope: str | None = None,
|
|
191
|
+
step_name: str = "",
|
|
192
|
+
) -> dict[str, Any]:
|
|
193
|
+
"""Compile a locator string + scope into a valid DslTarget dict.
|
|
194
|
+
|
|
195
|
+
Rules (in priority order):
|
|
196
|
+
1. text:<unique_button> + page scope → label + exact
|
|
197
|
+
2. text:<common_action> + row:N scope → label + css row selector
|
|
198
|
+
3. text:<text> + dialog scope → label (dialog内默认唯一)
|
|
199
|
+
4. placeholder:<text> → placeholder
|
|
200
|
+
5. css:<selector> → css (validated for specificity)
|
|
201
|
+
6. role:<role>:<name> → role + name
|
|
202
|
+
7. near:<text> → near_text
|
|
203
|
+
8. xpath:<expr> → xpath (last resort)
|
|
204
|
+
"""
|
|
205
|
+
target = parse_locator(locator_raw)
|
|
206
|
+
errors: list[str] = []
|
|
207
|
+
|
|
208
|
+
# text: → label
|
|
209
|
+
if "label" in target:
|
|
210
|
+
label_value = target["label"]
|
|
211
|
+
|
|
212
|
+
# Determine scope
|
|
213
|
+
if scope:
|
|
214
|
+
scope_match = SCOPE_RE.match(scope)
|
|
215
|
+
if scope_match:
|
|
216
|
+
# row scope: label + css row selector
|
|
217
|
+
row_n = int(scope_match.group("row"))
|
|
218
|
+
target["css"] = (
|
|
219
|
+
f".el-table__body tr:nth-child({row_n}) "
|
|
220
|
+
f"td:last-child .el-button--small"
|
|
221
|
+
)
|
|
222
|
+
elif scope == "dialog":
|
|
223
|
+
# dialog scope: label within dialog, no extra css needed
|
|
224
|
+
pass
|
|
225
|
+
# other scopes just use label as-is
|
|
226
|
+
|
|
227
|
+
# exact match for page-unique items
|
|
228
|
+
if label_value not in HIGH_RISK_LABELS:
|
|
229
|
+
target["exact"] = True
|
|
230
|
+
|
|
231
|
+
# High-risk label without row scope → warn but don't block
|
|
232
|
+
if label_value in HIGH_RISK_LABELS and not (scope and SCOPE_RE.match(scope)):
|
|
233
|
+
# Still produce valid target, but record warning
|
|
234
|
+
pass
|
|
235
|
+
|
|
236
|
+
# css: → hard hallucination check
|
|
237
|
+
if "css" in target:
|
|
238
|
+
css_value = target["css"]
|
|
239
|
+
_validate_css_not_hallucinated(css_value, step_name)
|
|
240
|
+
|
|
241
|
+
# role: → ensure name is present (already handled by parse_locator)
|
|
242
|
+
|
|
243
|
+
if errors:
|
|
244
|
+
raise ValueError("\n".join(errors))
|
|
245
|
+
|
|
246
|
+
return target
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
# ---------------------------------------------------------------------------
|
|
250
|
+
# Strategy 决策 — 硬约束
|
|
251
|
+
# ---------------------------------------------------------------------------
|
|
252
|
+
|
|
253
|
+
VERIFICATION_ACTIONS = {"verify", "assert_text", "assert_visible", "assert_url_contains", "assert_any_visible"}
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def compile_strategy(action: str) -> dict[str, Any]:
|
|
257
|
+
"""Decide strategy based on action type. Hard rule: verification → ai_only."""
|
|
258
|
+
if action in VERIFICATION_ACTIONS:
|
|
259
|
+
return deepcopy(AI_ONLY_STRATEGY)
|
|
260
|
+
if action == "act":
|
|
261
|
+
return deepcopy(AI_ONLY_STRATEGY)
|
|
262
|
+
return deepcopy(DEFAULT_UI_STRATEGY)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# ---------------------------------------------------------------------------
|
|
266
|
+
# Step 编译
|
|
267
|
+
# ---------------------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
def compile_step(step: dict[str, Any], index: int) -> dict[str, Any]:
|
|
270
|
+
"""Compile one simplified step into strict DSL step."""
|
|
271
|
+
name = str(step.get("name") or f"Step {index}")
|
|
272
|
+
action = str(step.get("action") or "").lower().strip()
|
|
273
|
+
|
|
274
|
+
compiled: dict[str, Any] = {
|
|
275
|
+
"id": f"s{index:02d}",
|
|
276
|
+
"name": name,
|
|
277
|
+
"depends_on": [],
|
|
278
|
+
"timeout_ms": int(step.get("timeout_ms", 30_000)),
|
|
279
|
+
"on_failure": step.get("on_failure", "stop"),
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
# --- Navigate → web/goto ---
|
|
283
|
+
if action == "navigate":
|
|
284
|
+
compiled["driver"] = "web"
|
|
285
|
+
compiled["action"] = "goto"
|
|
286
|
+
compiled["params"] = {"url": step.get("url", "")}
|
|
287
|
+
return compiled
|
|
288
|
+
|
|
289
|
+
# --- Wait ---
|
|
290
|
+
if action == "wait":
|
|
291
|
+
compiled["driver"] = "web"
|
|
292
|
+
compiled["action"] = "wait"
|
|
293
|
+
compiled["params"] = {"wait_for": step.get("wait_for", "load")}
|
|
294
|
+
compiled["timeout_ms"] = int(step.get("timeout_ms", 15_000))
|
|
295
|
+
return compiled
|
|
296
|
+
|
|
297
|
+
# --- Inject token ---
|
|
298
|
+
if action == "inject_token":
|
|
299
|
+
compiled["driver"] = "web"
|
|
300
|
+
compiled["action"] = "evaluate_js"
|
|
301
|
+
compiled["params"] = {"script": step.get("script", "")}
|
|
302
|
+
return compiled
|
|
303
|
+
|
|
304
|
+
# --- API step ---
|
|
305
|
+
if action in {"get", "post", "put", "patch", "delete"}:
|
|
306
|
+
compiled["driver"] = "api"
|
|
307
|
+
compiled["action"] = action
|
|
308
|
+
compiled["params"] = deepcopy(step.get("params", {}))
|
|
309
|
+
compiled["assertions"] = _compile_api_assertions(step.get("assertions", []))
|
|
310
|
+
compiled["extract_vars"] = deepcopy(step.get("extract_vars", {}))
|
|
311
|
+
return compiled
|
|
312
|
+
|
|
313
|
+
# --- UI action steps ---
|
|
314
|
+
if action in {"click", "fill", "select", "hover"}:
|
|
315
|
+
compiled["driver"] = "ui"
|
|
316
|
+
compiled["action"] = action
|
|
317
|
+
locator_raw = step.get("locator", "")
|
|
318
|
+
if locator_raw:
|
|
319
|
+
compiled["target"] = compile_target(
|
|
320
|
+
locator_raw,
|
|
321
|
+
scope=step.get("scope"),
|
|
322
|
+
step_name=name,
|
|
323
|
+
)
|
|
324
|
+
compiled["strategy"] = compile_strategy(action)
|
|
325
|
+
if action == "fill":
|
|
326
|
+
compiled["params"] = {"value": step.get("value", "")}
|
|
327
|
+
_validate_fill_locator(name, locator_raw)
|
|
328
|
+
return compiled
|
|
329
|
+
|
|
330
|
+
# --- UI verification step ---
|
|
331
|
+
if action == "verify":
|
|
332
|
+
compiled["driver"] = "ui"
|
|
333
|
+
compiled["action"] = "act"
|
|
334
|
+
locator_raw = step.get("locator", "")
|
|
335
|
+
if locator_raw:
|
|
336
|
+
compiled["target"] = compile_target(locator_raw, scope=step.get("scope"), step_name=name)
|
|
337
|
+
compiled["strategy"] = deepcopy(AI_ONLY_STRATEGY)
|
|
338
|
+
expect = step.get("expect", "")
|
|
339
|
+
compiled["prompt"] = _build_verify_prompt(name, expect, step)
|
|
340
|
+
compiled["timeout_ms"] = int(step.get("timeout_ms", 15_000))
|
|
341
|
+
return compiled
|
|
342
|
+
|
|
343
|
+
# --- AI act step (with prompt) ---
|
|
344
|
+
if action == "act":
|
|
345
|
+
compiled["driver"] = "ui"
|
|
346
|
+
compiled["action"] = "act"
|
|
347
|
+
locator_raw = step.get("locator", "")
|
|
348
|
+
if locator_raw:
|
|
349
|
+
compiled["target"] = compile_target(locator_raw, scope=step.get("scope"), step_name=name)
|
|
350
|
+
compiled["strategy"] = deepcopy(AI_ONLY_STRATEGY)
|
|
351
|
+
compiled["prompt"] = step.get("prompt", "")
|
|
352
|
+
compiled["timeout_ms"] = int(step.get("timeout_ms", 15_000))
|
|
353
|
+
return compiled
|
|
354
|
+
|
|
355
|
+
# Fallback
|
|
356
|
+
raise ValueError(f"Unknown action '{action}' in step '{name}'")
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def _compile_api_assertions(raw_assertions: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
360
|
+
"""Compile simplified API assertions into strict format."""
|
|
361
|
+
compiled: list[dict[str, Any]] = []
|
|
362
|
+
for a in raw_assertions:
|
|
363
|
+
atype = str(a.get("type", "status_code"))
|
|
364
|
+
compiled.append({
|
|
365
|
+
"type": atype,
|
|
366
|
+
"expected": a.get("expected"),
|
|
367
|
+
"path": a.get("path"),
|
|
368
|
+
})
|
|
369
|
+
return compiled
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _build_verify_prompt(step_name: str, expect: str, step: dict[str, Any]) -> str:
|
|
373
|
+
"""Build AI prompt for verification step."""
|
|
374
|
+
if step.get("prompt"):
|
|
375
|
+
return step["prompt"]
|
|
376
|
+
if expect.startswith("text:"):
|
|
377
|
+
text = expect[5:]
|
|
378
|
+
return f"检查页面是否出现'{text}'的提示消息,确认操作已成功完成"
|
|
379
|
+
if expect == "dialog_visible":
|
|
380
|
+
return "检查对话框是否已打开并显示"
|
|
381
|
+
if expect == "dialog_closed":
|
|
382
|
+
return "检查对话框是否已关闭"
|
|
383
|
+
return f"验证:{expect}"
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
# ---------------------------------------------------------------------------
|
|
387
|
+
# Auth setup 编译
|
|
388
|
+
# ---------------------------------------------------------------------------
|
|
389
|
+
|
|
390
|
+
def compile_auth_steps(auth: dict[str, Any] | None, base_url: str = "${base_url}") -> list[dict[str, Any]]:
|
|
391
|
+
"""Compile auth config into API login + web inject steps."""
|
|
392
|
+
if not auth:
|
|
393
|
+
return []
|
|
394
|
+
|
|
395
|
+
method = str(auth.get("method", "")).lower()
|
|
396
|
+
steps: list[dict[str, Any]] = []
|
|
397
|
+
|
|
398
|
+
if method == "api_login":
|
|
399
|
+
login_url = auth.get("login_url", f"{base_url}/auth/login_by_id")
|
|
400
|
+
login_body = auth.get("login_body", {})
|
|
401
|
+
token_path = auth.get("token_path", "headers.X-Jwt-Token")
|
|
402
|
+
app_id = auth.get("app_id")
|
|
403
|
+
|
|
404
|
+
steps.append({
|
|
405
|
+
"id": "api_auth_login",
|
|
406
|
+
"name": "获取JWT令牌",
|
|
407
|
+
"driver": "api",
|
|
408
|
+
"action": "post",
|
|
409
|
+
"params": {"url": login_url, "json": login_body},
|
|
410
|
+
"assertions": [
|
|
411
|
+
{"type": "status_code", "expected": 200},
|
|
412
|
+
{"type": "json_path_exists", "path": token_path},
|
|
413
|
+
],
|
|
414
|
+
"extract_vars": {"jwt_token": {"path": token_path, "source": "jsonpath"}},
|
|
415
|
+
"depends_on": [],
|
|
416
|
+
"timeout_ms": 15_000,
|
|
417
|
+
"on_failure": "stop",
|
|
418
|
+
})
|
|
419
|
+
|
|
420
|
+
if app_id:
|
|
421
|
+
steps.append({
|
|
422
|
+
"id": "api_app_token",
|
|
423
|
+
"name": "获取应用访问Token",
|
|
424
|
+
"driver": "api",
|
|
425
|
+
"action": "post",
|
|
426
|
+
"params": {
|
|
427
|
+
"url": auth.get("app_token_url", f"{base_url}/admin/app/access"),
|
|
428
|
+
"json": {"id": app_id},
|
|
429
|
+
"headers": {"Authorization": "Bearer ${jwt_token}"},
|
|
430
|
+
},
|
|
431
|
+
"assertions": [{"type": "status_code", "expected": 200}],
|
|
432
|
+
"extract_vars": {"token_url": {"path": "body.data.url", "source": "jsonpath"}},
|
|
433
|
+
"depends_on": ["api_auth_login"],
|
|
434
|
+
"timeout_ms": 15_000,
|
|
435
|
+
"on_failure": "stop",
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
steps.append({
|
|
439
|
+
"id": "web_auth_inject",
|
|
440
|
+
"name": "Token注入",
|
|
441
|
+
"driver": "web",
|
|
442
|
+
"action": "goto",
|
|
443
|
+
"params": {"url": "${token_url}"},
|
|
444
|
+
"depends_on": ["api_app_token"],
|
|
445
|
+
"timeout_ms": 30_000,
|
|
446
|
+
"on_failure": "stop",
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
return steps
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
# ---------------------------------------------------------------------------
|
|
453
|
+
# Main compile entry
|
|
454
|
+
# ---------------------------------------------------------------------------
|
|
455
|
+
|
|
456
|
+
def _check_terminology(steps: list[dict[str, Any]]) -> list[str]:
|
|
457
|
+
"""Check for terminology hallucination in step names vs locator values."""
|
|
458
|
+
# Commonly hallucinated term pairs: (AI-invented, source-code-actual)
|
|
459
|
+
TERM_PAIRS: list[tuple[str, str]] = [
|
|
460
|
+
("搜索", "查询"),
|
|
461
|
+
("搜索", "筛选"),
|
|
462
|
+
("新增", "添加"),
|
|
463
|
+
("编辑", "修改"),
|
|
464
|
+
("删除", "移除"),
|
|
465
|
+
("保存", "提交"),
|
|
466
|
+
("关闭", "取消"),
|
|
467
|
+
]
|
|
468
|
+
warnings: list[str] = []
|
|
469
|
+
for step in steps:
|
|
470
|
+
name = str(step.get("name", ""))
|
|
471
|
+
locator_raw = str(step.get("_locator_raw", ""))
|
|
472
|
+
for ai_term, src_term in TERM_PAIRS:
|
|
473
|
+
if ai_term in name and src_term in locator_raw:
|
|
474
|
+
warnings.append(
|
|
475
|
+
f"Step '{name}' uses '{ai_term}' but locator contains '{src_term}'. "
|
|
476
|
+
f"Verify the button text in source code — use the exact source text."
|
|
477
|
+
)
|
|
478
|
+
return warnings
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def compile_case(case_desc: dict[str, Any]) -> dict[str, Any]:
|
|
482
|
+
"""Compile one simplified case description into strict DSL."""
|
|
483
|
+
name = str(case_desc.get("name") or "Unnamed Case")
|
|
484
|
+
tags = list(case_desc.get("tags") or ["ui-mainflow"])
|
|
485
|
+
variables = dict(case_desc.get("variables") or {})
|
|
486
|
+
base_url = variables.get("base_url", "${base_url}")
|
|
487
|
+
|
|
488
|
+
# Compile auth
|
|
489
|
+
auth_steps = compile_auth_steps(case_desc.get("auth"), base_url)
|
|
490
|
+
|
|
491
|
+
# Compile business steps
|
|
492
|
+
raw_steps = list(case_desc.get("steps") or [])
|
|
493
|
+
business_steps: list[dict[str, Any]] = []
|
|
494
|
+
for i, raw_step in enumerate(raw_steps):
|
|
495
|
+
compiled_step = compile_step(raw_step, i + 1)
|
|
496
|
+
# Preserve original locator string for terminology checking
|
|
497
|
+
compiled_step["_locator_raw"] = raw_step.get("locator", "")
|
|
498
|
+
business_steps.append(compiled_step)
|
|
499
|
+
|
|
500
|
+
# Terminology check (warning, not error)
|
|
501
|
+
term_warnings = _check_terminology(business_steps)
|
|
502
|
+
if term_warnings:
|
|
503
|
+
for w in term_warnings:
|
|
504
|
+
print(f"TERMINOLOGY WARNING: {w}", file=__import__("sys").stderr)
|
|
505
|
+
|
|
506
|
+
# Merge: auth steps first, then business steps
|
|
507
|
+
all_steps = auth_steps + business_steps
|
|
508
|
+
|
|
509
|
+
# Wire dependencies: auth steps are already wired; business steps depend on previous
|
|
510
|
+
first_business_idx = len(auth_steps)
|
|
511
|
+
for i, step in enumerate(all_steps):
|
|
512
|
+
if i >= first_business_idx and i > 0:
|
|
513
|
+
prev_id = all_steps[i - 1]["id"]
|
|
514
|
+
if not step.get("depends_on"):
|
|
515
|
+
step["depends_on"] = [prev_id]
|
|
516
|
+
|
|
517
|
+
dsl = {
|
|
518
|
+
"name": name,
|
|
519
|
+
"description": case_desc.get("description", ""),
|
|
520
|
+
"tags": tags,
|
|
521
|
+
"variables": variables,
|
|
522
|
+
"steps": all_steps,
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return {
|
|
526
|
+
"project_id": int(case_desc.get("project_id", 1)),
|
|
527
|
+
"name": name,
|
|
528
|
+
"description": case_desc.get("description", ""),
|
|
529
|
+
"tags": tags,
|
|
530
|
+
"dsl_json": dsl,
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def compile_all(payload: dict[str, Any]) -> dict[str, Any]:
|
|
535
|
+
"""Compile all cases in the payload."""
|
|
536
|
+
metadata = dict(payload.get("metadata") or {})
|
|
537
|
+
cases = [compile_case(c) for c in payload.get("cases", [])]
|
|
538
|
+
|
|
539
|
+
return {
|
|
540
|
+
"metadata": {
|
|
541
|
+
**metadata,
|
|
542
|
+
"compiled_at": datetime.now(timezone.utc).isoformat(),
|
|
543
|
+
"compiled_by": "compile_dsl.py (hard-constraint layer)",
|
|
544
|
+
},
|
|
545
|
+
"cases": cases,
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
# ---------------------------------------------------------------------------
|
|
550
|
+
# CLI
|
|
551
|
+
# ---------------------------------------------------------------------------
|
|
552
|
+
|
|
553
|
+
def main() -> int:
|
|
554
|
+
parser = argparse.ArgumentParser(
|
|
555
|
+
description="Compile AI-friendly case descriptions into strict DSL JSON."
|
|
556
|
+
)
|
|
557
|
+
parser.add_argument("input", help="Simplified case descriptions JSON")
|
|
558
|
+
parser.add_argument("--output", required=True, help="Output path for case-import-payloads.json")
|
|
559
|
+
args = parser.parse_args()
|
|
560
|
+
|
|
561
|
+
input_path = Path(args.input)
|
|
562
|
+
output_path = Path(args.output)
|
|
563
|
+
|
|
564
|
+
raw = json.loads(input_path.read_text(encoding="utf-8"))
|
|
565
|
+
|
|
566
|
+
# Accept both array and {cases: [...]} format
|
|
567
|
+
if isinstance(raw, list):
|
|
568
|
+
raw = {"metadata": {}, "cases": raw}
|
|
569
|
+
|
|
570
|
+
try:
|
|
571
|
+
result = compile_all(raw)
|
|
572
|
+
except ValueError as exc:
|
|
573
|
+
print(f"COMPILE ERROR: {exc}", file=__import__("sys").stderr)
|
|
574
|
+
return 1
|
|
575
|
+
|
|
576
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
577
|
+
output_path.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
578
|
+
print(json.dumps({"output": str(output_path), "case_count": len(result["cases"])}, ensure_ascii=False))
|
|
579
|
+
return 0
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
if __name__ == "__main__":
|
|
583
|
+
raise SystemExit(main())
|