@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
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workflow-wayfinder
|
|
3
|
+
description: "Use when a requirement is too large or foggy for one clarification session before baseline. Builds a decision map with destination, frontier, fog, out-of-scope, and small Research/Prototype/Grilling/Task tickets that can be resolved one at a time before entering requirement baseline."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Workflow: Wayfinder(需求探索地图)
|
|
7
|
+
|
|
8
|
+
本技能是需求主链的固定第二步。无论需求表面上是否简单,都先建立探索地图;目标不是写 baseline,而是把目标、已知事实、边界、迷雾和可逐个解决的 ticket 显式化。
|
|
9
|
+
|
|
10
|
+
## Outputs
|
|
11
|
+
|
|
12
|
+
Always create or update:
|
|
13
|
+
|
|
14
|
+
- `.planning/requirements/{功能名}/决策地图.md`
|
|
15
|
+
- `.planning/requirements/{功能名}/决策地图.meta.json`
|
|
16
|
+
- `.planning/requirements/{功能名}/wayfinder/WF-xxx.md` for each ticket
|
|
17
|
+
|
|
18
|
+
`决策地图.meta.json` minimum fields:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"feature_name": "",
|
|
23
|
+
"status": "mapping | tickets_open | ready_for_baseline | blocked",
|
|
24
|
+
"destination": "",
|
|
25
|
+
"open_ticket_count": 0,
|
|
26
|
+
"resolved_ticket_count": 0,
|
|
27
|
+
"frontier_ticket_ids": [],
|
|
28
|
+
"next_ticket_id": "",
|
|
29
|
+
"blocking_reason": "",
|
|
30
|
+
"ready_for_baseline": false
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Each ticket file has YAML front matter:
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
id: WF-001
|
|
38
|
+
status: open # open | claimed | resolved | blocked | out_of_scope
|
|
39
|
+
type: research # research | prototype | grilling | task
|
|
40
|
+
blocked_by: []
|
|
41
|
+
claimed_by:
|
|
42
|
+
claimed_at:
|
|
43
|
+
decision_level: L1
|
|
44
|
+
resolved_at:
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Map Vocabulary
|
|
48
|
+
|
|
49
|
+
| Section | Meaning |
|
|
50
|
+
|---------|---------|
|
|
51
|
+
| Destination | 最终要抵达的业务目标,用一句话描述,不写实现方案 |
|
|
52
|
+
| Known Land | 已确认事实、项目现状、用户已拍板决策 |
|
|
53
|
+
| Frontier | 当前可推进的最外层问题,不依赖其他未解决问题 |
|
|
54
|
+
| Fog | 依赖 Frontier 答案才能定义的问题 |
|
|
55
|
+
| Out of Scope | 明确不做、暂缓、或历史拒绝的范围 |
|
|
56
|
+
|
|
57
|
+
The map is an index, not the source of full ticket truth:
|
|
58
|
+
|
|
59
|
+
- `决策地图.md` keeps Destination, Known Land, Decisions so far, Not yet specified, Out of Scope, and Ticket index.
|
|
60
|
+
- Ticket details, evidence, answers, prototypes, and discussion summaries live in `wayfinder/WF-xxx.md`.
|
|
61
|
+
- Do not duplicate full ticket answers into the map. Add only one-line decision gists with links to the ticket file.
|
|
62
|
+
|
|
63
|
+
## Ticket Types
|
|
64
|
+
|
|
65
|
+
| Type | Use when | Output |
|
|
66
|
+
|------|----------|--------|
|
|
67
|
+
| Research | Need docs, code reading, external API/library confirmation, or primary-source investigation | Cited Markdown finding or code evidence notes |
|
|
68
|
+
| Prototype | Need runnable proof to compare logic, UX, state machine, parsing, or integration feasibility | Throwaway prototype and result summary |
|
|
69
|
+
| Grilling | Need user/business decision; cannot be discovered from code or docs | One-question-at-a-time decision record |
|
|
70
|
+
| Task | Need a small local artifact update that clarifies the map, such as glossary cleanup or source indexing | Updated artifact with completion note |
|
|
71
|
+
|
|
72
|
+
Do not merge `Grilling` and `Task`: a question to the user and a local artifact update have different completion gates.
|
|
73
|
+
|
|
74
|
+
## Phase 1 — Chart The First Map
|
|
75
|
+
|
|
76
|
+
1. Read `.planning/requirements/{功能名}/入口分诊.md`.
|
|
77
|
+
2. Read the raw user input and only the project context needed to understand the uncertainty.
|
|
78
|
+
3. Run the **Source Coherence Gate** below before declaring Fog or creating any Grilling ticket.
|
|
79
|
+
4. Write:
|
|
80
|
+
- Destination
|
|
81
|
+
- Known Land
|
|
82
|
+
- Frontier
|
|
83
|
+
- Fog
|
|
84
|
+
- Out of Scope
|
|
85
|
+
5. If there is no meaningful Fog after charting, mark `ready_for_baseline: true`; do not bypass the mandatory `workflow-domain-modeling` and `requirement-feasibility-review` steps.
|
|
86
|
+
|
|
87
|
+
### Source Coherence Gate
|
|
88
|
+
|
|
89
|
+
Do not treat each paragraph as an isolated requirement. Build one coherent behavior model from the entire source first:
|
|
90
|
+
|
|
91
|
+
1. Classify statements as `background/problem`, `normative rule`, `trigger/process`, `example/mock`, or `exception/history`.
|
|
92
|
+
2. Join statements that describe different stages of the same behavior. A risk stated in the background is not a separate feature when a later process already supplies the confirmation, validation, or recovery mechanism.
|
|
93
|
+
3. Resolve apparent conflicts in this order:
|
|
94
|
+
- confirmed user decision;
|
|
95
|
+
- explicit normative rule (`must`, limit, formula, state/result);
|
|
96
|
+
- explicit trigger/process;
|
|
97
|
+
- scoped exception or historical rule;
|
|
98
|
+
- example, mock data, OCR text, or background motivation.
|
|
99
|
+
4. Prefer an interpretation that preserves all higher-authority statements without inventing another dialog, threshold, state, field, or API.
|
|
100
|
+
5. Record a lower-authority conflicting example as non-normative evidence; do not promote it into a question when the formal rule already determines behavior.
|
|
101
|
+
|
|
102
|
+
A Grilling ticket is allowed only when at least two coherent interpretations remain after this gate and they would change a user-visible outcome, money rule, state transition, permission, data ownership, API contract, or acceptance result. The ticket must list those interpretations and the exact consequence of each. If one interpretation is supported by the full source, choose it confidently and record the reasoning in Known Land or Decisions so far.
|
|
103
|
+
|
|
104
|
+
## Phase 2 — Create Tickets
|
|
105
|
+
|
|
106
|
+
Ticket rules:
|
|
107
|
+
|
|
108
|
+
- Each ticket must fit one agent session.
|
|
109
|
+
- Every ticket has a clear done condition.
|
|
110
|
+
- Every ticket declares dependencies by ticket ID.
|
|
111
|
+
- Frontier tickets have no unresolved dependencies.
|
|
112
|
+
- L1/L2 decisions generally outrank L3/L4 details unless a lower-level fact blocks them.
|
|
113
|
+
- Do not create a Grilling ticket merely because a detail is not repeated in every section. First prove that the Source Coherence Gate cannot resolve it.
|
|
114
|
+
|
|
115
|
+
Ticket template:
|
|
116
|
+
|
|
117
|
+
```markdown
|
|
118
|
+
---
|
|
119
|
+
id: WF-{序号}
|
|
120
|
+
status: open
|
|
121
|
+
type: research | prototype | grilling | task
|
|
122
|
+
blocked_by: []
|
|
123
|
+
claimed_by:
|
|
124
|
+
claimed_at:
|
|
125
|
+
decision_level: L1 | L2 | L3 | L4 | N/A
|
|
126
|
+
resolved_at:
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## WF-{序号}: {ticket title}
|
|
130
|
+
|
|
131
|
+
### Question
|
|
132
|
+
{one thing this ticket must answer}
|
|
133
|
+
|
|
134
|
+
### Done Condition
|
|
135
|
+
{what must be true for this ticket to resolve}
|
|
136
|
+
|
|
137
|
+
### Findings / Answer
|
|
138
|
+
{filled after resolution}
|
|
139
|
+
|
|
140
|
+
### Map Update
|
|
141
|
+
{Known Land / Frontier / Fog / Out of Scope changes}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Phase 3 — Resolve One Frontier Ticket
|
|
145
|
+
|
|
146
|
+
Pick exactly one ticket:
|
|
147
|
+
|
|
148
|
+
1. Prefer unresolved Frontier tickets.
|
|
149
|
+
2. Within Frontier, use L1 → L2 → L3 → L4.
|
|
150
|
+
3. Claim it before work by setting `status=claimed`, `claimed_by={agent/session identifier}`, and `claimed_at={ISO time}` in the ticket front matter.
|
|
151
|
+
4. Re-read the map and ticket immediately after claiming. If another claim appeared first, stop and choose another Frontier ticket.
|
|
152
|
+
5. If a ticket is `Grilling`, ask exactly one question and wait for the answer.
|
|
153
|
+
6. If a ticket is `Research`, use high-trust sources first; for code facts, cite file paths and line numbers.
|
|
154
|
+
7. If a ticket is `Prototype`, keep it throwaway and record only the decision result.
|
|
155
|
+
8. If a ticket is `Task`, update only the artifact named in the ticket.
|
|
156
|
+
|
|
157
|
+
Before asking a Grilling question, re-read the raw source around every cited statement and write into the ticket:
|
|
158
|
+
|
|
159
|
+
- the two or more surviving interpretations;
|
|
160
|
+
- why background + formal rules + process cannot reconcile them;
|
|
161
|
+
- the different acceptance or data consequences.
|
|
162
|
+
|
|
163
|
+
If this evidence cannot be written, reclassify the ticket as Research/Task or resolve it from the source; do not ask the user.
|
|
164
|
+
|
|
165
|
+
After resolution, update the map immediately. Do not solve hidden Fog tickets before their dependencies clear.
|
|
166
|
+
|
|
167
|
+
## Phase 4 — Exit Criteria
|
|
168
|
+
|
|
169
|
+
Exit this step to `workflow-domain-modeling` only when:
|
|
170
|
+
|
|
171
|
+
- Destination is still valid.
|
|
172
|
+
- All L1/L2 blocking decisions are resolved.
|
|
173
|
+
- Remaining L3/L4 questions are either resolved or explicitly non-blocking.
|
|
174
|
+
- Out-of-scope items are recorded.
|
|
175
|
+
- There is enough Known Land to decompose FP/BR/BC without inventing business rules.
|
|
176
|
+
|
|
177
|
+
If open tickets remain but are not needed for the active slice, record them as deferred exploration with re-entry conditions.
|
|
178
|
+
|
|
179
|
+
When exit criteria pass:
|
|
180
|
+
|
|
181
|
+
1. Set `决策地图.meta.json.status = ready_for_baseline`.
|
|
182
|
+
2. Set `决策地图.meta.json.ready_for_baseline = true`.
|
|
183
|
+
3. Let the orchestrator advance to `workflow-domain-modeling`. Do not update the triage route and do not enter baseline directly.
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { formatDoctor, runHostDoctor } from "./doctor.ts";
|
|
6
|
+
import { initializeProject, loadProjectContext, resolveProjectRoot } from "./project-context.ts";
|
|
7
|
+
import { aggregateUsage, renderUsage } from "./usage-stats.ts";
|
|
8
|
+
import { projectSessionDir } from "./paths.ts";
|
|
9
|
+
|
|
10
|
+
function usage(): string {
|
|
11
|
+
return [
|
|
12
|
+
"Usage:",
|
|
13
|
+
" devflow-pi init [project]",
|
|
14
|
+
" devflow-pi doctor [project]",
|
|
15
|
+
" devflow-pi usage [project]",
|
|
16
|
+
" devflow-pi [project] [--resume [session-id]] [--prompt <text>] [--mcp]",
|
|
17
|
+
].join("\n");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function option(args: string[], name: string): string | undefined {
|
|
21
|
+
const index = args.indexOf(name);
|
|
22
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function optionalOption(args: string[], name: string): boolean | string | undefined {
|
|
26
|
+
const index = args.indexOf(name);
|
|
27
|
+
if (index < 0) return undefined;
|
|
28
|
+
const value = args[index + 1];
|
|
29
|
+
return value && !value.startsWith("-") ? value : true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function main(): Promise<void> {
|
|
33
|
+
const args = process.argv.slice(2);
|
|
34
|
+
const command = args[0];
|
|
35
|
+
if (command === "--help" || command === "-h" || command === "help") {
|
|
36
|
+
console.log(usage());
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (command === "init") {
|
|
40
|
+
const requested = args[1] ? path.resolve(args[1]) : process.cwd();
|
|
41
|
+
const root = await resolveProjectRoot(requested, { explicit: requested, allowUninitialized: true });
|
|
42
|
+
const { context, created } = await initializeProject(root);
|
|
43
|
+
console.log(`${created ? "Initialized" : "Already initialized"}: ${context.root}`);
|
|
44
|
+
console.log(`projectId: ${context.manifest.projectId}`);
|
|
45
|
+
console.log(`planning: ${context.planningRoot}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const positionalProject = command && !["doctor", "usage"].includes(command) && !command.startsWith("-") ? command : undefined;
|
|
49
|
+
const requestedProject = ["doctor", "usage"].includes(command || "") ? args[1] : positionalProject;
|
|
50
|
+
const root = await resolveProjectRoot(process.cwd(), { explicit: requestedProject });
|
|
51
|
+
const project = await loadProjectContext(root);
|
|
52
|
+
if (command === "doctor") {
|
|
53
|
+
const report = await runHostDoctor(project);
|
|
54
|
+
console.log(formatDoctor(report));
|
|
55
|
+
if (!report.ok) process.exitCode = 1;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (command === "usage") {
|
|
59
|
+
console.log(renderUsage(await aggregateUsage(projectSessionDir(project.manifest.projectId))));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const originalOmpAgentDir = process.env.PI_CODING_AGENT_DIR;
|
|
63
|
+
const { runInteractiveRuntime } = await import("./runtime.ts");
|
|
64
|
+
await runInteractiveRuntime(project, {
|
|
65
|
+
initialPrompt: option(args, "--prompt"),
|
|
66
|
+
originalOmpAgentDir,
|
|
67
|
+
resume: optionalOption(args, "--resume"),
|
|
68
|
+
enableMCP: args.includes("--mcp"),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
main().catch(error => {
|
|
73
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
74
|
+
console.error(`Devflow Pi failed: ${message}`);
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
});
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const PRODUCT_NAME = "Devflow Pi";
|
|
2
|
+
export const PRODUCT_VERSION = "0.1.0";
|
|
3
|
+
export const PROJECT_SCHEMA_VERSION = 1;
|
|
4
|
+
export const OMP_RUNTIME_VERSION = "17.2.7";
|
|
5
|
+
|
|
6
|
+
export const DEVFLOW_PLUGIN_NAMES = [
|
|
7
|
+
"devflow-core",
|
|
8
|
+
"devflow-intel",
|
|
9
|
+
"devflow-requirements",
|
|
10
|
+
"devflow-design",
|
|
11
|
+
"devflow-implementation",
|
|
12
|
+
"devflow-quality",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
export const REQUIRED_MODEL_ROLES = ["@smol", "@task", "@slow"] as const;
|
package/src/doctor.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { access, lstat, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { DEVFLOW_PLUGIN_NAMES, OMP_RUNTIME_VERSION, PRODUCT_VERSION } from "./constants.ts";
|
|
5
|
+
import { devflowAgentDir, devflowHome, ompSdkManifestPath, pluginRoots, projectSessionDir } from "./paths.ts";
|
|
6
|
+
import type { ProjectContext } from "./project-context.ts";
|
|
7
|
+
|
|
8
|
+
export interface DoctorCheck {
|
|
9
|
+
id: string;
|
|
10
|
+
ok: boolean;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface HostDoctorReport {
|
|
15
|
+
ok: boolean;
|
|
16
|
+
checks: DoctorCheck[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function exists(target: string): Promise<boolean> {
|
|
20
|
+
try {
|
|
21
|
+
await access(target);
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function bunVersionOk(version: string): boolean {
|
|
29
|
+
const [major = 0, minor = 0, patch = 0] = version.split(".").map(Number);
|
|
30
|
+
return major > 1 || (major === 1 && (minor > 3 || (minor === 3 && patch >= 14)));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function runHostDoctor(project: ProjectContext): Promise<HostDoctorReport> {
|
|
34
|
+
const checks: DoctorCheck[] = [];
|
|
35
|
+
const add = (id: string, ok: boolean, message: string) => checks.push({ id, ok, message });
|
|
36
|
+
add("branch-runtime", true, `devflow-pi ${PRODUCT_VERSION}; OMP SDK ${OMP_RUNTIME_VERSION}`);
|
|
37
|
+
add("bun", bunVersionOk(Bun.version), bunVersionOk(Bun.version) ? Bun.version : `${Bun.version}; requires >=1.3.14`);
|
|
38
|
+
add("project-root", path.isAbsolute(project.root), project.root);
|
|
39
|
+
add("project-id", Boolean(project.manifest.projectId), project.manifest.projectId);
|
|
40
|
+
const planningStat = await lstat(project.planningRoot).catch(() => null);
|
|
41
|
+
add("planning-root", Boolean(planningStat?.isDirectory() && !planningStat.isSymbolicLink()), project.planningRoot);
|
|
42
|
+
const roots = pluginRoots();
|
|
43
|
+
for (let index = 0; index < DEVFLOW_PLUGIN_NAMES.length; index += 1) {
|
|
44
|
+
const plugin = DEVFLOW_PLUGIN_NAMES[index];
|
|
45
|
+
const root = roots[index];
|
|
46
|
+
add(`plugin:${plugin}`, await exists(path.join(root, "skills")), root);
|
|
47
|
+
}
|
|
48
|
+
const sdkManifest = ompSdkManifestPath();
|
|
49
|
+
let installedVersion = "missing";
|
|
50
|
+
if (await exists(sdkManifest)) {
|
|
51
|
+
const parsed = JSON.parse(await readFile(sdkManifest, "utf8")) as { version?: string };
|
|
52
|
+
installedVersion = parsed.version || "unknown";
|
|
53
|
+
}
|
|
54
|
+
add("omp-sdk", installedVersion === OMP_RUNTIME_VERSION, installedVersion);
|
|
55
|
+
add("agent-dir", true, devflowAgentDir());
|
|
56
|
+
add("session-isolation", true, projectSessionDir(project.manifest.projectId));
|
|
57
|
+
add("devflow-home", true, devflowHome());
|
|
58
|
+
return { ok: checks.every(check => check.ok), checks };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function formatDoctor(report: HostDoctorReport): string {
|
|
62
|
+
return [
|
|
63
|
+
`Devflow Pi doctor: ${report.ok ? "PASS" : "FAIL"}`,
|
|
64
|
+
...report.checks.map(check => ` [${check.ok ? "PASS" : "FAIL"}] ${check.id}: ${check.message}`),
|
|
65
|
+
].join("\n");
|
|
66
|
+
}
|
package/src/paths.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { DEVFLOW_PLUGIN_NAMES } from "./constants.ts";
|
|
6
|
+
|
|
7
|
+
export function packageRoot(): string {
|
|
8
|
+
return path.resolve(import.meta.dir, "..");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function devflowHome(): string {
|
|
12
|
+
return path.resolve(process.env.DEVFLOW_HOME || path.join(os.homedir(), ".devflow-pi"));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function devflowAgentDir(): string {
|
|
16
|
+
return path.join(devflowHome(), "agent");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function sharedOmpAgentDir(originalAgentDir?: string): string {
|
|
20
|
+
return path.resolve(
|
|
21
|
+
process.env.DEVFLOW_SHARED_OMP_AGENT_DIR ||
|
|
22
|
+
originalAgentDir ||
|
|
23
|
+
path.join(os.homedir(), ".omp", "agent"),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function pluginRoots(root = packageRoot()): string[] {
|
|
28
|
+
return DEVFLOW_PLUGIN_NAMES.map(name => path.join(root, "plugins", name));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function projectSessionDir(projectId: string): string {
|
|
32
|
+
return path.join(devflowHome(), "sessions", projectId);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function ompSdkManifestPath(): string {
|
|
36
|
+
return fileURLToPath(import.meta.resolve("@oh-my-pi/pi-coding-agent/package.json"));
|
|
37
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
import { aggregateUsage, renderUsage } from "./usage-stats.ts";
|
|
4
|
+
|
|
5
|
+
export interface PlatformExtensionOptions {
|
|
6
|
+
sessionRoot: string;
|
|
7
|
+
projectId: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function createPlatformExtension(options: PlatformExtensionOptions) {
|
|
11
|
+
return function devflowPlatform(pi: ExtensionAPI) {
|
|
12
|
+
const { z } = pi.zod;
|
|
13
|
+
pi.setLabel("Devflow Platform Runtime");
|
|
14
|
+
pi.registerTool({
|
|
15
|
+
name: "devflow_usage_stats",
|
|
16
|
+
label: "Devflow Usage Stats",
|
|
17
|
+
description: "Read token and cost totals from this Devflow project's isolated session store.",
|
|
18
|
+
parameters: z.object({
|
|
19
|
+
from: z.string().optional(),
|
|
20
|
+
to: z.string().optional(),
|
|
21
|
+
format: z.enum(["table", "json"]).default("table"),
|
|
22
|
+
}),
|
|
23
|
+
approval: "read",
|
|
24
|
+
async execute(_id, params) {
|
|
25
|
+
const input = params as { from?: string; to?: string; format: "table" | "json" };
|
|
26
|
+
const totals = await aggregateUsage(options.sessionRoot, { from: input.from, to: input.to });
|
|
27
|
+
return {
|
|
28
|
+
content: [
|
|
29
|
+
{
|
|
30
|
+
type: "text" as const,
|
|
31
|
+
text: input.format === "json" ? JSON.stringify({ projectId: options.projectId, ...totals }) : renderUsage(totals),
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
details: { projectId: options.projectId, totals },
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
pi.registerCommand("devflow-usage", {
|
|
39
|
+
description: "Show project-scoped Devflow token and cost totals",
|
|
40
|
+
handler: async (_args, ctx) => {
|
|
41
|
+
const totals = await aggregateUsage(options.sessionRoot);
|
|
42
|
+
ctx.ui.notify(renderUsage(totals), "info");
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { access, lstat, mkdir, readFile, realpath, rename, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { PROJECT_SCHEMA_VERSION } from "./constants.ts";
|
|
6
|
+
|
|
7
|
+
export interface ProjectManifest {
|
|
8
|
+
schemaVersion: number;
|
|
9
|
+
projectId: string;
|
|
10
|
+
name: string;
|
|
11
|
+
createdAt: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ProjectContext {
|
|
15
|
+
root: string;
|
|
16
|
+
manifestPath: string;
|
|
17
|
+
manifest: ProjectManifest;
|
|
18
|
+
planningRoot: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const PROJECT_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
22
|
+
const CONTROL_BOUNDARY_PATHS = [
|
|
23
|
+
".planning/STATE.md",
|
|
24
|
+
".planning/.devflow.lock",
|
|
25
|
+
".planning/workflows",
|
|
26
|
+
".planning/artifacts",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
async function exists(target: string): Promise<boolean> {
|
|
30
|
+
try {
|
|
31
|
+
await access(target);
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function canonicalDirectory(candidate: string): Promise<string> {
|
|
39
|
+
const resolved = path.resolve(candidate);
|
|
40
|
+
const stat = await lstat(resolved).catch(() => null);
|
|
41
|
+
if (!stat?.isDirectory()) throw new Error(`Project directory does not exist: ${resolved}`);
|
|
42
|
+
return realpath(resolved);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function nearestManifest(start: string): Promise<string | null> {
|
|
46
|
+
let current = await canonicalDirectory(start);
|
|
47
|
+
while (true) {
|
|
48
|
+
const manifest = path.join(current, ".devflow", "project.json");
|
|
49
|
+
if (await exists(manifest)) return current;
|
|
50
|
+
const parent = path.dirname(current);
|
|
51
|
+
if (parent === current) return null;
|
|
52
|
+
current = parent;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function gitRoot(start: string): Promise<string | null> {
|
|
57
|
+
const processHandle = Bun.spawn(["git", "-C", start, "rev-parse", "--show-toplevel"], {
|
|
58
|
+
stdout: "pipe",
|
|
59
|
+
stderr: "ignore",
|
|
60
|
+
});
|
|
61
|
+
if ((await processHandle.exited) !== 0) return null;
|
|
62
|
+
const output = (await new Response(processHandle.stdout).text()).trim();
|
|
63
|
+
return output ? canonicalDirectory(output) : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function resolveProjectRoot(
|
|
67
|
+
start = process.cwd(),
|
|
68
|
+
options: { explicit?: string; allowUninitialized?: boolean } = {},
|
|
69
|
+
): Promise<string> {
|
|
70
|
+
if (options.explicit) return canonicalDirectory(options.explicit);
|
|
71
|
+
const manifestRoot = await nearestManifest(start);
|
|
72
|
+
if (manifestRoot) return manifestRoot;
|
|
73
|
+
const repositoryRoot = await gitRoot(await canonicalDirectory(start));
|
|
74
|
+
if (repositoryRoot) return repositoryRoot;
|
|
75
|
+
if (options.allowUninitialized) return canonicalDirectory(start);
|
|
76
|
+
throw new Error("No Devflow project found. Run `devflow-pi init` from the project root first.");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function validateManifest(value: unknown, manifestPath: string): ProjectManifest {
|
|
80
|
+
if (!value || typeof value !== "object") throw new Error(`Invalid project manifest: ${manifestPath}`);
|
|
81
|
+
const record = value as Record<string, unknown>;
|
|
82
|
+
if (record.schemaVersion !== PROJECT_SCHEMA_VERSION) {
|
|
83
|
+
throw new Error(`Unsupported project schema in ${manifestPath}: ${String(record.schemaVersion)}`);
|
|
84
|
+
}
|
|
85
|
+
if (typeof record.projectId !== "string" || !PROJECT_ID_PATTERN.test(record.projectId)) {
|
|
86
|
+
throw new Error(`Invalid projectId in ${manifestPath}`);
|
|
87
|
+
}
|
|
88
|
+
if (typeof record.name !== "string" || !record.name || typeof record.createdAt !== "string") {
|
|
89
|
+
throw new Error(`Incomplete project manifest: ${manifestPath}`);
|
|
90
|
+
}
|
|
91
|
+
return record as unknown as ProjectManifest;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function assertPlanningBoundary(root: string): Promise<string> {
|
|
95
|
+
const canonicalRoot = await canonicalDirectory(root);
|
|
96
|
+
const planningRoot = path.join(canonicalRoot, ".planning");
|
|
97
|
+
const stat = await lstat(planningRoot).catch(() => null);
|
|
98
|
+
if (stat?.isSymbolicLink()) throw new Error(`Refusing symlinked planning root: ${planningRoot}`);
|
|
99
|
+
if (stat && !stat.isDirectory()) throw new Error(`Planning root is not a directory: ${planningRoot}`);
|
|
100
|
+
if (stat) {
|
|
101
|
+
const canonicalPlanning = await realpath(planningRoot);
|
|
102
|
+
if (!canonicalPlanning.startsWith(`${canonicalRoot}${path.sep}`)) {
|
|
103
|
+
throw new Error(`Planning root escapes project boundary: ${canonicalPlanning}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const relative of CONTROL_BOUNDARY_PATHS) {
|
|
107
|
+
const target = path.join(canonicalRoot, relative);
|
|
108
|
+
const targetStat = await lstat(target).catch(() => null);
|
|
109
|
+
if (targetStat?.isSymbolicLink()) throw new Error(`Refusing symlinked Devflow control path: ${target}`);
|
|
110
|
+
if (targetStat) {
|
|
111
|
+
const canonicalTarget = await realpath(target);
|
|
112
|
+
if (!canonicalTarget.startsWith(`${canonicalRoot}${path.sep}`)) {
|
|
113
|
+
throw new Error(`Devflow control path escapes project boundary: ${canonicalTarget}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return planningRoot;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function loadProjectContext(root: string): Promise<ProjectContext> {
|
|
121
|
+
const canonicalRoot = await canonicalDirectory(root);
|
|
122
|
+
const manifestPath = path.join(canonicalRoot, ".devflow", "project.json");
|
|
123
|
+
const manifest = validateManifest(JSON.parse(await readFile(manifestPath, "utf8")), manifestPath);
|
|
124
|
+
const planningRoot = await assertPlanningBoundary(canonicalRoot);
|
|
125
|
+
return { root: canonicalRoot, manifestPath, manifest, planningRoot };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function atomicWriteJson(target: string, value: unknown): Promise<void> {
|
|
129
|
+
const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
130
|
+
await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
|
|
131
|
+
await rename(temporary, target);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function initializeProject(root: string): Promise<{ context: ProjectContext; created: boolean }> {
|
|
135
|
+
const canonicalRoot = await canonicalDirectory(root);
|
|
136
|
+
const devflowDir = path.join(canonicalRoot, ".devflow");
|
|
137
|
+
const planningRoot = path.join(canonicalRoot, ".planning");
|
|
138
|
+
await assertPlanningBoundary(canonicalRoot);
|
|
139
|
+
await mkdir(devflowDir, { recursive: true });
|
|
140
|
+
await mkdir(planningRoot, { recursive: true });
|
|
141
|
+
const manifestPath = path.join(devflowDir, "project.json");
|
|
142
|
+
if (await exists(manifestPath)) {
|
|
143
|
+
return { context: await loadProjectContext(canonicalRoot), created: false };
|
|
144
|
+
}
|
|
145
|
+
const manifest: ProjectManifest = {
|
|
146
|
+
schemaVersion: PROJECT_SCHEMA_VERSION,
|
|
147
|
+
projectId: randomUUID(),
|
|
148
|
+
name: path.basename(canonicalRoot),
|
|
149
|
+
createdAt: new Date().toISOString(),
|
|
150
|
+
};
|
|
151
|
+
await atomicWriteJson(manifestPath, manifest);
|
|
152
|
+
return {
|
|
153
|
+
context: { root: canonicalRoot, manifestPath, manifest, planningRoot },
|
|
154
|
+
created: true,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function assertContainedPath(root: string, target: string, label = "path"): string {
|
|
159
|
+
const canonicalRoot = path.resolve(root);
|
|
160
|
+
const resolved = path.resolve(canonicalRoot, target);
|
|
161
|
+
if (resolved !== canonicalRoot && !resolved.startsWith(`${canonicalRoot}${path.sep}`)) {
|
|
162
|
+
throw new Error(`${label} escapes project boundary: ${target}`);
|
|
163
|
+
}
|
|
164
|
+
return resolved;
|
|
165
|
+
}
|