popeye-cli 1.2.1 → 1.4.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/.env.example +4 -1
- package/CONTRIBUTING.md +10 -0
- package/README.md +224 -17
- package/dist/adapters/claude.d.ts +3 -2
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +214 -0
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/gemini.d.ts +2 -2
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/grok.d.ts +2 -1
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js.map +1 -1
- package/dist/adapters/index.d.ts +8 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/openai.d.ts +2 -2
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js.map +1 -1
- package/dist/cli/commands/create.d.ts.map +1 -1
- package/dist/cli/commands/create.js +25 -5
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +354 -28
- package/dist/cli/interactive.js.map +1 -1
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/schema.d.ts +4 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +2 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/generators/all.d.ts +70 -0
- package/dist/generators/all.d.ts.map +1 -0
- package/dist/generators/all.js +826 -0
- package/dist/generators/all.js.map +1 -0
- package/dist/generators/fullstack.d.ts +9 -0
- package/dist/generators/fullstack.d.ts.map +1 -1
- package/dist/generators/fullstack.js.map +1 -1
- package/dist/generators/index.d.ts +3 -1
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +33 -0
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/templates/index.d.ts +2 -0
- package/dist/generators/templates/index.d.ts.map +1 -1
- package/dist/generators/templates/index.js +2 -0
- package/dist/generators/templates/index.js.map +1 -1
- package/dist/generators/templates/website.d.ts +85 -0
- package/dist/generators/templates/website.d.ts.map +1 -0
- package/dist/generators/templates/website.js +877 -0
- package/dist/generators/templates/website.js.map +1 -0
- package/dist/generators/website.d.ts +56 -0
- package/dist/generators/website.d.ts.map +1 -0
- package/dist/generators/website.js +269 -0
- package/dist/generators/website.js.map +1 -0
- package/dist/types/consensus.d.ts +18 -23
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +8 -3
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/project.d.ts +130 -17
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +55 -8
- package/dist/types/project.js.map +1 -1
- package/dist/types/workflow.d.ts +2 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +2 -1
- package/dist/types/workflow.js.map +1 -1
- package/dist/upgrade/context.d.ts +37 -0
- package/dist/upgrade/context.d.ts.map +1 -0
- package/dist/upgrade/context.js +284 -0
- package/dist/upgrade/context.js.map +1 -0
- package/dist/upgrade/handlers.d.ts +103 -0
- package/dist/upgrade/handlers.d.ts.map +1 -0
- package/dist/upgrade/handlers.js +384 -0
- package/dist/upgrade/handlers.js.map +1 -0
- package/dist/upgrade/index.d.ts +26 -0
- package/dist/upgrade/index.d.ts.map +1 -0
- package/dist/upgrade/index.js +194 -0
- package/dist/upgrade/index.js.map +1 -0
- package/dist/upgrade/transitions.d.ts +34 -0
- package/dist/upgrade/transitions.d.ts.map +1 -0
- package/dist/upgrade/transitions.js +56 -0
- package/dist/upgrade/transitions.js.map +1 -0
- package/dist/workflow/consensus.d.ts +2 -1
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/index.d.ts +6 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +8 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/plan-mode.d.ts +3 -3
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +41 -5
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/plan-parser.d.ts +97 -0
- package/dist/workflow/plan-parser.d.ts.map +1 -0
- package/dist/workflow/plan-parser.js +235 -0
- package/dist/workflow/plan-parser.js.map +1 -0
- package/dist/workflow/plan-storage.d.ts +40 -12
- package/dist/workflow/plan-storage.d.ts.map +1 -1
- package/dist/workflow/plan-storage.js +47 -20
- package/dist/workflow/plan-storage.js.map +1 -1
- package/dist/workflow/seo-tests.d.ts +43 -0
- package/dist/workflow/seo-tests.d.ts.map +1 -0
- package/dist/workflow/seo-tests.js +192 -0
- package/dist/workflow/seo-tests.js.map +1 -0
- package/dist/workflow/separation-guard.d.ts +35 -0
- package/dist/workflow/separation-guard.d.ts.map +1 -0
- package/dist/workflow/separation-guard.js +154 -0
- package/dist/workflow/separation-guard.js.map +1 -0
- package/dist/workflow/task-workflow.d.ts.map +1 -1
- package/dist/workflow/task-workflow.js +3 -2
- package/dist/workflow/task-workflow.js.map +1 -1
- package/dist/workflow/test-runner.d.ts.map +1 -1
- package/dist/workflow/test-runner.js +128 -0
- package/dist/workflow/test-runner.js.map +1 -1
- package/dist/workflow/workspace-manager.d.ts +31 -20
- package/dist/workflow/workspace-manager.d.ts.map +1 -1
- package/dist/workflow/workspace-manager.js +38 -9
- package/dist/workflow/workspace-manager.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/claude.ts +221 -4
- package/src/adapters/gemini.ts +2 -2
- package/src/adapters/grok.ts +2 -1
- package/src/adapters/index.ts +15 -0
- package/src/adapters/openai.ts +2 -2
- package/src/cli/commands/create.ts +25 -5
- package/src/cli/index.ts +5 -2
- package/src/cli/interactive.ts +400 -29
- package/src/config/schema.ts +2 -1
- package/src/generators/all.ts +897 -0
- package/src/generators/fullstack.ts +10 -0
- package/src/generators/index.ts +54 -0
- package/src/generators/templates/index.ts +2 -0
- package/src/generators/templates/website.ts +906 -0
- package/src/generators/website.ts +350 -0
- package/src/types/consensus.ts +20 -8
- package/src/types/index.ts +35 -0
- package/src/types/project.ts +157 -11
- package/src/types/workflow.ts +2 -1
- package/src/upgrade/context.ts +332 -0
- package/src/upgrade/handlers.ts +477 -0
- package/src/upgrade/index.ts +244 -0
- package/src/upgrade/transitions.ts +80 -0
- package/src/workflow/consensus.ts +3 -2
- package/src/workflow/index.ts +8 -0
- package/src/workflow/plan-mode.ts +44 -10
- package/src/workflow/plan-parser.ts +317 -0
- package/src/workflow/plan-storage.ts +69 -30
- package/src/workflow/seo-tests.ts +246 -0
- package/src/workflow/separation-guard.ts +200 -0
- package/src/workflow/task-workflow.ts +3 -2
- package/src/workflow/test-runner.ts +149 -0
- package/src/workflow/workspace-manager.ts +68 -31
- package/tests/cli/model-command.test.ts +93 -0
- package/tests/types/project.test.ts +90 -15
- package/tests/types/workflow-schema.test.ts +59 -0
- package/tests/upgrade/context.test.ts +211 -0
- package/tests/upgrade/transitions.test.ts +85 -0
package/src/types/workflow.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
+
import { OutputLanguageSchema } from './project.js';
|
|
7
8
|
import type { OutputLanguage, OpenAIModel } from './project.js';
|
|
8
9
|
import type { ConsensusIteration } from './consensus.js';
|
|
9
10
|
|
|
@@ -191,7 +192,7 @@ export const ProjectStateSchema = z.object({
|
|
|
191
192
|
id: z.string(),
|
|
192
193
|
name: z.string(),
|
|
193
194
|
idea: z.string(),
|
|
194
|
-
language:
|
|
195
|
+
language: OutputLanguageSchema,
|
|
195
196
|
openaiModel: z.enum(['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini']),
|
|
196
197
|
phase: WorkflowPhaseSchema,
|
|
197
198
|
status: ProjectStatusSchema,
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upgrade context builder
|
|
3
|
+
* Builds rich context about existing project structure so the planner
|
|
4
|
+
* knows what it's building on top of after an upgrade
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { promises as fs } from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import type { OutputLanguage } from '../types/project.js';
|
|
10
|
+
import { languageToApps } from '../types/project.js';
|
|
11
|
+
import type { UpgradeTransition } from './transitions.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Upgrade context passed to planning mode
|
|
15
|
+
*/
|
|
16
|
+
export interface UpgradeContext {
|
|
17
|
+
/** Summary text for the planner */
|
|
18
|
+
summary: string;
|
|
19
|
+
/** Apps that already exist (from the old project type) */
|
|
20
|
+
existingApps: string[];
|
|
21
|
+
/** Apps that were just added by the upgrade */
|
|
22
|
+
newApps: string[];
|
|
23
|
+
/** The original project idea */
|
|
24
|
+
originalIdea: string;
|
|
25
|
+
/** The original language before upgrade */
|
|
26
|
+
fromLanguage: OutputLanguage;
|
|
27
|
+
/** The new language after upgrade */
|
|
28
|
+
toLanguage: OutputLanguage;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Scan an app directory for key structural information
|
|
33
|
+
*
|
|
34
|
+
* @param appDir - Path to the app directory
|
|
35
|
+
* @param appName - Name of the app (frontend, backend, website)
|
|
36
|
+
* @returns Summary of the app structure
|
|
37
|
+
*/
|
|
38
|
+
async function scanAppStructure(appDir: string, appName: string): Promise<string> {
|
|
39
|
+
const lines: string[] = [];
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
await fs.access(appDir);
|
|
43
|
+
} catch {
|
|
44
|
+
return ` ${appName}: (not found)`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
lines.push(` ${appName}/`);
|
|
48
|
+
|
|
49
|
+
// Check for package.json to understand dependencies and scripts
|
|
50
|
+
const pkgPath = path.join(appDir, 'package.json');
|
|
51
|
+
try {
|
|
52
|
+
const pkgContent = await fs.readFile(pkgPath, 'utf-8');
|
|
53
|
+
const pkg = JSON.parse(pkgContent);
|
|
54
|
+
if (pkg.dependencies) {
|
|
55
|
+
const deps = Object.keys(pkg.dependencies).slice(0, 10);
|
|
56
|
+
lines.push(` Dependencies: ${deps.join(', ')}${Object.keys(pkg.dependencies).length > 10 ? '...' : ''}`);
|
|
57
|
+
}
|
|
58
|
+
if (pkg.scripts) {
|
|
59
|
+
const scripts = Object.keys(pkg.scripts).slice(0, 8);
|
|
60
|
+
lines.push(` Scripts: ${scripts.join(', ')}`);
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// No package.json
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Check for requirements.txt (Python)
|
|
67
|
+
const reqPath = path.join(appDir, 'requirements.txt');
|
|
68
|
+
try {
|
|
69
|
+
const reqContent = await fs.readFile(reqPath, 'utf-8');
|
|
70
|
+
const deps = reqContent.split('\n').filter(l => l.trim() && !l.startsWith('#')).slice(0, 10);
|
|
71
|
+
lines.push(` Python dependencies: ${deps.join(', ')}${deps.length >= 10 ? '...' : ''}`);
|
|
72
|
+
} catch {
|
|
73
|
+
// No requirements.txt
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check for pyproject.toml (Python)
|
|
77
|
+
const pyprojectPath = path.join(appDir, 'pyproject.toml');
|
|
78
|
+
try {
|
|
79
|
+
await fs.access(pyprojectPath);
|
|
80
|
+
lines.push(` Has pyproject.toml`);
|
|
81
|
+
} catch {
|
|
82
|
+
// No pyproject.toml
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Scan for key directories and files
|
|
86
|
+
try {
|
|
87
|
+
const entries = await fs.readdir(appDir, { withFileTypes: true });
|
|
88
|
+
const dirs = entries.filter(e => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules' && e.name !== '__pycache__');
|
|
89
|
+
const files = entries.filter(e => e.isFile() && !e.name.startsWith('.'));
|
|
90
|
+
|
|
91
|
+
if (dirs.length > 0) {
|
|
92
|
+
lines.push(` Directories: ${dirs.map(d => d.name).join(', ')}`);
|
|
93
|
+
}
|
|
94
|
+
if (files.length > 0) {
|
|
95
|
+
const keyFiles = files.map(f => f.name).slice(0, 12);
|
|
96
|
+
lines.push(` Key files: ${keyFiles.join(', ')}${files.length > 12 ? '...' : ''}`);
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
// Can't read directory
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check for API routes or endpoints
|
|
103
|
+
const apiDirs = ['src/routes', 'src/api', 'routes', 'api', 'src/app/api'];
|
|
104
|
+
for (const apiDir of apiDirs) {
|
|
105
|
+
const fullPath = path.join(appDir, apiDir);
|
|
106
|
+
try {
|
|
107
|
+
const entries = await fs.readdir(fullPath);
|
|
108
|
+
const routeFiles = entries.filter(f => !f.startsWith('.'));
|
|
109
|
+
if (routeFiles.length > 0) {
|
|
110
|
+
lines.push(` API routes (${apiDir}/): ${routeFiles.slice(0, 8).join(', ')}${routeFiles.length > 8 ? '...' : ''}`);
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// No API directory
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return lines.join('\n');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Scan for shared packages (contracts, design-tokens, ui)
|
|
122
|
+
*
|
|
123
|
+
* @param projectDir - Project root directory
|
|
124
|
+
* @returns Summary of shared packages
|
|
125
|
+
*/
|
|
126
|
+
async function scanSharedPackages(projectDir: string): Promise<string> {
|
|
127
|
+
const packagesDir = path.join(projectDir, 'packages');
|
|
128
|
+
const lines: string[] = [];
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const entries = await fs.readdir(packagesDir, { withFileTypes: true });
|
|
132
|
+
const packages = entries.filter(e => e.isDirectory());
|
|
133
|
+
|
|
134
|
+
if (packages.length === 0) return '';
|
|
135
|
+
|
|
136
|
+
lines.push('Shared packages:');
|
|
137
|
+
for (const pkg of packages) {
|
|
138
|
+
const pkgJsonPath = path.join(packagesDir, pkg.name, 'package.json');
|
|
139
|
+
try {
|
|
140
|
+
const content = await fs.readFile(pkgJsonPath, 'utf-8');
|
|
141
|
+
const pkgJson = JSON.parse(content);
|
|
142
|
+
lines.push(` ${pkg.name}: ${pkgJson.description || '(no description)'}`);
|
|
143
|
+
} catch {
|
|
144
|
+
lines.push(` ${pkg.name}/`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
// No packages directory
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return lines.join('\n');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Check for contracts between apps (API schemas, shared types)
|
|
156
|
+
*
|
|
157
|
+
* @param projectDir - Project root directory
|
|
158
|
+
* @returns Summary of contracts/shared types
|
|
159
|
+
*/
|
|
160
|
+
async function scanContracts(projectDir: string): Promise<string> {
|
|
161
|
+
const contractPaths = [
|
|
162
|
+
path.join(projectDir, 'packages', 'contracts'),
|
|
163
|
+
path.join(projectDir, 'packages', 'shared'),
|
|
164
|
+
path.join(projectDir, 'packages', 'types'),
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
for (const contractDir of contractPaths) {
|
|
168
|
+
try {
|
|
169
|
+
const entries = await fs.readdir(contractDir);
|
|
170
|
+
const files = entries.filter(f => !f.startsWith('.') && f !== 'node_modules');
|
|
171
|
+
if (files.length > 0) {
|
|
172
|
+
return `Shared contracts: ${files.join(', ')}`;
|
|
173
|
+
}
|
|
174
|
+
} catch {
|
|
175
|
+
// Directory doesn't exist
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return '';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Read the existing plan if available
|
|
184
|
+
*
|
|
185
|
+
* @param projectDir - Project root directory
|
|
186
|
+
* @returns Plan summary or empty string
|
|
187
|
+
*/
|
|
188
|
+
async function getExistingPlanSummary(projectDir: string): Promise<string> {
|
|
189
|
+
const planPaths = [
|
|
190
|
+
path.join(projectDir, 'docs', 'PLAN.md'),
|
|
191
|
+
path.join(projectDir, 'docs', 'PLAN-DRAFT.md'),
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
for (const planPath of planPaths) {
|
|
195
|
+
try {
|
|
196
|
+
const content = await fs.readFile(planPath, 'utf-8');
|
|
197
|
+
// Return first 2000 chars as summary
|
|
198
|
+
const summary = content.slice(0, 2000);
|
|
199
|
+
return `Previous plan summary:\n${summary}${content.length > 2000 ? '\n... (truncated)' : ''}`;
|
|
200
|
+
} catch {
|
|
201
|
+
// File doesn't exist
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return '';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Build rich context about the project after an upgrade
|
|
210
|
+
* This context is passed to the planner so it knows what already exists
|
|
211
|
+
* and can focus the plan on the new apps + integration
|
|
212
|
+
*
|
|
213
|
+
* @param projectDir - Project root directory
|
|
214
|
+
* @param transition - The upgrade transition details
|
|
215
|
+
* @param originalIdea - The original project idea
|
|
216
|
+
* @param fromLanguage - The language before upgrade
|
|
217
|
+
* @returns Upgrade context with summary for the planner
|
|
218
|
+
*/
|
|
219
|
+
export async function buildUpgradeContext(
|
|
220
|
+
projectDir: string,
|
|
221
|
+
transition: UpgradeTransition,
|
|
222
|
+
originalIdea: string,
|
|
223
|
+
fromLanguage: OutputLanguage,
|
|
224
|
+
): Promise<UpgradeContext> {
|
|
225
|
+
const existingApps = languageToApps(fromLanguage);
|
|
226
|
+
const newApps = transition.newApps;
|
|
227
|
+
|
|
228
|
+
const sections: string[] = [];
|
|
229
|
+
|
|
230
|
+
// Header
|
|
231
|
+
sections.push(`=== PROJECT EXPANSION: ${fromLanguage} -> ${transition.to} ===`);
|
|
232
|
+
sections.push('');
|
|
233
|
+
sections.push(`This is an EXPANSION of an existing project, NOT a new project.`);
|
|
234
|
+
sections.push(`Original idea: ${originalIdea}`);
|
|
235
|
+
sections.push('');
|
|
236
|
+
|
|
237
|
+
// What already exists
|
|
238
|
+
sections.push('== EXISTING APPS (already built - DO NOT rebuild) ==');
|
|
239
|
+
const appsDir = path.join(projectDir, 'apps');
|
|
240
|
+
for (const app of existingApps) {
|
|
241
|
+
const appDir = path.join(appsDir, app);
|
|
242
|
+
const structure = await scanAppStructure(appDir, app);
|
|
243
|
+
sections.push(structure);
|
|
244
|
+
}
|
|
245
|
+
// Also scan root if code was not yet in apps/ (single-app pre-restructure state is gone after upgrade,
|
|
246
|
+
// but the code is now in apps/)
|
|
247
|
+
sections.push('');
|
|
248
|
+
|
|
249
|
+
// What's new
|
|
250
|
+
sections.push('== NEW APPS TO PLAN (focus your plan here) ==');
|
|
251
|
+
for (const app of newApps) {
|
|
252
|
+
sections.push(` ${app}: Scaffolded but needs full implementation planning`);
|
|
253
|
+
const appDir = path.join(appsDir, app);
|
|
254
|
+
const structure = await scanAppStructure(appDir, app);
|
|
255
|
+
sections.push(structure);
|
|
256
|
+
}
|
|
257
|
+
sections.push('');
|
|
258
|
+
|
|
259
|
+
// Shared packages
|
|
260
|
+
const sharedPkgs = await scanSharedPackages(projectDir);
|
|
261
|
+
if (sharedPkgs) {
|
|
262
|
+
sections.push('== SHARED PACKAGES ==');
|
|
263
|
+
sections.push(sharedPkgs);
|
|
264
|
+
sections.push('');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Contracts
|
|
268
|
+
const contracts = await scanContracts(projectDir);
|
|
269
|
+
if (contracts) {
|
|
270
|
+
sections.push('== CONTRACTS ==');
|
|
271
|
+
sections.push(contracts);
|
|
272
|
+
sections.push('');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Previous plan context
|
|
276
|
+
const planSummary = await getExistingPlanSummary(projectDir);
|
|
277
|
+
if (planSummary) {
|
|
278
|
+
sections.push('== PREVIOUS PLAN (for reference only - existing apps are already implemented) ==');
|
|
279
|
+
sections.push(planSummary);
|
|
280
|
+
sections.push('');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Integration instructions
|
|
284
|
+
sections.push('== PLANNING INSTRUCTIONS ==');
|
|
285
|
+
sections.push('');
|
|
286
|
+
sections.push('Your plan MUST:');
|
|
287
|
+
sections.push(`1. Focus ONLY on the new ${newApps.join(', ')} app(s) - do NOT replan existing ${existingApps.join(', ')} app(s)`);
|
|
288
|
+
sections.push('2. Include integration tasks between new and existing apps:');
|
|
289
|
+
|
|
290
|
+
// Generate specific integration guidance based on what's being added
|
|
291
|
+
if (newApps.includes('website') && existingApps.includes('frontend')) {
|
|
292
|
+
sections.push(' - Shared design tokens and UI components between frontend and website');
|
|
293
|
+
sections.push(' - Consistent navigation and branding across frontend app and marketing website');
|
|
294
|
+
}
|
|
295
|
+
if (newApps.includes('website') && existingApps.includes('backend')) {
|
|
296
|
+
sections.push(' - Website API calls to backend (e.g., contact forms, newsletter signup)');
|
|
297
|
+
sections.push(' - Shared authentication if needed (SSO between website and app)');
|
|
298
|
+
}
|
|
299
|
+
if (newApps.includes('frontend') && existingApps.includes('backend')) {
|
|
300
|
+
sections.push(' - API contracts: frontend must consume existing backend API endpoints');
|
|
301
|
+
sections.push(' - Shared types/interfaces between frontend and backend');
|
|
302
|
+
sections.push(' - Authentication flow: frontend login using backend auth endpoints');
|
|
303
|
+
}
|
|
304
|
+
if (newApps.includes('backend') && existingApps.includes('frontend')) {
|
|
305
|
+
sections.push(' - API endpoints that the existing frontend needs');
|
|
306
|
+
sections.push(' - Data models and database schema');
|
|
307
|
+
sections.push(' - Authentication and authorization backend');
|
|
308
|
+
}
|
|
309
|
+
if (newApps.includes('frontend') && newApps.includes('backend')) {
|
|
310
|
+
sections.push(' - Full API contract design between new frontend and backend');
|
|
311
|
+
sections.push(' - Shared types/interfaces');
|
|
312
|
+
sections.push(' - Authentication flow end-to-end');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const appTagMap: Record<string, string> = { frontend: '[FE]', backend: '[BE]', website: '[WEB]' };
|
|
316
|
+
const appTags = newApps.map(a => appTagMap[a] || `[${String(a).toUpperCase()}]`);
|
|
317
|
+
sections.push(`3. Tag tasks with app targets: ${appTags.join(', ')} and [INT] for integration tasks`);
|
|
318
|
+
sections.push('4. Include Docker Compose updates for new services');
|
|
319
|
+
sections.push('5. Include tests for new apps and integration tests');
|
|
320
|
+
sections.push('');
|
|
321
|
+
|
|
322
|
+
const summary = sections.join('\n');
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
summary,
|
|
326
|
+
existingApps,
|
|
327
|
+
newApps,
|
|
328
|
+
originalIdea,
|
|
329
|
+
fromLanguage,
|
|
330
|
+
toLanguage: transition.to,
|
|
331
|
+
};
|
|
332
|
+
}
|