edsger 0.38.3 → 0.39.1
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/dist/api/web-deploy.d.ts +60 -0
- package/dist/api/web-deploy.js +198 -0
- package/dist/phases/analyze-logs/index.js +1 -1
- package/dist/phases/analyze-logs/prompts.d.ts +1 -1
- package/dist/phases/analyze-logs/prompts.js +7 -39
- package/dist/phases/app-store-generation/__tests__/prompts.test.js +1 -44
- package/dist/phases/app-store-generation/index.js +1 -1
- package/dist/phases/app-store-generation/prompts.d.ts +1 -1
- package/dist/phases/app-store-generation/prompts.js +14 -79
- package/dist/phases/autonomous/index.js +1 -1
- package/dist/phases/autonomous/prompts.d.ts +1 -2
- package/dist/phases/autonomous/prompts.js +15 -58
- package/dist/phases/branch-planning/index.js +1 -1
- package/dist/phases/branch-planning/prompts.d.ts +1 -1
- package/dist/phases/branch-planning/prompts.js +15 -77
- package/dist/phases/bug-fixing/analyzer.js +2 -54
- package/dist/phases/bug-fixing/prompts.d.ts +1 -1
- package/dist/phases/bug-fixing/prompts.js +8 -62
- package/dist/phases/code-implementation/prompts.d.ts +1 -1
- package/dist/phases/code-implementation/prompts.js +12 -78
- package/dist/phases/code-implementation-verification/agent.d.ts +1 -1
- package/dist/phases/code-implementation-verification/agent.js +2 -2
- package/dist/phases/code-implementation-verification/prompts.d.ts +1 -2
- package/dist/phases/code-implementation-verification/prompts.js +12 -99
- package/dist/phases/code-refine/prompts.d.ts +1 -1
- package/dist/phases/code-refine/prompts.js +16 -89
- package/dist/phases/code-refine/refine-iteration.js +1 -1
- package/dist/phases/code-testing/analyzer.js +1 -1
- package/dist/phases/code-testing/prompts.d.ts +1 -1
- package/dist/phases/code-testing/prompts.js +10 -91
- package/dist/phases/feature-analysis/index.js +1 -1
- package/dist/phases/feature-analysis/prompts.d.ts +1 -1
- package/dist/phases/feature-analysis/prompts.js +8 -188
- package/dist/phases/feature-analysis-verification/agent.js +1 -1
- package/dist/phases/feature-analysis-verification/prompts.d.ts +1 -1
- package/dist/phases/feature-analysis-verification/prompts.js +10 -69
- package/dist/phases/functional-testing/prompts.d.ts +1 -2
- package/dist/phases/functional-testing/prompts.js +10 -78
- package/dist/phases/growth-analysis/index.js +1 -1
- package/dist/phases/growth-analysis/prompts.d.ts +1 -1
- package/dist/phases/growth-analysis/prompts.js +14 -143
- package/dist/phases/intelligence-analysis/__tests__/prompts.test.js +1 -73
- package/dist/phases/intelligence-analysis/agent.js +250 -18
- package/dist/phases/intelligence-analysis/index.js +11 -4
- package/dist/phases/intelligence-analysis/prompts.d.ts +1 -1
- package/dist/phases/intelligence-analysis/prompts.js +30 -116
- package/dist/phases/output-contracts.d.ts +7 -0
- package/dist/phases/output-contracts.js +824 -0
- package/dist/phases/pr-execution/index.js +2 -2
- package/dist/phases/pr-execution/prompts.d.ts +2 -2
- package/dist/phases/pr-execution/prompts.js +28 -122
- package/dist/phases/pr-splitting/index.js +1 -1
- package/dist/phases/pr-splitting/prompts.d.ts +1 -1
- package/dist/phases/pr-splitting/prompts.js +15 -91
- package/dist/phases/task/index.js +1 -1
- package/dist/phases/task/prompts.d.ts +1 -1
- package/dist/phases/task/prompts.js +8 -15
- package/dist/phases/technical-design/index.js +1 -1
- package/dist/phases/technical-design/prompts.d.ts +1 -1
- package/dist/phases/technical-design/prompts.js +17 -89
- package/dist/phases/technical-design-verification/agent.d.ts +1 -1
- package/dist/phases/technical-design-verification/agent.js +2 -2
- package/dist/phases/technical-design-verification/prompts.d.ts +1 -2
- package/dist/phases/technical-design-verification/prompts.js +12 -88
- package/dist/phases/test-cases-analysis/index.js +1 -1
- package/dist/phases/test-cases-analysis/prompts.d.ts +1 -1
- package/dist/phases/test-cases-analysis/prompts.js +8 -125
- package/dist/phases/user-stories-analysis/index.js +1 -1
- package/dist/phases/user-stories-analysis/prompts.d.ts +1 -1
- package/dist/phases/user-stories-analysis/prompts.js +10 -155
- package/dist/services/skill-resolver.d.ts +46 -0
- package/dist/services/skill-resolver.js +121 -0
- package/dist/skills/phase/analyze-logs/SKILL.md +24 -0
- package/dist/skills/phase/app-store-generation/SKILL.md +60 -0
- package/dist/skills/phase/autonomous/SKILL.md +52 -0
- package/dist/skills/phase/branch-planning/SKILL.md +55 -0
- package/dist/skills/phase/bug-fixing/SKILL.md +47 -0
- package/dist/skills/phase/code-implementation/SKILL.md +68 -0
- package/dist/skills/phase/code-implementation-verification/SKILL.md +76 -0
- package/dist/skills/phase/code-refine/SKILL.md +62 -0
- package/dist/skills/phase/code-testing/SKILL.md +78 -0
- package/dist/skills/phase/feature-analysis/SKILL.md +117 -0
- package/dist/skills/phase/feature-analysis-verification/SKILL.md +46 -0
- package/dist/skills/phase/functional-testing/SKILL.md +62 -0
- package/dist/skills/phase/growth-analysis/SKILL.md +97 -0
- package/dist/skills/phase/incremental-sync/SKILL.md +48 -0
- package/dist/skills/phase/intelligence-analysis/SKILL.md +52 -0
- package/dist/skills/phase/pr-execution/SKILL.md +53 -0
- package/dist/skills/phase/pr-splitting/SKILL.md +70 -0
- package/dist/skills/phase/task/SKILL.md +19 -0
- package/dist/skills/phase/technical-design/SKILL.md +75 -0
- package/dist/skills/phase/technical-design-verification/SKILL.md +43 -0
- package/dist/skills/phase/test-cases-analysis/SKILL.md +88 -0
- package/dist/skills/phase/user-stories-analysis/SKILL.md +118 -0
- package/package.json +2 -2
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface WebDeployConfig {
|
|
2
|
+
id: string;
|
|
3
|
+
product_id: string;
|
|
4
|
+
platform: 'vercel' | 'netlify';
|
|
5
|
+
project_id: string | null;
|
|
6
|
+
project_name: string | null;
|
|
7
|
+
project_url: string | null;
|
|
8
|
+
framework: string | null;
|
|
9
|
+
repo_full_name: string | null;
|
|
10
|
+
has_credentials: boolean;
|
|
11
|
+
is_connected: boolean;
|
|
12
|
+
created_by: string;
|
|
13
|
+
created_at: string;
|
|
14
|
+
updated_at: string;
|
|
15
|
+
}
|
|
16
|
+
export interface VercelProject {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
framework: string | null;
|
|
20
|
+
repo: string | null;
|
|
21
|
+
url: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface VercelDeployment {
|
|
24
|
+
id: string;
|
|
25
|
+
url: string | null;
|
|
26
|
+
state: 'READY' | 'BUILDING' | 'ERROR' | 'QUEUED' | 'CANCELED';
|
|
27
|
+
target: 'production' | null;
|
|
28
|
+
branch: string | null;
|
|
29
|
+
commit: string | null;
|
|
30
|
+
pr: string | null;
|
|
31
|
+
created_at: string;
|
|
32
|
+
ready_at: string | null;
|
|
33
|
+
}
|
|
34
|
+
export interface VercelEnvVar {
|
|
35
|
+
id: string;
|
|
36
|
+
key: string;
|
|
37
|
+
value: string;
|
|
38
|
+
target: string[];
|
|
39
|
+
type: string;
|
|
40
|
+
}
|
|
41
|
+
export declare function getWebDeployConfigs(productId: string, verbose?: boolean): Promise<WebDeployConfig[]>;
|
|
42
|
+
export declare function upsertWebDeployConfig(params: {
|
|
43
|
+
product_id: string;
|
|
44
|
+
platform: string;
|
|
45
|
+
credentials?: {
|
|
46
|
+
api_token: string;
|
|
47
|
+
};
|
|
48
|
+
}, verbose?: boolean): Promise<WebDeployConfig | null>;
|
|
49
|
+
export declare function deleteWebDeployConfig(configId: string, verbose?: boolean): Promise<void>;
|
|
50
|
+
export declare function connectWebDeploy(configId: string, verbose?: boolean): Promise<{
|
|
51
|
+
user: unknown;
|
|
52
|
+
connected: boolean;
|
|
53
|
+
} | null>;
|
|
54
|
+
export declare function listVercelProjects(configId: string, verbose?: boolean): Promise<VercelProject[]>;
|
|
55
|
+
export declare function createVercelProject(configId: string, name: string, repo: string, framework?: string, rootDirectory?: string, verbose?: boolean): Promise<VercelProject | null>;
|
|
56
|
+
export declare function linkVercelProject(configId: string, project: VercelProject, verbose?: boolean): Promise<WebDeployConfig | null>;
|
|
57
|
+
export declare function listDeployments(configId: string, limit?: number, verbose?: boolean): Promise<VercelDeployment[]>;
|
|
58
|
+
export declare function listEnvVars(configId: string, verbose?: boolean): Promise<VercelEnvVar[]>;
|
|
59
|
+
export declare function upsertEnvVar(configId: string, key: string, value: string, target: string[], verbose?: boolean): Promise<VercelEnvVar | null>;
|
|
60
|
+
export declare function deleteEnvVar(configId: string, envId: string, verbose?: boolean): Promise<void>;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { logError, logInfo } from '../utils/logger.js';
|
|
2
|
+
import { callMcpEndpoint } from './mcp-client.js';
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Helpers
|
|
5
|
+
// =============================================================================
|
|
6
|
+
function parseMcpResult(result, fallback) {
|
|
7
|
+
const r = result;
|
|
8
|
+
const text = r.content?.[0]?.text;
|
|
9
|
+
if (!text) {
|
|
10
|
+
return fallback;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(text);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return fallback;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// Config CRUD
|
|
21
|
+
// =============================================================================
|
|
22
|
+
export async function getWebDeployConfigs(productId, verbose) {
|
|
23
|
+
if (verbose) {
|
|
24
|
+
logInfo(`Fetching web deploy configs for product: ${productId}`);
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const result = await callMcpEndpoint('web_deploy/configs/list', {
|
|
28
|
+
product_id: productId,
|
|
29
|
+
});
|
|
30
|
+
return parseMcpResult(result, []);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
if (verbose) {
|
|
34
|
+
logError(`Failed to fetch web deploy configs: ${error instanceof Error ? error.message : String(error)}`);
|
|
35
|
+
}
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function upsertWebDeployConfig(params, verbose) {
|
|
40
|
+
if (verbose) {
|
|
41
|
+
logInfo(`Upserting web deploy config: ${params.platform}`);
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const result = await callMcpEndpoint('web_deploy/configs/upsert', params);
|
|
45
|
+
return parseMcpResult(result, null);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
logError(`Failed to upsert config: ${error instanceof Error ? error.message : String(error)}`);
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export async function deleteWebDeployConfig(configId, verbose) {
|
|
53
|
+
if (verbose) {
|
|
54
|
+
logInfo(`Deleting web deploy config: ${configId}`);
|
|
55
|
+
}
|
|
56
|
+
await callMcpEndpoint('web_deploy/configs/delete', {
|
|
57
|
+
config_id: configId,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// =============================================================================
|
|
61
|
+
// Connect
|
|
62
|
+
// =============================================================================
|
|
63
|
+
export async function connectWebDeploy(configId, verbose) {
|
|
64
|
+
if (verbose) {
|
|
65
|
+
logInfo(`Validating web deploy connection: ${configId}`);
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const result = await callMcpEndpoint('web_deploy/connect', {
|
|
69
|
+
config_id: configId,
|
|
70
|
+
});
|
|
71
|
+
return parseMcpResult(result, null);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
logError(`Failed to connect: ${error instanceof Error ? error.message : String(error)}`);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// =============================================================================
|
|
79
|
+
// Projects
|
|
80
|
+
// =============================================================================
|
|
81
|
+
export async function listVercelProjects(configId, verbose) {
|
|
82
|
+
if (verbose) {
|
|
83
|
+
logInfo(`Listing Vercel projects for config: ${configId}`);
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const result = await callMcpEndpoint('web_deploy/projects/list', {
|
|
87
|
+
config_id: configId,
|
|
88
|
+
});
|
|
89
|
+
return parseMcpResult(result, []);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
logError(`Failed to list projects: ${error instanceof Error ? error.message : String(error)}`);
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export async function createVercelProject(configId, name, repo, framework, rootDirectory, verbose) {
|
|
97
|
+
if (verbose) {
|
|
98
|
+
logInfo(`Creating Vercel project: ${name} → ${repo}`);
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const result = await callMcpEndpoint('web_deploy/projects/create', {
|
|
102
|
+
config_id: configId,
|
|
103
|
+
name,
|
|
104
|
+
repo,
|
|
105
|
+
framework,
|
|
106
|
+
root_directory: rootDirectory,
|
|
107
|
+
});
|
|
108
|
+
return parseMcpResult(result, null);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
logError(`Failed to create project: ${error instanceof Error ? error.message : String(error)}`);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export async function linkVercelProject(configId, project, verbose) {
|
|
116
|
+
if (verbose) {
|
|
117
|
+
logInfo(`Linking Vercel project: ${project.name}`);
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const result = await callMcpEndpoint('web_deploy/projects/link', {
|
|
121
|
+
config_id: configId,
|
|
122
|
+
project_id: project.id,
|
|
123
|
+
project_name: project.name,
|
|
124
|
+
project_url: project.url,
|
|
125
|
+
framework: project.framework,
|
|
126
|
+
repo_full_name: project.repo,
|
|
127
|
+
});
|
|
128
|
+
return parseMcpResult(result, null);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
logError(`Failed to link project: ${error instanceof Error ? error.message : String(error)}`);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// =============================================================================
|
|
136
|
+
// Deployments
|
|
137
|
+
// =============================================================================
|
|
138
|
+
export async function listDeployments(configId, limit, verbose) {
|
|
139
|
+
if (verbose) {
|
|
140
|
+
logInfo(`Listing deployments for config: ${configId}`);
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const result = await callMcpEndpoint('web_deploy/deployments/list', {
|
|
144
|
+
config_id: configId,
|
|
145
|
+
limit,
|
|
146
|
+
});
|
|
147
|
+
return parseMcpResult(result, []);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
logError(`Failed to list deployments: ${error instanceof Error ? error.message : String(error)}`);
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// =============================================================================
|
|
155
|
+
// Environment Variables
|
|
156
|
+
// =============================================================================
|
|
157
|
+
export async function listEnvVars(configId, verbose) {
|
|
158
|
+
if (verbose) {
|
|
159
|
+
logInfo(`Listing env vars for config: ${configId}`);
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const result = await callMcpEndpoint('web_deploy/env/list', {
|
|
163
|
+
config_id: configId,
|
|
164
|
+
});
|
|
165
|
+
return parseMcpResult(result, []);
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
logError(`Failed to list env vars: ${error instanceof Error ? error.message : String(error)}`);
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
export async function upsertEnvVar(configId, key, value, target, verbose) {
|
|
173
|
+
if (verbose) {
|
|
174
|
+
logInfo(`Upserting env var: ${key}`);
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const result = await callMcpEndpoint('web_deploy/env/upsert', {
|
|
178
|
+
config_id: configId,
|
|
179
|
+
key,
|
|
180
|
+
value,
|
|
181
|
+
target,
|
|
182
|
+
});
|
|
183
|
+
return parseMcpResult(result, null);
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
logError(`Failed to upsert env var: ${error instanceof Error ? error.message : String(error)}`);
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
export async function deleteEnvVar(configId, envId, verbose) {
|
|
191
|
+
if (verbose) {
|
|
192
|
+
logInfo(`Deleting env var: ${envId}`);
|
|
193
|
+
}
|
|
194
|
+
await callMcpEndpoint('web_deploy/env/delete', {
|
|
195
|
+
config_id: configId,
|
|
196
|
+
env_id: envId,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
@@ -57,7 +57,7 @@ export async function analyzeLogs(options) {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
logInfo(`${readyGroups.length} group(s) ready for analysis (inactive >1 hour)`);
|
|
60
|
-
const systemPrompt = createAnalyzeLogsSystemPrompt();
|
|
60
|
+
const systemPrompt = await createAnalyzeLogsSystemPrompt();
|
|
61
61
|
let totalFeedbacks = 0;
|
|
62
62
|
let totalLogs = 0;
|
|
63
63
|
// 4. Process each user group
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type ProductLog } from '../../services/product-logs.js';
|
|
2
|
-
export declare function createAnalyzeLogsSystemPrompt(): string
|
|
2
|
+
export declare function createAnalyzeLogsSystemPrompt(projectDir?: string): Promise<string>;
|
|
3
3
|
export declare function createAnalyzeLogsUserPrompt(productName: string, userId: string | null, logs: ProductLog[]): string;
|
|
@@ -1,43 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
3. Identify opportunities for product improvement
|
|
8
|
-
4. Generate actionable feedback items that the product team can act on
|
|
9
|
-
|
|
10
|
-
For each issue or improvement you find, output a structured feedback item.
|
|
11
|
-
|
|
12
|
-
IMPORTANT RULES:
|
|
13
|
-
- Only report genuine issues or meaningful improvements, not noise
|
|
14
|
-
- Be specific about what happened and what should change
|
|
15
|
-
- Prioritize by impact: bugs > usability issues > minor improvements
|
|
16
|
-
- If the logs show normal, successful usage with no issues, say so and return an empty array
|
|
17
|
-
- Do NOT fabricate issues that aren't supported by the log data
|
|
18
|
-
|
|
19
|
-
Return your analysis as JSON in this exact format:
|
|
20
|
-
\`\`\`json
|
|
21
|
-
{
|
|
22
|
-
"summary": "Brief summary of what the user did and any issues found",
|
|
23
|
-
"feedbacks": [
|
|
24
|
-
{
|
|
25
|
-
"category": "bug|feature_request|improvement|question|other",
|
|
26
|
-
"title": "Short descriptive title (max 200 chars)",
|
|
27
|
-
"description": "Detailed description of the issue or suggestion (max 5000 chars)",
|
|
28
|
-
"priority": "low|medium|high|critical"
|
|
1
|
+
import { resolveSkill } from '../../services/skill-resolver.js';
|
|
2
|
+
import { OUTPUT_CONTRACTS } from '../output-contracts.js';
|
|
3
|
+
export async function createAnalyzeLogsSystemPrompt(projectDir) {
|
|
4
|
+
const skill = await resolveSkill('phase/analyze-logs', { projectDir });
|
|
5
|
+
if (!skill) {
|
|
6
|
+
throw new Error('Failed to load skill: phase/analyze-logs');
|
|
29
7
|
}
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
\`\`\`
|
|
33
|
-
|
|
34
|
-
If no issues are found, return:
|
|
35
|
-
\`\`\`json
|
|
36
|
-
{
|
|
37
|
-
"summary": "User session appears normal with no issues detected",
|
|
38
|
-
"feedbacks": []
|
|
39
|
-
}
|
|
40
|
-
\`\`\``;
|
|
8
|
+
return `${skill.prompt}\n\n${OUTPUT_CONTRACTS['analyze-logs']}`;
|
|
41
9
|
}
|
|
42
10
|
export function createAnalyzeLogsUserPrompt(productName, userId, logs) {
|
|
43
11
|
const logsText = logs
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import assert from 'node:assert';
|
|
5
5
|
import { describe, it } from 'node:test';
|
|
6
|
-
import { createAppStorePromptWithContext,
|
|
6
|
+
import { createAppStorePromptWithContext, STORE_SCREENSHOT_SIZES, } from '../prompts.js';
|
|
7
7
|
// ============================================================
|
|
8
8
|
// STORE_SCREENSHOT_SIZES
|
|
9
9
|
// ============================================================
|
|
@@ -67,49 +67,6 @@ void describe('STORE_SCREENSHOT_SIZES', () => {
|
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
// ============================================================
|
|
70
|
-
// createAppStoreSystemPrompt
|
|
71
|
-
// ============================================================
|
|
72
|
-
void describe('createAppStoreSystemPrompt', () => {
|
|
73
|
-
void it('should return a string containing ASO reference', () => {
|
|
74
|
-
const result = createAppStoreSystemPrompt();
|
|
75
|
-
const lower = result.toLowerCase();
|
|
76
|
-
assert.ok(lower.includes('aso') || lower.includes('app store optimization'), 'System prompt should mention ASO or app store optimization');
|
|
77
|
-
});
|
|
78
|
-
void it('should contain NO PLACEHOLDERS rule', () => {
|
|
79
|
-
const result = createAppStoreSystemPrompt();
|
|
80
|
-
assert.ok(result.includes('NO PLACEHOLDERS'), 'System prompt should contain "NO PLACEHOLDERS" rule');
|
|
81
|
-
});
|
|
82
|
-
void it('should contain JSON format specification with app_store key', () => {
|
|
83
|
-
const result = createAppStoreSystemPrompt();
|
|
84
|
-
assert.ok(result.includes('"app_store"'), 'System prompt should contain "app_store" in JSON format spec');
|
|
85
|
-
});
|
|
86
|
-
void it('should include codebase exploration when hasCodebase=true', () => {
|
|
87
|
-
const result = createAppStoreSystemPrompt(true);
|
|
88
|
-
assert.ok(result.includes('Explore the Codebase'), 'System prompt with hasCodebase=true should include "Explore the Codebase"');
|
|
89
|
-
});
|
|
90
|
-
void it('should NOT include codebase exploration when hasCodebase=false (default)', () => {
|
|
91
|
-
const result = createAppStoreSystemPrompt();
|
|
92
|
-
assert.ok(!result.includes('Explore the Codebase'), 'System prompt with hasCodebase=false should NOT include "Explore the Codebase"');
|
|
93
|
-
});
|
|
94
|
-
void it('should also NOT include codebase exploration when explicitly passed false', () => {
|
|
95
|
-
const result = createAppStoreSystemPrompt(false);
|
|
96
|
-
assert.ok(!result.includes('Explore the Codebase'), 'System prompt with hasCodebase=false should NOT include "Explore the Codebase"');
|
|
97
|
-
});
|
|
98
|
-
void it('should contain character limit requirements', () => {
|
|
99
|
-
const result = createAppStoreSystemPrompt();
|
|
100
|
-
assert.ok(result.includes('30 characters'), 'Should mention 30 character limit for app name');
|
|
101
|
-
assert.ok(result.includes('80 characters'), 'Should mention 80 character limit for short description');
|
|
102
|
-
assert.ok(result.includes('4000 characters'), 'Should mention 4000 character limit for description');
|
|
103
|
-
assert.ok(result.includes('100 characters'), 'Should mention 100 character limit for keywords');
|
|
104
|
-
});
|
|
105
|
-
void it('should contain screenshot design standards', () => {
|
|
106
|
-
const result = createAppStoreSystemPrompt();
|
|
107
|
-
assert.ok(result.includes('Screenshot Design Standards'), 'Should contain screenshot design standards section');
|
|
108
|
-
assert.ok(result.includes('gradient'), 'Should mention gradient backgrounds');
|
|
109
|
-
assert.ok(result.includes('headline'), 'Should mention headline text');
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
// ============================================================
|
|
113
70
|
// createAppStorePromptWithContext
|
|
114
71
|
// ============================================================
|
|
115
72
|
void describe('createAppStorePromptWithContext', () => {
|
|
@@ -53,7 +53,7 @@ export const generateAppStoreAssets = async (options, config
|
|
|
53
53
|
logInfo(`Repository cloned to: ${repoCwd}`);
|
|
54
54
|
// Prepare context and prompt
|
|
55
55
|
const { context, prompt: analysisPrompt } = await prepareAppStoreContext(productId, targetStore, locale, verbose, true);
|
|
56
|
-
const systemPrompt = createAppStoreSystemPrompt(true);
|
|
56
|
+
const systemPrompt = await createAppStoreSystemPrompt(true);
|
|
57
57
|
logInfo('Starting AI generation for app store assets...');
|
|
58
58
|
const aiResult = await executeAppStoreQuery(analysisPrompt, systemPrompt, config, verbose, repoCwd);
|
|
59
59
|
if (!aiResult) {
|
|
@@ -39,5 +39,5 @@ export declare const STORE_SCREENSHOT_SIZES: {
|
|
|
39
39
|
};
|
|
40
40
|
export type StoreType = 'apple' | 'google';
|
|
41
41
|
export type DeviceSize = string;
|
|
42
|
-
export declare
|
|
42
|
+
export declare const createAppStoreSystemPrompt: (hasCodebase?: boolean, projectDir?: string) => Promise<string>;
|
|
43
43
|
export declare function createAppStorePromptWithContext(productId: string, contextInfo: string, targetStore: 'apple' | 'google' | 'all', locale: string, hasCodebase?: boolean): string;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AI prompts for app store screenshot and listing content generation.
|
|
3
3
|
*/
|
|
4
|
+
import { processConditionals, resolveSkill, } from '../../services/skill-resolver.js';
|
|
5
|
+
import { OUTPUT_CONTRACTS } from '../output-contracts.js';
|
|
4
6
|
export const STORE_SCREENSHOT_SIZES = {
|
|
5
7
|
apple: {
|
|
6
8
|
iphone_6_7: { width: 1290, height: 2796, label: 'iPhone 6.7"' },
|
|
@@ -13,86 +15,19 @@ export const STORE_SCREENSHOT_SIZES = {
|
|
|
13
15
|
tablet_10: { width: 1920, height: 1200, label: '10" Tablet' },
|
|
14
16
|
},
|
|
15
17
|
};
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
+
export const createAppStoreSystemPrompt = async (hasCodebase = false, projectDir) => {
|
|
19
|
+
const skill = await resolveSkill('phase/app-store-generation', { projectDir });
|
|
20
|
+
if (!skill) {
|
|
21
|
+
throw new Error('Failed to load skill: phase/app-store-generation');
|
|
22
|
+
}
|
|
23
|
+
let { prompt } = skill;
|
|
24
|
+
prompt = processConditionals(prompt, {
|
|
25
|
+
hasCodebase,
|
|
26
|
+
});
|
|
27
|
+
return `${prompt}
|
|
18
28
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
**Analysis Process**:${hasCodebase
|
|
22
|
-
? `
|
|
23
|
-
1. **Explore the Codebase**: Use your tools to read README, CLAUDE.md, package.json, landing pages, and key source files to deeply understand the product.
|
|
24
|
-
2. **Understand the Product**: Build a complete picture of features, UX flows, and value proposition.`
|
|
25
|
-
: `
|
|
26
|
-
1. **Understand the Product**: Analyze the product from the provided context.`}
|
|
27
|
-
${hasCodebase ? '3' : '2'}. **Identify Key Features**: Pick 5-8 standout features to highlight in screenshots.
|
|
28
|
-
${hasCodebase ? '4' : '3'}. **Create Screenshot Compositions**: Design each as a complete HTML page with gradient background, headline text overlay, and a device-framed mock app UI.
|
|
29
|
-
${hasCodebase ? '5' : '4'}. **Write Store Listings**: Generate optimized app name, subtitle, description, and keywords.
|
|
30
|
-
|
|
31
|
-
**Screenshot Design Standards**:
|
|
32
|
-
- Each screenshot must tell a story about a specific feature
|
|
33
|
-
- Professional gradient backgrounds (not flat colors)
|
|
34
|
-
- Bold headline text (2-5 words) at top or bottom, large and readable
|
|
35
|
-
- Optional subheadline with supporting detail
|
|
36
|
-
- The "app mock" area should show realistic UI with mock data (real-looking names, numbers, dates)
|
|
37
|
-
- Modern CSS: flexbox, grid, gradients, shadows, rounded corners
|
|
38
|
-
- Text must be legible at all display sizes
|
|
39
|
-
- Each HTML template is a complete self-contained page with inline CSS
|
|
40
|
-
- The template renders at exact store-required dimensions
|
|
41
|
-
|
|
42
|
-
**Store Listing Standards**:
|
|
43
|
-
- App name: max 30 characters, compelling and searchable
|
|
44
|
-
- Subtitle: max 30 characters (Apple), punchy value proposition
|
|
45
|
-
- Promotional text: max 170 characters (Apple), highlights current promotions or features — can be updated without a new app version
|
|
46
|
-
- Short description: max 80 characters (Google Play)
|
|
47
|
-
- Description: max 4000 characters, benefit-focused with feature bullets
|
|
48
|
-
- Keywords: max 100 characters (Apple), comma-separated, no spaces after commas
|
|
49
|
-
- What's New: concise release notes highlighting latest improvements
|
|
50
|
-
|
|
51
|
-
**CRITICAL - NO PLACEHOLDERS**: All content must be fully written using real product details. Never use brackets like [Feature] or [App Name].
|
|
52
|
-
|
|
53
|
-
**CRITICAL - Result Format**:
|
|
54
|
-
Return ONLY a JSON object. No text before or after.
|
|
55
|
-
|
|
56
|
-
\`\`\`json
|
|
57
|
-
{
|
|
58
|
-
"app_store": {
|
|
59
|
-
"listings": {
|
|
60
|
-
"en-US": {
|
|
61
|
-
"app_name": "Product Name",
|
|
62
|
-
"subtitle": "Short value proposition",
|
|
63
|
-
"promotional_text": "Highlight a current promotion, new feature, or seasonal message (170 chars max)",
|
|
64
|
-
"short_description": "One-liner for Google Play (80 chars max)",
|
|
65
|
-
"description": "Full store description with features and benefits",
|
|
66
|
-
"keywords": "keyword1,keyword2,keyword3",
|
|
67
|
-
"whats_new": "Latest improvements and fixes",
|
|
68
|
-
"primary_category": "Category",
|
|
69
|
-
"secondary_category": "Category"
|
|
70
|
-
}
|
|
71
|
-
},
|
|
72
|
-
"screenshots": [
|
|
73
|
-
{
|
|
74
|
-
"feature_highlight": "Feature being showcased",
|
|
75
|
-
"headline": "Bold 2-5 Word Headline",
|
|
76
|
-
"subheadline": "Supporting detail line (optional)",
|
|
77
|
-
"background_gradient": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
|
78
|
-
"text_color": "#ffffff",
|
|
79
|
-
"device_frame": "iphone|ipad|browser|macbook",
|
|
80
|
-
"html_template": "<html>...(complete self-contained HTML page showing the app mock with realistic data)...</html>"
|
|
81
|
-
}
|
|
82
|
-
]
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
\`\`\`
|
|
86
|
-
|
|
87
|
-
**Screenshot HTML Template Guidelines**:
|
|
88
|
-
- Each template should render the mock app UI (NOT the store composition — composition is handled separately)
|
|
89
|
-
- Include realistic mock data that demonstrates the feature
|
|
90
|
-
- Use modern, clean design matching current app design trends
|
|
91
|
-
- Mobile screenshots: design for portrait orientation
|
|
92
|
-
- Tablet/desktop screenshots: design for the appropriate orientation
|
|
93
|
-
- Set appropriate viewport meta tags for the target device size
|
|
94
|
-
- The HTML renders the "screen content" — it will be wrapped in device frame + background + text overlay automatically`;
|
|
95
|
-
}
|
|
29
|
+
${OUTPUT_CONTRACTS['app-store-generation']}`;
|
|
30
|
+
};
|
|
96
31
|
export function createAppStorePromptWithContext(productId, contextInfo, targetStore, locale, hasCodebase = false) {
|
|
97
32
|
const googleOrBothRequirements = targetStore === 'google'
|
|
98
33
|
? 'Google Play Store only'
|
|
@@ -56,7 +56,7 @@ async function* prompt(userPrompt) {
|
|
|
56
56
|
* Run a single autonomous iteration using Claude Code SDK
|
|
57
57
|
*/
|
|
58
58
|
async function runIteration(featureId, featureDescription, config, verbose) {
|
|
59
|
-
const systemPrompt = createAutonomousSystemPrompt(
|
|
59
|
+
const systemPrompt = await createAutonomousSystemPrompt(featureId);
|
|
60
60
|
const userPromptText = createAutonomousUserPrompt(featureDescription);
|
|
61
61
|
let lastAssistantResponse = '';
|
|
62
62
|
for await (const message of query({
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Prompts for the autonomous development phase
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
export declare function createAutonomousSystemPrompt(_config: EdsgerConfig, featureId: string): string;
|
|
4
|
+
export declare const createAutonomousSystemPrompt: (featureId: string, projectDir?: string) => Promise<string>;
|
|
6
5
|
export declare function createAutonomousUserPrompt(featureDescription: string): string;
|
|
@@ -1,64 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Prompts for the autonomous development phase
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
1. **ANALYZE**: Use Glob and Read to understand the current state of the code
|
|
20
|
-
2. **DECIDE**: Based on the objective, determine the single most impactful next step
|
|
21
|
-
3. **IMPLEMENT**: Make the changes using Edit/Write tools
|
|
22
|
-
4. **TEST**: Run relevant tests or builds to verify your changes work
|
|
23
|
-
5. **COMMIT**: Commit your changes with a conventional commit message (feat:, fix:, refactor:, docs:, test:, chore:)
|
|
24
|
-
|
|
25
|
-
**Types of work you may do**:
|
|
26
|
-
- Implement new feature code toward the objective
|
|
27
|
-
- Fix bugs discovered during analysis
|
|
28
|
-
- Refactor code to improve quality or maintainability
|
|
29
|
-
- Add or improve tests
|
|
30
|
-
- Update documentation
|
|
31
|
-
- Improve error handling or validation
|
|
32
|
-
|
|
33
|
-
**Important Rules**:
|
|
34
|
-
1. You are already on the correct branch - do NOT create or switch branches
|
|
35
|
-
2. Focus on ONE coherent change per iteration
|
|
36
|
-
3. Use conventional commit messages that describe what and why
|
|
37
|
-
4. Follow existing code patterns and conventions in the repository
|
|
38
|
-
5. Ensure proper TypeScript types and interfaces
|
|
39
|
-
6. Add appropriate error handling and validation
|
|
40
|
-
7. Handle pre-commit hooks properly:
|
|
41
|
-
- If lint-staged fails: Fix formatting and linting issues, then retry commit
|
|
42
|
-
- If AI code review fails: Use SKIP_REVIEW=1 git commit -m "message"
|
|
43
|
-
- Last resort: git commit --no-verify -m "message"
|
|
44
|
-
|
|
45
|
-
**CRITICAL - Result Format**:
|
|
46
|
-
You MUST end your response with a JSON object in this EXACT format:
|
|
47
|
-
|
|
48
|
-
\`\`\`json
|
|
49
|
-
{
|
|
50
|
-
"autonomous_result": {
|
|
51
|
-
"feature_id": "${featureId}",
|
|
52
|
-
"task_type": "feature|bugfix|refactor|test|docs|chore",
|
|
53
|
-
"summary": "Brief description of what was done",
|
|
54
|
-
"files_modified": ["file1.ts", "file2.tsx"],
|
|
55
|
-
"commit_hash": "abc123..."
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
\`\`\`
|
|
59
|
-
|
|
60
|
-
Remember: Focus on making real, meaningful progress toward the objective. Quality over quantity.`;
|
|
61
|
-
}
|
|
4
|
+
import { resolveSkill, substituteVariables, } from '../../services/skill-resolver.js';
|
|
5
|
+
import { OUTPUT_CONTRACTS } from '../output-contracts.js';
|
|
6
|
+
export const createAutonomousSystemPrompt = async (featureId, projectDir) => {
|
|
7
|
+
const skill = await resolveSkill('phase/autonomous', {
|
|
8
|
+
projectDir,
|
|
9
|
+
});
|
|
10
|
+
if (!skill) {
|
|
11
|
+
throw new Error('Failed to load skill: phase/autonomous');
|
|
12
|
+
}
|
|
13
|
+
let { prompt } = skill;
|
|
14
|
+
prompt = substituteVariables(prompt, { FEATURE_ID: featureId });
|
|
15
|
+
return `${prompt}
|
|
16
|
+
|
|
17
|
+
${OUTPUT_CONTRACTS['autonomous']}`;
|
|
18
|
+
};
|
|
62
19
|
export function createAutonomousUserPrompt(featureDescription) {
|
|
63
20
|
return `## Objective
|
|
64
21
|
|
|
@@ -170,7 +170,7 @@ export const planFeatureBranches = async (options, config
|
|
|
170
170
|
// Build the user prompt based on mode
|
|
171
171
|
const contextInfo = formatContextForPrompt(context.feature, context.product, context.user_stories, context.test_cases);
|
|
172
172
|
const existingBranchesInfo = formatExistingBranchesForPrompt(context.existing_branches);
|
|
173
|
-
const systemPrompt = createBranchPlanningSystemPrompt(config, featureId);
|
|
173
|
+
const systemPrompt = await createBranchPlanningSystemPrompt(config, featureId);
|
|
174
174
|
const userPrompt = await buildUserPrompt({
|
|
175
175
|
isIncrementalUpdate,
|
|
176
176
|
feedbacksContext,
|
|
@@ -2,7 +2,7 @@ import { type EdsgerConfig } from '../../types/index.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Create the system prompt for branch planning
|
|
4
4
|
*/
|
|
5
|
-
export declare function createBranchPlanningSystemPrompt(config: EdsgerConfig, featureId: string): string
|
|
5
|
+
export declare function createBranchPlanningSystemPrompt(config: EdsgerConfig, featureId: string, projectDir?: string): Promise<string>;
|
|
6
6
|
/**
|
|
7
7
|
* Create the user prompt with context for branch planning
|
|
8
8
|
*/
|