ai-engineering-init 1.4.3 → 1.5.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 (129) hide show
  1. package/.cursor/skills/bug-detective/SKILL.md +19 -19
  2. package/.cursor/skills/project-navigator/SKILL.md +164 -258
  3. package/package.json +7 -1
  4. package/scripts/build-skills.js +180 -0
  5. package/src/platform-map.json +56 -0
  6. package/src/skills/add-skill/SKILL.md +488 -0
  7. package/src/skills/add-todo/SKILL.md +269 -0
  8. package/src/skills/api-development/SKILL.md +266 -0
  9. package/src/skills/architecture-design/SKILL.md +262 -0
  10. package/src/skills/backend-annotations/SKILL.md +302 -0
  11. package/src/skills/banana-image/CHANGELOG.md +37 -0
  12. package/src/skills/banana-image/README.md +146 -0
  13. package/src/skills/banana-image/SKILL.md +171 -0
  14. package/src/skills/banana-image/assets/logo.png +0 -0
  15. package/src/skills/banana-image/references/advanced-usage.md +189 -0
  16. package/src/skills/banana-image/scripts/apply_template.py +125 -0
  17. package/src/skills/banana-image/scripts/banana_image_exec.ts +412 -0
  18. package/src/skills/banana-image/scripts/batch_prep.py +82 -0
  19. package/src/skills/banana-image/scripts/package-lock.json +1437 -0
  20. package/src/skills/banana-image/scripts/package.json +18 -0
  21. package/src/skills/banana-image/scripts/requirements.txt +10 -0
  22. package/src/skills/banana-image/templates/poster.json +22 -0
  23. package/src/skills/banana-image/templates/product.json +17 -0
  24. package/src/skills/banana-image/templates/social.json +22 -0
  25. package/src/skills/banana-image/templates/thumbnail.json +17 -0
  26. package/src/skills/brainstorm/SKILL.md +216 -0
  27. package/src/skills/bug-detective/SKILL.md +256 -0
  28. package/src/skills/bug-detective/references/error-patterns.md +242 -0
  29. package/src/skills/check/SKILL.md +367 -0
  30. package/src/skills/code-patterns/SKILL.md +280 -0
  31. package/src/skills/code-patterns/references/leniu-code-patterns.md +87 -0
  32. package/src/skills/codex-code-review/SKILL.md +135 -0
  33. package/src/skills/collaborating-with-codex/SKILL.md +174 -0
  34. package/src/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
  35. package/src/skills/collaborating-with-gemini/SKILL.md +194 -0
  36. package/src/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
  37. package/src/skills/crud/SKILL.md +265 -0
  38. package/src/skills/crud-development/SKILL.md +409 -0
  39. package/src/skills/data-permission/SKILL.md +292 -0
  40. package/src/skills/data-permission/references/custom-data-scope.md +90 -0
  41. package/src/skills/database-ops/SKILL.md +407 -0
  42. package/src/skills/dev/SKILL.md +187 -0
  43. package/src/skills/error-handler/SKILL.md +371 -0
  44. package/src/skills/file-oss-management/SKILL.md +255 -0
  45. package/src/skills/file-oss-management/references/entities.md +105 -0
  46. package/src/skills/file-oss-management/references/service-impl.md +104 -0
  47. package/src/skills/git-workflow/SKILL.md +397 -0
  48. package/src/skills/init-docs/SKILL.md +194 -0
  49. package/src/skills/json-serialization/SKILL.md +357 -0
  50. package/src/skills/leniu-api-development/SKILL.md +319 -0
  51. package/src/skills/leniu-api-development/references/real-examples.md +273 -0
  52. package/src/skills/leniu-architecture-design/SKILL.md +383 -0
  53. package/src/skills/leniu-backend-annotations/SKILL.md +277 -0
  54. package/src/skills/leniu-brainstorm/SKILL.md +242 -0
  55. package/src/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
  56. package/src/skills/leniu-code-patterns/SKILL.md +411 -0
  57. package/src/skills/leniu-crud-development/SKILL.md +404 -0
  58. package/src/skills/leniu-crud-development/references/templates.md +597 -0
  59. package/src/skills/leniu-customization-location/SKILL.md +410 -0
  60. package/src/skills/leniu-data-permission/SKILL.md +341 -0
  61. package/src/skills/leniu-database-ops/SKILL.md +426 -0
  62. package/src/skills/leniu-error-handler/SKILL.md +462 -0
  63. package/src/skills/leniu-java-amount-handling/SKILL.md +461 -0
  64. package/src/skills/leniu-java-code-style/SKILL.md +510 -0
  65. package/src/skills/leniu-java-concurrent/SKILL.md +400 -0
  66. package/src/skills/leniu-java-entity/SKILL.md +237 -0
  67. package/src/skills/leniu-java-entity/references/templates.md +237 -0
  68. package/src/skills/leniu-java-export/SKILL.md +570 -0
  69. package/src/skills/leniu-java-logging/SKILL.md +229 -0
  70. package/src/skills/leniu-java-logging/references/data-mask.md +46 -0
  71. package/src/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
  72. package/src/skills/leniu-java-mq/SKILL.md +338 -0
  73. package/src/skills/leniu-java-mybatis/SKILL.md +267 -0
  74. package/src/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
  75. package/src/skills/leniu-java-report-query-param/SKILL.md +291 -0
  76. package/src/skills/leniu-java-task/SKILL.md +367 -0
  77. package/src/skills/leniu-java-total-line/SKILL.md +196 -0
  78. package/src/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
  79. package/src/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
  80. package/src/skills/leniu-mealtime/SKILL.md +215 -0
  81. package/src/skills/leniu-redis-cache/SKILL.md +331 -0
  82. package/src/skills/leniu-report-customization/SKILL.md +335 -0
  83. package/src/skills/leniu-report-customization/references/table-fields.md +93 -0
  84. package/src/skills/leniu-report-standard-customization/SKILL.md +328 -0
  85. package/src/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
  86. package/src/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
  87. package/src/skills/leniu-security-guard/SKILL.md +306 -0
  88. package/src/skills/leniu-utils-toolkit/SKILL.md +380 -0
  89. package/src/skills/mysql-debug/SKILL.md +364 -0
  90. package/src/skills/next/SKILL.md +137 -0
  91. package/src/skills/openspec-apply-change/SKILL.md +165 -0
  92. package/src/skills/openspec-archive-change/SKILL.md +122 -0
  93. package/src/skills/openspec-bulk-archive-change/SKILL.md +254 -0
  94. package/src/skills/openspec-continue-change/SKILL.md +126 -0
  95. package/src/skills/openspec-explore/SKILL.md +299 -0
  96. package/src/skills/openspec-ff-change/SKILL.md +109 -0
  97. package/src/skills/openspec-new-change/SKILL.md +82 -0
  98. package/src/skills/openspec-onboard/SKILL.md +414 -0
  99. package/src/skills/openspec-sync-specs/SKILL.md +146 -0
  100. package/src/skills/openspec-verify-change/SKILL.md +176 -0
  101. package/src/skills/performance-doctor/SKILL.md +303 -0
  102. package/src/skills/progress/SKILL.md +193 -0
  103. package/src/skills/project-navigator/SKILL.md +211 -0
  104. package/src/skills/redis-cache/SKILL.md +333 -0
  105. package/src/skills/redis-cache/references/listeners.md +23 -0
  106. package/src/skills/scheduled-jobs/SKILL.md +314 -0
  107. package/src/skills/security-guard/SKILL.md +353 -0
  108. package/src/skills/security-guard/references/encrypt-config.md +103 -0
  109. package/src/skills/security-guard/references/sensitive-strategies.md +42 -0
  110. package/src/skills/sms-mail/SKILL.md +308 -0
  111. package/src/skills/sms-mail/references/mail-config.md +88 -0
  112. package/src/skills/sms-mail/references/sms-config.md +74 -0
  113. package/src/skills/social-login/SKILL.md +266 -0
  114. package/src/skills/social-login/references/provider-configs.md +118 -0
  115. package/src/skills/start/SKILL.md +154 -0
  116. package/src/skills/store-pc/SKILL.md +366 -0
  117. package/src/skills/sync/SKILL.md +149 -0
  118. package/src/skills/task-tracker/SKILL.md +307 -0
  119. package/src/skills/tech-decision/SKILL.md +393 -0
  120. package/src/skills/tenant-management/SKILL.md +288 -0
  121. package/src/skills/tenant-management/references/tenant-scenarios.md +91 -0
  122. package/src/skills/test-development/SKILL.md +301 -0
  123. package/src/skills/test-development/references/parameterized-examples.md +119 -0
  124. package/src/skills/ui-pc/SKILL.md +438 -0
  125. package/src/skills/update-status/SKILL.md +159 -0
  126. package/src/skills/utils-toolkit/SKILL.md +362 -0
  127. package/src/skills/utils-toolkit/references/redis-utils-api.md +56 -0
  128. package/src/skills/websocket-sse/SKILL.md +271 -0
  129. package/src/skills/workflow-engine/SKILL.md +321 -0
