popeye-cli 1.9.4 → 1.10.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.
Files changed (68) hide show
  1. package/cheatsheet.md +65 -0
  2. package/dist/cli/commands/debug-context.d.ts +64 -0
  3. package/dist/cli/commands/debug-context.d.ts.map +1 -0
  4. package/dist/cli/commands/debug-context.js +221 -0
  5. package/dist/cli/commands/debug-context.js.map +1 -0
  6. package/dist/cli/commands/debug-prompts.d.ts +25 -0
  7. package/dist/cli/commands/debug-prompts.d.ts.map +1 -0
  8. package/dist/cli/commands/debug-prompts.js +80 -0
  9. package/dist/cli/commands/debug-prompts.js.map +1 -0
  10. package/dist/cli/commands/debug.d.ts +68 -0
  11. package/dist/cli/commands/debug.d.ts.map +1 -0
  12. package/dist/cli/commands/debug.js +543 -0
  13. package/dist/cli/commands/debug.js.map +1 -0
  14. package/dist/cli/commands/index.d.ts +1 -0
  15. package/dist/cli/commands/index.d.ts.map +1 -1
  16. package/dist/cli/commands/index.js +1 -0
  17. package/dist/cli/commands/index.js.map +1 -1
  18. package/dist/cli/index.d.ts.map +1 -1
  19. package/dist/cli/index.js +2 -1
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/cli/interactive.d.ts.map +1 -1
  22. package/dist/cli/interactive.js +25 -0
  23. package/dist/cli/interactive.js.map +1 -1
  24. package/dist/generators/all.d.ts.map +1 -1
  25. package/dist/generators/all.js +3 -0
  26. package/dist/generators/all.js.map +1 -1
  27. package/dist/generators/templates/database-docker.d.ts.map +1 -1
  28. package/dist/generators/templates/database-docker.js +11 -0
  29. package/dist/generators/templates/database-docker.js.map +1 -1
  30. package/dist/generators/templates/fullstack.d.ts +4 -1
  31. package/dist/generators/templates/fullstack.d.ts.map +1 -1
  32. package/dist/generators/templates/fullstack.js +6 -2
  33. package/dist/generators/templates/fullstack.js.map +1 -1
  34. package/package.json +1 -1
  35. package/skills/ARBITRATOR.md +137 -0
  36. package/skills/ARCHITECT.md +167 -0
  37. package/skills/AUDITOR.md +128 -0
  38. package/skills/AUDIT_REPORT_SCHEMA.md +20 -0
  39. package/skills/BACKEND_PROGRAMMER.md +95 -0
  40. package/skills/CONSENSUS_PACKET_SCHEMA.md +166 -0
  41. package/skills/DB_EXPERT.md +106 -0
  42. package/skills/DEBUGGER.md +286 -0
  43. package/skills/DISPATCHER.md +157 -0
  44. package/skills/FRONTEND_PROGRAMMER.md +84 -0
  45. package/skills/JOURNALIST.md +247 -0
  46. package/skills/MARKETING_EXPERT.md +23 -0
  47. package/skills/PHASE_GATE_ENGINE_SPEC.md +78 -0
  48. package/skills/PLAN_PACKET_SCHEMA.md +222 -0
  49. package/skills/POPEYE_CONSTITUTION.md +177 -0
  50. package/skills/POPEYE_FULL_AUTONOMY_PIPELINE.md +484 -0
  51. package/skills/PRODUCTION_READINESS_SCHEMA.md +19 -0
  52. package/skills/QA_TESTER.md +40 -0
  53. package/skills/RCA_PACKET_SCHEMA.md +22 -0
  54. package/skills/RELEASE_MANAGER.md +60 -0
  55. package/skills/REVIEWER.md +133 -0
  56. package/skills/SOCIAL_EXPERT.md +22 -0
  57. package/skills/UI_UX_SPECIALIST.md +22 -0
  58. package/skills/WEBSITE_PROGRAMMER.md +37 -0
  59. package/src/cli/commands/debug-context.ts +265 -0
  60. package/src/cli/commands/debug-prompts.ts +91 -0
  61. package/src/cli/commands/debug.ts +662 -0
  62. package/src/cli/commands/index.ts +1 -0
  63. package/src/cli/index.ts +2 -0
  64. package/src/cli/interactive.ts +27 -0
  65. package/src/generators/all.ts +3 -0
  66. package/src/generators/templates/database-docker.ts +11 -0
  67. package/src/generators/templates/fullstack.ts +6 -2
  68. package/tests/cli/commands/debug.test.ts +376 -0
