popeye-cli 1.0.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 (209) hide show
  1. package/.env.example +25 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +320 -0
  4. package/dist/adapters/claude.d.ts +82 -0
  5. package/dist/adapters/claude.d.ts.map +1 -0
  6. package/dist/adapters/claude.js +230 -0
  7. package/dist/adapters/claude.js.map +1 -0
  8. package/dist/adapters/openai.d.ts +48 -0
  9. package/dist/adapters/openai.d.ts.map +1 -0
  10. package/dist/adapters/openai.js +257 -0
  11. package/dist/adapters/openai.js.map +1 -0
  12. package/dist/auth/claude.d.ts +44 -0
  13. package/dist/auth/claude.d.ts.map +1 -0
  14. package/dist/auth/claude.js +139 -0
  15. package/dist/auth/claude.js.map +1 -0
  16. package/dist/auth/index.d.ts +61 -0
  17. package/dist/auth/index.d.ts.map +1 -0
  18. package/dist/auth/index.js +141 -0
  19. package/dist/auth/index.js.map +1 -0
  20. package/dist/auth/keychain.d.ts +66 -0
  21. package/dist/auth/keychain.d.ts.map +1 -0
  22. package/dist/auth/keychain.js +125 -0
  23. package/dist/auth/keychain.js.map +1 -0
  24. package/dist/auth/openai-entry.d.ts +9 -0
  25. package/dist/auth/openai-entry.d.ts.map +1 -0
  26. package/dist/auth/openai-entry.js +410 -0
  27. package/dist/auth/openai-entry.js.map +1 -0
  28. package/dist/auth/openai.d.ts +71 -0
  29. package/dist/auth/openai.d.ts.map +1 -0
  30. package/dist/auth/openai.js +212 -0
  31. package/dist/auth/openai.js.map +1 -0
  32. package/dist/auth/server.d.ts +32 -0
  33. package/dist/auth/server.d.ts.map +1 -0
  34. package/dist/auth/server.js +213 -0
  35. package/dist/auth/server.js.map +1 -0
  36. package/dist/cli/commands/auth.d.ts +10 -0
  37. package/dist/cli/commands/auth.d.ts.map +1 -0
  38. package/dist/cli/commands/auth.js +162 -0
  39. package/dist/cli/commands/auth.js.map +1 -0
  40. package/dist/cli/commands/config.d.ts +10 -0
  41. package/dist/cli/commands/config.d.ts.map +1 -0
  42. package/dist/cli/commands/config.js +215 -0
  43. package/dist/cli/commands/config.js.map +1 -0
  44. package/dist/cli/commands/create.d.ts +10 -0
  45. package/dist/cli/commands/create.d.ts.map +1 -0
  46. package/dist/cli/commands/create.js +240 -0
  47. package/dist/cli/commands/create.js.map +1 -0
  48. package/dist/cli/commands/index.d.ts +10 -0
  49. package/dist/cli/commands/index.d.ts.map +1 -0
  50. package/dist/cli/commands/index.js +10 -0
  51. package/dist/cli/commands/index.js.map +1 -0
  52. package/dist/cli/commands/resume.d.ts +18 -0
  53. package/dist/cli/commands/resume.d.ts.map +1 -0
  54. package/dist/cli/commands/resume.js +241 -0
  55. package/dist/cli/commands/resume.js.map +1 -0
  56. package/dist/cli/commands/status.d.ts +18 -0
  57. package/dist/cli/commands/status.d.ts.map +1 -0
  58. package/dist/cli/commands/status.js +154 -0
  59. package/dist/cli/commands/status.js.map +1 -0
  60. package/dist/cli/index.d.ts +17 -0
  61. package/dist/cli/index.d.ts.map +1 -0
  62. package/dist/cli/index.js +71 -0
  63. package/dist/cli/index.js.map +1 -0
  64. package/dist/cli/interactive.d.ts +9 -0
  65. package/dist/cli/interactive.d.ts.map +1 -0
  66. package/dist/cli/interactive.js +330 -0
  67. package/dist/cli/interactive.js.map +1 -0
  68. package/dist/cli/output.d.ts +182 -0
  69. package/dist/cli/output.d.ts.map +1 -0
  70. package/dist/cli/output.js +355 -0
  71. package/dist/cli/output.js.map +1 -0
  72. package/dist/config/defaults.d.ts +57 -0
  73. package/dist/config/defaults.d.ts.map +1 -0
  74. package/dist/config/defaults.js +103 -0
  75. package/dist/config/defaults.js.map +1 -0
  76. package/dist/config/index.d.ts +138 -0
  77. package/dist/config/index.d.ts.map +1 -0
  78. package/dist/config/index.js +244 -0
  79. package/dist/config/index.js.map +1 -0
  80. package/dist/config/schema.d.ts +220 -0
  81. package/dist/config/schema.d.ts.map +1 -0
  82. package/dist/config/schema.js +141 -0
  83. package/dist/config/schema.js.map +1 -0
  84. package/dist/generators/index.d.ts +101 -0
  85. package/dist/generators/index.d.ts.map +1 -0
  86. package/dist/generators/index.js +200 -0
  87. package/dist/generators/index.js.map +1 -0
  88. package/dist/generators/python.d.ts +48 -0
  89. package/dist/generators/python.d.ts.map +1 -0
  90. package/dist/generators/python.js +262 -0
  91. package/dist/generators/python.js.map +1 -0
  92. package/dist/generators/templates/index.d.ts +6 -0
  93. package/dist/generators/templates/index.d.ts.map +1 -0
  94. package/dist/generators/templates/index.js +6 -0
  95. package/dist/generators/templates/index.js.map +1 -0
  96. package/dist/generators/templates/python.d.ts +53 -0
  97. package/dist/generators/templates/python.d.ts.map +1 -0
  98. package/dist/generators/templates/python.js +454 -0
  99. package/dist/generators/templates/python.js.map +1 -0
  100. package/dist/generators/templates/typescript.d.ts +53 -0
  101. package/dist/generators/templates/typescript.d.ts.map +1 -0
  102. package/dist/generators/templates/typescript.js +394 -0
  103. package/dist/generators/templates/typescript.js.map +1 -0
  104. package/dist/generators/typescript.d.ts +64 -0
  105. package/dist/generators/typescript.d.ts.map +1 -0
  106. package/dist/generators/typescript.js +271 -0
  107. package/dist/generators/typescript.js.map +1 -0
  108. package/dist/index.d.ts +7 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +12 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/state/index.d.ts +168 -0
  113. package/dist/state/index.d.ts.map +1 -0
  114. package/dist/state/index.js +338 -0
  115. package/dist/state/index.js.map +1 -0
  116. package/dist/state/persistence.d.ts +91 -0
  117. package/dist/state/persistence.d.ts.map +1 -0
  118. package/dist/state/persistence.js +201 -0
  119. package/dist/state/persistence.js.map +1 -0
  120. package/dist/types/cli.d.ts +132 -0
  121. package/dist/types/cli.d.ts.map +1 -0
  122. package/dist/types/cli.js +17 -0
  123. package/dist/types/cli.js.map +1 -0
  124. package/dist/types/consensus.d.ts +111 -0
  125. package/dist/types/consensus.d.ts.map +1 -0
  126. package/dist/types/consensus.js +29 -0
  127. package/dist/types/consensus.js.map +1 -0
  128. package/dist/types/index.d.ts +9 -0
  129. package/dist/types/index.d.ts.map +1 -0
  130. package/dist/types/index.js +13 -0
  131. package/dist/types/index.js.map +1 -0
  132. package/dist/types/project.d.ts +73 -0
  133. package/dist/types/project.d.ts.map +1 -0
  134. package/dist/types/project.js +55 -0
  135. package/dist/types/project.js.map +1 -0
  136. package/dist/types/workflow.d.ts +236 -0
  137. package/dist/types/workflow.d.ts.map +1 -0
  138. package/dist/types/workflow.js +74 -0
  139. package/dist/types/workflow.js.map +1 -0
  140. package/dist/workflow/consensus.d.ts +89 -0
  141. package/dist/workflow/consensus.d.ts.map +1 -0
  142. package/dist/workflow/consensus.js +220 -0
  143. package/dist/workflow/consensus.js.map +1 -0
  144. package/dist/workflow/execution-mode.d.ts +82 -0
  145. package/dist/workflow/execution-mode.d.ts.map +1 -0
  146. package/dist/workflow/execution-mode.js +346 -0
  147. package/dist/workflow/execution-mode.js.map +1 -0
  148. package/dist/workflow/index.d.ts +110 -0
  149. package/dist/workflow/index.d.ts.map +1 -0
  150. package/dist/workflow/index.js +283 -0
  151. package/dist/workflow/index.js.map +1 -0
  152. package/dist/workflow/plan-mode.d.ts +83 -0
  153. package/dist/workflow/plan-mode.d.ts.map +1 -0
  154. package/dist/workflow/plan-mode.js +241 -0
  155. package/dist/workflow/plan-mode.js.map +1 -0
  156. package/dist/workflow/test-runner.d.ts +87 -0
  157. package/dist/workflow/test-runner.d.ts.map +1 -0
  158. package/dist/workflow/test-runner.js +273 -0
  159. package/dist/workflow/test-runner.js.map +1 -0
  160. package/eslint.config.js +25 -0
  161. package/package.json +66 -0
  162. package/src/adapters/claude.ts +298 -0
  163. package/src/adapters/openai.ts +300 -0
  164. package/src/auth/claude.ts +166 -0
  165. package/src/auth/index.ts +171 -0
  166. package/src/auth/keychain.ts +138 -0
  167. package/src/auth/openai-entry.ts +410 -0
  168. package/src/auth/openai.ts +260 -0
  169. package/src/auth/server.ts +252 -0
  170. package/src/cli/commands/auth.ts +194 -0
  171. package/src/cli/commands/config.ts +241 -0
  172. package/src/cli/commands/create.ts +308 -0
  173. package/src/cli/commands/index.ts +10 -0
  174. package/src/cli/commands/resume.ts +304 -0
  175. package/src/cli/commands/status.ts +189 -0
  176. package/src/cli/index.ts +90 -0
  177. package/src/cli/interactive.ts +418 -0
  178. package/src/cli/output.ts +410 -0
  179. package/src/config/defaults.ts +114 -0
  180. package/src/config/index.ts +315 -0
  181. package/src/config/schema.ts +164 -0
  182. package/src/generators/index.ts +251 -0
  183. package/src/generators/python.ts +318 -0
  184. package/src/generators/templates/index.ts +6 -0
  185. package/src/generators/templates/python.ts +465 -0
  186. package/src/generators/templates/typescript.ts +417 -0
  187. package/src/generators/typescript.ts +340 -0
  188. package/src/index.ts +13 -0
  189. package/src/state/index.ts +454 -0
  190. package/src/state/persistence.ts +230 -0
  191. package/src/types/cli.ts +146 -0
  192. package/src/types/consensus.ts +116 -0
  193. package/src/types/index.ts +64 -0
  194. package/src/types/project.ts +85 -0
  195. package/src/types/workflow.ts +149 -0
  196. package/src/workflow/consensus.ts +299 -0
  197. package/src/workflow/execution-mode.ts +517 -0
  198. package/src/workflow/index.ts +396 -0
  199. package/src/workflow/plan-mode.ts +356 -0
  200. package/src/workflow/test-runner.ts +345 -0
  201. package/tests/adapters/openai.test.ts +145 -0
  202. package/tests/config/config.test.ts +208 -0
  203. package/tests/generators/generators.test.ts +185 -0
  204. package/tests/types/consensus.test.ts +152 -0
  205. package/tests/types/project.test.ts +134 -0
  206. package/tests/workflow/consensus.test.ts +221 -0
  207. package/tests/workflow/test-runner.test.ts +214 -0
  208. package/tsconfig.json +25 -0
  209. package/vitest.config.ts +22 -0
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Project generators module
3
+ * Provides unified API for generating Python and TypeScript projects
4
+ */
5
+
6
+ import type { ProjectSpec, OutputLanguage } from '../types/project.js';
7
+ import {
8
+ generatePythonProject,
9
+ validatePythonProject,
10
+ addPythonModule,
11
+ getPythonProjectFiles,
12
+ type GenerationResult,
13
+ } from './python.js';
14
+ import {
15
+ generateTypeScriptProject,
16
+ validateTypeScriptProject,
17
+ addTypeScriptModule,
18
+ getTypeScriptProjectFiles,
19
+ } from './typescript.js';
20
+
21
+ // Re-export (explicitly to avoid name conflicts)
22
+ export {
23
+ generatePythonProject,
24
+ validatePythonProject,
25
+ addPythonModule,
26
+ getPythonProjectFiles,
27
+ type GenerationResult,
28
+ } from './python.js';
29
+ export {
30
+ generateTypeScriptProject,
31
+ validateTypeScriptProject,
32
+ addTypeScriptModule,
33
+ getTypeScriptProjectFiles,
34
+ addDependency,
35
+ updateScripts,
36
+ } from './typescript.js';
37
+ export * from './templates/index.js';
38
+
39
+ /**
40
+ * Generate a project based on the specification
41
+ *
42
+ * @param spec - Project specification
43
+ * @param outputDir - Output directory
44
+ * @returns Generation result
45
+ */
46
+ export async function generateProject(
47
+ spec: ProjectSpec,
48
+ outputDir: string
49
+ ): Promise<GenerationResult> {
50
+ switch (spec.language) {
51
+ case 'python':
52
+ return generatePythonProject(spec, outputDir);
53
+ case 'typescript':
54
+ return generateTypeScriptProject(spec, outputDir);
55
+ default:
56
+ return {
57
+ success: false,
58
+ projectDir: outputDir,
59
+ filesCreated: [],
60
+ error: `Unsupported language: ${spec.language}`,
61
+ };
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Validate a project structure
67
+ *
68
+ * @param projectDir - Project directory
69
+ * @param language - Project language
70
+ * @returns Validation result
71
+ */
72
+ export async function validateProject(
73
+ projectDir: string,
74
+ language: OutputLanguage
75
+ ): Promise<{
76
+ valid: boolean;
77
+ missingFiles: string[];
78
+ }> {
79
+ switch (language) {
80
+ case 'python':
81
+ return validatePythonProject(projectDir);
82
+ case 'typescript':
83
+ return validateTypeScriptProject(projectDir);
84
+ default:
85
+ return {
86
+ valid: false,
87
+ missingFiles: ['Unknown language'],
88
+ };
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Add a module to an existing project
94
+ *
95
+ * @param projectDir - Project directory
96
+ * @param moduleName - Module name
97
+ * @param language - Project language
98
+ * @returns Files created
99
+ */
100
+ export async function addModule(
101
+ projectDir: string,
102
+ moduleName: string,
103
+ language: OutputLanguage
104
+ ): Promise<string[]> {
105
+ switch (language) {
106
+ case 'python':
107
+ return addPythonModule(projectDir, moduleName);
108
+ case 'typescript':
109
+ return addTypeScriptModule(projectDir, moduleName);
110
+ default:
111
+ throw new Error(`Unsupported language: ${language}`);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Get the list of files that would be generated for a project
117
+ *
118
+ * @param projectName - Project name
119
+ * @param language - Project language
120
+ * @returns List of relative file paths
121
+ */
122
+ export function getProjectFiles(projectName: string, language: OutputLanguage): string[] {
123
+ switch (language) {
124
+ case 'python':
125
+ return getPythonProjectFiles(projectName);
126
+ case 'typescript':
127
+ return getTypeScriptProjectFiles(projectName);
128
+ default:
129
+ return [];
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Get the default test command for a language
135
+ *
136
+ * @param language - Project language
137
+ * @returns Test command
138
+ */
139
+ export function getTestCommand(language: OutputLanguage): string {
140
+ switch (language) {
141
+ case 'python':
142
+ return 'python -m pytest tests/ -v';
143
+ case 'typescript':
144
+ return 'npm test';
145
+ default:
146
+ return 'echo "No test command configured"';
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Get the default build command for a language
152
+ *
153
+ * @param language - Project language
154
+ * @returns Build command
155
+ */
156
+ export function getBuildCommand(language: OutputLanguage): string {
157
+ switch (language) {
158
+ case 'python':
159
+ return 'python -m pip install -e .';
160
+ case 'typescript':
161
+ return 'npm run build';
162
+ default:
163
+ return 'echo "No build command configured"';
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Get the default lint command for a language
169
+ *
170
+ * @param language - Project language
171
+ * @returns Lint command
172
+ */
173
+ export function getLintCommand(language: OutputLanguage): string {
174
+ switch (language) {
175
+ case 'python':
176
+ return 'ruff check src/ tests/';
177
+ case 'typescript':
178
+ return 'npm run lint';
179
+ default:
180
+ return 'echo "No lint command configured"';
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Get the file extension for a language
186
+ *
187
+ * @param language - Project language
188
+ * @returns File extension (without dot)
189
+ */
190
+ export function getFileExtension(language: OutputLanguage): string {
191
+ switch (language) {
192
+ case 'python':
193
+ return 'py';
194
+ case 'typescript':
195
+ return 'ts';
196
+ default:
197
+ return 'txt';
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Get the source directory name for a language
203
+ *
204
+ * @param language - Project language
205
+ * @returns Source directory name
206
+ */
207
+ export function getSourceDir(_language: OutputLanguage): string {
208
+ return 'src';
209
+ }
210
+
211
+ /**
212
+ * Get the test directory name for a language
213
+ *
214
+ * @param language - Project language
215
+ * @returns Test directory name
216
+ */
217
+ export function getTestDir(_language: OutputLanguage): string {
218
+ return 'tests';
219
+ }
220
+
221
+ /**
222
+ * Check if a project directory already exists and has content
223
+ *
224
+ * @param projectDir - Project directory
225
+ * @returns True if directory exists and has content
226
+ */
227
+ export async function projectDirExists(projectDir: string): Promise<boolean> {
228
+ const { promises: fs } = await import('node:fs');
229
+
230
+ try {
231
+ const files = await fs.readdir(projectDir);
232
+ return files.length > 0;
233
+ } catch {
234
+ return false;
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Clean up a failed project generation
240
+ *
241
+ * @param projectDir - Project directory
242
+ */
243
+ export async function cleanupProject(projectDir: string): Promise<void> {
244
+ const { promises: fs } = await import('node:fs');
245
+
246
+ try {
247
+ await fs.rm(projectDir, { recursive: true, force: true });
248
+ } catch {
249
+ // Ignore errors
250
+ }
251
+ }
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Python project generator
3
+ * Creates complete Python project scaffolding
4
+ */
5
+
6
+ import { promises as fs } from 'node:fs';
7
+ import path from 'node:path';
8
+ import type { ProjectSpec } from '../types/project.js';
9
+ import {
10
+ generatePyprojectToml,
11
+ generateRequirementsTxt,
12
+ generateMainInit,
13
+ generateMainPy,
14
+ generateConftest,
15
+ generateTestMain,
16
+ generateDockerfile,
17
+ generateDockerCompose,
18
+ generateGitignore,
19
+ generateEnvExample,
20
+ generateReadme,
21
+ generateMakefile,
22
+ } from './templates/python.js';
23
+
24
+ /**
25
+ * Project generation result
26
+ */
27
+ export interface GenerationResult {
28
+ success: boolean;
29
+ projectDir: string;
30
+ filesCreated: string[];
31
+ error?: string;
32
+ }
33
+
34
+ /**
35
+ * Create a directory if it doesn't exist
36
+ */
37
+ async function ensureDir(dirPath: string): Promise<void> {
38
+ await fs.mkdir(dirPath, { recursive: true });
39
+ }
40
+
41
+ /**
42
+ * Write a file with content
43
+ */
44
+ async function writeFile(filePath: string, content: string): Promise<void> {
45
+ await fs.writeFile(filePath, content, 'utf-8');
46
+ }
47
+
48
+ /**
49
+ * Convert project name to Python package name
50
+ */
51
+ function toPythonPackageName(name: string): string {
52
+ return name.toLowerCase().replace(/-/g, '_').replace(/[^a-z0-9_]/g, '');
53
+ }
54
+
55
+ /**
56
+ * Generate a complete Python project
57
+ *
58
+ * @param spec - Project specification
59
+ * @param outputDir - Output directory
60
+ * @returns Generation result
61
+ */
62
+ export async function generatePythonProject(
63
+ spec: ProjectSpec,
64
+ outputDir: string
65
+ ): Promise<GenerationResult> {
66
+ const projectName = spec.name || 'my-project';
67
+ const packageName = toPythonPackageName(projectName);
68
+ const projectDir = path.join(outputDir, projectName);
69
+ const filesCreated: string[] = [];
70
+
71
+ try {
72
+ // Create project directory structure
73
+ await ensureDir(projectDir);
74
+ await ensureDir(path.join(projectDir, 'src', packageName));
75
+ await ensureDir(path.join(projectDir, 'tests'));
76
+ await ensureDir(path.join(projectDir, 'data'));
77
+
78
+ // Generate and write files
79
+ const files: Array<{ path: string; content: string }> = [
80
+ // Root files
81
+ {
82
+ path: path.join(projectDir, 'pyproject.toml'),
83
+ content: generatePyprojectToml(projectName),
84
+ },
85
+ {
86
+ path: path.join(projectDir, 'requirements.txt'),
87
+ content: generateRequirementsTxt(),
88
+ },
89
+ {
90
+ path: path.join(projectDir, '.gitignore'),
91
+ content: generateGitignore(),
92
+ },
93
+ {
94
+ path: path.join(projectDir, '.env.example'),
95
+ content: generateEnvExample(),
96
+ },
97
+ {
98
+ path: path.join(projectDir, 'README.md'),
99
+ content: generateReadme(projectName, spec.idea),
100
+ },
101
+ {
102
+ path: path.join(projectDir, 'Makefile'),
103
+ content: generateMakefile(projectName),
104
+ },
105
+ {
106
+ path: path.join(projectDir, 'Dockerfile'),
107
+ content: generateDockerfile(projectName),
108
+ },
109
+ {
110
+ path: path.join(projectDir, 'docker-compose.yml'),
111
+ content: generateDockerCompose(projectName),
112
+ },
113
+
114
+ // Source files
115
+ {
116
+ path: path.join(projectDir, 'src', packageName, '__init__.py'),
117
+ content: generateMainInit(projectName),
118
+ },
119
+ {
120
+ path: path.join(projectDir, 'src', packageName, 'main.py'),
121
+ content: generateMainPy(projectName),
122
+ },
123
+ {
124
+ path: path.join(projectDir, 'src', '__init__.py'),
125
+ content: '# Source root\n',
126
+ },
127
+
128
+ // Test files
129
+ {
130
+ path: path.join(projectDir, 'tests', '__init__.py'),
131
+ content: '# Tests package\n',
132
+ },
133
+ {
134
+ path: path.join(projectDir, 'tests', 'conftest.py'),
135
+ content: generateConftest(),
136
+ },
137
+ {
138
+ path: path.join(projectDir, 'tests', 'test_main.py'),
139
+ content: generateTestMain(projectName),
140
+ },
141
+
142
+ // Data placeholder
143
+ {
144
+ path: path.join(projectDir, 'data', '.gitkeep'),
145
+ content: '',
146
+ },
147
+ ];
148
+
149
+ // Write all files
150
+ for (const file of files) {
151
+ await writeFile(file.path, file.content);
152
+ filesCreated.push(file.path);
153
+ }
154
+
155
+ return {
156
+ success: true,
157
+ projectDir,
158
+ filesCreated,
159
+ };
160
+ } catch (error) {
161
+ return {
162
+ success: false,
163
+ projectDir,
164
+ filesCreated,
165
+ error: error instanceof Error ? error.message : 'Unknown error',
166
+ };
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Get the list of files that would be generated
172
+ *
173
+ * @param projectName - Project name
174
+ * @returns List of relative file paths
175
+ */
176
+ export function getPythonProjectFiles(projectName: string): string[] {
177
+ const packageName = toPythonPackageName(projectName);
178
+
179
+ return [
180
+ 'pyproject.toml',
181
+ 'requirements.txt',
182
+ '.gitignore',
183
+ '.env.example',
184
+ 'README.md',
185
+ 'Makefile',
186
+ 'Dockerfile',
187
+ 'docker-compose.yml',
188
+ `src/${packageName}/__init__.py`,
189
+ `src/${packageName}/main.py`,
190
+ 'src/__init__.py',
191
+ 'tests/__init__.py',
192
+ 'tests/conftest.py',
193
+ 'tests/test_main.py',
194
+ 'data/.gitkeep',
195
+ ];
196
+ }
197
+
198
+ /**
199
+ * Validate a Python project structure
200
+ *
201
+ * @param projectDir - Project directory
202
+ * @returns Validation result
203
+ */
204
+ export async function validatePythonProject(projectDir: string): Promise<{
205
+ valid: boolean;
206
+ missingFiles: string[];
207
+ }> {
208
+ const missingFiles: string[] = [];
209
+
210
+ const requiredFiles = [
211
+ 'pyproject.toml',
212
+ 'requirements.txt',
213
+ 'README.md',
214
+ 'src',
215
+ 'tests',
216
+ ];
217
+
218
+ for (const file of requiredFiles) {
219
+ const filePath = path.join(projectDir, file);
220
+ try {
221
+ await fs.access(filePath);
222
+ } catch {
223
+ missingFiles.push(file);
224
+ }
225
+ }
226
+
227
+ return {
228
+ valid: missingFiles.length === 0,
229
+ missingFiles,
230
+ };
231
+ }
232
+
233
+ /**
234
+ * Add a new Python module to an existing project
235
+ *
236
+ * @param projectDir - Project directory
237
+ * @param moduleName - Module name
238
+ * @returns Files created
239
+ */
240
+ export async function addPythonModule(
241
+ projectDir: string,
242
+ moduleName: string
243
+ ): Promise<string[]> {
244
+ const packageName = toPythonPackageName(moduleName);
245
+ const filesCreated: string[] = [];
246
+
247
+ // Find the src directory
248
+ const srcDir = path.join(projectDir, 'src');
249
+ const dirs = await fs.readdir(srcDir);
250
+ const packageDir = dirs.find((d) => !d.startsWith('.') && d !== '__init__.py');
251
+
252
+ if (!packageDir) {
253
+ throw new Error('Could not find package directory in src/');
254
+ }
255
+
256
+ const moduleDir = path.join(srcDir, packageDir, packageName);
257
+ await ensureDir(moduleDir);
258
+
259
+ // Create module files
260
+ const initContent = `"""
261
+ ${moduleName} module.
262
+ """
263
+ `;
264
+ await writeFile(path.join(moduleDir, '__init__.py'), initContent);
265
+ filesCreated.push(path.join(moduleDir, '__init__.py'));
266
+
267
+ const moduleContent = `"""
268
+ ${moduleName} implementation.
269
+ """
270
+
271
+ import logging
272
+
273
+ logger = logging.getLogger(__name__)
274
+
275
+
276
+ class ${moduleName.charAt(0).toUpperCase() + moduleName.slice(1).replace(/-/g, '')}:
277
+ """
278
+ ${moduleName} class.
279
+ """
280
+
281
+ def __init__(self) -> None:
282
+ """Initialize ${moduleName}."""
283
+ logger.info(f"Initializing {self.__class__.__name__}")
284
+
285
+ def run(self) -> None:
286
+ """
287
+ Run the ${moduleName} logic.
288
+
289
+ This method should be implemented with the actual functionality.
290
+ """
291
+ raise NotImplementedError("Implement this method")
292
+ `;
293
+ await writeFile(path.join(moduleDir, 'module.py'), moduleContent);
294
+ filesCreated.push(path.join(moduleDir, 'module.py'));
295
+
296
+ // Create test file
297
+ const testDir = path.join(projectDir, 'tests');
298
+ const testContent = `"""
299
+ Tests for ${moduleName} module.
300
+ """
301
+
302
+ import pytest
303
+
304
+ # from src.${packageDir}.${packageName}.module import ${moduleName.charAt(0).toUpperCase() + moduleName.slice(1).replace(/-/g, '')}
305
+
306
+
307
+ class Test${moduleName.charAt(0).toUpperCase() + moduleName.slice(1).replace(/-/g, '')}:
308
+ """Test cases for ${moduleName}."""
309
+
310
+ def test_placeholder(self) -> None:
311
+ """Placeholder test."""
312
+ assert True
313
+ `;
314
+ await writeFile(path.join(testDir, `test_${packageName}.py`), testContent);
315
+ filesCreated.push(path.join(testDir, `test_${packageName}.py`));
316
+
317
+ return filesCreated;
318
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Template module exports
3
+ */
4
+
5
+ export * as pythonTemplates from './python.js';
6
+ export * as typescriptTemplates from './typescript.js';