@@ -0,0 +1,412 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Banana Image - Gemini AI 图片生成执行脚本
4
+ * 直接调用 Gemini API,不依赖 MCP 服务
5
+ */
6
+
7
+ import { GoogleGenAI, Part } from "@google/genai";
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ import { ProxyAgent, setGlobalDispatcher } from "undici";
11
+
12
+ // 配置代理支持
13
+ const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
14
+ if (proxyUrl) {
15
+ const dispatcher = new ProxyAgent(proxyUrl);
16
+ setGlobalDispatcher(dispatcher);
17
+ }
18
+
19
+ // ============================================================================
20
+ // 类型定义
21
+ // ============================================================================
22
+
23
+ interface BananaImageOptions {
24
+ prompt: string;
25
+ model?: "flash" | "pro";
26
+ aspectRatio?: string;
27
+ resolution?: string;
28
+ negativePrompt?: string;
29
+ outputDir?: string;
30
+ enableGrounding?: boolean;
31
+ inputImage?: string;
32
+ systemInstruction?: string;
33
+ count?: number;
34
+ }
35
+
36
+ interface GeneratedImage {
37
+ path: string;
38
+ width?: number;
39
+ height?: number;
40
+ model: string;
41
+ prompt: string;
42
+ }
43
+
44
+ interface BananaImageResult {
45
+ success: boolean;
46
+ images?: GeneratedImage[];
47
+ error?: string;
48
+ metadata?: {
49
+ model: string;
50
+ aspectRatio?: string;
51
+ resolution?: string;
52
+ duration: number;
53
+ };
54
+ }
55
+
56
+ // ============================================================================
57
+ // 常量
58
+ // ============================================================================
59
+
60
+ const MODEL_MAP = {
61
+ flash: "gemini-2.5-flash-image",
62
+ pro: "gemini-3-pro-image-preview",
63
+ } as const;
64
+
65
+ const VALID_ASPECT_RATIOS = [
66
+ "1:1",
67
+ "2:3",
68
+ "3:2",
69
+ "3:4",
70
+ "4:3",
71
+ "4:5",
72
+ "5:4",
73
+ "9:16",
74
+ "16:9",
75
+ "21:9",
76
+ ];
77
+
78
+ const VALID_RESOLUTIONS = ["1K", "2K", "4K"];
79
+
80
+ const DEFAULT_OUTPUT_DIR = path.join(process.cwd(), "images");
81
+
82
+ // ============================================================================
83
+ // 工具函数
84
+ // ============================================================================
85
+
86
+ function generateFilename(prefix: string = "banana"): string {
87
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
88
+ const random = Math.random().toString(36).substring(2, 8);
89
+ return `${prefix}_${timestamp}_${random}.png`;
90
+ }
91
+
92
+ function ensureOutputDir(dir: string): void {
93
+ if (!fs.existsSync(dir)) {
94
+ fs.mkdirSync(dir, { recursive: true });
95
+ }
96
+ }
97
+
98
+ function loadImageAsBase64(imagePath: string): string {
99
+ const buffer = fs.readFileSync(imagePath);
100
+ return buffer.toString("base64");
101
+ }
102
+
103
+ function getMimeType(imagePath: string): string {
104
+ const ext = path.extname(imagePath).toLowerCase();
105
+ const mimeTypes: Record<string, string> = {
106
+ ".png": "image/png",
107
+ ".jpg": "image/jpeg",
108
+ ".jpeg": "image/jpeg",
109
+ ".gif": "image/gif",
110
+ ".webp": "image/webp",
111
+ };
112
+ return mimeTypes[ext] || "image/png";
113
+ }
114
+
115
+ // ============================================================================
116
+ // 核心生成逻辑
117
+ // ============================================================================
118
+
119
+ async function generateImages(
120
+ options: BananaImageOptions,
121
+ ): Promise<BananaImageResult> {
122
+ const startTime = Date.now();
123
+
124
+ // 验证 API Key
125
+ const apiKey = process.env.GEMINI_API_KEY;
126
+ if (!apiKey) {
127
+ return {
128
+ success: false,
129
+ error: "GEMINI_API_KEY environment variable is not set",
130
+ };
131
+ }
132
+
133
+ // 参数处理
134
+ const modelTier = options.model || "pro";
135
+ const modelName = MODEL_MAP[modelTier];
136
+ const outputDir = options.outputDir || DEFAULT_OUTPUT_DIR;
137
+ const aspectRatio = options.aspectRatio || "1:1";
138
+ const resolution = options.resolution || (modelTier === "pro" ? "4K" : "1K");
139
+ const count = Math.min(options.count || 1, 4);
140
+
141
+ // 验证参数
142
+ if (
143
+ options.aspectRatio &&
144
+ !VALID_ASPECT_RATIOS.includes(options.aspectRatio)
145
+ ) {
146
+ return {
147
+ success: false,
148
+ error: `Invalid aspect ratio: ${options.aspectRatio}. Valid: ${VALID_ASPECT_RATIOS.join(", ")}`,
149
+ };
150
+ }
151
+
152
+ if (
153
+ options.resolution &&
154
+ !VALID_RESOLUTIONS.includes(options.resolution.toUpperCase())
155
+ ) {
156
+ return {
157
+ success: false,
158
+ error: `Invalid resolution: ${options.resolution}. Valid: ${VALID_RESOLUTIONS.join(", ")}`,
159
+ };
160
+ }
161
+
162
+ ensureOutputDir(outputDir);
163
+
164
+ try {
165
+ // 支持自定义 API 地址(如第三方代理)
166
+ const apiBase = process.env.GEMINI_API_BASE || process.env.GOOGLE_API_BASE;
167
+ const aiConfig: { apiKey: string; httpOptions?: { baseUrl: string } } = { apiKey };
168
+ if (apiBase) {
169
+ aiConfig.httpOptions = { baseUrl: apiBase };
170
+ }
171
+ const ai = new GoogleGenAI(aiConfig);
172
+
173
+ // 构建内容
174
+ const contents: (string | Part)[] = [];
175
+
176
+ // 添加系统指令
177
+ if (options.systemInstruction) {
178
+ contents.push(options.systemInstruction);
179
+ }
180
+
181
+ // 添加输入图片(编辑模式)
182
+ if (options.inputImage && fs.existsSync(options.inputImage)) {
183
+ const imageData = loadImageAsBase64(options.inputImage);
184
+ const mimeType = getMimeType(options.inputImage);
185
+ contents.push({
186
+ inlineData: {
187
+ data: imageData,
188
+ mimeType,
189
+ },
190
+ });
191
+ }
192
+
193
+ // 构建提示词
194
+ let fullPrompt = options.prompt;
195
+ if (options.negativePrompt) {
196
+ fullPrompt += `\n\nAvoid: ${options.negativePrompt}`;
197
+ }
198
+ contents.push(fullPrompt);
199
+
200
+ // 构建配置
201
+ // 注意:只有 Pro 模型支持 imageConfig 和 tools
202
+ const config: Record<string, unknown> = {};
203
+
204
+ if (modelTier === "pro") {
205
+ // Pro 模型支持宽高比和分辨率配置
206
+ config.imageConfig = {
207
+ aspectRatio,
208
+ imageSize: resolution.toUpperCase(),
209
+ };
210
+
211
+ // Google Search grounding
212
+ if (options.enableGrounding) {
213
+ config.tools = [{ googleSearch: {} }];
214
+ }
215
+ }
216
+ // Flash 模型不支持 imageConfig,使用默认配置
217
+
218
+ // 调用 API
219
+ const generatedImages: GeneratedImage[] = [];
220
+
221
+ for (let i = 0; i < count; i++) {
222
+ const response = await ai.models.generateContent({
223
+ model: modelName,
224
+ contents: contents.length === 1 ? contents[0] : contents,
225
+ config,
226
+ });
227
+
228
+ // 提取图片
229
+ const candidates = response.candidates || [];
230
+ for (const candidate of candidates) {
231
+ const parts = candidate.content?.parts || [];
232
+ for (const part of parts) {
233
+ if ("inlineData" in part && part.inlineData?.data) {
234
+ const filename = generateFilename(`banana_${modelTier}`);
235
+ const filePath = path.join(outputDir, filename);
236
+
237
+ // 保存图片
238
+ const buffer = Buffer.from(part.inlineData.data, "base64");
239
+ fs.writeFileSync(filePath, buffer);
240
+
241
+ generatedImages.push({
242
+ path: filePath,
243
+ model: modelName,
244
+ prompt: options.prompt,
245
+ });
246
+ }
247
+ }
248
+ }
249
+ }
250
+
251
+ if (generatedImages.length === 0) {
252
+ return {
253
+ success: false,
254
+ error: "No images were generated. The content may have been filtered.",
255
+ };
256
+ }
257
+
258
+ const duration = Date.now() - startTime;
259
+
260
+ return {
261
+ success: true,
262
+ images: generatedImages,
263
+ metadata: {
264
+ model: modelName,
265
+ aspectRatio,
266
+ resolution,
267
+ duration,
268
+ },
269
+ };
270
+ } catch (error) {
271
+ const errorMessage = error instanceof Error ? error.message : String(error);
272
+ return {
273
+ success: false,
274
+ error: `Image generation failed: ${errorMessage}`,
275
+ };
276
+ }
277
+ }
278
+
279
+ // ============================================================================
280
+ // CLI 入口
281
+ // ============================================================================
282
+
283
+ function printUsage(): void {
284
+ console.log(`
285
+ Banana Image - Gemini AI 图片生成
286
+
287
+ Usage:
288
+ npx tsx banana_image_exec.ts [options]
289
+
290
+ Options:
291
+ --prompt, -p <text> 图片描述(必需)
292
+ --model, -m <type> 模型选择: flash | pro (默认: pro)
293
+ --aspect-ratio, -a <r> 宽高比: 1:1, 16:9, 9:16, 4:3, 3:2, 21:9 等
294
+ --resolution, -r <res> 分辨率: 1K, 2K, 4K (默认: Pro=4K, Flash=1K)
295
+ --negative, -n <text> 排除内容描述
296
+ --output, -o <dir> 输出目录 (默认: ./images)
297
+ --grounding, -g 启用 Google Search 锚定 (仅 Pro)
298
+ --input, -i <path> 输入图片路径(编辑模式)
299
+ --system, -s <text> 系统指令
300
+ --count, -c <num> 生成数量 (1-4)
301
+ --help, -h 显示帮助
302
+
303
+ Examples:
304
+ # 快速生成
305
+ npx tsx banana_image_exec.ts -p "可爱的柴犬" -m flash
306
+
307
+ # 4K 高质量
308
+ npx tsx banana_image_exec.ts -p "专业产品照片" -m pro -r 4K -a 4:5
309
+
310
+ # 编辑图片
311
+ npx tsx banana_image_exec.ts -p "将背景改为黄昏" -i /path/to/image.png
312
+ `);
313
+ }
314
+
315
+ function parseArgs(): BananaImageOptions | null {
316
+ const args = process.argv.slice(2);
317
+
318
+ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
319
+ printUsage();
320
+ return null;
321
+ }
322
+
323
+ const options: BananaImageOptions = {
324
+ prompt: "",
325
+ };
326
+
327
+ for (let i = 0; i < args.length; i++) {
328
+ const arg = args[i];
329
+ const next = args[i + 1];
330
+
331
+ switch (arg) {
332
+ case "--prompt":
333
+ case "-p":
334
+ options.prompt = next || "";
335
+ i++;
336
+ break;
337
+ case "--model":
338
+ case "-m":
339
+ if (next === "flash" || next === "pro") {
340
+ options.model = next;
341
+ }
342
+ i++;
343
+ break;
344
+ case "--aspect-ratio":
345
+ case "-a":
346
+ options.aspectRatio = next;
347
+ i++;
348
+ break;
349
+ case "--resolution":
350
+ case "-r":
351
+ options.resolution = next;
352
+ i++;
353
+ break;
354
+ case "--negative":
355
+ case "-n":
356
+ options.negativePrompt = next;
357
+ i++;
358
+ break;
359
+ case "--output":
360
+ case "-o":
361
+ options.outputDir = next;
362
+ i++;
363
+ break;
364
+ case "--grounding":
365
+ case "-g":
366
+ options.enableGrounding = true;
367
+ break;
368
+ case "--input":
369
+ case "-i":
370
+ options.inputImage = next;
371
+ i++;
372
+ break;
373
+ case "--system":
374
+ case "-s":
375
+ options.systemInstruction = next;
376
+ i++;
377
+ break;
378
+ case "--count":
379
+ case "-c":
380
+ options.count = parseInt(next, 10) || 1;
381
+ i++;
382
+ break;
383
+ }
384
+ }
385
+
386
+ if (!options.prompt) {
387
+ console.error("Error: --prompt is required");
388
+ printUsage();
389
+ return null;
390
+ }
391
+
392
+ return options;
393
+ }
394
+
395
+ async function main(): Promise<void> {
396
+ const options = parseArgs();
397
+ if (!options) {
398
+ process.exit(1);
399
+ }
400
+
401
+ const result = await generateImages(options);
402
+ console.log(JSON.stringify(result, null, 2));
403
+
404
+ if (!result.success) {
405
+ process.exit(1);
406
+ }
407
+ }
408
+
409
+ main().catch((error) => {
410
+ console.error(JSON.stringify({ success: false, error: String(error) }));
411
+ process.exit(1);
412
+ });
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 批量图片生成准备脚本
4
+ 将文本文件(每行一个 prompt)或 CSV 转换为 JSON 格式的 prompt 列表
5
+ """
6
+
7
+ import sys
8
+ import json
9
+ import csv
10
+ import argparse
11
+ from pathlib import Path
12
+
13
+
14
+ def parse_text_file(filepath: Path) -> list[dict]:
15
+ """解析纯文本文件,每行一个 prompt"""
16
+ prompts = []
17
+ with open(filepath, "r", encoding="utf-8") as f:
18
+ for i, line in enumerate(f, 1):
19
+ line = line.strip()
20
+ if line and not line.startswith("#"):
21
+ prompts.append({"id": i, "prompt": line})
22
+ return prompts
23
+
24
+
25
+ def parse_csv_file(filepath: Path, prompt_column: str = "prompt") -> list[dict]:
26
+ """解析 CSV 文件,提取指定列作为 prompt"""
27
+ prompts = []
28
+ with open(filepath, "r", encoding="utf-8") as f:
29
+ reader = csv.DictReader(f)
30
+ for i, row in enumerate(reader, 1):
31
+ prompt = row.get(prompt_column) or row.get(list(row.keys())[0])
32
+ if prompt and prompt.strip():
33
+ entry = {"id": i, "prompt": prompt.strip()}
34
+ # 保留其他列作为参数
35
+ for key, value in row.items():
36
+ if key != prompt_column and value:
37
+ entry[key] = value
38
+ prompts.append(entry)
39
+ return prompts
40
+
41
+
42
+ def main():
43
+ parser = argparse.ArgumentParser(
44
+ description="将文本或 CSV 文件转换为批量生成的 JSON 格式"
45
+ )
46
+ parser.add_argument("file", help="输入文件路径")
47
+ parser.add_argument(
48
+ "--column", "-c", default="prompt", help="CSV 中 prompt 所在列名 (默认: prompt)"
49
+ )
50
+ parser.add_argument("--output", "-o", help="输出文件路径 (默认: 标准输出)")
51
+
52
+ args = parser.parse_args()
53
+ filepath = Path(args.file)
54
+
55
+ if not filepath.exists():
56
+ print(json.dumps({"error": f"文件不存在: {filepath}"}))
57
+ sys.exit(1)
58
+
59
+ try:
60
+ if filepath.suffix.lower() == ".csv":
61
+ prompts = parse_csv_file(filepath, args.column)
62
+ else:
63
+ prompts = parse_text_file(filepath)
64
+
65
+ result = {"source": str(filepath), "count": len(prompts), "prompts": prompts}
66
+
67
+ output = json.dumps(result, ensure_ascii=False, indent=2)
68
+
69
+ if args.output:
70
+ with open(args.output, "w", encoding="utf-8") as f:
71
+ f.write(output)
72
+ print(f"已保存到: {args.output}")
73
+ else:
74
+ print(output)
75
+
76
+ except Exception as e:
77
+ print(json.dumps({"error": str(e)}))
78
+ sys.exit(1)
79
+
80
+
81
+ if __name__ == "__main__":
82
+ main()