@@ -0,0 +1,22 @@
1
+ # RCA PACKET SCHEMA
2
+ Purpose: Machine-verifiable root cause analysis artifact.
3
+
4
+ Required Fields:
5
+
6
+ - rca_id (UUID)
7
+ - timestamp
8
+ - incident_type
9
+ - severity
10
+ - reproduction_steps
11
+ - affected_files[]
12
+ - execution_trace
13
+ - root_cause_statement
14
+ - responsible_layer
15
+ - origin_phase
16
+ - governance_gap
17
+ - recommended_fix_owner
18
+ - requires_consensus (boolean)
19
+ - requires_change_request (boolean)
20
+
21
+ No vague root cause allowed.
22
+ Must identify structural origin.
@@ -0,0 +1,60 @@
1
+ # Skill: RELEASE MANAGER
2
+ Role Type: Production Readiness Authority
3
+ Authority Level: Final Deployment Validator
4
+
5
+ ---
6
+
7
+ ## Objective
8
+
9
+ Ensure system is ready for deployment and produce final release artifacts.
10
+
11
+ ---
12
+
13
+ ## Responsibilities
14
+
15
+ - Validate Production Gate results
16
+ - Generate Release Notes
17
+ - Generate Deployment Instructions
18
+ - Verify versioning
19
+ - Verify changelog
20
+ - Tag release version (conceptually)
21
+
22
+ ---
23
+
24
+ ## Required Inputs
25
+
26
+ - Production Readiness Report
27
+ - Audit Report
28
+ - Repo Snapshot
29
+ - Final Plan Packet
30
+
31
+ ---
32
+
33
+ ## Output
34
+
35
+ # RELEASE PACKAGE
36
+
37
+ ## Version
38
+ Semantic version increment
39
+
40
+ ## Included Features
41
+ List from Master Plan
42
+
43
+ ## Known Risks
44
+ Must be empty for PASS
45
+
46
+ ## Deployment Steps
47
+ - Build commands
48
+ - Env var setup
49
+ - DB migration command
50
+ - Start commands
51
+
52
+ ## Rollback Plan
53
+ Clear rollback steps
54
+
55
+ ---
56
+
57
+ ## Definition of Done
58
+
59
+ Release package stored under:
60
+ `/docs/release/`
@@ -0,0 +1,133 @@
1
+ # Skill: REVIEWER (PLAN CONSENSUS REVIEWER)
2
+ Role Type: Independent Plan Auditor
3
+ Authority Level: Gatekeeper for Consensus Approval
4
+
5
+ ---
6
+
7
+ ## Objective
8
+
9
+ Review a proposed plan produced by Dispatcher/Popeye using an independent LLM perspective,
10
+ detect gaps/hallucinations/shortcuts, and issue a structured vote that can be used for consensus.
11
+
12
+ Reviewer is NOT an implementer.
13
+ Reviewer is NOT a co-author (unless explicitly requested by Arbitrator to propose minimal corrections).
14
+ Reviewer is an evidence-based plan auditor.
15
+
16
+ ---
17
+
18
+ ## Required Input: PLAN PACKET (Mandatory)
19
+
20
+ Reviewer only operates on a "Plan Packet" containing:
21
+
22
+ 1) **Master Plan (approved version)**
23
+ 2) **Proposed Plan** (the artifact under review)
24
+ 3) **Repo Snapshot Summary** (what exists now)
25
+ 4) **Constraints** (tech stack, env, policies)
26
+ 5) **Constitution** (governing rules)
27
+ 6) **Acceptance Criteria / Definition of Done**
28
+
29
+ If any component is missing → Reviewer must return **BLOCKED: Missing Inputs**.
30
+
31
+ ---
32
+
33
+ ## Primary Responsibilities
34
+
35
+ - Validate alignment with the Master Plan and Constitution
36
+ - Validate completeness: all scenarios, edge cases, and integration paths addressed
37
+ - Validate feasibility: steps are implementable in the repo as it exists
38
+ - Detect hallucinations: invented files, APIs, schema, env vars, services
39
+ - Detect shortcuts: mocks, TODOs, “later” wiring, vague testing
40
+ - Verify roles are correctly assigned and synchronized
41
+ - Verify artifacts + phase gates are present (architecture, DB, QA, review)
42
+
43
+ ---
44
+
45
+ ## Non-Responsibilities
46
+
47
+ - Does NOT implement
48
+ - Does NOT rewrite the whole plan
49
+ - Does NOT change scope without a Change Request
50
+ - Does NOT relax consensus rules
51
+
52
+ ---
53
+
54
+ ## Review Method (Must Follow)
55
+
56
+ 1) **Coverage Scan**
57
+ - Does the plan cover 100% of Master Plan deliverables?
58
+ - Are all roles accounted for (Architect/DB/BE/FE/QA/etc.)?
59
+
60
+ 2) **Integration Scan**
61
+ - FE↔BE wiring explicit?
62
+ - DB↔BE wiring explicit?
63
+ - Auth, env vars, migrations, deployment accounted for?
64
+
65
+ 3) **Evidence Scan**
66
+ - Does it reference repo paths and existing modules accurately?
67
+ - Does it specify new files to be created deterministically?
68
+
69
+ 4) **Risk & Scenario Scan**
70
+ - Failure modes covered? (timeouts, validation errors, empty states, rate limits, migrations fail, etc.)
71
+ - “Same resolution” comparisons, backward compat, rollback?
72
+
73
+ 5) **Testability Scan**
74
+ - Are tests specified as executable steps? (not “write tests”)
75
+ - Are critical paths and integration tests listed?
76
+
77
+ ---
78
+
79
+ ## Output: REVIEW VOTE (Strict Format)
80
+
81
+ Reviewer MUST output the following structure:
82
+
83
+ ### 1) Verdict
84
+ - **APPROVE**
85
+ - **APPROVE WITH MINOR CHANGES**
86
+ - **REJECT**
87
+ - **BLOCKED: MISSING INPUTS**
88
+
89
+ ### 2) Score (0–100)
90
+ A numeric score representing confidence the plan can be executed without shortcuts/hallucinations.
91
+
92
+ ### 3) Blocking Issues (if any)
93
+ Each blocking issue must include:
94
+ - **ID**
95
+ - **Problem**
96
+ - **Why it violates Master Plan/Constitution**
97
+ - **Exact fix requirement**
98
+ - **Where it should be fixed (which artifact/role)**
99
+
100
+ ### 4) Non-Blocking Improvements
101
+ Concrete improvements that strengthen the plan.
102
+
103
+ ### 5) Evidence & Consistency Notes
104
+ List any suspected hallucinations or ambiguous references.
105
+
106
+ ### 6) Minimal Patch Suggestions (Optional)
107
+ If "Approve with minor changes", provide minimal diffs / bullet edits.
108
+
109
+ ---
110
+
111
+ ## Automatic Rejection Triggers
112
+
113
+ Return **REJECT** if any exist:
114
+
115
+ - Implementation begins before architecture/DB/QA plans are gated
116
+ - Any plan step relies on "mock", "placeholder", "TODO" without explicit approval
117
+ - Unowned decisions (e.g. schema by BE, architecture by FE)
118
+ - Missing end-to-end integration steps
119
+ - Vague test plan (no named tests, no commands, no expected outcomes)
120
+ - Invented repo state (files/routes/env vars that don’t exist)
121
+
122
+ ---
123
+
124
+ ## Definition of Done
125
+
126
+ Reviewer is done when:
127
+ - A valid structured vote is returned
128
+ - Issues are categorized as blocking vs non-blocking
129
+ - Fix directives are precise enough for Dispatcher/Arbitrator to act on
130
+
131
+ ---
132
+
133
+ End of Skill.
@@ -0,0 +1,22 @@
1
+ # Skill: SOCIAL EXPERT
2
+ Role Type: Distribution Strategist
3
+
4
+ ---
5
+
6
+ ## Objective
7
+
8
+ Translate product and marketing strategy into platform-native content.
9
+
10
+ ---
11
+
12
+ ## Responsibilities
13
+
14
+ - Platform strategy
15
+ - Content formats
16
+ - Community guidelines alignment
17
+ - Engagement loops
18
+
19
+ ---
20
+
21
+ Definition of Done:
22
+ Distribution plan aligned with marketing strategy.
@@ -0,0 +1,22 @@
1
+ # Skill: UI/UX SPECIALIST
2
+ Role Type: Experience Authority
3
+
4
+ ---
5
+
6
+ ## Objective
7
+
8
+ Ensure usability, clarity, accessibility, and design coherence.
9
+
10
+ ---
11
+
12
+ ## Responsibilities
13
+
14
+ - UX flows
15
+ - Design system alignment
16
+ - Accessibility review
17
+ - Interaction validation
18
+
19
+ ---
20
+
21
+ Anti-Shortcut:
22
+ No cosmetic-only review — must evaluate usability.
@@ -0,0 +1,37 @@
1
+ # Skill: WEBSITE PROGRAMMER
2
+ Role Type: Marketing Site Implementer
3
+
4
+ ---
5
+
6
+ ## Objective
7
+
8
+ Implement production-grade website aligned with:
9
+
10
+ - Brand
11
+ - Product positioning
12
+ - Architecture (if integrated)
13
+ - Marketing strategy
14
+
15
+ ---
16
+
17
+ ## Responsibilities
18
+
19
+ - Landing pages
20
+ - Pricing pages
21
+ - Documentation pages
22
+ - SEO structure
23
+ - Analytics integration
24
+
25
+ ---
26
+
27
+ ## Anti-Shortcut Rules
28
+
29
+ - No generic template text
30
+ - No placeholder copy
31
+ - No mismatched branding
32
+ - No default theme if brand colors defined
33
+
34
+ ---
35
+
36
+ Definition of Done:
37
+ Website reflects actual product capabilities and branding.
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Debug context helpers
3
+ * Deterministic functions for error analysis and smart file selection.
4
+ * Pure functions, easily testable.
5
+ */
6
+
7
+ import path from 'node:path';
8
+
9
+ /**
10
+ * Entry in the lightweight file index (paths + metadata, no content).
11
+ */
12
+ export interface FileIndexEntry {
13
+ relativePath: string;
14
+ size: number;
15
+ mtime: number;
16
+ isConfig: boolean;
17
+ }
18
+
19
+ /** Config file patterns that are always considered relevant */
20
+ const CONFIG_PATTERNS = [
21
+ 'package.json', 'package-lock.json', 'tsconfig.json', 'vite.config',
22
+ 'pyproject.toml', 'requirements.txt', 'Pipfile',
23
+ 'docker-compose.yml', 'docker-compose.yaml', 'Dockerfile',
24
+ '.env.example', '.env.local', 'alembic.ini',
25
+ 'next.config', 'tailwind.config', 'postcss.config',
26
+ 'jest.config', 'vitest.config', 'pytest.ini', 'setup.cfg',
27
+ ];
28
+
29
+ /**
30
+ * Check if a file path matches a config pattern.
31
+ *
32
+ * @param filePath - Relative path to check.
33
+ * @returns True if the file is a known config file.
34
+ */
35
+ export function isConfigFile(filePath: string): boolean {
36
+ const basename = path.basename(filePath);
37
+ return CONFIG_PATTERNS.some((p) => basename.startsWith(p));
38
+ }
39
+
40
+ /**
41
+ * Extract file paths mentioned in stack traces.
42
+ * Supports Python tracebacks, TypeScript/JS errors, and generic path patterns.
43
+ *
44
+ * @param text - Error text or stack trace.
45
+ * @returns Deduplicated array of file paths found in the text.
46
+ */
47
+ export function extractPathsFromError(text: string): string[] {
48
+ const paths = new Set<string>();
49
+
50
+ // Python traceback: File "/app/src/module/file.py", line 42
51
+ const pyPattern = /File "([^"]+\.py[cw]?)", line \d+/g;
52
+ for (const match of text.matchAll(pyPattern)) {
53
+ paths.add(normalizePath(match[1]));
54
+ }
55
+
56
+ // TS/JS errors: src/components/App.tsx(15,3) or src/components/App.tsx:15:3
57
+ const tsPattern = /([a-zA-Z0-9_./\\-]+\.(?:ts|tsx|js|jsx|mjs|cjs))[\s:(]/g;
58
+ for (const match of text.matchAll(tsPattern)) {
59
+ paths.add(normalizePath(match[1]));
60
+ }
61
+
62
+ // Docker / generic paths: /app/src/..., ./src/...
63
+ const genericPattern = /(?:\/app\/|\.\/)((?:src|app|lib|tests?|config)\/[a-zA-Z0-9_./-]+\.\w+)/g;
64
+ for (const match of text.matchAll(genericPattern)) {
65
+ paths.add(normalizePath(match[1]));
66
+ }
67
+
68
+ // Module not found patterns: Cannot find module './foo/bar'
69
+ const modulePattern = /Cannot find module ['"]([^'"]+)['"]/g;
70
+ for (const match of text.matchAll(modulePattern)) {
71
+ const mod = match[1];
72
+ if (mod.startsWith('.') || mod.startsWith('/')) {
73
+ paths.add(normalizePath(mod));
74
+ }
75
+ }
76
+
77
+ // ModuleNotFoundError: No module named 'src.foo.bar'
78
+ const pyModulePattern = /No module named ['"]([^'"]+)['"]/g;
79
+ for (const match of text.matchAll(pyModulePattern)) {
80
+ const dotPath = match[1].replace(/\./g, '/');
81
+ paths.add(dotPath);
82
+ }
83
+
84
+ return Array.from(paths);
85
+ }
86
+
87
+ /** Tech keyword map: keyword -> tags */
88
+ const TECH_KEYWORDS: Record<string, string[]> = {
89
+ 'alembic': ['alembic', 'database', 'migration'],
90
+ 'sqlalchemy': ['sqlalchemy', 'database', 'orm'],
91
+ 'prisma': ['prisma', 'database', 'orm'],
92
+ 'docker': ['docker', 'container'],
93
+ 'docker-compose': ['docker', 'container', 'compose'],
94
+ 'vite': ['vite', 'bundler', 'frontend'],
95
+ 'webpack': ['webpack', 'bundler', 'frontend'],
96
+ 'next': ['nextjs', 'react', 'frontend'],
97
+ 'fastapi': ['fastapi', 'backend', 'python'],
98
+ 'flask': ['flask', 'backend', 'python'],
99
+ 'express': ['express', 'backend', 'node'],
100
+ 'postgres': ['postgres', 'database'],
101
+ 'redis': ['redis', 'cache'],
102
+ 'tailwind': ['tailwind', 'css', 'frontend'],
103
+ 'pytest': ['pytest', 'testing', 'python'],
104
+ 'jest': ['jest', 'testing', 'node'],
105
+ 'vitest': ['vitest', 'testing', 'node'],
106
+ 'nginx': ['nginx', 'proxy'],
107
+ 'cors': ['cors', 'api'],
108
+ 'migration': ['migration', 'database'],
109
+ 'celery': ['celery', 'queue', 'python'],
110
+ };
111
+
112
+ /**
113
+ * Detect framework/tech keywords from error text.
114
+ *
115
+ * @param text - Error text or stack trace.
116
+ * @returns Object with deduplicated tags array.
117
+ */
118
+ export function detectTechFromError(text: string): { tags: string[] } {
119
+ const tags = new Set<string>();
120
+ const lower = text.toLowerCase();
121
+
122
+ for (const [keyword, keywordTags] of Object.entries(TECH_KEYWORDS)) {
123
+ if (lower.includes(keyword)) {
124
+ for (const tag of keywordTags) {
125
+ tags.add(tag);
126
+ }
127
+ }
128
+ }
129
+
130
+ return { tags: Array.from(tags) };
131
+ }
132
+
133
+ /**
134
+ * Select relevant files from the project index based on error paths and tech tags.
135
+ * Returns file paths sorted by relevance (direct matches first, then config, then nearby).
136
+ *
137
+ * @param fileIndex - Lightweight file index.
138
+ * @param errorPaths - Paths extracted from the error.
139
+ * @param tags - Tech tags detected from the error.
140
+ * @returns Array of relative paths to load.
141
+ */
142
+ export function selectRelevantFiles(
143
+ fileIndex: FileIndexEntry[],
144
+ errorPaths: string[],
145
+ tags: string[]
146
+ ): string[] {
147
+ if (fileIndex.length === 0) return [];
148
+
149
+ const selected = new Set<string>();
150
+ const MAX_FILES = 15;
151
+
152
+ // 1. Direct matches: files mentioned in the error
153
+ for (const errorPath of errorPaths) {
154
+ for (const entry of fileIndex) {
155
+ if (
156
+ entry.relativePath.endsWith(errorPath) ||
157
+ entry.relativePath === errorPath ||
158
+ entry.relativePath.includes(errorPath)
159
+ ) {
160
+ selected.add(entry.relativePath);
161
+ }
162
+ }
163
+ }
164
+
165
+ // 2. Sibling files: files in the same directory as matches
166
+ const matchedDirs = new Set<string>();
167
+ for (const sel of selected) {
168
+ matchedDirs.add(path.dirname(sel));
169
+ }
170
+ for (const dir of matchedDirs) {
171
+ for (const entry of fileIndex) {
172
+ if (path.dirname(entry.relativePath) === dir && selected.size < MAX_FILES) {
173
+ selected.add(entry.relativePath);
174
+ }
175
+ }
176
+ }
177
+
178
+ // 3. Tag-based: config files related to detected tech
179
+ if (tags.includes('database') || tags.includes('migration')) {
180
+ for (const entry of fileIndex) {
181
+ if (
182
+ entry.relativePath.includes('alembic') ||
183
+ entry.relativePath.includes('migration') ||
184
+ entry.relativePath.includes('prisma') ||
185
+ entry.relativePath.includes('schema.sql')
186
+ ) {
187
+ if (selected.size < MAX_FILES) selected.add(entry.relativePath);
188
+ }
189
+ }
190
+ }
191
+ if (tags.includes('docker') || tags.includes('compose')) {
192
+ for (const entry of fileIndex) {
193
+ if (
194
+ entry.relativePath.includes('docker') ||
195
+ entry.relativePath.includes('Dockerfile')
196
+ ) {
197
+ if (selected.size < MAX_FILES) selected.add(entry.relativePath);
198
+ }
199
+ }
200
+ }
201
+
202
+ // 4. Fallback: always include config files if we have few matches
203
+ if (selected.size < 5) {
204
+ for (const entry of fileIndex) {
205
+ if (entry.isConfig && selected.size < MAX_FILES) {
206
+ selected.add(entry.relativePath);
207
+ }
208
+ }
209
+ }
210
+
211
+ return Array.from(selected).slice(0, MAX_FILES);
212
+ }
213
+
214
+ /** Image file extensions that Claude can read via the Read tool */
215
+ const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg']);
216
+
217
+ /**
218
+ * Extract image/screenshot file paths from user input text.
219
+ * Detects both quoted and unquoted absolute/relative paths ending in image extensions.
220
+ *
221
+ * @param text - User input text.
222
+ * @returns Array of image file paths found in the text.
223
+ */
224
+ export function extractImagePaths(text: string): string[] {
225
+ const paths = new Set<string>();
226
+
227
+ // Quoted paths: '/path/to/image.png' or "/path/to/image.png"
228
+ const quotedPattern = /['"]([^'"]+\.(?:png|jpg|jpeg|gif|webp|bmp|svg))['"]/gi;
229
+ for (const match of text.matchAll(quotedPattern)) {
230
+ paths.add(match[1]);
231
+ }
232
+
233
+ // Unquoted absolute paths: /path/to/image.png (no spaces allowed in unquoted)
234
+ const absolutePattern = /(\/[^\s'"]+\.(?:png|jpg|jpeg|gif|webp|bmp|svg))/gi;
235
+ for (const match of text.matchAll(absolutePattern)) {
236
+ paths.add(match[1]);
237
+ }
238
+
239
+ return Array.from(paths);
240
+ }
241
+
242
+ /**
243
+ * Check if a file path points to an image file.
244
+ *
245
+ * @param filePath - File path to check.
246
+ * @returns True if the file has an image extension.
247
+ */
248
+ export function isImageFile(filePath: string): boolean {
249
+ const ext = path.extname(filePath).toLowerCase();
250
+ return IMAGE_EXTENSIONS.has(ext);
251
+ }
252
+
253
+ /**
254
+ * Normalize a file path by stripping common prefixes (/app/, ./, etc.).
255
+ *
256
+ * @param p - Raw file path from error text.
257
+ * @returns Normalized relative path.
258
+ */
259
+ function normalizePath(p: string): string {
260
+ let normalized = p.replace(/\\/g, '/');
261
+ // Strip common container prefixes
262
+ normalized = normalized.replace(/^\/app\//, '');
263
+ normalized = normalized.replace(/^\.\//, '');
264
+ return normalized;
265
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Debug session prompts
3
+ * System prompt with strict output contract and conversation history formatting.
4
+ */
5
+
6
+ /**
7
+ * Message in the debug conversation history.
8
+ */
9
+ export interface DebugMessage {
10
+ role: 'user' | 'assistant';
11
+ content: string;
12
+ }
13
+
14
+ /**
15
+ * Build the system prompt for the debug session.
16
+ *
17
+ * @returns The system prompt instructing Claude on debug response format.
18
+ */
19
+ export function getDebugSystemPrompt(): string {
20
+ return `You are an expert debugger embedded in the Popeye CLI. Your role is to diagnose
21
+ errors in projects generated by Popeye (full-stack apps, backends, frontends, websites).
22
+
23
+ You have access to the project's file system through your tools. When the user pastes an error,
24
+ diagnose it using the project context provided and the files you can read.
25
+
26
+ ## Response Format
27
+
28
+ You MUST structure every response with these sections:
29
+
30
+ ### Diagnosis
31
+ Explain what is wrong and why it is happening. Be specific -- reference exact module names,
32
+ configuration keys, or missing dependencies.
33
+
34
+ ### Evidence
35
+ List the specific files and lines that confirm your diagnosis. Use the format:
36
+ - \`path/to/file.ts:42\` -- description of what you found
37
+
38
+ ### Proposed Fix
39
+ Provide the exact changes needed. Show file paths and the code to add, modify, or remove.
40
+ Use diff-style formatting when helpful:
41
+ \`\`\`diff
42
+ - old line
43
+ + new line
44
+ \`\`\`
45
+
46
+ ### Commands to Verify
47
+ List shell commands the user should run to confirm the fix works. For example:
48
+ \`\`\`bash
49
+ docker-compose up --build
50
+ npm run dev
51
+ pytest tests/ -v
52
+ \`\`\`
53
+
54
+ ### Ready to Apply?
55
+ End with a short note reminding the user they can type \`/fix\` in the debug session
56
+ to have Popeye apply the proposed changes automatically.
57
+
58
+ ## Screenshots and Images
59
+ - Users may paste file paths to screenshots or images (e.g., \`/path/to/Screenshot.png\`).
60
+ - When you see an image path in the user's message, use the **Read** tool to view it.
61
+ The Read tool supports PNG, JPG, GIF, WebP, and other image formats.
62
+ - After viewing the screenshot, analyze what it shows (UI bugs, missing elements, error dialogs,
63
+ browser console errors, terminal output, etc.) and include your visual findings in the Diagnosis.
64
+ - If the image path is invalid or unreadable, tell the user and ask them to verify the path.
65
+
66
+ ## Rules
67
+ - Never guess. If you need more context, ask the user or read additional files.
68
+ - Reference actual file paths from the project, not hypothetical ones.
69
+ - When multiple issues exist, address the root cause first.
70
+ - Keep explanations concise -- developers are your audience.
71
+ - If the error is outside the project (e.g., a system-level issue), say so clearly.`;
72
+ }
73
+
74
+ /**
75
+ * Format conversation history for inclusion in the prompt.
76
+ *
77
+ * @param history - Array of debug messages.
78
+ * @returns Formatted string of conversation history.
79
+ */
80
+ export function formatConversationHistory(history: DebugMessage[]): string {
81
+ if (history.length === 0) return '';
82
+
83
+ const lines: string[] = ['## Previous Conversation'];
84
+
85
+ for (const msg of history) {
86
+ const role = msg.role === 'user' ? 'User' : 'Assistant';
87
+ lines.push(`\n**${role}:**\n${msg.content}`);
88
+ }
89
+
90
+ return lines.join('\n');
91
+ }