@guanlin4924/devflow-pi 0.1.93 → 0.1.94
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 +6 -6
- package/package.json +1 -1
- package/plugins/devflow-core/extensions/devflow-core.ts +87 -33
- package/plugins/devflow-core/runtime/hooks/context_sync_gate.py +3 -3
- package/plugins/devflow-core/runtime/hooks/hook_common.py +65 -11
- package/plugins/devflow-core/runtime/hooks/start.py +13 -13
- package/plugins/devflow-core/skills/wireframe-proposal/SKILL.md +2 -2
- package/plugins/devflow-core/skills/wireframe-validate/SKILL.md +4 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +19 -19
- package/plugins/devflow-core/skills/workflow-orchestrator/agents/openai.yaml +2 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/acceptance_test_runner.py +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/advance_state.rb +9 -9
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/api_test_matrix.py +2 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_assessment.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_brief.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_feasibility.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_invalidation.rb +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_commit.rb +4 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_validate.py +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_source_resolver.rb +4 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/checker_adapter.rb +7 -7
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/control_io.rb +51 -7
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/cross_track_consistency_check.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +6 -6
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_runtime.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_state.rb +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_workflow.rb +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/inspect_state.rb +13 -13
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/machine_evidence.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/manage_lessons.rb +7 -7
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_recover.rb +2 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_run.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/phase_ownership.rb +6 -6
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/repair_control_plane.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_coverage.rb +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/review_preflight.py +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/review_run.py +18 -18
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/runtime_context_collector.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/session_rollover.rb +4 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/static_runtime_context_collector.rb +2 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/sync_artifacts.rb +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/tdd_evidence.py +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/test_command_policy.py +2 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/test_task_binding_check.py +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/update_triage_route.rb +3 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +6 -6
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_commit.rb +5 -5
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_validate.py +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/acceptance-remediation.yaml +12 -12
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +205 -205
- package/plugins/devflow-core/skills/workflow-pipeline/SKILL.md +3 -3
- package/plugins/devflow-core/skills/workflow-pipeline/references/pipeline-flow.md +10 -10
- package/plugins/devflow-core/skills/workflow-project-context/SKILL.md +14 -14
- package/plugins/devflow-core/skills/workflow-project-context/assets/agents-md-template.md +7 -7
- package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-backend-spec-template.md +4 -4
- package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-frontend-spec-template.md +4 -4
- package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-navigation-template.md +1 -1
- package/plugins/devflow-core/skills/workflow-project-context/assets/context-domain-index-template.md +4 -4
- package/plugins/devflow-core/skills/workflow-project-context/assets/context-spec-index-template.md +10 -10
- package/plugins/devflow-core/skills/workflow-project-context/assets/execution-contract-template.md +2 -2
- package/plugins/devflow-core/skills/workflow-project-context/assets/navigation-index-template.md +18 -18
- package/plugins/devflow-core/skills/workflow-project-context/assets/project-initialization-template.md +32 -32
- package/plugins/devflow-core/skills/workflow-project-context/assets/rules-index-template.md +30 -30
- package/plugins/devflow-core/skills/workflow-project-context/assets/state-template.md +7 -7
- package/plugins/devflow-core/skills/workflow-project-context/references/navigation-guide.md +37 -37
- package/plugins/devflow-core/skills/workflow-project-context/scripts/validate_initialization.py +16 -16
- package/plugins/devflow-design/skills/workflow-api-call-guide/SKILL.md +4 -4
- package/plugins/devflow-design/skills/workflow-api-contract/SKILL.md +8 -8
- package/plugins/devflow-design/skills/workflow-api-supplement/SKILL.md +5 -5
- package/plugins/devflow-design/skills/workflow-architecture-feasibility/SKILL.md +1 -1
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/SKILL.md +39 -39
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/assets/backend-spec-template.md +10 -10
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/affected-module-exploration.md +2 -2
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/code-explore-guide.md +1 -1
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/project-rule-contract.md +20 -20
- package/plugins/devflow-design/skills/workflow-design-rule-pack/SKILL.md +8 -8
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/index.md +3 -3
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/accessibility.md +2 -2
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/component-library.md +2 -2
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/design-systems.md +1 -1
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/responsive-grid.md +2 -2
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/stack-guidelines.md +6 -6
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/ux-patterns.md +2 -2
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tokens/project-tokens.md +1 -1
- package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/SKILL.md +12 -12
- package/plugins/devflow-design/skills/workflow-design-rule-pack/scripts/install.py +2 -2
- package/plugins/devflow-design/skills/workflow-feature-breakdown/SKILL.md +30 -30
- package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/output-template.md +2 -2
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/SKILL.md +41 -41
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/assets/frontend-spec-template.md +15 -15
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/affected-module-exploration.md +2 -2
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/project-rule-contract.md +25 -25
- package/plugins/devflow-design/skills/workflow-integration-verify/SKILL.md +13 -13
- package/plugins/devflow-design/skills/workflow-ui-spec/SKILL.md +23 -23
- package/plugins/devflow-design/skills/workflow-ui-spec/assets/ui-spec-template.md +3 -3
- package/plugins/devflow-implementation/agents/implementation-worker.md +2 -2
- package/plugins/devflow-implementation/skills/workflow-bug-fix/SKILL.md +9 -9
- package/plugins/devflow-implementation/skills/workflow-bug-fix/references/lesson-format.md +2 -2
- package/plugins/devflow-implementation/skills/workflow-bug-fix/references/root-cause-ladder.md +1 -1
- package/plugins/devflow-implementation/skills/workflow-change-plan/SKILL.md +17 -17
- package/plugins/devflow-implementation/skills/workflow-change-plan/references/code-conventions.md +1 -1
- package/plugins/devflow-implementation/skills/workflow-change-plan/references/output-template.md +3 -3
- package/plugins/devflow-implementation/skills/workflow-change-plan/references/project-rule-contract.md +16 -16
- package/plugins/devflow-implementation/skills/workflow-execute-phase/SKILL.md +38 -38
- package/plugins/devflow-implementation/skills/workflow-execute-phase/references/execute-phase.md +1 -1
- package/plugins/devflow-intel/agents/project-scout.md +1 -1
- package/plugins/devflow-intel/extensions/devflow-intel.ts +20 -19
- package/plugins/devflow-intel/skills/code-index-query/SKILL.md +6 -6
- package/plugins/devflow-intel/skills/workflow-code-index/SKILL.md +12 -12
- package/plugins/devflow-intel/skills/workflow-code-index/references/discovery.md +2 -2
- package/plugins/devflow-intel/skills/workflow-code-index/references/query-guide.md +1 -1
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/code_index.py +8 -8
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/discovery.py +2 -2
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/scanner.py +4 -1
- package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/project_binding_check.py +4 -4
- package/plugins/devflow-intel/skills/workflow-context-sync/SKILL.md +14 -14
- package/plugins/devflow-intel/skills/workflow-context-sync/scripts/context_sync_cursor.py +2 -2
- package/plugins/devflow-intel/skills/workflow-update-navigation/SKILL.md +26 -26
- package/plugins/devflow-intel/skills/workflow-update-navigation/references/incremental-update-guide.md +2 -2
- package/plugins/devflow-quality/skills/code-review/SKILL.md +12 -12
- package/plugins/devflow-quality/skills/code-review/references/spec-checklist.md +1 -1
- package/plugins/devflow-quality/skills/workflow-acceptance-test-design/SKILL.md +8 -8
- package/plugins/devflow-quality/skills/workflow-gen-test-guide/SKILL.md +6 -6
- package/plugins/devflow-quality/skills/workflow-gen-test-guide/legacy/SKILL.md +14 -14
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/SKILL.md +23 -23
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/codebase-discovery.md +9 -9
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/project-dsl-contract.md +1 -1
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/compile_dsl.py +2 -2
- package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/discover_codebases.py +2 -2
- package/plugins/devflow-quality/skills/workflow-release-closeout/SKILL.md +15 -15
- package/plugins/devflow-quality/skills/workflow-run-acceptance-tests/SKILL.md +5 -5
- package/plugins/devflow-requirements/skills/requirement-feasibility-review/SKILL.md +1 -1
- package/plugins/devflow-requirements/skills/workflow-demand-clarify/SKILL.md +10 -10
- package/plugins/devflow-requirements/skills/workflow-domain-modeling/SKILL.md +25 -25
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/SKILL.md +49 -49
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/assets/baseline-template.md +22 -22
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/antipatterns.md +3 -3
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/challenge-review-guide.md +3 -3
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/project-context-guide.md +11 -11
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/questioning.md +1 -1
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/requirement-review-checklist.md +4 -4
- package/plugins/devflow-requirements/skills/workflow-requirement-triage/SKILL.md +16 -16
- package/plugins/devflow-requirements/skills/workflow-wayfinder/SKILL.md +6 -6
- package/src/cli.ts +15 -0
- package/src/constants.ts +1 -1
- package/src/initialization-store.ts +3 -2
- package/src/project-context.ts +36 -14
- package/src/project-documents.ts +61 -61
- package/src/project-initializer.ts +9 -8
- package/src/project-layout.ts +217 -0
- package/src/runtime.ts +3 -3
- package/templates/commands/start.md +4 -4
- package/templates/omp/AGENTS.md +1 -1
- package/templates/omp/hook-config.json +7 -7
- package/templates/omp/index-policy.json +8 -8
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ devflow-pi
|
|
|
31
31
|
/usage-stats
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
`/devflow project-init` 会先读取当前项目已有的 `.planning/PROJECT.md` 与 `.planning/navigation/INDEX.md`:仅回填仍存在于项目根目录内的已登记前后端路径,删除或越界的历史路径会被忽略。随后可选择复用已有边界、复用边界并重建索引,或手动覆盖。初始化模式使用选择器提供“只读规划(默认)”和“写入初始化”;选择后者并确认,只会创建 Devflow 的 `.planning/` 与 `.context/` 初始化产物,不会修改业务源码、依赖或项目配置。
|
|
34
|
+
`/devflow project-init` 会先读取当前项目已有的 `.devflow/planning/PROJECT.md` 与 `.devflow/planning/navigation/INDEX.md`:仅回填仍存在于项目根目录内的已登记前后端路径,删除或越界的历史路径会被忽略。随后可选择复用已有边界、复用边界并重建索引,或手动覆盖。初始化模式使用选择器提供“只读规划(默认)”和“写入初始化”;选择后者并确认,只会创建 Devflow 的 `.devflow/planning/` 与 `.devflow/context/` 初始化产物,不会修改业务源码、依赖或项目配置。
|
|
35
35
|
|
|
36
36
|
用量统计默认只读取当前项目的隔离会话:`/usage-stats`。需要明确查看历史 OMP 会话时,使用 `/usage-stats omp`,或加筛选条件:`/usage-stats omp --from 2026-08-01 --to 2026-08-10`。历史统计保留真实会话路径,不再把不同项目的长会话文件合并成 `main`;统计只使用 assistant message 的原始 usage,不虚构工具调用成本分摊。
|
|
37
37
|
|
|
@@ -40,7 +40,7 @@ devflow-pi
|
|
|
40
40
|
专项 Pi 的隔离边界:
|
|
41
41
|
|
|
42
42
|
- 项目身份位于 `<project>/.devflow/project.json`;
|
|
43
|
-
- 流程状态和代码索引仍位于 `<project>/.planning/`;
|
|
43
|
+
- 流程状态和代码索引仍位于 `<project>/.devflow/planning/`;
|
|
44
44
|
- Session 位于 `~/.devflow-pi/sessions/<projectId>/`;
|
|
45
45
|
- 模型认证默认复用 `~/.omp/agent/`,但不复用官方 OMP Session;
|
|
46
46
|
- 只显式加载产品内六个插件根,不自动混入用户或项目的环境 Extension;
|
|
@@ -92,7 +92,7 @@ skills:
|
|
|
92
92
|
|
|
93
93
|
Devflow 会在输入框上方显示“当前 Feature 交付链路”:它基于只读状态快照展示已知步骤、当前节点、完成数、工作流/执行状态、当前允许的操作,以及安全恢复方案。使用 `/devflow control` 打开交互式控制台:它只会在状态检查器允许时提供启动当前步骤、审查并批准推进、安全恢复或取消当前 Feature;每个有写入影响的操作都会重新读取状态并沿用已有确认流程。面板不提供重试或重置当前节点,避免破坏 revision、checkpoint 和证据链。
|
|
94
94
|
|
|
95
|
-
正式节点经用户批准后,Devflow 会把已校验的状态和产物引用固化为内容校验的 handoff,再自动创建干净 Session 执行下一节点。交接期间工作流冻结;只有新 Session 成功认领并消费同一份 handoff 后才解除冻结。Devflow 不使用 OMP 的模型摘要 handoff,因此 `.planning/STATE.md` 仍是唯一流程事实源。
|
|
95
|
+
正式节点经用户批准后,Devflow 会把已校验的状态和产物引用固化为内容校验的 handoff,再自动创建干净 Session 执行下一节点。交接期间工作流冻结;只有新 Session 成功认领并消费同一份 handoff 后才解除冻结。Devflow 不使用 OMP 的模型摘要 handoff,因此 `.devflow/planning/STATE.md` 仍是唯一流程事实源。
|
|
96
96
|
|
|
97
97
|
### 需求验收测试与 TDD 证据链
|
|
98
98
|
|
|
@@ -110,10 +110,10 @@ Devflow 会在输入框上方显示“当前 Feature 交付链路”:它基于
|
|
|
110
110
|
- `tdd_required` 任务必须先由 Core 记录准备快照,再记录 RED(命中预期失败)、同一目标的 GREEN 和回归;缺失或环境失败不算完成。
|
|
111
111
|
- API、跨数据域、边界、迁移、工作负载与架构风险会派生相应的 `api`、`permission`、`boundary`、`migration`、`performance`、`resilience` TC 义务。
|
|
112
112
|
- 每个 TC 还要声明证明策略:`replay` 用组件/页面/API/集成的稳定回放;`causal` 用已定位的首个偏差加 HAR、控制台、DOM 快照、截图或日志等受控文件证明,Core 会记录文件哈希;`observation` 用于尚不可复现、也未能证明根因的问题,只能保持阻塞并带观测计划。后两者都不能被“AI 已复现”或 Markdown 自述放行。
|
|
113
|
-
- 代码完成后,`workflow-run-acceptance-tests` 只能运行绑定到冻结 TC、且已在初始化 `.planning/toolchain.md` 发现的测试运行器命令。任何失败、未执行、人工待执行或环境阻塞用例都会阻止 CP-4。
|
|
113
|
+
- 代码完成后,`workflow-run-acceptance-tests` 只能运行绑定到冻结 TC、且已在初始化 `.devflow/planning/toolchain.md` 发现的测试运行器命令。任何失败、未执行、人工待执行或环境阻塞用例都会阻止 CP-4。
|
|
114
114
|
- 验收不是代码完成后的临时手工检查。它是需求基线在编码前派生并冻结的完整测试集,覆盖主流程、异常、边界、权限、迁移、兼容、韧性和工作负载义务;代码只能补充执行适配信息,不能改变测试意图。
|
|
115
115
|
- 验收失败不会创建另一套人工台账,而是进入 `acceptance-remediation`:诊断 → 修复 → code-index 刷新 → 原冻结 `TC-*` 重跑。
|
|
116
|
-
- CP-4 通过后只进入一个 `workflow-release-closeout` 步骤,统一写入 `.planning/milestones/current-CLOSEOUT.md` 并更新项目状态、导航和版本记录。
|
|
116
|
+
- CP-4 通过后只进入一个 `workflow-release-closeout` 步骤,统一写入 `.devflow/planning/milestones/current-CLOSEOUT.md` 并更新项目状态、导航和版本记录。
|
|
117
117
|
|
|
118
118
|
正常使用不需要记忆底层命令:在 Devflow 工作流中按步骤继续即可。需要排查时,可用 `devflow_acceptance_tests` 查看冻结套件,或由工作流调用 `devflow_tdd_evidence` 与 `devflow_run_acceptance_tests` 生成 Core-owned 证据;不要手写“已测试”来替代结果文件。
|
|
119
119
|
|
|
@@ -146,7 +146,7 @@ npm install -g @guanlin4924/devflow-pi@latest
|
|
|
146
146
|
|
|
147
147
|
## 核心不变量
|
|
148
148
|
|
|
149
|
-
- `.planning/STATE.md`、workflow 和 artifact registry 只允许 Devflow 工具写入。
|
|
149
|
+
- `.devflow/planning/STATE.md`、workflow 和 artifact registry 只允许 Devflow 工具写入。
|
|
150
150
|
- 子 agent 不更新控制面,只返回证据、结构化审查结果或隔离 patch。
|
|
151
151
|
- 索引是加速器,不是真相源;过期时回退到 LSP、搜索、源码和测试。
|
|
152
152
|
- 人工 checkpoint 不得在 headless 模式静默通过。
|
package/package.json
CHANGED
|
@@ -16,6 +16,7 @@ import { runProjectInitialization } from "../../../src/project-initializer.ts";
|
|
|
16
16
|
import { projectControlDatabasePath } from "../../../src/paths.ts";
|
|
17
17
|
import { generateProjectDocuments } from "../../../src/project-documents.ts";
|
|
18
18
|
import { exportInitializationSnapshot, importLegacyInitialization, switchInitializationAuthority } from "../../../src/project-migration.ts";
|
|
19
|
+
import { migrateLegacyProjectLayout } from "../../../src/project-layout.ts";
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
export type DevflowIntent = "idle" | "explain-state" | "project-init" | "requirement-draft" | "workflow-execute";
|
|
@@ -351,7 +352,7 @@ export function discoverExistingProjectInitialization(cwd: string, options: { al
|
|
|
351
352
|
}
|
|
352
353
|
}
|
|
353
354
|
if (!options.allowLegacyImport) return null;
|
|
354
|
-
const sources = [".planning/PROJECT.md", ".planning/navigation/INDEX.md"];
|
|
355
|
+
const sources = [".devflow/planning/PROJECT.md", ".devflow/planning/navigation/INDEX.md"];
|
|
355
356
|
const records: Array<ExistingProjectInitialization> = [];
|
|
356
357
|
for (const sourcePath of sources) {
|
|
357
358
|
const absolute = path.join(cwd, sourcePath);
|
|
@@ -531,13 +532,13 @@ type DoctorContext = {
|
|
|
531
532
|
};
|
|
532
533
|
|
|
533
534
|
const CONTROL_PATHS = [
|
|
534
|
-
".planning/STATE.md",
|
|
535
|
-
".planning/.next-intent",
|
|
536
|
-
".planning/.devflow.lock",
|
|
537
|
-
".planning/artifacts/index.yaml",
|
|
535
|
+
".devflow/planning/STATE.md",
|
|
536
|
+
".devflow/planning/.next-intent",
|
|
537
|
+
".devflow/planning/.devflow.lock",
|
|
538
|
+
".devflow/planning/artifacts/index.yaml",
|
|
538
539
|
];
|
|
539
540
|
|
|
540
|
-
const CONTROL_PREFIXES = [".planning/workflows/", ".planning/contracts/", ".planning/evidence/", ".planning/review-runs/"];
|
|
541
|
+
const CONTROL_PREFIXES = [".devflow/planning/workflows/", ".devflow/planning/contracts/", ".devflow/planning/evidence/", ".devflow/planning/review-runs/"];
|
|
541
542
|
|
|
542
543
|
const REQUIRED_EXTENSION_TOOLS = [
|
|
543
544
|
"devflow_doctor",
|
|
@@ -703,16 +704,63 @@ function assertRuntimeProjectBoundary(cwd: string): void {
|
|
|
703
704
|
if (configured && realpathSync(configured) !== resolvedCwd) {
|
|
704
705
|
throw new Error(`Devflow project-root mismatch: session=${resolvedCwd}, configured=${configured}`);
|
|
705
706
|
}
|
|
706
|
-
const
|
|
707
|
+
const legacyPlanning = path.join(resolvedCwd, ".planning");
|
|
708
|
+
const legacyContext = path.join(resolvedCwd, ".context");
|
|
709
|
+
const planningMarkers = ["STATE.md", "PROJECT.md", ".devflow.lock", "navigation/INDEX.md", "code-index/INDEX.md", "workflows", "contracts", "evidence", "review-runs", "requirements", "features", "handoffs", "artifacts", "phase-ownership.yaml"];
|
|
710
|
+
const contextMarkers = ["spec/index.md", "index.md", "project/index.md", "knowledge/index.md"];
|
|
711
|
+
const legacyMarker = planningMarkers.some(relative => existsSync(path.join(legacyPlanning, relative)))
|
|
712
|
+
|| contextMarkers.some(relative => existsSync(path.join(legacyContext, relative)));
|
|
713
|
+
const activeLegacy = ["STATE.md", "workflows", "contracts", "evidence", "review-runs", "requirements", "features", "handoffs", "artifacts", "phase-ownership.yaml"]
|
|
714
|
+
.find(relative => existsSync(path.join(legacyPlanning, relative)));
|
|
715
|
+
if (legacyMarker) {
|
|
716
|
+
const manifestPath = path.join(resolvedCwd, ".devflow", "project.json");
|
|
717
|
+
let manifestValid = false;
|
|
718
|
+
try {
|
|
719
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as Record<string, unknown>;
|
|
720
|
+
manifestValid = manifest.schemaVersion === 1
|
|
721
|
+
&& typeof manifest.projectId === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(manifest.projectId)
|
|
722
|
+
&& typeof manifest.name === "string" && manifest.name.trim().length > 0
|
|
723
|
+
&& typeof manifest.createdAt === "string" && Number.isFinite(Date.parse(manifest.createdAt))
|
|
724
|
+
&& [undefined, 1, 2].includes(manifest.layoutVersion as undefined | number);
|
|
725
|
+
} catch { /* reported below */ }
|
|
726
|
+
if (!manifestValid) throw new Error("DEVFLOW_LEGACY_LAYOUT_CONFIRMATION_REQUIRED: valid .devflow/project.json is required before legacy layout migration");
|
|
727
|
+
let migrationCompletePrecheck = false;
|
|
728
|
+
try {
|
|
729
|
+
const marker = JSON.parse(readFileSync(path.join(resolvedCwd, ".devflow", "layout-migration.json"), "utf8")) as Record<string, unknown>;
|
|
730
|
+
migrationCompletePrecheck = marker.schemaVersion === 1 && marker.status === "complete" && Array.isArray(marker.migrated);
|
|
731
|
+
} catch { /* reported by the normal conflict checks below */ }
|
|
732
|
+
if (activeLegacy && !migrationCompletePrecheck) {
|
|
733
|
+
throw new Error(`DEVFLOW_ACTIVE_LEGACY_LAYOUT: .planning/${activeLegacy} requires dedicated control-plane migration`);
|
|
734
|
+
}
|
|
735
|
+
const canonicalExists = existsSync(path.join(resolvedCwd, ".devflow", "planning")) || existsSync(path.join(resolvedCwd, ".devflow", "context"));
|
|
736
|
+
const migrationComplete = (() => {
|
|
737
|
+
try {
|
|
738
|
+
const marker = JSON.parse(readFileSync(path.join(resolvedCwd, ".devflow", "layout-migration.json"), "utf8")) as Record<string, unknown>;
|
|
739
|
+
return marker.schemaVersion === 1 && marker.status === "complete" && Array.isArray(marker.migrated);
|
|
740
|
+
} catch { return false; }
|
|
741
|
+
})();
|
|
742
|
+
if (manifestValid && (() => {
|
|
743
|
+
try { return (JSON.parse(readFileSync(manifestPath, "utf8")) as Record<string, unknown>).layoutVersion === 2; } catch { return false; }
|
|
744
|
+
})() && migrationComplete) {
|
|
745
|
+
const planningLegacy = planningMarkers.some(relative => existsSync(path.join(legacyPlanning, relative)));
|
|
746
|
+
const contextLegacy = contextMarkers.some(relative => existsSync(path.join(legacyContext, relative)));
|
|
747
|
+
if ((planningLegacy && !existsSync(path.join(resolvedCwd, ".devflow", "planning"))) || (contextLegacy && !existsSync(path.join(resolvedCwd, ".devflow", "context")))) {
|
|
748
|
+
throw new Error("DEVFLOW_LEGACY_REINTRODUCTION: v2 project will not backfill a missing canonical directory");
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
if (canonicalExists && !migrationComplete) throw new Error("DEVFLOW_LAYOUT_CONFLICT: canonical and legacy layouts coexist without a completed migration marker");
|
|
752
|
+
if (!canonicalExists) throw new Error("DEVFLOW_LAYOUT_MIGRATION_REQUIRED: run project initialization to migrate legacy read-only artifacts");
|
|
753
|
+
}
|
|
754
|
+
const planningRoot = path.join(resolvedCwd, ".devflow/planning");
|
|
707
755
|
if (existsSync(planningRoot) && lstatSync(planningRoot).isSymbolicLink()) {
|
|
708
756
|
throw new Error(`Refusing symlinked Devflow planning root: ${planningRoot}`);
|
|
709
757
|
}
|
|
710
758
|
for (const relative of [
|
|
711
|
-
".planning/STATE.md",
|
|
712
|
-
".planning/.devflow.lock",
|
|
713
|
-
".planning/workflows",
|
|
714
|
-
".planning/artifacts",
|
|
715
|
-
".planning/review-runs",
|
|
759
|
+
".devflow/planning/STATE.md",
|
|
760
|
+
".devflow/planning/.devflow.lock",
|
|
761
|
+
".devflow/planning/workflows",
|
|
762
|
+
".devflow/planning/artifacts",
|
|
763
|
+
".devflow/planning/review-runs",
|
|
716
764
|
]) {
|
|
717
765
|
const target = path.join(resolvedCwd, relative);
|
|
718
766
|
try {
|
|
@@ -744,29 +792,29 @@ function safeControlPath(cwd: string, raw: string | undefined, kind: "state" | "
|
|
|
744
792
|
const relative = normalizedProjectPath(raw, cwd);
|
|
745
793
|
const allowed =
|
|
746
794
|
kind === "state"
|
|
747
|
-
? relative === ".planning/STATE.md"
|
|
748
|
-
: Boolean(relative?.startsWith(".planning/workflows/") && relative.endsWith(".yaml"));
|
|
795
|
+
? relative === ".devflow/planning/STATE.md"
|
|
796
|
+
: Boolean(relative?.startsWith(".devflow/planning/workflows/") && relative.endsWith(".yaml"));
|
|
749
797
|
if (!relative || !allowed) throw new Error(`Invalid ${kind} control path: ${raw}`);
|
|
750
798
|
return relative;
|
|
751
799
|
}
|
|
752
800
|
|
|
753
801
|
function isCanonicalRequirementGraphPath(filePath: string): boolean {
|
|
754
|
-
return /^\.planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:governance|evidence|definitions|behavior-graph|requirement-graph|slices|acceptance|tests|traceability|graph\.meta)\.json$/u.test(filePath)
|
|
755
|
-
|| /^\.planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:context\/[^/\\]+\.json|control-events\.jsonl)$/u.test(filePath);
|
|
802
|
+
return /^\.devflow\/planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:governance|evidence|definitions|behavior-graph|requirement-graph|slices|acceptance|tests|traceability|graph\.meta)\.json$/u.test(filePath)
|
|
803
|
+
|| /^\.devflow\/planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:context\/[^/\\]+\.json|control-events\.jsonl)$/u.test(filePath);
|
|
756
804
|
}
|
|
757
805
|
|
|
758
806
|
function isCoreOwnedGovernancePath(filePath: string): boolean {
|
|
759
807
|
// 规范需求图谱只能由 Core 的 devflow_requirement_graph writer 写入;the
|
|
760
808
|
// same ownership extends to its views, machine evidence, and audit records.
|
|
761
809
|
return isCanonicalRequirementGraphPath(filePath)
|
|
762
|
-
|| /^\.planning\/evidence\/[^/\\\x00-\x1F\x7F]+\/[^/\\]+\.machine\.json$/u.test(filePath)
|
|
763
|
-
|| /^\.planning\/contracts\/[^/\\\x00-\x1F\x7F]+\/(?:EC-[A-Fa-f0-9]+\.json|control-events\.jsonl)$/u.test(filePath)
|
|
764
|
-
|| filePath.startsWith(".planning/review-runs/")
|
|
765
|
-
|| /^\.planning\/phases\/[^/\\\x00-\x1F\x7F]+\/REVIEW\.md$/u.test(filePath);
|
|
810
|
+
|| /^\.devflow\/planning\/evidence\/[^/\\\x00-\x1F\x7F]+\/[^/\\]+\.machine\.json$/u.test(filePath)
|
|
811
|
+
|| /^\.devflow\/planning\/contracts\/[^/\\\x00-\x1F\x7F]+\/(?:EC-[A-Fa-f0-9]+\.json|control-events\.jsonl)$/u.test(filePath)
|
|
812
|
+
|| filePath.startsWith(".devflow/planning/review-runs/")
|
|
813
|
+
|| /^\.devflow\/planning\/phases\/[^/\\\x00-\x1F\x7F]+\/REVIEW\.md$/u.test(filePath);
|
|
766
814
|
}
|
|
767
815
|
|
|
768
816
|
function planningFeatureFromPath(filePath: string): string | null {
|
|
769
|
-
const match = filePath.match(/^\.planning\/(?:requirements|features)\/([^/\\\x00-\x1F\x7F]+)(?:\/|$)/u);
|
|
817
|
+
const match = filePath.match(/^\.devflow\/planning\/(?:requirements|features)\/([^/\\\x00-\x1F\x7F]+)(?:\/|$)/u);
|
|
770
818
|
return match?.[1] ?? null;
|
|
771
819
|
}
|
|
772
820
|
|
|
@@ -1083,8 +1131,8 @@ function projectInitializationDirective(summary: string): string {
|
|
|
1083
1131
|
return [
|
|
1084
1132
|
"<devflow-project-initialization-directive>",
|
|
1085
1133
|
"用户已批准完整项目初始化。现在处于执行模式;不要再创建 Todo、不要读取或写入 xd://devflow_contract。",
|
|
1086
|
-
"执行 skill://workflow-project-context,按已确认的项目边界建立 .planning/PROJECT.md、.planning/navigation/INDEX.md、.context/spec/index.md,并按用户选择建立 full 代码索引。",
|
|
1087
|
-
"只写项目初始化所需的 .planning/ 与 .context/ 控制产物;不得改动业务源码、依赖或配置。完成后运行项目初始化校验,报告生成文件和验证结果并停止,等待用户审查。",
|
|
1134
|
+
"执行 skill://workflow-project-context,按已确认的项目边界建立 .devflow/planning/PROJECT.md、.devflow/planning/navigation/INDEX.md、.devflow/context/spec/index.md,并按用户选择建立 full 代码索引。",
|
|
1135
|
+
"只写项目初始化所需的 .devflow/planning/ 与 .devflow/context/ 控制产物;不得改动业务源码、依赖或配置。完成后运行项目初始化校验,报告生成文件和验证结果并停止,等待用户审查。",
|
|
1088
1136
|
summary,
|
|
1089
1137
|
"</devflow-project-initialization-directive>",
|
|
1090
1138
|
].join("\n");
|
|
@@ -1177,7 +1225,7 @@ export function isShellControlMutation(toolName: string, input: unknown): boolea
|
|
|
1177
1225
|
/workflow-orchestrator\/scripts\/(?:advance_state|init_state|init_workflow|sync_artifacts|requirement_graph|machine_evidence|execution_contract)\.rb[^\n]*--write/.test(
|
|
1178
1226
|
command,
|
|
1179
1227
|
);
|
|
1180
|
-
const namesControlPath = [...CONTROL_PATHS, ...CONTROL_PREFIXES].some(item => command.includes(item)) || /\.planning\/requirements\//.test(command);
|
|
1228
|
+
const namesControlPath = [...CONTROL_PATHS, ...CONTROL_PREFIXES].some(item => command.includes(item)) || /\.devflow\/planning\/requirements\//.test(command);
|
|
1181
1229
|
// `2>` and `2>>` only redirect stderr. They frequently accompany read-only
|
|
1182
1230
|
// probes of control-plane files and must not be treated as a file mutation.
|
|
1183
1231
|
// Keep bare `>`/`>>` and explicit stdout `1>`/`1>>` guarded.
|
|
@@ -1424,7 +1472,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1424
1472
|
const feature = activeContract.feature;
|
|
1425
1473
|
const step = activeContract.step;
|
|
1426
1474
|
if (!/^[\p{L}\p{N}._-]{1,160}$/u.test(feature) || !/^[\p{L}\p{N}._-]{1,160}$/u.test(step)) return;
|
|
1427
|
-
const directory = path.join(cwd, ".planning", "evidence", feature);
|
|
1475
|
+
const directory = path.join(cwd, ".devflow/planning", "evidence", feature);
|
|
1428
1476
|
const attempt = typeof extra.attempt === "number" && Number.isInteger(extra.attempt) && extra.attempt >= 0
|
|
1429
1477
|
? extra.attempt
|
|
1430
1478
|
: undefined;
|
|
@@ -1619,7 +1667,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1619
1667
|
? (rawStep as { id: string }).id
|
|
1620
1668
|
: undefined;
|
|
1621
1669
|
if (!feature || !step) return undefined;
|
|
1622
|
-
const contractsDir = path.join(cwd, ".planning", "contracts", feature);
|
|
1670
|
+
const contractsDir = path.join(cwd, ".devflow/planning", "contracts", feature);
|
|
1623
1671
|
if (!existsSync(contractsDir)) return undefined;
|
|
1624
1672
|
const candidates = readdirSync(contractsDir)
|
|
1625
1673
|
.filter(name => name.startsWith("EC-") && name.endsWith(".json"))
|
|
@@ -1755,7 +1803,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1755
1803
|
`初始化方式:${modeSummary}`,
|
|
1756
1804
|
].join("\n");
|
|
1757
1805
|
const consequence = mode === "apply"
|
|
1758
|
-
? "确认后会创建 Devflow 的 .planning/ 与 .context/ 初始化产物;不会修改业务源码、依赖或项目配置。"
|
|
1806
|
+
? "确认后会创建 Devflow 的 .devflow/planning/ 与 .devflow/context/ 初始化产物;不会修改业务源码、依赖或项目配置。"
|
|
1759
1807
|
: "确认后只进入只读规划,不会创建工作流或写入项目文件。";
|
|
1760
1808
|
const confirmed = await ctx.ui.confirm("确认项目初始化范围", `${summary}\n\n${consequence}`);
|
|
1761
1809
|
if (!confirmed) {
|
|
@@ -2004,7 +2052,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2004
2052
|
const runtimeBootstrapAuthorizations = new Set<string>();
|
|
2005
2053
|
|
|
2006
2054
|
async function enqueueControlledRolloverRequest(cwd: string, rolloverId: string, sourceSessionId: string) {
|
|
2007
|
-
const requestPath = path.join(cwd, ".planning", "rollover-request.json");
|
|
2055
|
+
const requestPath = path.join(cwd, ".devflow/planning", "rollover-request.json");
|
|
2008
2056
|
const temporary = `${requestPath}.${randomUUID()}.tmp`;
|
|
2009
2057
|
await writeFile(temporary, JSON.stringify({ schema_version: 1, rollover_id: rolloverId, source_session_id: sourceSessionId }) + "\n", "utf8");
|
|
2010
2058
|
await rename(temporary, requestPath);
|
|
@@ -2403,6 +2451,12 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2403
2451
|
pi.on("session_start", async (_event, ctx) => {
|
|
2404
2452
|
projectRoot = ctx.cwd;
|
|
2405
2453
|
sessionId = randomUUID();
|
|
2454
|
+
try {
|
|
2455
|
+
await migrateLegacyProjectLayout(ctx.cwd);
|
|
2456
|
+
} catch (error) {
|
|
2457
|
+
ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2406
2460
|
const result = await runHook(ctx.cwd, "start.py", {
|
|
2407
2461
|
session_id: sessionId,
|
|
2408
2462
|
cwd: ctx.cwd,
|
|
@@ -2736,7 +2790,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2736
2790
|
text: [
|
|
2737
2791
|
"<devflow-planner-directive>",
|
|
2738
2792
|
"用户已确认项目初始化的输入。你现在是 Planner,只允许只读分析和派发 project-scout / requirements-auditor / contract-auditor。",
|
|
2739
|
-
"不得创建 .planning 文件、不得调用状态迁移、不得执行实现。",
|
|
2793
|
+
"不得创建 .devflow/planning 文件、不得调用状态迁移、不得执行实现。",
|
|
2740
2794
|
"先输出:已知事实、待验证点、最少侦察任务和必须由用户决定的问题。没有证据时写 unknown。",
|
|
2741
2795
|
initialization.summary,
|
|
2742
2796
|
"</devflow-planner-directive>",
|
|
@@ -2841,7 +2895,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
2841
2895
|
"--root", ctx.cwd,
|
|
2842
2896
|
"--feature", activeContract.feature,
|
|
2843
2897
|
"--step", activeContract.step,
|
|
2844
|
-
"--contract", `.planning/contracts/${activeContract.feature}/${activeContract.contractId}.json`,
|
|
2898
|
+
"--contract", `.devflow/planning/contracts/${activeContract.feature}/${activeContract.contractId}.json`,
|
|
2845
2899
|
"--obligations", "[]",
|
|
2846
2900
|
"--review-agent", "devflow-code-reviewer",
|
|
2847
2901
|
"--stage", "entry",
|
|
@@ -3625,7 +3679,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
3625
3679
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
3626
3680
|
const feature = normalizeContractSegment(params.feature, "feature");
|
|
3627
3681
|
const taskId = normalizeContractSegment(params.taskId, "taskId");
|
|
3628
|
-
const root = path.join(ctx.cwd, ".planning", "evidence", feature);
|
|
3682
|
+
const root = path.join(ctx.cwd, ".devflow/planning", "evidence", feature);
|
|
3629
3683
|
const destination = path.join(root, `${taskId}.json`);
|
|
3630
3684
|
const validatePack = (pack: unknown): string[] => {
|
|
3631
3685
|
if (!pack || typeof pack !== "object" || Array.isArray(pack)) return ["pack must be an object"];
|
|
@@ -3718,7 +3772,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
3718
3772
|
const written = await runRuby(ctx.cwd, "workflow-orchestrator/scripts/execution_contract.rb", createArgs);
|
|
3719
3773
|
if (!controlPlaneSucceeded(written)) return renderResult(written, { action: input.action });
|
|
3720
3774
|
const writtenPayload = parseJsonResult(written);
|
|
3721
|
-
const destination = typeof writtenPayload?.path === "string" ? writtenPayload.path : ".planning/contracts/";
|
|
3775
|
+
const destination = typeof writtenPayload?.path === "string" ? writtenPayload.path : ".devflow/planning/contracts/";
|
|
3722
3776
|
contract.contractPath = destination;
|
|
3723
3777
|
activeContract = contract;
|
|
3724
3778
|
contractAgentCalls = 0;
|
|
@@ -4264,7 +4318,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
4264
4318
|
[
|
|
4265
4319
|
"<devflow-planner-directive>",
|
|
4266
4320
|
"用户已确认项目初始化的输入。你现在是 Planner,只允许只读分析和派发 project-scout / requirements-auditor / contract-auditor。",
|
|
4267
|
-
"不得创建 .planning 文件、不得调用状态迁移、不得执行实现。",
|
|
4321
|
+
"不得创建 .devflow/planning 文件、不得调用状态迁移、不得执行实现。",
|
|
4268
4322
|
"先输出:已知事实、待验证点、最少侦察任务和必须由用户决定的问题。没有证据时写 unknown。",
|
|
4269
4323
|
initialization.summary,
|
|
4270
4324
|
"</devflow-planner-directive>",
|
|
@@ -34,11 +34,11 @@ def _run_light_sync(root: Path, files: list[str], timeout: int) -> tuple[bool, s
|
|
|
34
34
|
"--root",
|
|
35
35
|
str(root),
|
|
36
36
|
"--output",
|
|
37
|
-
".planning/code-index",
|
|
37
|
+
".devflow/planning/code-index",
|
|
38
38
|
"--changed-files",
|
|
39
39
|
*files,
|
|
40
40
|
],
|
|
41
|
-
[sys.executable, str(script), "validate", "--index", ".planning/code-index"],
|
|
41
|
+
[sys.executable, str(script), "validate", "--index", ".devflow/planning/code-index"],
|
|
42
42
|
[
|
|
43
43
|
sys.executable,
|
|
44
44
|
str(script),
|
|
@@ -46,7 +46,7 @@ def _run_light_sync(root: Path, files: list[str], timeout: int) -> tuple[bool, s
|
|
|
46
46
|
"--root",
|
|
47
47
|
str(root),
|
|
48
48
|
"--index",
|
|
49
|
-
".planning/code-index",
|
|
49
|
+
".devflow/planning/code-index",
|
|
50
50
|
],
|
|
51
51
|
]
|
|
52
52
|
output = []
|
|
@@ -11,6 +11,7 @@ import re
|
|
|
11
11
|
import subprocess
|
|
12
12
|
import tempfile
|
|
13
13
|
import time
|
|
14
|
+
from datetime import datetime
|
|
14
15
|
from contextlib import contextmanager
|
|
15
16
|
from pathlib import Path
|
|
16
17
|
from typing import Any, Iterator
|
|
@@ -26,16 +27,16 @@ DEFAULT_POLICY: dict[str, Any] = {
|
|
|
26
27
|
"mode": "strict",
|
|
27
28
|
"protected_roots": [],
|
|
28
29
|
"required_evidence": [
|
|
29
|
-
".planning/rules/INDEX.md",
|
|
30
|
-
".context/spec/index.md",
|
|
31
|
-
".planning/navigation/INDEX.md",
|
|
30
|
+
".devflow/planning/rules/INDEX.md",
|
|
31
|
+
".devflow/context/spec/index.md",
|
|
32
|
+
".devflow/planning/navigation/INDEX.md",
|
|
32
33
|
],
|
|
33
|
-
"optional_evidence": [".planning/code-index/INDEX.md"],
|
|
34
|
+
"optional_evidence": [".devflow/planning/code-index/INDEX.md"],
|
|
34
35
|
"allowed_before_gate": [
|
|
35
36
|
"AGENTS.md",
|
|
36
37
|
"README.md",
|
|
37
|
-
".planning/**",
|
|
38
|
-
".context/**",
|
|
38
|
+
".devflow/planning/**",
|
|
39
|
+
".devflow/context/**",
|
|
39
40
|
".agent-gates/**",
|
|
40
41
|
".omp/**",
|
|
41
42
|
".skills/**",
|
|
@@ -46,6 +47,8 @@ DEFAULT_POLICY: dict[str, Any] = {
|
|
|
46
47
|
"light_timeout_seconds": 120,
|
|
47
48
|
"full_file_threshold": 30,
|
|
48
49
|
"ignore": [
|
|
50
|
+
".devflow/planning/**",
|
|
51
|
+
".devflow/context/**",
|
|
49
52
|
".planning/**",
|
|
50
53
|
".context/**",
|
|
51
54
|
".agent-gates/**",
|
|
@@ -122,18 +125,69 @@ def cli_dir_name() -> str:
|
|
|
122
125
|
return ".omp"
|
|
123
126
|
|
|
124
127
|
|
|
128
|
+
def assert_project_layout_ready(root: Path) -> None:
|
|
129
|
+
legacy_planning = root / ".planning"
|
|
130
|
+
legacy_context = root / ".context"
|
|
131
|
+
planning_markers = ("STATE.md", "PROJECT.md", ".devflow.lock", "navigation/INDEX.md", "code-index/INDEX.md", "workflows", "contracts", "evidence", "review-runs", "requirements", "features", "handoffs", "artifacts", "phase-ownership.yaml")
|
|
132
|
+
context_markers = ("spec/index.md", "index.md", "project/index.md", "knowledge/index.md")
|
|
133
|
+
legacy_markers = [legacy_planning / item for item in planning_markers] + [legacy_context / item for item in context_markers]
|
|
134
|
+
if not any(item.exists() for item in legacy_markers):
|
|
135
|
+
return
|
|
136
|
+
try:
|
|
137
|
+
manifest = json.loads((root / ".devflow/project.json").read_text(encoding="utf-8"))
|
|
138
|
+
created_at = str(manifest.get("createdAt", "")).replace("Z", "+00:00")
|
|
139
|
+
datetime.fromisoformat(created_at)
|
|
140
|
+
valid_manifest = (
|
|
141
|
+
manifest.get("schemaVersion") == 1
|
|
142
|
+
and re.fullmatch(r"[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}", str(manifest.get("projectId", "")), re.I)
|
|
143
|
+
and bool(str(manifest.get("name", "")).strip())
|
|
144
|
+
and manifest.get("layoutVersion") in (None, 1, 2)
|
|
145
|
+
)
|
|
146
|
+
except (OSError, ValueError, TypeError, json.JSONDecodeError):
|
|
147
|
+
valid_manifest = False
|
|
148
|
+
manifest = {}
|
|
149
|
+
if not valid_manifest:
|
|
150
|
+
raise RuntimeError("DEVFLOW_LEGACY_LAYOUT_CONFIRMATION_REQUIRED: valid project manifest required")
|
|
151
|
+
try:
|
|
152
|
+
marker = json.loads((root / ".devflow/layout-migration.json").read_text(encoding="utf-8"))
|
|
153
|
+
migration_complete = marker.get("schemaVersion") == 1 and marker.get("status") == "complete" and isinstance(marker.get("migrated"), list)
|
|
154
|
+
except (OSError, TypeError, json.JSONDecodeError):
|
|
155
|
+
migration_complete = False
|
|
156
|
+
for relative in ("STATE.md", "workflows", "contracts", "evidence", "review-runs", "requirements", "features", "handoffs", "artifacts", "phase-ownership.yaml"):
|
|
157
|
+
if (legacy_planning / relative).exists() and not migration_complete:
|
|
158
|
+
raise RuntimeError(f"DEVFLOW_ACTIVE_LEGACY_LAYOUT: .planning/{relative} requires dedicated control-plane migration")
|
|
159
|
+
canonical_exists = (root / ".devflow/planning").exists() or (root / ".devflow/context").exists()
|
|
160
|
+
if manifest.get("layoutVersion") == 2 and not migration_complete:
|
|
161
|
+
raise RuntimeError("DEVFLOW_LEGACY_REINTRODUCTION: v2 project cannot import legacy layout")
|
|
162
|
+
if manifest.get("layoutVersion") == 2 and migration_complete:
|
|
163
|
+
planning_legacy = any((legacy_planning / item).exists() for item in planning_markers)
|
|
164
|
+
context_legacy = any((legacy_context / item).exists() for item in context_markers)
|
|
165
|
+
if (planning_legacy and not (root / ".devflow/planning").exists()) or (context_legacy and not (root / ".devflow/context").exists()):
|
|
166
|
+
raise RuntimeError("DEVFLOW_LEGACY_REINTRODUCTION: v2 project will not backfill a missing canonical directory")
|
|
167
|
+
if canonical_exists and not migration_complete:
|
|
168
|
+
raise RuntimeError("DEVFLOW_LAYOUT_CONFLICT: canonical and legacy layouts coexist without migration marker")
|
|
169
|
+
if not canonical_exists:
|
|
170
|
+
raise RuntimeError("DEVFLOW_LAYOUT_MIGRATION_REQUIRED: run project initialization")
|
|
171
|
+
|
|
172
|
+
|
|
125
173
|
def project_root(event: dict[str, Any] | None = None) -> Path:
|
|
126
174
|
event = event or {}
|
|
127
175
|
explicit = os.environ.get("OMP_PROJECT_DIR")
|
|
128
176
|
candidate = Path(explicit or event.get("cwd") or os.getcwd()).expanduser().resolve()
|
|
129
177
|
if candidate.is_file():
|
|
130
178
|
candidate = candidate.parent
|
|
179
|
+
# The project manifest is the authoritative root marker. Generated
|
|
180
|
+
# directories are deliberately ignored because a nested business module
|
|
181
|
+
# may legitimately use the same names.
|
|
131
182
|
for current in (candidate, *candidate.parents):
|
|
132
|
-
if
|
|
133
|
-
(current
|
|
134
|
-
|
|
135
|
-
|
|
183
|
+
if (current / ".devflow" / "project.json").is_file():
|
|
184
|
+
assert_project_layout_ready(current)
|
|
185
|
+
return current
|
|
186
|
+
for current in (candidate, *candidate.parents):
|
|
187
|
+
if (current / ".git").exists():
|
|
188
|
+
assert_project_layout_ready(current)
|
|
136
189
|
return current
|
|
190
|
+
assert_project_layout_ready(candidate)
|
|
137
191
|
return candidate
|
|
138
192
|
|
|
139
193
|
|
|
@@ -274,7 +328,7 @@ def path_matches(path: str, patterns: list[str]) -> bool:
|
|
|
274
328
|
|
|
275
329
|
|
|
276
330
|
def _navigation_codebase_roots(root: Path) -> list[str]:
|
|
277
|
-
path = root / ".planning/navigation/INDEX.md"
|
|
331
|
+
path = root / ".devflow/planning/navigation/INDEX.md"
|
|
278
332
|
try:
|
|
279
333
|
text = path.read_text(encoding="utf-8")
|
|
280
334
|
except (FileNotFoundError, PermissionError):
|
|
@@ -29,15 +29,15 @@ def detect_cli_dir(project_dir: Path) -> str:
|
|
|
29
29
|
def load_config(project_dir: Path, cli_dir: str) -> dict:
|
|
30
30
|
defaults = {
|
|
31
31
|
"sources": {
|
|
32
|
-
"state": ".planning/STATE.md",
|
|
33
|
-
"workflow": ".context/workflow.md",
|
|
32
|
+
"state": ".devflow/planning/STATE.md",
|
|
33
|
+
"workflow": ".devflow/context/workflow.md",
|
|
34
34
|
"indexes": [
|
|
35
|
-
".planning/rules/INDEX.md",
|
|
36
|
-
".context/spec/index.md",
|
|
37
|
-
".planning/navigation/INDEX.md",
|
|
38
|
-
".planning/code-index/INDEX.md",
|
|
35
|
+
".devflow/planning/rules/INDEX.md",
|
|
36
|
+
".devflow/context/spec/index.md",
|
|
37
|
+
".devflow/planning/navigation/INDEX.md",
|
|
38
|
+
".devflow/planning/code-index/INDEX.md",
|
|
39
39
|
],
|
|
40
|
-
"lessons": ".context/lessons/README.md",
|
|
40
|
+
"lessons": ".devflow/context/lessons/README.md",
|
|
41
41
|
"commands": f"{cli_dir}/commands/start.md",
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -196,7 +196,7 @@ def main():
|
|
|
196
196
|
|
|
197
197
|
# 项目身份
|
|
198
198
|
project_name = project_dir.name
|
|
199
|
-
project_md = read_file(project_dir / ".planning" / "PROJECT.md")
|
|
199
|
+
project_md = read_file(project_dir / ".devflow/planning" / "PROJECT.md")
|
|
200
200
|
for line in project_md.split("\n"):
|
|
201
201
|
if line.startswith("- **Name**:"):
|
|
202
202
|
project_name = line.split(":")[-1].strip()
|
|
@@ -205,13 +205,13 @@ def main():
|
|
|
205
205
|
output.write(
|
|
206
206
|
"<session-context>\n"
|
|
207
207
|
f"你正在 {project_name} 项目中进行研发工作。\n"
|
|
208
|
-
"该项目使用 .planning/ 跟踪研发流水线进度,状态写入和上下文同步由 OMP extension 强制执行。\n"
|
|
208
|
+
"该项目使用 .devflow/planning/ 跟踪研发流水线进度,状态写入和上下文同步由 OMP extension 强制执行。\n"
|
|
209
209
|
"索引只用于加速定位;发现索引缺失或过期时,直接回退到 LSP、搜索和源码,并报告降级状态。\n"
|
|
210
210
|
"</session-context>\n\n"
|
|
211
211
|
)
|
|
212
212
|
|
|
213
213
|
# 跨 clear 意图
|
|
214
|
-
intent_path = project_dir / ".planning" / ".next-intent"
|
|
214
|
+
intent_path = project_dir / ".devflow/planning" / ".next-intent"
|
|
215
215
|
intent = {}
|
|
216
216
|
if intent_path.exists():
|
|
217
217
|
try:
|
|
@@ -229,7 +229,7 @@ def main():
|
|
|
229
229
|
output.write("\n</intent>\n\n")
|
|
230
230
|
|
|
231
231
|
# Pipeline 状态
|
|
232
|
-
state_path = sources.get("state", ".planning/STATE.md")
|
|
232
|
+
state_path = sources.get("state", ".devflow/planning/STATE.md")
|
|
233
233
|
state_text = load_state_section(project_dir, state_path)
|
|
234
234
|
if state_text:
|
|
235
235
|
output.write("<pipeline-state>\n")
|
|
@@ -237,7 +237,7 @@ def main():
|
|
|
237
237
|
output.write("\n</pipeline-state>\n\n")
|
|
238
238
|
|
|
239
239
|
# 研发流程
|
|
240
|
-
workflow_path = sources.get("workflow", ".context/workflow.md")
|
|
240
|
+
workflow_path = sources.get("workflow", ".devflow/context/workflow.md")
|
|
241
241
|
workflow_text = read_file(project_dir / workflow_path)
|
|
242
242
|
if workflow_text:
|
|
243
243
|
output.write("<workflow>\n")
|
|
@@ -254,7 +254,7 @@ def main():
|
|
|
254
254
|
output.write("</index-entrypoints>\n\n")
|
|
255
255
|
|
|
256
256
|
# 经验教训
|
|
257
|
-
lessons_path = sources.get("lessons", ".context/lessons/README.md")
|
|
257
|
+
lessons_path = sources.get("lessons", ".devflow/context/lessons/README.md")
|
|
258
258
|
lessons_text = read_file(project_dir / lessons_path)
|
|
259
259
|
if lessons_text and lessons_text.strip():
|
|
260
260
|
output.write("<lessons>\n")
|
|
@@ -7,8 +7,8 @@ description: Generate or revise a low-fidelity wireframe proposal from controlle
|
|
|
7
7
|
|
|
8
8
|
Read the controlled requirement baseline and the wireframe schema/registry. Produce exactly two candidate files:
|
|
9
9
|
|
|
10
|
-
- .planning/features/{feature}/proposal.yaml
|
|
11
|
-
- .planning/features/{feature}/questions.yaml
|
|
10
|
+
- .devflow/planning/features/{feature}/proposal.yaml
|
|
11
|
+
- .devflow/planning/features/{feature}/questions.yaml
|
|
12
12
|
|
|
13
13
|
Use only registered element types and screen-level requirement_refs. Keep IDs stable during revision. Do not add API fields, coordinates, CSS, nested children, or approval fields. Emit valid YAML only and never claim that the proposal is validated or approved. The validator and Core decide legality, fingerprints, and approval.
|
|
14
14
|
|
|
@@ -11,10 +11,10 @@ Run the bundled validator through Core's `devflow_skill_script` tool:
|
|
|
11
11
|
devflow_skill_script(
|
|
12
12
|
script="wireframe-validate",
|
|
13
13
|
args=[
|
|
14
|
-
"--proposal", ".planning/features/<feature>/proposal.yaml",
|
|
15
|
-
"--traceability", ".planning/requirements/<feature>/traceability.json",
|
|
16
|
-
"--questions", ".planning/features/<feature>/questions.yaml",
|
|
17
|
-
"--canonical-out", ".planning/features/<feature>/canonical.json"
|
|
14
|
+
"--proposal", ".devflow/planning/features/<feature>/proposal.yaml",
|
|
15
|
+
"--traceability", ".devflow/planning/requirements/<feature>/traceability.json",
|
|
16
|
+
"--questions", ".devflow/planning/features/<feature>/questions.yaml",
|
|
17
|
+
"--canonical-out", ".devflow/planning/features/<feature>/canonical.json"
|
|
18
18
|
]
|
|
19
19
|
)
|
|
20
20
|
```
|