@tuan_son.dinh/gsd 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +453 -0
- package/dist/app-paths.d.ts +4 -0
- package/dist/app-paths.js +6 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +269 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +70 -0
- package/dist/logo.d.ts +16 -0
- package/dist/logo.js +25 -0
- package/dist/onboarding.d.ts +43 -0
- package/dist/onboarding.js +418 -0
- package/dist/pi-migration.d.ts +14 -0
- package/dist/pi-migration.js +57 -0
- package/dist/resource-loader.d.ts +22 -0
- package/dist/resource-loader.js +60 -0
- package/dist/tool-bootstrap.d.ts +4 -0
- package/dist/tool-bootstrap.js +74 -0
- package/dist/wizard.d.ts +7 -0
- package/dist/wizard.js +25 -0
- package/package.json +60 -0
- package/patches/@mariozechner+pi-coding-agent+0.57.1.patch +108 -0
- package/patches/@mariozechner+pi-tui+0.57.1.patch +47 -0
- package/pkg/dist/modes/interactive/theme/dark.json +85 -0
- package/pkg/dist/modes/interactive/theme/light.json +84 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.js +949 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
- package/pkg/package.json +8 -0
- package/scripts/postinstall.js +127 -0
- package/src/resources/GSD-WORKFLOW.md +661 -0
- package/src/resources/agents/researcher.md +29 -0
- package/src/resources/agents/scout.md +56 -0
- package/src/resources/agents/worker.md +31 -0
- package/src/resources/extensions/ask-user-questions.ts +249 -0
- package/src/resources/extensions/bg-shell/index.ts +2808 -0
- package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
- package/src/resources/extensions/browser-tools/core.js +1057 -0
- package/src/resources/extensions/browser-tools/index.ts +4989 -0
- package/src/resources/extensions/browser-tools/package.json +20 -0
- package/src/resources/extensions/context7/index.ts +428 -0
- package/src/resources/extensions/context7/package.json +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +352 -0
- package/src/resources/extensions/google-search/index.ts +323 -0
- package/src/resources/extensions/google-search/package.json +9 -0
- package/src/resources/extensions/gsd/activity-log.ts +69 -0
- package/src/resources/extensions/gsd/auto.ts +2744 -0
- package/src/resources/extensions/gsd/commands.ts +313 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +85 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +521 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +176 -0
- package/src/resources/extensions/gsd/doctor.ts +690 -0
- package/src/resources/extensions/gsd/files.ts +732 -0
- package/src/resources/extensions/gsd/git-service.ts +597 -0
- package/src/resources/extensions/gsd/gitignore.ts +168 -0
- package/src/resources/extensions/gsd/guided-flow.ts +817 -0
- package/src/resources/extensions/gsd/index.ts +558 -0
- package/src/resources/extensions/gsd/metrics.ts +374 -0
- package/src/resources/extensions/gsd/migrate/command.ts +218 -0
- package/src/resources/extensions/gsd/migrate/index.ts +42 -0
- package/src/resources/extensions/gsd/migrate/parser.ts +323 -0
- package/src/resources/extensions/gsd/migrate/parsers.ts +624 -0
- package/src/resources/extensions/gsd/migrate/preview.ts +48 -0
- package/src/resources/extensions/gsd/migrate/transformer.ts +346 -0
- package/src/resources/extensions/gsd/migrate/types.ts +370 -0
- package/src/resources/extensions/gsd/migrate/validator.ts +55 -0
- package/src/resources/extensions/gsd/migrate/writer.ts +539 -0
- package/src/resources/extensions/gsd/observability-validator.ts +408 -0
- package/src/resources/extensions/gsd/package.json +11 -0
- package/src/resources/extensions/gsd/paths.ts +308 -0
- package/src/resources/extensions/gsd/preferences.ts +757 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +50 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +29 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +189 -0
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
- package/src/resources/extensions/gsd/prompts/execute-task.md +61 -0
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +59 -0
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +23 -0
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +11 -0
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +65 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +51 -0
- package/src/resources/extensions/gsd/prompts/queue.md +85 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +39 -0
- package/src/resources/extensions/gsd/prompts/research-milestone.md +37 -0
- package/src/resources/extensions/gsd/prompts/research-slice.md +28 -0
- package/src/resources/extensions/gsd/prompts/review-migration.md +66 -0
- package/src/resources/extensions/gsd/prompts/run-uat.md +109 -0
- package/src/resources/extensions/gsd/prompts/system.md +187 -0
- package/src/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
- package/src/resources/extensions/gsd/session-forensics.ts +487 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +137 -0
- package/src/resources/extensions/gsd/state.ts +460 -0
- package/src/resources/extensions/gsd/templates/context.md +76 -0
- package/src/resources/extensions/gsd/templates/decisions.md +8 -0
- package/src/resources/extensions/gsd/templates/milestone-summary.md +73 -0
- package/src/resources/extensions/gsd/templates/plan.md +131 -0
- package/src/resources/extensions/gsd/templates/preferences.md +24 -0
- package/src/resources/extensions/gsd/templates/project.md +31 -0
- package/src/resources/extensions/gsd/templates/reassessment.md +28 -0
- package/src/resources/extensions/gsd/templates/requirements.md +81 -0
- package/src/resources/extensions/gsd/templates/research.md +46 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +118 -0
- package/src/resources/extensions/gsd/templates/slice-context.md +58 -0
- package/src/resources/extensions/gsd/templates/slice-summary.md +99 -0
- package/src/resources/extensions/gsd/templates/state.md +19 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +52 -0
- package/src/resources/extensions/gsd/templates/task-summary.md +57 -0
- package/src/resources/extensions/gsd/templates/uat.md +54 -0
- package/src/resources/extensions/gsd/tests/activity-log-prune.test.ts +327 -0
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +225 -0
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +341 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +689 -0
- package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/doctor.test.ts +505 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +1313 -0
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +308 -0
- package/src/resources/extensions/gsd/tests/metrics-io.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +217 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +390 -0
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +786 -0
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +657 -0
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +443 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +420 -0
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +309 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +1351 -0
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +163 -0
- package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +521 -0
- package/src/resources/extensions/gsd/tests/requirements.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +34 -0
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +348 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +247 -0
- package/src/resources/extensions/gsd/tests/workflow-config.test.mjs +53 -0
- package/src/resources/extensions/gsd/tests/workspace-index.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +253 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/worktree.test.ts +264 -0
- package/src/resources/extensions/gsd/types.ts +159 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +184 -0
- package/src/resources/extensions/gsd/workspace-index.ts +203 -0
- package/src/resources/extensions/gsd/worktree-command.ts +845 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +392 -0
- package/src/resources/extensions/gsd/worktree.ts +183 -0
- package/src/resources/extensions/mac-tools/index.ts +852 -0
- package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
- package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
- package/src/resources/extensions/mcporter/index.ts +429 -0
- package/src/resources/extensions/remote-questions/config.ts +81 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +128 -0
- package/src/resources/extensions/remote-questions/format.ts +163 -0
- package/src/resources/extensions/remote-questions/manager.ts +192 -0
- package/src/resources/extensions/remote-questions/remote-command.ts +307 -0
- package/src/resources/extensions/remote-questions/slack-adapter.ts +92 -0
- package/src/resources/extensions/remote-questions/status.ts +31 -0
- package/src/resources/extensions/remote-questions/store.ts +77 -0
- package/src/resources/extensions/remote-questions/types.ts +75 -0
- package/src/resources/extensions/search-the-web/cache.ts +78 -0
- package/src/resources/extensions/search-the-web/command-search-provider.ts +95 -0
- package/src/resources/extensions/search-the-web/format.ts +258 -0
- package/src/resources/extensions/search-the-web/http.ts +238 -0
- package/src/resources/extensions/search-the-web/index.ts +65 -0
- package/src/resources/extensions/search-the-web/native-search.ts +157 -0
- package/src/resources/extensions/search-the-web/provider.ts +118 -0
- package/src/resources/extensions/search-the-web/tavily.ts +116 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +561 -0
- package/src/resources/extensions/search-the-web/tool-search.ts +576 -0
- package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
- package/src/resources/extensions/shared/confirm-ui.ts +126 -0
- package/src/resources/extensions/shared/interview-ui.ts +613 -0
- package/src/resources/extensions/shared/next-action-ui.ts +197 -0
- package/src/resources/extensions/shared/progress-widget.ts +282 -0
- package/src/resources/extensions/shared/terminal.ts +23 -0
- package/src/resources/extensions/shared/thinking-widget.ts +107 -0
- package/src/resources/extensions/shared/ui.ts +400 -0
- package/src/resources/extensions/shared/wizard-ui.ts +551 -0
- package/src/resources/extensions/slash-commands/audit.ts +88 -0
- package/src/resources/extensions/slash-commands/clear.ts +10 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +297 -0
- package/src/resources/extensions/slash-commands/create-slash-command.ts +234 -0
- package/src/resources/extensions/slash-commands/index.ts +12 -0
- package/src/resources/extensions/subagent/agents.ts +126 -0
- package/src/resources/extensions/subagent/index.ts +1020 -0
- package/src/resources/extensions/voice/index.ts +195 -0
- package/src/resources/extensions/voice/speech-recognizer.swift +154 -0
- package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
- package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
- package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
- package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
- package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
- package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
- package/src/resources/skills/frontend-design/SKILL.md +45 -0
- package/src/resources/skills/swiftui/SKILL.md +208 -0
- package/src/resources/skills/swiftui/references/animations.md +921 -0
- package/src/resources/skills/swiftui/references/architecture.md +1561 -0
- package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/src/resources/skills/swiftui/references/navigation.md +1492 -0
- package/src/resources/skills/swiftui/references/networking-async.md +214 -0
- package/src/resources/skills/swiftui/references/performance.md +1706 -0
- package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/src/resources/skills/swiftui/references/state-management.md +1443 -0
- package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
- package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
// Old .planning directory parser orchestrator
|
|
2
|
+
// Walks a .planning directory tree, delegates to per-file parsers,
|
|
3
|
+
// and assembles the complete typed PlanningProject.
|
|
4
|
+
// Zero Pi dependencies — uses only Node built-ins + local parsers.
|
|
5
|
+
|
|
6
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
7
|
+
import { join, basename } from 'node:path';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
parseOldRoadmap,
|
|
11
|
+
parseOldPlan,
|
|
12
|
+
parseOldSummary,
|
|
13
|
+
parseOldRequirements,
|
|
14
|
+
parseOldProject,
|
|
15
|
+
parseOldState,
|
|
16
|
+
parseOldConfig,
|
|
17
|
+
} from './parsers.ts';
|
|
18
|
+
import { validatePlanningDirectory } from './validator.ts';
|
|
19
|
+
|
|
20
|
+
import type {
|
|
21
|
+
PlanningProject,
|
|
22
|
+
PlanningPhase,
|
|
23
|
+
PlanningQuickTask,
|
|
24
|
+
PlanningMilestone,
|
|
25
|
+
PlanningResearch,
|
|
26
|
+
PlanningPhaseFile,
|
|
27
|
+
} from './types.ts';
|
|
28
|
+
|
|
29
|
+
// ─── Helpers ───────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
/** Read a file, returning null if it doesn't exist. */
|
|
32
|
+
function readOptional(path: string): string | null {
|
|
33
|
+
try {
|
|
34
|
+
return readFileSync(path, 'utf-8');
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** List directory entries (names only), returning [] if dir doesn't exist. */
|
|
41
|
+
function listDir(path: string): string[] {
|
|
42
|
+
try {
|
|
43
|
+
return readdirSync(path);
|
|
44
|
+
} catch {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Check if a path is a directory. */
|
|
50
|
+
function isDir(path: string): boolean {
|
|
51
|
+
try {
|
|
52
|
+
return statSync(path).isDirectory();
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Extract phase number and slug from a directory name like "29-auth-system" or "01.2-setup". */
|
|
59
|
+
function parsePhaseDir(dirName: string): { number: number; slug: string } | null {
|
|
60
|
+
const match = dirName.match(/^(\d+(?:\.\d+)?)-(.+)$/);
|
|
61
|
+
if (!match) return null;
|
|
62
|
+
return { number: parseFloat(match[1]), slug: match[2] };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Extract quick task number and slug from a directory name like "001-fix-login". */
|
|
66
|
+
function parseQuickDir(dirName: string): { number: number; slug: string } | null {
|
|
67
|
+
const match = dirName.match(/^(\d+)-(.+)$/);
|
|
68
|
+
if (!match) return null;
|
|
69
|
+
return { number: parseInt(match[1], 10), slug: match[2] };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── Phase Scanner ─────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
/** Plan file pattern: NN-NN-PLAN.md (e.g. 29-01-PLAN.md) */
|
|
75
|
+
const PLAN_RE = /^(\d+(?:\.\d+)?)-(\d+)-PLAN\.md$/i;
|
|
76
|
+
|
|
77
|
+
/** Summary file pattern: NN-NN-SUMMARY.md (e.g. 29-01-SUMMARY.md) */
|
|
78
|
+
const SUMMARY_RE = /^(\d+(?:\.\d+)?)-(\d+)-SUMMARY\.md$/i;
|
|
79
|
+
|
|
80
|
+
/** Research file pattern: contains RESEARCH (case-insensitive) */
|
|
81
|
+
const RESEARCH_RE = /research/i;
|
|
82
|
+
|
|
83
|
+
/** Verification file pattern: contains VERIFICATION (case-insensitive) */
|
|
84
|
+
const VERIFICATION_RE = /verification/i;
|
|
85
|
+
|
|
86
|
+
function scanPhaseDirectory(phaseDir: string, dirName: string, parsed: ReturnType<typeof parsePhaseDir>): PlanningPhase {
|
|
87
|
+
const phase: PlanningPhase = {
|
|
88
|
+
dirName,
|
|
89
|
+
number: parsed!.number,
|
|
90
|
+
slug: parsed!.slug,
|
|
91
|
+
plans: {},
|
|
92
|
+
summaries: {},
|
|
93
|
+
research: [],
|
|
94
|
+
verifications: [],
|
|
95
|
+
extraFiles: [],
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const entries = listDir(phaseDir);
|
|
99
|
+
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
const entryPath = join(phaseDir, entry);
|
|
102
|
+
|
|
103
|
+
// Skip directories within phase dirs
|
|
104
|
+
if (isDir(entryPath)) continue;
|
|
105
|
+
|
|
106
|
+
const planMatch = entry.match(PLAN_RE);
|
|
107
|
+
if (planMatch) {
|
|
108
|
+
const planNumber = planMatch[2];
|
|
109
|
+
const content = readFileSync(entryPath, 'utf-8');
|
|
110
|
+
phase.plans[planNumber] = parseOldPlan(content, entry, planNumber);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const summaryMatch = entry.match(SUMMARY_RE);
|
|
115
|
+
if (summaryMatch) {
|
|
116
|
+
const planNumber = summaryMatch[2];
|
|
117
|
+
const content = readFileSync(entryPath, 'utf-8');
|
|
118
|
+
phase.summaries[planNumber] = parseOldSummary(content, entry, planNumber);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (VERIFICATION_RE.test(entry)) {
|
|
123
|
+
const content = readFileSync(entryPath, 'utf-8');
|
|
124
|
+
phase.verifications.push({ fileName: entry, content });
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (RESEARCH_RE.test(entry)) {
|
|
129
|
+
const content = readFileSync(entryPath, 'utf-8');
|
|
130
|
+
phase.research.push({ fileName: entry, content });
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Everything else is an extra file
|
|
135
|
+
const content = readFileSync(entryPath, 'utf-8');
|
|
136
|
+
phase.extraFiles.push({ fileName: entry, content });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return phase;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ─── Quick Task Scanner ────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
function scanQuickDirectory(quickDir: string): PlanningQuickTask[] {
|
|
145
|
+
const tasks: PlanningQuickTask[] = [];
|
|
146
|
+
const entries = listDir(quickDir).sort();
|
|
147
|
+
|
|
148
|
+
for (const dirName of entries) {
|
|
149
|
+
const dirPath = join(quickDir, dirName);
|
|
150
|
+
if (!isDir(dirPath)) continue;
|
|
151
|
+
|
|
152
|
+
const parsed = parseQuickDir(dirName);
|
|
153
|
+
if (!parsed) continue;
|
|
154
|
+
|
|
155
|
+
// Look for NNN-PLAN.md and NNN-SUMMARY.md
|
|
156
|
+
const files = listDir(dirPath);
|
|
157
|
+
let plan: string | null = null;
|
|
158
|
+
let summary: string | null = null;
|
|
159
|
+
|
|
160
|
+
for (const file of files) {
|
|
161
|
+
if (/^\d+-PLAN\.md$/i.test(file)) {
|
|
162
|
+
plan = readFileSync(join(dirPath, file), 'utf-8');
|
|
163
|
+
} else if (/^\d+-SUMMARY\.md$/i.test(file)) {
|
|
164
|
+
summary = readFileSync(join(dirPath, file), 'utf-8');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
tasks.push({
|
|
169
|
+
dirName,
|
|
170
|
+
number: parsed.number,
|
|
171
|
+
slug: parsed.slug,
|
|
172
|
+
plan,
|
|
173
|
+
summary,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return tasks;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ─── Milestones Scanner ────────────────────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
function scanMilestonesDirectory(msDir: string): PlanningMilestone[] {
|
|
183
|
+
const entries = listDir(msDir);
|
|
184
|
+
if (entries.length === 0) return [];
|
|
185
|
+
|
|
186
|
+
// Group files by milestone ID prefix (e.g. "v2.2" from "v2.2-ROADMAP.md")
|
|
187
|
+
const grouped = new Map<string, { requirements: string | null; roadmap: string | null; extraFiles: PlanningPhaseFile[] }>();
|
|
188
|
+
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
const entryPath = join(msDir, entry);
|
|
191
|
+
if (isDir(entryPath)) continue;
|
|
192
|
+
|
|
193
|
+
// Extract milestone ID: everything before the first dash-followed-by-uppercase or common suffix
|
|
194
|
+
const idMatch = entry.match(/^(.+?)-(ROADMAP|REQUIREMENTS|SUMMARY)\.md$/i);
|
|
195
|
+
if (idMatch) {
|
|
196
|
+
const id = idMatch[1];
|
|
197
|
+
const type = idMatch[2].toUpperCase();
|
|
198
|
+
if (!grouped.has(id)) grouped.set(id, { requirements: null, roadmap: null, extraFiles: [] });
|
|
199
|
+
const ms = grouped.get(id)!;
|
|
200
|
+
const content = readFileSync(entryPath, 'utf-8');
|
|
201
|
+
|
|
202
|
+
if (type === 'REQUIREMENTS') ms.requirements = content;
|
|
203
|
+
else if (type === 'ROADMAP') ms.roadmap = content;
|
|
204
|
+
else ms.extraFiles.push({ fileName: entry, content });
|
|
205
|
+
} else {
|
|
206
|
+
// Non-standard file — try to extract ID from filename
|
|
207
|
+
const simpleMatch = entry.match(/^(.+?)\./);
|
|
208
|
+
const id = simpleMatch ? simpleMatch[1] : entry;
|
|
209
|
+
if (!grouped.has(id)) grouped.set(id, { requirements: null, roadmap: null, extraFiles: [] });
|
|
210
|
+
const content = readFileSync(entryPath, 'utf-8');
|
|
211
|
+
grouped.get(id)!.extraFiles.push({ fileName: entry, content });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return Array.from(grouped.entries()).map(([id, data]) => ({
|
|
216
|
+
id,
|
|
217
|
+
requirements: data.requirements,
|
|
218
|
+
roadmap: data.roadmap,
|
|
219
|
+
extraFiles: data.extraFiles,
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ─── Research Scanner ──────────────────────────────────────────────────────
|
|
224
|
+
|
|
225
|
+
function scanResearchDirectory(researchDir: string): PlanningResearch[] {
|
|
226
|
+
const entries = listDir(researchDir);
|
|
227
|
+
const research: PlanningResearch[] = [];
|
|
228
|
+
|
|
229
|
+
for (const entry of entries) {
|
|
230
|
+
const entryPath = join(researchDir, entry);
|
|
231
|
+
if (isDir(entryPath)) continue;
|
|
232
|
+
const content = readFileSync(entryPath, 'utf-8');
|
|
233
|
+
research.push({ fileName: entry, content });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return research;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ─── Main Orchestrator ─────────────────────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Parse an old .planning directory into a complete typed PlanningProject.
|
|
243
|
+
*
|
|
244
|
+
* Handles:
|
|
245
|
+
* - Top-level files: PROJECT.md, ROADMAP.md, REQUIREMENTS.md, STATE.md, config.json
|
|
246
|
+
* - Phase directories with plans, summaries, research, verification, extras
|
|
247
|
+
* - Duplicate phase numbers (full directory name as key)
|
|
248
|
+
* - .archive/ skipping
|
|
249
|
+
* - Orphan summaries (summaries without matching plans)
|
|
250
|
+
* - Quick tasks from quick/ directory
|
|
251
|
+
* - Milestones from milestones/ directory
|
|
252
|
+
* - Research from research/ directory
|
|
253
|
+
*
|
|
254
|
+
* Missing files produce null values, not thrown errors.
|
|
255
|
+
* Use validatePlanningDirectory() for pre-flight structural checks.
|
|
256
|
+
*/
|
|
257
|
+
export async function parsePlanningDirectory(path: string): Promise<PlanningProject> {
|
|
258
|
+
// Run validation first
|
|
259
|
+
const validation = await validatePlanningDirectory(path);
|
|
260
|
+
|
|
261
|
+
// Parse top-level files
|
|
262
|
+
const projectContent = readOptional(join(path, 'PROJECT.md'));
|
|
263
|
+
const project = projectContent !== null ? parseOldProject(projectContent) : null;
|
|
264
|
+
|
|
265
|
+
const roadmapContent = readOptional(join(path, 'ROADMAP.md'));
|
|
266
|
+
const roadmap = roadmapContent !== null ? parseOldRoadmap(roadmapContent) : null;
|
|
267
|
+
|
|
268
|
+
const reqContent = readOptional(join(path, 'REQUIREMENTS.md'));
|
|
269
|
+
const requirements = reqContent !== null ? parseOldRequirements(reqContent) : [];
|
|
270
|
+
|
|
271
|
+
const stateContent = readOptional(join(path, 'STATE.md'));
|
|
272
|
+
const state = stateContent !== null ? parseOldState(stateContent) : null;
|
|
273
|
+
|
|
274
|
+
const configContent = readOptional(join(path, 'config.json'));
|
|
275
|
+
const config = configContent !== null ? parseOldConfig(configContent) : null;
|
|
276
|
+
|
|
277
|
+
// Scan phases/ directory
|
|
278
|
+
const phases: Record<string, PlanningPhase> = {};
|
|
279
|
+
const phasesDir = join(path, 'phases');
|
|
280
|
+
|
|
281
|
+
if (isDir(phasesDir)) {
|
|
282
|
+
const phaseDirs = listDir(phasesDir).sort();
|
|
283
|
+
|
|
284
|
+
for (const dirName of phaseDirs) {
|
|
285
|
+
// Skip .archive and hidden directories
|
|
286
|
+
if (dirName.startsWith('.')) continue;
|
|
287
|
+
|
|
288
|
+
const dirPath = join(phasesDir, dirName);
|
|
289
|
+
if (!isDir(dirPath)) continue;
|
|
290
|
+
|
|
291
|
+
const parsed = parsePhaseDir(dirName);
|
|
292
|
+
if (!parsed) continue;
|
|
293
|
+
|
|
294
|
+
phases[dirName] = scanPhaseDirectory(dirPath, dirName, parsed);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Scan quick/ directory
|
|
299
|
+
const quickDir = join(path, 'quick');
|
|
300
|
+
const quickTasks = isDir(quickDir) ? scanQuickDirectory(quickDir) : [];
|
|
301
|
+
|
|
302
|
+
// Scan milestones/ directory
|
|
303
|
+
const msDir = join(path, 'milestones');
|
|
304
|
+
const milestones = isDir(msDir) ? scanMilestonesDirectory(msDir) : [];
|
|
305
|
+
|
|
306
|
+
// Scan research/ directory
|
|
307
|
+
const researchDir = join(path, 'research');
|
|
308
|
+
const research = isDir(researchDir) ? scanResearchDirectory(researchDir) : [];
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
path,
|
|
312
|
+
project,
|
|
313
|
+
roadmap,
|
|
314
|
+
requirements,
|
|
315
|
+
state,
|
|
316
|
+
config,
|
|
317
|
+
phases,
|
|
318
|
+
quickTasks,
|
|
319
|
+
milestones,
|
|
320
|
+
research,
|
|
321
|
+
validation,
|
|
322
|
+
};
|
|
323
|
+
}
|