@guanlin4924/devflow-pi 0.1.92 → 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 +22 -17
- 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 +3 -1
- package/plugins/devflow-intel/extensions/devflow-intel.ts +20 -19
- package/plugins/devflow-intel/skills/code-index-query/SKILL.md +8 -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 +13 -13
- 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 +23 -9
- 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/src/project-context.ts
CHANGED
|
@@ -5,9 +5,11 @@ import path from "node:path";
|
|
|
5
5
|
import { PROJECT_SCHEMA_VERSION } from "./constants.ts";
|
|
6
6
|
import { InitializationStore } from "./initialization-store.ts";
|
|
7
7
|
import { projectControlDatabasePath } from "./paths.ts";
|
|
8
|
+
import { DEVFLOW_MANIFEST, migrateLegacyProjectLayout, PLANNING_DIRECTORY, projectLayout } from "./project-layout.ts";
|
|
8
9
|
|
|
9
10
|
export interface ProjectManifest {
|
|
10
11
|
schemaVersion: number;
|
|
12
|
+
layoutVersion: number;
|
|
11
13
|
projectId: string;
|
|
12
14
|
name: string;
|
|
13
15
|
createdAt: string;
|
|
@@ -23,11 +25,12 @@ export interface ProjectContext {
|
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
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;
|
|
28
|
+
const PROJECT_LAYOUT_VERSION = 2;
|
|
26
29
|
const CONTROL_BOUNDARY_PATHS = [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
`${PLANNING_DIRECTORY}/STATE.md`,
|
|
31
|
+
`${PLANNING_DIRECTORY}/.devflow.lock`,
|
|
32
|
+
`${PLANNING_DIRECTORY}/workflows`,
|
|
33
|
+
`${PLANNING_DIRECTORY}/artifacts`,
|
|
31
34
|
];
|
|
32
35
|
|
|
33
36
|
async function assertNoSymlinkDescendants(target: string): Promise<void> {
|
|
@@ -58,7 +61,7 @@ async function canonicalDirectory(candidate: string): Promise<string> {
|
|
|
58
61
|
async function nearestManifest(start: string): Promise<string | null> {
|
|
59
62
|
let current = await canonicalDirectory(start);
|
|
60
63
|
while (true) {
|
|
61
|
-
const manifest = path.join(current,
|
|
64
|
+
const manifest = path.join(current, DEVFLOW_MANIFEST);
|
|
62
65
|
if (await exists(manifest)) return current;
|
|
63
66
|
const parent = path.dirname(current);
|
|
64
67
|
if (parent === current) return null;
|
|
@@ -95,18 +98,21 @@ function validateManifest(value: unknown, manifestPath: string): ProjectManifest
|
|
|
95
98
|
if (record.schemaVersion !== PROJECT_SCHEMA_VERSION) {
|
|
96
99
|
throw new Error(`Unsupported project schema in ${manifestPath}: ${String(record.schemaVersion)}`);
|
|
97
100
|
}
|
|
101
|
+
if (record.layoutVersion !== undefined && record.layoutVersion !== 1 && record.layoutVersion !== PROJECT_LAYOUT_VERSION) {
|
|
102
|
+
throw new Error(`Unsupported project layout in ${manifestPath}: ${String(record.layoutVersion)}`);
|
|
103
|
+
}
|
|
98
104
|
if (typeof record.projectId !== "string" || !PROJECT_ID_PATTERN.test(record.projectId)) {
|
|
99
105
|
throw new Error(`Invalid projectId in ${manifestPath}`);
|
|
100
106
|
}
|
|
101
107
|
if (typeof record.name !== "string" || !record.name || typeof record.createdAt !== "string") {
|
|
102
108
|
throw new Error(`Incomplete project manifest: ${manifestPath}`);
|
|
103
109
|
}
|
|
104
|
-
return record as unknown as ProjectManifest;
|
|
110
|
+
return { ...record, layoutVersion: Number(record.layoutVersion ?? 1) } as unknown as ProjectManifest;
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
export async function assertPlanningBoundary(root: string): Promise<string> {
|
|
108
114
|
const canonicalRoot = await canonicalDirectory(root);
|
|
109
|
-
const planningRoot =
|
|
115
|
+
const planningRoot = projectLayout(canonicalRoot).planningRoot;
|
|
110
116
|
const stat = await lstat(planningRoot).catch(() => null);
|
|
111
117
|
if (stat?.isSymbolicLink()) throw new Error(`Refusing symlinked planning root: ${planningRoot}`);
|
|
112
118
|
if (stat && !stat.isDirectory()) throw new Error(`Planning root is not a directory: ${planningRoot}`);
|
|
@@ -133,8 +139,16 @@ export async function assertPlanningBoundary(root: string): Promise<string> {
|
|
|
133
139
|
|
|
134
140
|
export async function loadProjectContext(root: string): Promise<ProjectContext> {
|
|
135
141
|
const canonicalRoot = await canonicalDirectory(root);
|
|
136
|
-
const manifestPath =
|
|
137
|
-
const
|
|
142
|
+
const manifestPath = projectLayout(canonicalRoot).manifestPath;
|
|
143
|
+
const manifestValue = JSON.parse(await readFile(manifestPath, "utf8")) as Record<string, unknown>;
|
|
144
|
+
validateManifest(manifestValue, manifestPath);
|
|
145
|
+
await migrateLegacyProjectLayout(canonicalRoot);
|
|
146
|
+
if (manifestValue.layoutVersion !== PROJECT_LAYOUT_VERSION) {
|
|
147
|
+
manifestValue.layoutVersion = PROJECT_LAYOUT_VERSION;
|
|
148
|
+
await atomicWriteJson(manifestPath, manifestValue);
|
|
149
|
+
}
|
|
150
|
+
const manifest = validateManifest(manifestValue, manifestPath);
|
|
151
|
+
await mkdir(projectLayout(canonicalRoot).planningRoot, { recursive: true });
|
|
138
152
|
const planningRoot = await assertPlanningBoundary(canonicalRoot);
|
|
139
153
|
if (!(await exists(projectControlDatabasePath(manifest.projectId)))) throw new Error("Project initialization database is not registered; run explicit project initialization first");
|
|
140
154
|
return attachInitializationStore({ root: canonicalRoot, manifestPath, manifest, planningRoot });
|
|
@@ -160,22 +174,30 @@ async function atomicWriteJson(target: string, value: unknown): Promise<void> {
|
|
|
160
174
|
|
|
161
175
|
export async function initializeProject(root: string): Promise<{ context: ProjectContext; created: boolean }> {
|
|
162
176
|
const canonicalRoot = await canonicalDirectory(root);
|
|
163
|
-
const devflowDir =
|
|
164
|
-
const planningRoot = path.join(canonicalRoot, ".planning");
|
|
165
|
-
await assertPlanningBoundary(canonicalRoot);
|
|
177
|
+
const { devflowRoot: devflowDir, manifestPath, planningRoot } = projectLayout(canonicalRoot);
|
|
166
178
|
await mkdir(devflowDir, { recursive: true });
|
|
167
|
-
await
|
|
168
|
-
const manifestPath = path.join(devflowDir, "project.json");
|
|
179
|
+
await assertPlanningBoundary(canonicalRoot);
|
|
169
180
|
if (await exists(manifestPath)) {
|
|
170
181
|
return { context: await loadProjectContext(canonicalRoot), created: false };
|
|
171
182
|
}
|
|
172
183
|
const manifest: ProjectManifest = {
|
|
173
184
|
schemaVersion: PROJECT_SCHEMA_VERSION,
|
|
185
|
+
// Establish a v1-compatible pending identity while legacy material is
|
|
186
|
+
// assessed; only commit v2 after migration succeeds.
|
|
187
|
+
layoutVersion: 1,
|
|
174
188
|
projectId: randomUUID(),
|
|
175
189
|
name: path.basename(canonicalRoot),
|
|
176
190
|
createdAt: new Date().toISOString(),
|
|
177
191
|
};
|
|
178
192
|
await atomicWriteJson(manifestPath, manifest);
|
|
193
|
+
// Explicit initialization establishes project identity before attempting a
|
|
194
|
+
// legacy-layout migration. This prevents a tracked v1 project with no local
|
|
195
|
+
// manifest from being permanently skipped, while active/conflicting legacy
|
|
196
|
+
// control state still fails closed in migrateLegacyProjectLayout.
|
|
197
|
+
await migrateLegacyProjectLayout(canonicalRoot);
|
|
198
|
+
manifest.layoutVersion = PROJECT_LAYOUT_VERSION;
|
|
199
|
+
await atomicWriteJson(manifestPath, manifest);
|
|
200
|
+
await mkdir(planningRoot, { recursive: true });
|
|
179
201
|
const bootstrapStore = await InitializationStore.open({ root: canonicalRoot, manifest });
|
|
180
202
|
try { await bootstrapStore.registerWorkspace(); } finally { bootstrapStore.close(); }
|
|
181
203
|
return {
|
package/src/project-documents.ts
CHANGED
|
@@ -152,9 +152,9 @@ function classifyDocument(document: SourceDocument): DocumentClassification {
|
|
|
152
152
|
function shouldInspectDocument(relativePath: string): boolean {
|
|
153
153
|
if (!/\.(?:md|mdx|txt|adoc|rst|yaml|yml|json)$/i.test(relativePath)) return false;
|
|
154
154
|
if (/^(?:\.git|node_modules|dist|build|coverage|vendor|target)\//.test(relativePath)) return false;
|
|
155
|
-
if (/^\.planning\/code-index\//.test(relativePath)) return false;
|
|
156
|
-
if (/^\.context\/requirements\/_template\//.test(relativePath)) return false;
|
|
157
|
-
return /^(?:README|docs\/|features\/|requirements\/|\.context\/|\.planning\/requirements\/|\.planning\/features\/|test-environment-guide|skit\/docs\/|text\/)/i.test(relativePath);
|
|
155
|
+
if (/^\.devflow\/planning\/code-index\//.test(relativePath)) return false;
|
|
156
|
+
if (/^\.devflow\/context\/requirements\/_template\//.test(relativePath)) return false;
|
|
157
|
+
return /^(?:README|docs\/|features\/|requirements\/|\.devflow\/context\/|\.devflow\/planning\/requirements\/|\.devflow\/planning\/features\/|test-environment-guide|skit\/docs\/|text\/)/i.test(relativePath);
|
|
158
158
|
}
|
|
159
159
|
async function discoverSourceDocuments(root: string): Promise<DocumentKnowledge> {
|
|
160
160
|
const documents: SourceDocument[] = [];
|
|
@@ -232,7 +232,7 @@ function knowledgePagePath(type: KnowledgePageType, titleOrId: string): string {
|
|
|
232
232
|
incident: "incidents",
|
|
233
233
|
lesson: "lessons",
|
|
234
234
|
} satisfies Record<KnowledgePageType, string>)[type];
|
|
235
|
-
return `.context/knowledge/${folder}/${slug(titleOrId)}.md`;
|
|
235
|
+
return `.devflow/context/knowledge/${folder}/${slug(titleOrId)}.md`;
|
|
236
236
|
}
|
|
237
237
|
function sourceTable(items: Array<{ source: SourceDocument; classification: DocumentClassification }>): string {
|
|
238
238
|
if (!items.length) return "未发现可归类的已有文档。";
|
|
@@ -305,24 +305,24 @@ function renderDocuments(context: ProjectContext, facts: Fact[], intro: string,
|
|
|
305
305
|
const capabilities = facts.filter(fact => fact.kind === "capability");
|
|
306
306
|
const repositories = [...new Set(facts.map(fact => fact.repository).filter(Boolean))].sort();
|
|
307
307
|
const outputs: Array<{ targetPath: string; body: string }> = [];
|
|
308
|
-
outputs.push({ targetPath: ".planning/PROJECT.md", body: [
|
|
308
|
+
outputs.push({ targetPath: ".devflow/planning/PROJECT.md", body: [
|
|
309
309
|
`# ${context.manifest.name}`, "", intro, "", "## 代码库组成", "",
|
|
310
310
|
table(["代码库", "文件数量", "已观察能力"], repositories.map(repository => [repository, assets.filter(fact => fact.repository === repository).length, [...new Set(facts.filter(fact => fact.repository === repository && ["api", "page", "data_model", "integration"].includes(fact.kind)).map(fact => fact.kind))].join("、") || "未发现"])),
|
|
311
311
|
"", "## 技术概况", "", table(["声明", "来源类型"], technologies.slice(0, 100).map(fact => [fact.value.name ?? fact.value.extension, fact.value.declaration ?? "文件统计"])),
|
|
312
312
|
"", "## 已知边界", "", "当前信息来自本地静态扫描。接口声明不证明服务可访问,页面声明不证明运行时渲染,命令声明不表示已经执行。", "",
|
|
313
313
|
].join("\n") });
|
|
314
|
-
outputs.push({ targetPath: ".planning/toolchain.md", body: ["# 工具链", "", intro, "", table(["代码库", "名称", "命令", "工作目录", "执行状态"], commands.map(fact => [fact.repository, fact.value.name, fact.value.command, fact.value.workingDirectory || ".", "仅发现,未执行"])), ""].join("\n") });
|
|
315
|
-
outputs.push({ targetPath: ".planning/navigation/INDEX.md", body: ["# 项目导航", "", intro, "", table(["代码库", "导航说明", "扫描文件数"], repositories.map(repository => [repository, `.planning/navigation/${repository.replace(/[^\p{L}\p{N}._-]/gu, "-")}.md`, assets.filter(fact => fact.repository === repository).length])), ""].join("\n") });
|
|
314
|
+
outputs.push({ targetPath: ".devflow/planning/toolchain.md", body: ["# 工具链", "", intro, "", table(["代码库", "名称", "命令", "工作目录", "执行状态"], commands.map(fact => [fact.repository, fact.value.name, fact.value.command, fact.value.workingDirectory || ".", "仅发现,未执行"])), ""].join("\n") });
|
|
315
|
+
outputs.push({ targetPath: ".devflow/planning/navigation/INDEX.md", body: ["# 项目导航", "", intro, "", table(["代码库", "导航说明", "扫描文件数"], repositories.map(repository => [repository, `.devflow/planning/navigation/${repository.replace(/[^\p{L}\p{N}._-]/gu, "-")}.md`, assets.filter(fact => fact.repository === repository).length])), ""].join("\n") });
|
|
316
316
|
for (const repository of repositories) {
|
|
317
317
|
const local = facts.filter(fact => fact.repository === repository);
|
|
318
|
-
outputs.push({ targetPath: `.planning/navigation/${repository.replace(/[^\p{L}\p{N}._-]/gu, "-")}.md`, body: [
|
|
318
|
+
outputs.push({ targetPath: `.devflow/planning/navigation/${repository.replace(/[^\p{L}\p{N}._-]/gu, "-")}.md`, body: [
|
|
319
319
|
`# ${repository} 导航`, "", intro, "", "## 文件与入口", "",
|
|
320
320
|
table(["路径", "类型"], local.filter(fact => fact.kind === "asset").slice(0, 200).map(fact => [fact.value.path, fact.value.extension || "无扩展名"])),
|
|
321
321
|
"", "## 已发现能力", "", table(["类型", "对象"], local.filter(fact => ["api", "page", "data_model", "integration"].includes(fact.kind)).slice(0, 200).map(fact => [fact.kind, fact.value.path ?? fact.value.route ?? fact.value.name ?? fact.value.host ?? "未确认"])), "",
|
|
322
322
|
].join("\n") });
|
|
323
323
|
}
|
|
324
|
-
outputs.push({ targetPath: ".planning/rules/INDEX.md", body: ["# 项目规则来源", "", intro, "", table(["代码库", "规则文件", "性质"], rules.map(fact => [fact.repository, fact.value.path, fact.value.explicit ? "显式规则" : "惯例候选"])), "", "未登记的惯例候选不会自动成为强制规则。", ""].join("\n") });
|
|
325
|
-
outputs.push({ targetPath: ".planning/initialization/report.md", body: [
|
|
324
|
+
outputs.push({ targetPath: ".devflow/planning/rules/INDEX.md", body: ["# 项目规则来源", "", intro, "", table(["代码库", "规则文件", "性质"], rules.map(fact => [fact.repository, fact.value.path, fact.value.explicit ? "显式规则" : "惯例候选"])), "", "未登记的惯例候选不会自动成为强制规则。", ""].join("\n") });
|
|
325
|
+
outputs.push({ targetPath: ".devflow/planning/initialization/report.md", body: [
|
|
326
326
|
"# 项目初始化报告", "", intro, "", "## 数据可用范围", "",
|
|
327
327
|
table(["采集器", "支持范围", "限制"], capabilities.map(fact => [fact.objectKey, Array.isArray(fact.value.supported) ? fact.value.supported.join("、") : "未确认", Array.isArray(fact.value.limitations) ? fact.value.limitations.join(";") : "未确认"])),
|
|
328
328
|
"", "## 分项采集状态", "", table(["代码库", "专题", "状态", "诊断数"], collections.map(item => [item.repository || "全局", item.kind, item.status, item.diagnostics?.length ?? 0])),
|
|
@@ -383,13 +383,13 @@ function requirementTemplateBody(context: ProjectContext, type: KnowledgeDocumen
|
|
|
383
383
|
related_apis: [],
|
|
384
384
|
managed: true,
|
|
385
385
|
});
|
|
386
|
-
return `${header}# ${documentTitle(type)}\n\n本文件是需求级文档模板。创建具体需求时复制到 \`.context/requirements/REQ-xxx-短名/${type === "requirements" ? "requirement" : type}.md\`,并把 frontmatter 中的 \`requirement_id\`、\`status\`、关联路径和 API 补全。\n\n## 当前结论\n\n- 待补充。\n\n## 证据与约束\n\n- 初始化快照:${snapshotId}\n- 代码索引 generation:${generation ?? "未生成或已跳过"}\n- 需求级内容必须引用项目级文档和实际源码证据,不能只凭推断确认。\n\n## 待补充\n\n- 背景与目标\n- 范围与非目标\n- 设计决策\n- 验收条件\n`;
|
|
386
|
+
return `${header}# ${documentTitle(type)}\n\n本文件是需求级文档模板。创建具体需求时复制到 \`.devflow/context/requirements/REQ-xxx-短名/${type === "requirements" ? "requirement" : type}.md\`,并把 frontmatter 中的 \`requirement_id\`、\`status\`、关联路径和 API 补全。\n\n## 当前结论\n\n- 待补充。\n\n## 证据与约束\n\n- 初始化快照:${snapshotId}\n- 代码索引 generation:${generation ?? "未生成或已跳过"}\n- 需求级内容必须引用项目级文档和实际源码证据,不能只凭推断确认。\n\n## 待补充\n\n- 背景与目标\n- 范围与非目标\n- 设计决策\n- 验收条件\n`;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
function inboxRequirementBody(context: ProjectContext, source: SourceDocument, classification: DocumentClassification, snapshotId: string, generation?: string): Array<{ targetPath: string; body: string }> {
|
|
390
390
|
const requirementId = classification.requirementId ?? source.id;
|
|
391
391
|
const folder = `${requirementId}-${slug(source.title)}`;
|
|
392
|
-
const base = `.context/requirements/_inbox/${folder}`;
|
|
392
|
+
const base = `.devflow/context/requirements/_inbox/${folder}`;
|
|
393
393
|
const documentType = classification.documentType === "unknown" ? "requirements" : classification.documentType;
|
|
394
394
|
const body = `${frontmatter({ type: documentType === "requirements" ? "requirement-spec" : `requirement-${documentType}`, scope: "requirement", project: context.manifest.name, requirement_id: requirementId, status: "inbox", source_snapshot: snapshotId, code_index_generation: generation, source_documents: [source.path], managed: true })}# ${source.title}\n\n本文档由初始化阶段从已有资料归档而来,尚未确认是否为正式需求。\n\n## 来源摘要\n\n${source.excerpt || "无可用摘要。"}\n\n## 来源文档\n\n${table(["来源", "标题", "内容哈希"], [[source.path, source.title, source.contentHash]])}\n\n## 归类信息\n\n- 类型:${classification.documentType}\n- 置信度:${classification.confidence}\n- 理由:${classification.reasons.join(";")}\n\n## 待确认\n\n- 是否转为正式 REQ 编号\n- 是否拆分为多个需求\n- 是否补齐产品、技术、API 与用例文档\n`;
|
|
395
395
|
return [
|
|
@@ -449,7 +449,7 @@ function buildKnowledgePages(knowledge: DocumentKnowledge, facts: Fact[]): Knowl
|
|
|
449
449
|
const allSourceRefs = [...new Set([...pages.values()].flatMap(page => page.sourceRefs).filter(ref => ref.startsWith("source-")))];
|
|
450
450
|
pages.set("synthesis-project-overview", {
|
|
451
451
|
id: "synthesis-project-overview",
|
|
452
|
-
path: ".context/knowledge/synthesis/project-overview.md",
|
|
452
|
+
path: ".devflow/context/knowledge/synthesis/project-overview.md",
|
|
453
453
|
type: "synthesis",
|
|
454
454
|
title: "项目知识总览",
|
|
455
455
|
status: "draft",
|
|
@@ -513,7 +513,7 @@ function bugKnowledgeTemplates(context: ProjectContext, snapshotId: string, gene
|
|
|
513
513
|
const common = { project: context.manifest.name, status: "template", source_snapshot: snapshotId, code_index_generation: generation, managed: true };
|
|
514
514
|
return [
|
|
515
515
|
{
|
|
516
|
-
targetPath: ".context/knowledge/bugs/_template.md",
|
|
516
|
+
targetPath: ".devflow/context/knowledge/bugs/_template.md",
|
|
517
517
|
body: `${frontmatter({ ...common, type: "bug-report", id: "BUG-TEMPLATE", title: "BUG 模板", source_refs: [], related_requirements: [], related_code_facts: [], related_fixes: [], regression_tests: [], confidence: "manual" })}# BUG 模板
|
|
518
518
|
|
|
519
519
|
## Symptom
|
|
@@ -546,7 +546,7 @@ function bugKnowledgeTemplates(context: ProjectContext, snapshotId: string, gene
|
|
|
546
546
|
`,
|
|
547
547
|
},
|
|
548
548
|
{
|
|
549
|
-
targetPath: ".context/knowledge/fixes/_template.md",
|
|
549
|
+
targetPath: ".devflow/context/knowledge/fixes/_template.md",
|
|
550
550
|
body: `${frontmatter({ ...common, type: "bug-fix", id: "FIX-TEMPLATE", title: "FIX 模板", bug_refs: [], changed_code_facts: [], regression_tests: [], confidence: "manual" })}# FIX 模板
|
|
551
551
|
|
|
552
552
|
## Change Summary
|
|
@@ -571,7 +571,7 @@ function bugKnowledgeTemplates(context: ProjectContext, snapshotId: string, gene
|
|
|
571
571
|
`,
|
|
572
572
|
},
|
|
573
573
|
{
|
|
574
|
-
targetPath: ".context/knowledge/incidents/_template.md",
|
|
574
|
+
targetPath: ".devflow/context/knowledge/incidents/_template.md",
|
|
575
575
|
body: `${frontmatter({ ...common, type: "incident", id: "INC-TEMPLATE", title: "INCIDENT 模板", related_bugs: [], related_fixes: [], related_code_facts: [], confidence: "manual" })}# INCIDENT 模板
|
|
576
576
|
|
|
577
577
|
## Timeline
|
|
@@ -596,7 +596,7 @@ function bugKnowledgeTemplates(context: ProjectContext, snapshotId: string, gene
|
|
|
596
596
|
`,
|
|
597
597
|
},
|
|
598
598
|
{
|
|
599
|
-
targetPath: ".context/knowledge/lessons/_template.md",
|
|
599
|
+
targetPath: ".devflow/context/knowledge/lessons/_template.md",
|
|
600
600
|
body: `${frontmatter({ ...common, type: "lesson", id: "LESSON-TEMPLATE", title: "LESSON 模板", related_bugs: [], related_fixes: [], related_code_facts: [], confidence: "manual" })}# LESSON 模板
|
|
601
601
|
|
|
602
602
|
## Lesson
|
|
@@ -633,9 +633,9 @@ function initializationInterfaceContracts(): InterfaceContract[] {
|
|
|
633
633
|
name: "scan",
|
|
634
634
|
purpose: "执行项目初始化扫描并发布上下文快照。",
|
|
635
635
|
request: { action: "scan", mode: "full|fast", repositories: [{ name: "app", path: ".", role: "frontend|backend|shared|other" }], requiredKinds: FACT_KINDS, documentMode: "template|none", requestId: "stable-id" },
|
|
636
|
-
response: { runId: "uuid", snapshotId: "uuid", completeness: "complete|partial", database: "<control.db>", projectId: "uuid", workspaceId: "uuid", planningRoot: ".planning", diagnostics: [], documents: [{ targetPath: ".context/project/api.md", status: "complete|conflict|failed" }] },
|
|
636
|
+
response: { runId: "uuid", snapshotId: "uuid", completeness: "complete|partial", database: "<control.db>", projectId: "uuid", workspaceId: "uuid", planningRoot: ".devflow/planning", diagnostics: [], documents: [{ targetPath: ".devflow/context/project/api.md", status: "complete|conflict|failed" }] },
|
|
637
637
|
reads: ["source tree", ".devflow/project.json"],
|
|
638
|
-
writes: ["control.db", ".planning/code-index/", ".planning/document-index.json", ".planning/knowledge-index.json", ".context/", ".planning/PROJECT.md"],
|
|
638
|
+
writes: ["control.db", ".devflow/planning/code-index/", ".devflow/planning/document-index.json", ".devflow/planning/knowledge-index.json", ".devflow/context/", ".devflow/planning/PROJECT.md"],
|
|
639
639
|
},
|
|
640
640
|
{
|
|
641
641
|
name: "query",
|
|
@@ -657,9 +657,9 @@ function initializationInterfaceContracts(): InterfaceContract[] {
|
|
|
657
657
|
name: "generate-docs",
|
|
658
658
|
purpose: "针对已发布快照重试生成文档投影。",
|
|
659
659
|
request: { action: "generate-docs", snapshotId: "uuid" },
|
|
660
|
-
response: [{ targetPath: ".context/project/api.md", status: "complete|conflict|failed", outputPath: "path" }],
|
|
661
|
-
reads: ["control.db", ".planning/code-index/", "source documents"],
|
|
662
|
-
writes: [".context/", ".planning/document-index.json", ".planning/knowledge-index.json"],
|
|
660
|
+
response: [{ targetPath: ".devflow/context/project/api.md", status: "complete|conflict|failed", outputPath: "path" }],
|
|
661
|
+
reads: ["control.db", ".devflow/planning/code-index/", "source documents"],
|
|
662
|
+
writes: [".devflow/context/", ".devflow/planning/document-index.json", ".devflow/planning/knowledge-index.json"],
|
|
663
663
|
},
|
|
664
664
|
{
|
|
665
665
|
name: "config",
|
|
@@ -712,7 +712,7 @@ function initializationInterfaceContracts(): InterfaceContract[] {
|
|
|
712
712
|
{
|
|
713
713
|
name: "import-legacy",
|
|
714
714
|
purpose: "把旧版初始化产物导入数据库控制面。",
|
|
715
|
-
request: { action: "import-legacy", sources: [".planning/PROJECT.md"], requestId: "stable-id" },
|
|
715
|
+
request: { action: "import-legacy", sources: [".devflow/planning/PROJECT.md"], requestId: "stable-id" },
|
|
716
716
|
response: { runId: "uuid", snapshotId: "uuid", imports: [] },
|
|
717
717
|
reads: ["legacy initialization files"],
|
|
718
718
|
writes: ["control.db"],
|
|
@@ -720,7 +720,7 @@ function initializationInterfaceContracts(): InterfaceContract[] {
|
|
|
720
720
|
{
|
|
721
721
|
name: "export",
|
|
722
722
|
purpose: "把指定初始化快照导出为兼容文件。",
|
|
723
|
-
request: { action: "export", snapshotId: "uuid", targetPath: ".planning/project-context-export.json" },
|
|
723
|
+
request: { action: "export", snapshotId: "uuid", targetPath: ".devflow/planning/project-context-export.json" },
|
|
724
724
|
response: { path: "path", contentHash: "sha256:..." },
|
|
725
725
|
reads: ["control.db"],
|
|
726
726
|
writes: ["targetPath"],
|
|
@@ -744,7 +744,7 @@ function initializationInterfaceContracts(): InterfaceContract[] {
|
|
|
744
744
|
{
|
|
745
745
|
name: "backup",
|
|
746
746
|
purpose: "备份初始化控制数据库和内容状态。",
|
|
747
|
-
request: { action: "backup", targetPath: ".planning/backups/initialization.db" },
|
|
747
|
+
request: { action: "backup", targetPath: ".devflow/planning/backups/initialization.db" },
|
|
748
748
|
response: { backup: "absolute-path" },
|
|
749
749
|
reads: ["control.db", "managed initialization documents"],
|
|
750
750
|
writes: ["targetPath", "targetPath.manifest.json"],
|
|
@@ -752,7 +752,7 @@ function initializationInterfaceContracts(): InterfaceContract[] {
|
|
|
752
752
|
{
|
|
753
753
|
name: "restore",
|
|
754
754
|
purpose: "从已验证备份恢复初始化控制数据库。",
|
|
755
|
-
request: { action: "restore", targetPath: ".planning/backups/initialization.db" },
|
|
755
|
+
request: { action: "restore", targetPath: ".devflow/planning/backups/initialization.db" },
|
|
756
756
|
response: { restored: true, database: "control.db" },
|
|
757
757
|
reads: ["targetPath", "targetPath.manifest.json"],
|
|
758
758
|
writes: ["control.db"],
|
|
@@ -803,7 +803,7 @@ function initializationInterfaceContracts(): InterfaceContract[] {
|
|
|
803
803
|
gaps: [],
|
|
804
804
|
recommendedNextAction: "query|diff|generate-docs|scan|human-review|none",
|
|
805
805
|
},
|
|
806
|
-
reads: [".planning/interface-index.json", ".planning/knowledge-index.json", ".planning/code-index/", ".context/knowledge/", "control interfaces"],
|
|
806
|
+
reads: [".devflow/planning/interface-index.json", ".devflow/planning/knowledge-index.json", ".devflow/planning/code-index/", ".devflow/context/knowledge/", "control interfaces"],
|
|
807
807
|
writes: ["initialization projections through tools only"],
|
|
808
808
|
},
|
|
809
809
|
],
|
|
@@ -818,11 +818,11 @@ function initializationInterfaceContracts(): InterfaceContract[] {
|
|
|
818
818
|
actions: [
|
|
819
819
|
{
|
|
820
820
|
name: "scan/validate/audit/query",
|
|
821
|
-
purpose: "生成并查询 .planning/code-index/ 机器代码事实。",
|
|
822
|
-
request: { action: "scan|validate|audit|query", index: ".planning/code-index", root: "." },
|
|
821
|
+
purpose: "生成并查询 .devflow/planning/code-index/ 机器代码事实。",
|
|
822
|
+
request: { action: "scan|validate|audit|query", index: ".devflow/planning/code-index", root: "." },
|
|
823
823
|
response: { generation: "uuid", files: "jsonl", symbols: "jsonl", relations: "jsonl", diagnostics: "jsonl" },
|
|
824
824
|
reads: ["source tree"],
|
|
825
|
-
writes: [".planning/code-index/"],
|
|
825
|
+
writes: [".devflow/planning/code-index/"],
|
|
826
826
|
},
|
|
827
827
|
],
|
|
828
828
|
},
|
|
@@ -846,18 +846,18 @@ function initializationInterfaceContracts(): InterfaceContract[] {
|
|
|
846
846
|
},
|
|
847
847
|
{
|
|
848
848
|
id: "artifact-knowledge-index",
|
|
849
|
-
name: ".planning/knowledge-index.json",
|
|
849
|
+
name: ".devflow/planning/knowledge-index.json",
|
|
850
850
|
kind: "artifact",
|
|
851
|
-
path: ".planning/knowledge-index.json",
|
|
851
|
+
path: ".devflow/planning/knowledge-index.json",
|
|
852
852
|
purpose: "知识页、来源文档、需求、代码事实和预留 BUG 节点的桥接索引。",
|
|
853
853
|
approval: "read",
|
|
854
854
|
actions: [
|
|
855
855
|
{
|
|
856
856
|
name: "read",
|
|
857
857
|
purpose: "让 LLM 或工具快速发现知识层节点和绑定关系。",
|
|
858
|
-
request: { path: ".planning/knowledge-index.json" },
|
|
859
|
-
response: { schemaVersion: 1, bundleRoot: ".context/knowledge", pages: [], reservedBugKnowledgeNodes: [] },
|
|
860
|
-
reads: [".planning/knowledge-index.json"],
|
|
858
|
+
request: { path: ".devflow/planning/knowledge-index.json" },
|
|
859
|
+
response: { schemaVersion: 1, bundleRoot: ".devflow/context/knowledge", pages: [], reservedBugKnowledgeNodes: [] },
|
|
860
|
+
reads: [".devflow/planning/knowledge-index.json"],
|
|
861
861
|
writes: [],
|
|
862
862
|
},
|
|
863
863
|
],
|
|
@@ -912,8 +912,8 @@ function renderInterfaceOutputs(context: ProjectContext, snapshot: { id?: unknow
|
|
|
912
912
|
const generation = codeIndexGeneration(snapshot);
|
|
913
913
|
const contracts = initializationInterfaceContracts();
|
|
914
914
|
const outputs: Array<{ targetPath: string; body: string }> = [];
|
|
915
|
-
outputs.push({ targetPath: ".context/knowledge/interfaces/index.md", body: `${frontmatter({ type: "interface-index", title: "初始化接口目录", project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 初始化接口目录\n\n${contracts.map(contract => `- [${contract.name}](./${slug(contract.id)}.md) — ${contract.purpose}`).join("\n")}\n` });
|
|
916
|
-
for (const contract of contracts) outputs.push({ targetPath: `.context/knowledge/interfaces/${slug(contract.id)}.md`, body: renderInterfaceContract(context, contract, snapshotId, generation) });
|
|
915
|
+
outputs.push({ targetPath: ".devflow/context/knowledge/interfaces/index.md", body: `${frontmatter({ type: "interface-index", title: "初始化接口目录", project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 初始化接口目录\n\n${contracts.map(contract => `- [${contract.name}](./${slug(contract.id)}.md) — ${contract.purpose}`).join("\n")}\n` });
|
|
916
|
+
for (const contract of contracts) outputs.push({ targetPath: `.devflow/context/knowledge/interfaces/${slug(contract.id)}.md`, body: renderInterfaceContract(context, contract, snapshotId, generation) });
|
|
917
917
|
return { outputs, contracts };
|
|
918
918
|
}
|
|
919
919
|
|
|
@@ -923,9 +923,9 @@ function renderOpenWikiKnowledge(context: ProjectContext, facts: Fact[], snapsho
|
|
|
923
923
|
const pages = buildKnowledgePages(knowledge, facts);
|
|
924
924
|
const sourceByRef = new Map(knowledge.sources.map(source => [sourcePageId(source), source] as const));
|
|
925
925
|
const outputs: Array<{ targetPath: string; body: string }> = [];
|
|
926
|
-
outputs.push({ targetPath: ".context/knowledge/index.md", body: `${frontmatter({ type: "knowledge-bundle-index", title: `${context.manifest.name} Knowledge`, project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# ${context.manifest.name} Knowledge\n\n## Sources\n\n${pages.filter(page => page.type === "source-summary").map(page => `- [${page.title}](./sources/${path.basename(page.path)})`).join("\n") || "- 暂无"}\n\n## Entities\n\n${pages.filter(page => page.type === "entity").map(page => `- [${page.title}](./entities/${path.basename(page.path)})`).join("\n") || "- 暂无"}\n\n## Concepts\n\n${pages.filter(page => page.type === "concept").map(page => `- [${page.title}](./concepts/${path.basename(page.path)})`).join("\n") || "- 暂无"}\n\n## Requirements\n\n${pages.filter(page => page.type === "requirement").map(page => `- [${page.title}](./requirements/${path.basename(page.path)})`).join("\n") || "- 暂无"}\n\n## Synthesis\n\n${pages.filter(page => page.type === "synthesis").map(page => `- [${page.title}](./synthesis/${path.basename(page.path)})`).join("\n") || "- 暂无"}\n\n## Bugs / Fixes / Incidents / Lessons\n\n- [BUG 模板](./bugs/_template.md)\n- [FIX 模板](./fixes/_template.md)\n- [INCIDENT 模板](./incidents/_template.md)\n- [LESSON 模板](./lessons/_template.md)\n` });
|
|
927
|
-
outputs.push({ targetPath: ".context/knowledge/log.md", body: `${frontmatter({ type: "knowledge-log", title: "Knowledge Operation Log", project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# Knowledge Operation Log\n\n- 初始化生成 OpenWiki 风格知识层。\n- 原始文档保持在项目原位置,source-summary 记录摘要和引用。\n- 代码事实来自 \`.planning/code-index/\`,桥接关系写入 \`.planning/knowledge-index.json\`。\n` });
|
|
928
|
-
outputs.push({ targetPath: ".context/knowledge/raw/README.md", body: `${frontmatter({ type: "raw-source-readme", title: "Raw Source Policy", project: context.manifest.name, status: "draft", managed: true })}# Raw Source Policy\n\n本目录保留给后续需要复制或归档的原始资料。当前初始化默认不复制原文,只在 source-summary 中记录原路径和内容哈希,避免制造第二份权威原文。\n` });
|
|
926
|
+
outputs.push({ targetPath: ".devflow/context/knowledge/index.md", body: `${frontmatter({ type: "knowledge-bundle-index", title: `${context.manifest.name} Knowledge`, project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# ${context.manifest.name} Knowledge\n\n## Sources\n\n${pages.filter(page => page.type === "source-summary").map(page => `- [${page.title}](./sources/${path.basename(page.path)})`).join("\n") || "- 暂无"}\n\n## Entities\n\n${pages.filter(page => page.type === "entity").map(page => `- [${page.title}](./entities/${path.basename(page.path)})`).join("\n") || "- 暂无"}\n\n## Concepts\n\n${pages.filter(page => page.type === "concept").map(page => `- [${page.title}](./concepts/${path.basename(page.path)})`).join("\n") || "- 暂无"}\n\n## Requirements\n\n${pages.filter(page => page.type === "requirement").map(page => `- [${page.title}](./requirements/${path.basename(page.path)})`).join("\n") || "- 暂无"}\n\n## Synthesis\n\n${pages.filter(page => page.type === "synthesis").map(page => `- [${page.title}](./synthesis/${path.basename(page.path)})`).join("\n") || "- 暂无"}\n\n## Bugs / Fixes / Incidents / Lessons\n\n- [BUG 模板](./bugs/_template.md)\n- [FIX 模板](./fixes/_template.md)\n- [INCIDENT 模板](./incidents/_template.md)\n- [LESSON 模板](./lessons/_template.md)\n` });
|
|
927
|
+
outputs.push({ targetPath: ".devflow/context/knowledge/log.md", body: `${frontmatter({ type: "knowledge-log", title: "Knowledge Operation Log", project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# Knowledge Operation Log\n\n- 初始化生成 OpenWiki 风格知识层。\n- 原始文档保持在项目原位置,source-summary 记录摘要和引用。\n- 代码事实来自 \`.devflow/planning/code-index/\`,桥接关系写入 \`.devflow/planning/knowledge-index.json\`。\n` });
|
|
928
|
+
outputs.push({ targetPath: ".devflow/context/knowledge/raw/README.md", body: `${frontmatter({ type: "raw-source-readme", title: "Raw Source Policy", project: context.manifest.name, status: "draft", managed: true })}# Raw Source Policy\n\n本目录保留给后续需要复制或归档的原始资料。当前初始化默认不复制原文,只在 source-summary 中记录原路径和内容哈希,避免制造第二份权威原文。\n` });
|
|
929
929
|
outputs.push(...bugKnowledgeTemplates(context, snapshotId, generation));
|
|
930
930
|
for (const page of pages) outputs.push({ targetPath: page.path, body: renderKnowledgePage(context, page, sourceByRef, snapshotId, generation) });
|
|
931
931
|
return { outputs, pages };
|
|
@@ -935,12 +935,12 @@ function renderKnowledgeDocuments(context: ProjectContext, facts: Fact[], intro:
|
|
|
935
935
|
const snapshotId = String(snapshot.id ?? "");
|
|
936
936
|
const generation = codeIndexGeneration(snapshot);
|
|
937
937
|
const outputs: Array<{ targetPath: string; body: string }> = [];
|
|
938
|
-
outputs.push({ targetPath: ".context/index.md", body: `${frontmatter({ type: "knowledge-index", scope: "project", project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# ${context.manifest.name} 项目知识库\n\n## 项目级文档\n\n${KNOWLEDGE_DOCUMENT_TYPES.map(type => `- [${documentTitle(type)}](/project/${type}.md)`).join("\n")}\n\n## 需求级文档\n\n- 新需求使用 [.context/requirements/_template/index.md](/requirements/_template/index.md) 作为文档包模板。\n\n## 机器索引\n\n- 文档索引:\`.planning/document-index.json\`\n- 代码索引:\`.planning/code-index/\`\n` });
|
|
939
|
-
outputs.push({ targetPath: ".context/spec/index.md", body: `${frontmatter({ type: "compat-spec-index", scope: "project", project: context.manifest.name, status: "compat", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 兼容 Spec 入口\n\n本文件是项目初始化的兼容入口,用于旧流程查找 \`.context/spec/index.md\`。新的权威入口如下:\n\n- 项目知识库:[../index.md](../index.md)\n- 项目级文档:[../project/index.md](../project/index.md)\n- OpenWiki 风格知识层:[../knowledge/index.md](../knowledge/index.md)\n- 初始化接口目录:[../knowledge/interfaces/index.md](../knowledge/interfaces/index.md)\n- 文档索引:\`.planning/document-index.json\`\n- 知识索引:\`.planning/knowledge-index.json\`\n- 接口索引:\`.planning/interface-index.json\`\n- 代码索引:\`.planning/code-index/\`\n\n此兼容入口不表示需求、设计或 API 已批准;人工确认前所有生成内容保持 draft/compat 状态。\n` });
|
|
940
|
-
outputs.push({ targetPath: ".context/project/index.md", body: `${frontmatter({ type: "project-document-index", scope: "project", project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 项目级文档\n\n${KNOWLEDGE_DOCUMENT_TYPES.map(type => `- [${documentTitle(type)}](./${type}.md)`).join("\n")}\n` });
|
|
941
|
-
for (const type of KNOWLEDGE_DOCUMENT_TYPES) outputs.push({ targetPath: `.context/project/${type}.md`, body: projectKnowledgeBody(context, type, facts, intro, snapshotId, knowledge, generation) });
|
|
942
|
-
outputs.push({ targetPath: ".context/requirements/_template/index.md", body: `${frontmatter({ type: "requirement-document-index", scope: "requirement", project: context.manifest.name, requirement_id: "REQ-TEMPLATE", status: "template", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 需求级文档模板\n\n${KNOWLEDGE_DOCUMENT_TYPES.map(type => `- [${documentTitle(type)}](./${type === "requirements" ? "requirement" : type}.md)`).join("\n")}\n` });
|
|
943
|
-
for (const type of KNOWLEDGE_DOCUMENT_TYPES) outputs.push({ targetPath: `.context/requirements/_template/${type === "requirements" ? "requirement" : type}.md`, body: requirementTemplateBody(context, type, snapshotId, generation) });
|
|
938
|
+
outputs.push({ targetPath: ".devflow/context/index.md", body: `${frontmatter({ type: "knowledge-index", scope: "project", project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# ${context.manifest.name} 项目知识库\n\n## 项目级文档\n\n${KNOWLEDGE_DOCUMENT_TYPES.map(type => `- [${documentTitle(type)}](/project/${type}.md)`).join("\n")}\n\n## 需求级文档\n\n- 新需求使用 [.devflow/context/requirements/_template/index.md](/requirements/_template/index.md) 作为文档包模板。\n\n## 机器索引\n\n- 文档索引:\`.devflow/planning/document-index.json\`\n- 代码索引:\`.devflow/planning/code-index/\`\n` });
|
|
939
|
+
outputs.push({ targetPath: ".devflow/context/spec/index.md", body: `${frontmatter({ type: "compat-spec-index", scope: "project", project: context.manifest.name, status: "compat", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 兼容 Spec 入口\n\n本文件是项目初始化的兼容入口,用于旧流程查找 \`.devflow/context/spec/index.md\`。新的权威入口如下:\n\n- 项目知识库:[../index.md](../index.md)\n- 项目级文档:[../project/index.md](../project/index.md)\n- OpenWiki 风格知识层:[../knowledge/index.md](../knowledge/index.md)\n- 初始化接口目录:[../knowledge/interfaces/index.md](../knowledge/interfaces/index.md)\n- 文档索引:\`.devflow/planning/document-index.json\`\n- 知识索引:\`.devflow/planning/knowledge-index.json\`\n- 接口索引:\`.devflow/planning/interface-index.json\`\n- 代码索引:\`.devflow/planning/code-index/\`\n\n此兼容入口不表示需求、设计或 API 已批准;人工确认前所有生成内容保持 draft/compat 状态。\n` });
|
|
940
|
+
outputs.push({ targetPath: ".devflow/context/project/index.md", body: `${frontmatter({ type: "project-document-index", scope: "project", project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 项目级文档\n\n${KNOWLEDGE_DOCUMENT_TYPES.map(type => `- [${documentTitle(type)}](./${type}.md)`).join("\n")}\n` });
|
|
941
|
+
for (const type of KNOWLEDGE_DOCUMENT_TYPES) outputs.push({ targetPath: `.devflow/context/project/${type}.md`, body: projectKnowledgeBody(context, type, facts, intro, snapshotId, knowledge, generation) });
|
|
942
|
+
outputs.push({ targetPath: ".devflow/context/requirements/_template/index.md", body: `${frontmatter({ type: "requirement-document-index", scope: "requirement", project: context.manifest.name, requirement_id: "REQ-TEMPLATE", status: "template", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 需求级文档模板\n\n${KNOWLEDGE_DOCUMENT_TYPES.map(type => `- [${documentTitle(type)}](./${type === "requirements" ? "requirement" : type}.md)`).join("\n")}\n` });
|
|
943
|
+
for (const type of KNOWLEDGE_DOCUMENT_TYPES) outputs.push({ targetPath: `.devflow/context/requirements/_template/${type === "requirements" ? "requirement" : type}.md`, body: requirementTemplateBody(context, type, snapshotId, generation) });
|
|
944
944
|
for (const item of knowledge.classifications.filter(item => item.scope === "requirement" && item.confidence !== "exact").slice(0, 100)) {
|
|
945
945
|
const source = knowledge.sources.find(source => source.id === item.sourceDocumentId);
|
|
946
946
|
if (source) outputs.push(...inboxRequirementBody(context, source, item, snapshotId, generation));
|
|
@@ -952,21 +952,21 @@ function renderDocumentIndex(context: ProjectContext, snapshot: { id?: unknown;
|
|
|
952
952
|
const snapshotId = String(snapshot.id ?? "");
|
|
953
953
|
const generation = codeIndexGeneration(snapshot);
|
|
954
954
|
const documents = outputs
|
|
955
|
-
.filter(output => output.targetPath.startsWith(".context/"))
|
|
955
|
+
.filter(output => output.targetPath.startsWith(".devflow/context/"))
|
|
956
956
|
.map(output => {
|
|
957
957
|
const parts = output.targetPath.split("/");
|
|
958
958
|
const file = parts.at(-1) ?? "";
|
|
959
|
-
const scope: IndexedDocumentScope = parts[
|
|
960
|
-
const type = output.targetPath === ".context/index.md" ? "knowledge-index"
|
|
961
|
-
: output.targetPath === ".context/knowledge/index.md" ? "knowledge-bundle-index"
|
|
962
|
-
: scope === "knowledge" ? `knowledge-${parts[
|
|
959
|
+
const scope: IndexedDocumentScope = parts[2] === "project" ? "project" : parts[2] === "requirements" ? "requirement" : parts[2] === "knowledge" ? "knowledge" : "project";
|
|
960
|
+
const type = output.targetPath === ".devflow/context/index.md" ? "knowledge-index"
|
|
961
|
+
: output.targetPath === ".devflow/context/knowledge/index.md" ? "knowledge-bundle-index"
|
|
962
|
+
: scope === "knowledge" ? `knowledge-${parts[3] === "log.md" ? "log" : parts[3] ?? "page"}`
|
|
963
963
|
: scope === "project" ? `project-${file.replace(/\.md$/, "")}`
|
|
964
964
|
: `requirement-${file.replace(/^requirement\.md$/, "requirements").replace(/\.md$/, "")}`;
|
|
965
965
|
return {
|
|
966
966
|
path: output.targetPath,
|
|
967
967
|
type,
|
|
968
968
|
scope,
|
|
969
|
-
requirementId: scope === "requirement" ? (parts[
|
|
969
|
+
requirementId: scope === "requirement" ? (parts[3] === "_template" ? "REQ-TEMPLATE" : parts[3]) : undefined,
|
|
970
970
|
status: parts.includes("_template") ? "template" : parts.includes("_inbox") ? "inbox" : "draft",
|
|
971
971
|
managed: true,
|
|
972
972
|
sourceSnapshot: snapshotId,
|
|
@@ -974,7 +974,7 @@ function renderDocumentIndex(context: ProjectContext, snapshot: { id?: unknown;
|
|
|
974
974
|
};
|
|
975
975
|
});
|
|
976
976
|
return {
|
|
977
|
-
targetPath: ".planning/document-index.json",
|
|
977
|
+
targetPath: ".devflow/planning/document-index.json",
|
|
978
978
|
body: `${JSON.stringify({
|
|
979
979
|
schemaVersion: 2,
|
|
980
980
|
project: context.manifest.name,
|
|
@@ -993,14 +993,14 @@ function renderKnowledgeIndex(context: ProjectContext, snapshot: { id?: unknown;
|
|
|
993
993
|
const snapshotId = String(snapshot.id ?? "");
|
|
994
994
|
const generation = codeIndexGeneration(snapshot);
|
|
995
995
|
return {
|
|
996
|
-
targetPath: ".planning/knowledge-index.json",
|
|
996
|
+
targetPath: ".devflow/planning/knowledge-index.json",
|
|
997
997
|
body: `${JSON.stringify({
|
|
998
998
|
schemaVersion: 1,
|
|
999
999
|
project: context.manifest.name,
|
|
1000
1000
|
projectId: context.manifest.projectId,
|
|
1001
1001
|
sourceSnapshot: snapshotId,
|
|
1002
1002
|
codeIndexGeneration: generation ?? null,
|
|
1003
|
-
bundleRoot: ".context/knowledge",
|
|
1003
|
+
bundleRoot: ".devflow/context/knowledge",
|
|
1004
1004
|
sourceDocuments: knowledge.sources,
|
|
1005
1005
|
classifications: knowledge.classifications,
|
|
1006
1006
|
pages: pages.map(page => ({
|
|
@@ -1015,10 +1015,10 @@ function renderKnowledgeIndex(context: ProjectContext, snapshot: { id?: unknown;
|
|
|
1015
1015
|
confidence: page.confidence,
|
|
1016
1016
|
})),
|
|
1017
1017
|
reservedBugKnowledgeNodes: [
|
|
1018
|
-
{ id: "BUG-TEMPLATE", path: ".context/knowledge/bugs/_template.md", type: "bug-report", purpose: "问题现象、影响、复现、根因和回归绑定模板" },
|
|
1019
|
-
{ id: "FIX-TEMPLATE", path: ".context/knowledge/fixes/_template.md", type: "bug-fix", purpose: "修复动作、变更原因、变更文件和回归覆盖模板" },
|
|
1020
|
-
{ id: "INC-TEMPLATE", path: ".context/knowledge/incidents/_template.md", type: "incident", purpose: "事故时间线、影响、缓解和后续行动模板" },
|
|
1021
|
-
{ id: "LESSON-TEMPLATE", path: ".context/knowledge/lessons/_template.md", type: "lesson", purpose: "可复用排障经验和检查规则模板" },
|
|
1018
|
+
{ id: "BUG-TEMPLATE", path: ".devflow/context/knowledge/bugs/_template.md", type: "bug-report", purpose: "问题现象、影响、复现、根因和回归绑定模板" },
|
|
1019
|
+
{ id: "FIX-TEMPLATE", path: ".devflow/context/knowledge/fixes/_template.md", type: "bug-fix", purpose: "修复动作、变更原因、变更文件和回归覆盖模板" },
|
|
1020
|
+
{ id: "INC-TEMPLATE", path: ".devflow/context/knowledge/incidents/_template.md", type: "incident", purpose: "事故时间线、影响、缓解和后续行动模板" },
|
|
1021
|
+
{ id: "LESSON-TEMPLATE", path: ".devflow/context/knowledge/lessons/_template.md", type: "lesson", purpose: "可复用排障经验和检查规则模板" },
|
|
1022
1022
|
],
|
|
1023
1023
|
interfaces: interfaces.map(item => ({ id: item.id, name: item.name, kind: item.kind, path: item.path, purpose: item.purpose, approval: item.approval, actions: item.actions.map(action => action.name) })),
|
|
1024
1024
|
}, null, 2)}\n`,
|
|
@@ -1029,7 +1029,7 @@ function renderInterfaceIndex(context: ProjectContext, snapshot: { id?: unknown;
|
|
|
1029
1029
|
const snapshotId = String(snapshot.id ?? "");
|
|
1030
1030
|
const generation = codeIndexGeneration(snapshot);
|
|
1031
1031
|
return {
|
|
1032
|
-
targetPath: ".planning/interface-index.json",
|
|
1032
|
+
targetPath: ".devflow/planning/interface-index.json",
|
|
1033
1033
|
body: `${JSON.stringify({
|
|
1034
1034
|
schemaVersion: 1,
|
|
1035
1035
|
project: context.manifest.name,
|
|
@@ -1068,7 +1068,7 @@ export async function generateProjectDocuments(context: ProjectContext, snapshot
|
|
|
1068
1068
|
} while (after);
|
|
1069
1069
|
const run = runId ? store.getRun(runId) as { collections?: Array<{ repository?: string; kind?: string; status?: string; diagnostics?: unknown[] }> } | null : null;
|
|
1070
1070
|
const collections = run?.collections ?? [];
|
|
1071
|
-
const batchJob = store.createDocumentJob(snapshotId, { targetPath: ".planning/initialization/report.md", ownership: "generated", templateVersion: TEMPLATE_VERSION, modelRole: options.organizer ? `${options.organizer.role}:${options.organizer.model}` : options.mode, input: facts.map(fact => fact.objectKey) });
|
|
1071
|
+
const batchJob = store.createDocumentJob(snapshotId, { targetPath: ".devflow/planning/initialization/report.md", ownership: "generated", templateVersion: TEMPLATE_VERSION, modelRole: options.organizer ? `${options.organizer.role}:${options.organizer.model}` : options.mode, input: facts.map(fact => fact.objectKey) });
|
|
1072
1072
|
let organized;
|
|
1073
1073
|
try { organized = await narrative(context, "project-initialization", facts, options.mode, options.organizer); }
|
|
1074
1074
|
catch (error) { store.failDocumentJob(batchJob, error instanceof Error ? error.message : String(error)); throw error; }
|
|
@@ -4,8 +4,9 @@ import path from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import { FACT_KINDS, InitializationStore, type FactInput, type FactKind, type ScanConfigInput } from "./initialization-store.ts";
|
|
6
6
|
import type { ProjectContext } from "./project-context.ts";
|
|
7
|
+
import { DEVFLOW_SCAN_EXCLUDES, PLANNING_DIRECTORY } from "./project-layout.ts";
|
|
7
8
|
|
|
8
|
-
const DEFAULT_EXCLUDED = new Set([".git", ".devflow", ".planning", "node_modules", "dist", "build", "coverage", "target", "vendor", ".next", ".nuxt"]);
|
|
9
|
+
const DEFAULT_EXCLUDED = new Set([".git", ".devflow", ".planning", ".context", "node_modules", "dist", "build", "coverage", "target", "vendor", ".next", ".nuxt"]);
|
|
9
10
|
const TEXT_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".rb", ".go", ".java", ".kt", ".rs", ".php", ".sql", ".json", ".yaml", ".yml", ".toml", ".md"]);
|
|
10
11
|
|
|
11
12
|
type SourceFile = { repository: string; path: string; absolute: string; contentHash: string; size: number; text?: string };
|
|
@@ -78,7 +79,7 @@ async function scan(context: ProjectContext, config: ScanConfigInput): Promise<{
|
|
|
78
79
|
const diagnostics: ScanDiagnostic[] = [];
|
|
79
80
|
const repositoryVersions: Array<{ repository: string; revision: string | null }> = [];
|
|
80
81
|
const includes = config.includes ?? ["**/*"];
|
|
81
|
-
const excludes = config.excludes ?? [
|
|
82
|
+
const excludes = [...new Set([...DEVFLOW_SCAN_EXCLUDES, ...(config.excludes ?? [])])];
|
|
82
83
|
for (const repository of config.repositories) {
|
|
83
84
|
const root = path.resolve(context.root, repository.relativePath);
|
|
84
85
|
if (!path.isAbsolute(repository.relativePath) && root !== context.root && !root.startsWith(`${context.root}${path.sep}`)) throw new Error(`Repository escapes project root: ${repository.relativePath}`);
|
|
@@ -217,26 +218,39 @@ async function runProcess(argv: string[], cwd: string, label = path.basename(arg
|
|
|
217
218
|
|
|
218
219
|
async function buildCodeIndex(context: ProjectContext, config: ScanConfigInput): Promise<Record<string, unknown>> {
|
|
219
220
|
const script = path.resolve(import.meta.dir, "../plugins/devflow-intel/skills/workflow-code-index/scripts/code_index.py");
|
|
220
|
-
const output =
|
|
221
|
+
const output = `${PLANNING_DIRECTORY}/.code-index-pending-${randomUUID()}`;
|
|
221
222
|
const codebases = config.repositories.flatMap(repository => ["--codebase", `${repository.name}:${path.resolve(context.root, repository.relativePath)}:${repository.role}`]);
|
|
222
223
|
const scopeArgs = [
|
|
223
224
|
...(config.includes ?? ["**/*"]).flatMap(pattern => ["--include", pattern]),
|
|
224
|
-
...(config.excludes ?? [
|
|
225
|
+
...([...new Set([...DEVFLOW_SCAN_EXCLUDES, ...(config.excludes ?? [])])]).flatMap(pattern => ["--exclude", pattern]),
|
|
225
226
|
"--max-file-size", String(config.maxFileSize ?? 2 * 1024 * 1024),
|
|
226
227
|
];
|
|
227
228
|
await runProcess(["python3", script, "scan", "--root", context.root, "--output", output, ...scopeArgs, ...codebases], context.root, "code-index scan");
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
try {
|
|
230
|
+
await runProcess(["python3", script, "validate", "--index", output, "--level", "publish"], context.root, "code-index validate");
|
|
231
|
+
await runProcess(["python3", script, "audit", "--root", context.root, "--index", output, "--skill-root", path.resolve(import.meta.dir, "../plugins/devflow-intel/skills"), "--level", "publish"], context.root, "code-index audit");
|
|
232
|
+
} catch (error) {
|
|
233
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
234
|
+
return {
|
|
235
|
+
path: `${PLANNING_DIRECTORY}/code-index`,
|
|
236
|
+
stagingPath: output,
|
|
237
|
+
status: "degraded",
|
|
238
|
+
reason: "code_index_publish_failed",
|
|
239
|
+
fallback: "bounded_source_search",
|
|
240
|
+
diagnostics: [message],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
230
243
|
const snapshotPath = path.join(context.root, output, "SNAPSHOT.json");
|
|
231
244
|
const bytes = await readFile(snapshotPath); const snapshot = JSON.parse(bytes.toString("utf8")) as Record<string, unknown>;
|
|
232
245
|
if (!snapshot.generation || !snapshot.immutablePath) throw new Error("Code index did not publish an immutable generation");
|
|
233
246
|
const workspace = (snapshot.workspace ?? {}) as Record<string, unknown>;
|
|
234
|
-
return { path:
|
|
247
|
+
return { path: `${PLANNING_DIRECTORY}/code-index`, stagingPath: output, generation: snapshot.generation, immutablePath: snapshot.immutablePath, snapshotHash: digest(bytes), contentManifestHash: workspace.contentManifestHash, repositoryRevision: workspace.repositoryRevision, status: "complete" };
|
|
235
248
|
}
|
|
236
249
|
|
|
237
250
|
async function promoteCodeIndex(context: ProjectContext, indexRef: Record<string, unknown>): Promise<() => Promise<void>> {
|
|
251
|
+
if (indexRef.status !== "complete") return async () => {};
|
|
238
252
|
const stagingPath = typeof indexRef.stagingPath === "string" ? path.resolve(context.root, indexRef.stagingPath) : null;
|
|
239
|
-
const target = path.resolve(context.root, "
|
|
253
|
+
const target = path.resolve(context.root, PLANNING_DIRECTORY, "code-index");
|
|
240
254
|
if (!stagingPath) return async () => {};
|
|
241
255
|
const previous = `${target}.previous-${randomUUID()}`;
|
|
242
256
|
const existing = await stat(target).catch(() => null);
|
|
@@ -340,7 +354,7 @@ export async function runProjectInitialization(context: ProjectContext, input: {
|
|
|
340
354
|
} catch (error) {
|
|
341
355
|
const message = error instanceof Error ? error.message : String(error);
|
|
342
356
|
diagnostics.push(`Document generation failed independently: ${message}`);
|
|
343
|
-
documents = [{ targetPath: ".planning/initialization/report.md", status: "failed", error: message }];
|
|
357
|
+
documents = [{ targetPath: ".devflow/planning/initialization/report.md", status: "failed", error: message }];
|
|
344
358
|
}
|
|
345
359
|
}
|
|
346
360
|
return {
|