pug-site-core 2.0.22 → 3.0.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/index.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  fetchDataToJsonFile,
8
8
  buildFn,
9
9
  buildStatic,
10
+ createDebugTemplate,
10
11
  } from "./lib/generate.js";
11
12
 
12
13
  export const pugSiteCore = {
@@ -18,6 +19,7 @@ export const pugSiteCore = {
18
19
  buildStatic,
19
20
  translateLanguageData,
20
21
  processImagemin,
22
+ createDebugTemplate,
21
23
  };
22
24
 
23
25
  let curCmd = process.env.npm_lifecycle_event;
@@ -56,6 +58,9 @@ if (args.includes("dev")) {
56
58
  const imageminArgs = args.filter((arg) => arg !== "dev");
57
59
  await processImagemin(imageminArgs);
58
60
  break;
61
+ case "debug":
62
+ await createDebugTemplate();
63
+ break;
59
64
  default:
60
65
  console.log(`未知的命令: ${curCmd}`);
61
66
  }
File without changes
@@ -0,0 +1,628 @@
1
+ import { ChatOpenAI } from "@langchain/openai";
2
+ import { PromptTemplate } from "@langchain/core/prompts";
3
+ import { StructuredOutputParser } from "@langchain/core/output_parsers";
4
+ import { z } from "zod";
5
+ import { Document } from "@langchain/core/documents";
6
+ import { DirectoryLoader } from "langchain/document_loaders/fs/directory";
7
+ import { TextLoader } from "langchain/document_loaders/fs/text";
8
+ import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
9
+ import { FileManagementToolkit } from "@langchain/community/agent_toolkits";
10
+ import { VectorStore } from "@langchain/core/vectorstores";
11
+ import { MemoryVectorStore } from "langchain/vectorstores/memory";
12
+ import { OpenAIEmbeddings } from "@langchain/openai";
13
+ import { RunnableSequence } from "@langchain/core/runnables";
14
+ import fs from 'fs/promises';
15
+ import path from 'path';
16
+ import { fileURLToPath } from 'url';
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+
21
+ // DeepSeek API 配置
22
+ const llm = new ChatOpenAI({
23
+ model: "deepseek-chat",
24
+ temperature: 0.2, // 降低随机性,提高代码修改的准确性
25
+ openAIApiKey: process.env.DEEPSEEK_API_KEY || "your-api-key-here",
26
+ configuration: {
27
+ baseURL: "https://api.deepseek.com/v1"
28
+ }
29
+ });
30
+
31
+ // 定义结构化输出模式
32
+ const LocationSchema = z.object({
33
+ startLine: z.number().describe("修改开始行号"),
34
+ endLine: z.number().describe("修改结束行号"),
35
+ reason: z.string().describe("选择这个位置的原因"),
36
+ context: z.string().describe("相关的代码上下文")
37
+ });
38
+
39
+ const AnalysisResultSchema = z.object({
40
+ locations: z.array(LocationSchema).describe("可能的修改位置列表"),
41
+ analysis: z.string().describe("整体分析说明"),
42
+ confidence: z.number().min(0).max(1).describe("分析结果的置信度")
43
+ });
44
+
45
+ const ModificationResultSchema = z.object({
46
+ modifiedCode: z.string().describe("修改后的代码"),
47
+ explanation: z.string().describe("修改说明"),
48
+ changes: z.array(z.object({
49
+ type: z.enum(["added", "modified", "deleted"]).describe("修改类型"),
50
+ description: z.string().describe("具体的改动描述"),
51
+ lineNumbers: z.array(z.number()).describe("涉及的行号")
52
+ })).describe("修改详情列表"),
53
+ riskLevel: z.enum(["low", "medium", "high"]).describe("修改风险等级")
54
+ });
55
+
56
+ /**
57
+ * 使用LangChain优化的代码修改 Agent 类
58
+ */
59
+ class EnhancedCodeModificationAgent {
60
+ constructor(options = {}) {
61
+ this.projectRoot = options.projectRoot || process.cwd();
62
+ this.fileManagementToolkit = new FileManagementToolkit({
63
+ rootDir: this.projectRoot
64
+ });
65
+ this.vectorStore = null;
66
+ this.embeddings = new OpenAIEmbeddings({
67
+ openAIApiKey: process.env.OPENAI_API_KEY
68
+ });
69
+ this.textSplitter = new RecursiveCharacterTextSplitter({
70
+ chunkSize: 1000,
71
+ chunkOverlap: 200,
72
+ });
73
+
74
+ // 设置结构化输出解析器
75
+ this.analysisParser = StructuredOutputParser.fromZodSchema(AnalysisResultSchema);
76
+ this.modificationParser = StructuredOutputParser.fromZodSchema(ModificationResultSchema);
77
+ }
78
+
79
+ /**
80
+ * 初始化项目上下文向量存储
81
+ */
82
+ async initializeProjectContext() {
83
+ try {
84
+ const loader = new DirectoryLoader(this.projectRoot, {
85
+ ".js": (path) => new TextLoader(path),
86
+ ".ts": (path) => new TextLoader(path),
87
+ ".jsx": (path) => new TextLoader(path),
88
+ ".tsx": (path) => new TextLoader(path),
89
+ ".vue": (path) => new TextLoader(path),
90
+ ".py": (path) => new TextLoader(path),
91
+ ".md": (path) => new TextLoader(path),
92
+ });
93
+
94
+ const docs = await loader.load();
95
+ const splitDocs = await this.textSplitter.splitDocuments(docs);
96
+
97
+ this.vectorStore = await MemoryVectorStore.fromDocuments(
98
+ splitDocs,
99
+ this.embeddings
100
+ );
101
+
102
+ console.log(`已加载 ${docs.length} 个文件,分割为 ${splitDocs.length} 个文档块`);
103
+ } catch (error) {
104
+ console.error('初始化项目上下文失败:', error);
105
+ throw error;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * 使用LangChain工具进行文件操作
111
+ */
112
+ async readFileWithLangChain(filePath) {
113
+ try {
114
+ const tools = this.fileManagementToolkit.getTools();
115
+ const readTool = tools.find(tool => tool.name === 'read_file');
116
+
117
+ if (!readTool) {
118
+ throw new Error('Read file tool not found');
119
+ }
120
+
121
+ const result = await readTool.invoke({ file_path: filePath });
122
+ const lines = result.split('\n');
123
+
124
+ return {
125
+ success: true,
126
+ content: result,
127
+ lines,
128
+ metadata: {
129
+ lineCount: lines.length,
130
+ filePath: path.resolve(this.projectRoot, filePath)
131
+ }
132
+ };
133
+ } catch (error) {
134
+ return {
135
+ success: false,
136
+ error: error.message,
137
+ filePath
138
+ };
139
+ }
140
+ }
141
+
142
+ /**
143
+ * 使用LangChain工具写入文件
144
+ */
145
+ async writeFileWithLangChain(filePath, content) {
146
+ try {
147
+ const tools = this.fileManagementToolkit.getTools();
148
+ const writeTool = tools.find(tool => tool.name === 'write_file');
149
+
150
+ if (!writeTool) {
151
+ throw new Error('Write file tool not found');
152
+ }
153
+
154
+ await writeTool.invoke({
155
+ file_path: filePath,
156
+ text: content
157
+ });
158
+
159
+ return {
160
+ success: true,
161
+ message: `文件 ${filePath} 写入成功`,
162
+ timestamp: new Date().toISOString()
163
+ };
164
+ } catch (error) {
165
+ return {
166
+ success: false,
167
+ error: error.message,
168
+ filePath
169
+ };
170
+ }
171
+ }
172
+
173
+ /**
174
+ * 使用向量搜索获取相关上下文
175
+ */
176
+ async getRelevantContext(description, filePath = null) {
177
+ if (!this.vectorStore) {
178
+ await this.initializeProjectContext();
179
+ }
180
+
181
+ try {
182
+ // 构建搜索查询
183
+ let searchQuery = description;
184
+ if (filePath) {
185
+ searchQuery += ` file:${path.basename(filePath)}`;
186
+ }
187
+
188
+ const relevantDocs = await this.vectorStore.similaritySearch(searchQuery, 5);
189
+
190
+ return {
191
+ success: true,
192
+ contexts: relevantDocs.map(doc => ({
193
+ content: doc.pageContent,
194
+ metadata: doc.metadata,
195
+ relevanceScore: doc.score || 0
196
+ }))
197
+ };
198
+ } catch (error) {
199
+ return {
200
+ success: false,
201
+ error: error.message,
202
+ contexts: []
203
+ };
204
+ }
205
+ }
206
+
207
+ /**
208
+ * 使用结构化输出分析代码位置
209
+ */
210
+ async analyzeCodeLocationStructured(filePath, description) {
211
+ const fileResult = await this.readFileWithLangChain(filePath);
212
+ if (!fileResult.success) {
213
+ return { success: false, error: fileResult.error };
214
+ }
215
+
216
+ // 获取相关上下文
217
+ const contextResult = await this.getRelevantContext(description, filePath);
218
+ const relevantContext = contextResult.success ?
219
+ contextResult.contexts.map(ctx => ctx.content).join('\n\n') : '';
220
+
221
+ const analysisPrompt = PromptTemplate.fromTemplate(`
222
+ 你是一个专业的代码分析师。请分析以下代码文件,根据用户的描述找到需要修改的具体位置。
223
+
224
+ 文件路径: {filePath}
225
+ 文件内容:
226
+ \`\`\`
227
+ {fileContent}
228
+ \`\`\`
229
+
230
+ 相关项目上下文:
231
+ {relevantContext}
232
+
233
+ 用户描述: {description}
234
+
235
+ 请仔细分析代码结构和用户需求,返回可能的修改位置。考虑以下因素:
236
+ 1. 代码的逻辑结构和依赖关系
237
+ 2. 函数/类的边界
238
+ 3. 变量作用域
239
+ 4. 最小侵入性原则
240
+
241
+ {format_instructions}
242
+ `);
243
+
244
+ try {
245
+ const chain = RunnableSequence.from([
246
+ analysisPrompt,
247
+ llm,
248
+ this.analysisParser
249
+ ]);
250
+
251
+ const result = await chain.invoke({
252
+ filePath,
253
+ fileContent: fileResult.content,
254
+ relevantContext,
255
+ description,
256
+ format_instructions: this.analysisParser.getFormatInstructions()
257
+ });
258
+
259
+ return {
260
+ success: true,
261
+ ...result,
262
+ fileMetadata: fileResult.metadata
263
+ };
264
+ } catch (error) {
265
+ return {
266
+ success: false,
267
+ error: `分析失败: ${error.message}`,
268
+ fallbackLocations: this.fallbackLocationAnalysis(fileResult.lines, description)
269
+ };
270
+ }
271
+ }
272
+
273
+ /**
274
+ * 降级位置分析(当结构化分析失败时)
275
+ */
276
+ fallbackLocationAnalysis(lines, description) {
277
+ const keywords = description.toLowerCase().split(/\s+/);
278
+ const locations = [];
279
+
280
+ lines.forEach((line, index) => {
281
+ const lineContent = line.toLowerCase();
282
+ const matchCount = keywords.filter(keyword => lineContent.includes(keyword)).length;
283
+
284
+ if (matchCount > 0) {
285
+ locations.push({
286
+ startLine: index + 1,
287
+ endLine: index + 1,
288
+ reason: `包含关键词: ${keywords.filter(k => lineContent.includes(k)).join(', ')}`,
289
+ context: line.trim(),
290
+ confidence: matchCount / keywords.length
291
+ });
292
+ }
293
+ });
294
+
295
+ return locations.sort((a, b) => b.confidence - a.confidence).slice(0, 3);
296
+ }
297
+
298
+ /**
299
+ * 生成结构化代码修改
300
+ */
301
+ async generateCodeModificationStructured(filePath, startLine, endLine, description, context = '') {
302
+ const fileResult = await this.readFileWithLangChain(filePath);
303
+ if (!fileResult.success) {
304
+ return { success: false, error: fileResult.error };
305
+ }
306
+
307
+ const lines = fileResult.lines;
308
+ const beforeContext = lines.slice(Math.max(0, startLine - 6), startLine - 1).join('\n');
309
+ const targetCode = lines.slice(startLine - 1, endLine).join('\n');
310
+ const afterContext = lines.slice(endLine, Math.min(lines.length, endLine + 5)).join('\n');
311
+
312
+ // 获取项目上下文
313
+ const contextResult = await this.getRelevantContext(description, filePath);
314
+ const projectContext = contextResult.success ?
315
+ contextResult.contexts.slice(0, 3).map(ctx => ctx.content).join('\n\n') : '';
316
+
317
+ const modificationPrompt = PromptTemplate.fromTemplate(`
318
+ 你是一个专业的代码修改专家。请根据用户的需求修改指定的代码片段。
319
+
320
+ 文件路径: {filePath}
321
+ 修改范围: 第{startLine}行 到 第{endLine}行
322
+
323
+ 项目上下文信息:
324
+ {projectContext}
325
+
326
+ 附加上下文:
327
+ {context}
328
+
329
+ 修改前的代码上下文:
330
+ \`\`\`
331
+ {beforeContext}
332
+ --- 需要修改的代码开始 ---
333
+ {targetCode}
334
+ --- 需要修改的代码结束 ---
335
+ {afterContext}
336
+ \`\`\`
337
+
338
+ 用户需求: {description}
339
+
340
+ 请遵循以下原则:
341
+ 1. 保持原有的代码风格和缩进
342
+ 2. 确保修改后的代码语法正确
343
+ 3. 考虑代码的完整性和逻辑性
344
+ 4. 最小化对现有功能的影响
345
+ 5. 添加必要的注释说明
346
+
347
+ {format_instructions}
348
+ `);
349
+
350
+ try {
351
+ const chain = RunnableSequence.from([
352
+ modificationPrompt,
353
+ llm,
354
+ this.modificationParser
355
+ ]);
356
+
357
+ const result = await chain.invoke({
358
+ filePath,
359
+ startLine,
360
+ endLine,
361
+ beforeContext,
362
+ targetCode,
363
+ afterContext,
364
+ description,
365
+ context,
366
+ projectContext,
367
+ format_instructions: this.modificationParser.getFormatInstructions()
368
+ });
369
+
370
+ return {
371
+ success: true,
372
+ ...result,
373
+ originalCode: targetCode,
374
+ metadata: fileResult.metadata
375
+ };
376
+ } catch (error) {
377
+ return {
378
+ success: false,
379
+ error: `生成修改失败: ${error.message}`,
380
+ originalCode: targetCode
381
+ };
382
+ }
383
+ }
384
+
385
+ /**
386
+ * 应用代码修改(使用LangChain工具)
387
+ */
388
+ async applyModification(filePath, startLine, endLine, newCode) {
389
+ const fileResult = await this.readFileWithLangChain(filePath);
390
+ if (!fileResult.success) {
391
+ return { success: false, error: fileResult.error };
392
+ }
393
+
394
+ try {
395
+ const lines = fileResult.lines;
396
+ const newLines = [
397
+ ...lines.slice(0, startLine - 1),
398
+ ...newCode.split('\n'),
399
+ ...lines.slice(endLine)
400
+ ];
401
+
402
+ const newContent = newLines.join('\n');
403
+ const writeResult = await this.writeFileWithLangChain(filePath, newContent);
404
+
405
+ if (writeResult.success) {
406
+ return {
407
+ success: true,
408
+ message: `成功修改文件 ${filePath}`,
409
+ changes: {
410
+ linesAdded: newCode.split('\n').length,
411
+ linesRemoved: endLine - startLine + 1,
412
+ originalRange: { startLine, endLine },
413
+ modifiedAt: writeResult.timestamp
414
+ }
415
+ };
416
+ } else {
417
+ return { success: false, error: writeResult.error };
418
+ }
419
+ } catch (error) {
420
+ return {
421
+ success: false,
422
+ error: `应用修改失败: ${error.message}`
423
+ };
424
+ }
425
+ }
426
+
427
+ /**
428
+ * 智能代码修改 - 使用所有LangChain优化
429
+ */
430
+ async smartModifyWithEnhancements(filePath, description, autoApply = false) {
431
+ console.log(`开始智能分析文件: ${filePath}`);
432
+
433
+ try {
434
+ // 1. 使用结构化输出分析代码位置
435
+ const locationResult = await this.analyzeCodeLocationStructured(filePath, description);
436
+ if (!locationResult.success) {
437
+ return { success: false, error: locationResult.error };
438
+ }
439
+
440
+ console.log(`找到 ${locationResult.locations.length} 个可能的修改位置 (置信度: ${locationResult.confidence})`);
441
+
442
+ const results = [];
443
+
444
+ // 2. 对每个位置生成结构化修改
445
+ for (const location of locationResult.locations) {
446
+ console.log(`正在处理位置: 第${location.startLine}-${location.endLine}行`);
447
+
448
+ const modificationResult = await this.generateCodeModificationStructured(
449
+ filePath,
450
+ location.startLine,
451
+ location.endLine,
452
+ description,
453
+ location.context
454
+ );
455
+
456
+ if (modificationResult.success) {
457
+ const result = {
458
+ location,
459
+ modification: modificationResult,
460
+ applied: false,
461
+ riskAssessment: {
462
+ level: modificationResult.riskLevel,
463
+ considerations: this.assessModificationRisk(modificationResult)
464
+ }
465
+ };
466
+
467
+ // 如果启用自动应用且风险等级较低
468
+ if (autoApply && modificationResult.riskLevel === 'low') {
469
+ const applyResult = await this.applyModification(
470
+ filePath,
471
+ location.startLine,
472
+ location.endLine,
473
+ modificationResult.modifiedCode
474
+ );
475
+ result.applied = applyResult.success;
476
+ result.applyResult = applyResult;
477
+ }
478
+
479
+ results.push(result);
480
+ }
481
+ }
482
+
483
+ return {
484
+ success: true,
485
+ analysis: locationResult.analysis,
486
+ confidence: locationResult.confidence,
487
+ modifications: results,
488
+ projectContextUsed: true
489
+ };
490
+ } catch (error) {
491
+ return {
492
+ success: false,
493
+ error: `智能修改失败: ${error.message}`,
494
+ fallbackAvailable: true
495
+ };
496
+ }
497
+ }
498
+
499
+ /**
500
+ * 评估修改风险
501
+ */
502
+ assessModificationRisk(modificationResult) {
503
+ const considerations = [];
504
+
505
+ // 检查修改复杂度
506
+ const changeCount = modificationResult.changes.length;
507
+ if (changeCount > 5) {
508
+ considerations.push("修改涉及多处变更,建议仔细审查");
509
+ }
510
+
511
+ // 检查是否涉及删除操作
512
+ const hasDeletes = modificationResult.changes.some(change => change.type === 'deleted');
513
+ if (hasDeletes) {
514
+ considerations.push("包含代码删除操作,请确认不会影响现有功能");
515
+ }
516
+
517
+ // 检查修改说明的完整性
518
+ if (!modificationResult.explanation || modificationResult.explanation.length < 20) {
519
+ considerations.push("修改说明较简单,建议获取更详细的解释");
520
+ }
521
+
522
+ return considerations;
523
+ }
524
+
525
+ /**
526
+ * 批量修改多个文件 - 增强版
527
+ */
528
+ async batchModifyEnhanced(modifications, options = {}) {
529
+ const { autoApply = false, maxConcurrency = 3 } = options;
530
+ const results = [];
531
+
532
+ // 使用Promise控制并发数量
533
+ const semaphore = new Array(maxConcurrency).fill(null);
534
+
535
+ const processModification = async (mod) => {
536
+ try {
537
+ let result;
538
+
539
+ if (mod.startLine && mod.endLine) {
540
+ // 指定位置修改
541
+ const modResult = await this.generateCodeModificationStructured(
542
+ mod.filePath,
543
+ mod.startLine,
544
+ mod.endLine,
545
+ mod.description
546
+ );
547
+
548
+ if (modResult.success && autoApply && modResult.riskLevel === 'low') {
549
+ const applyResult = await this.applyModification(
550
+ mod.filePath,
551
+ mod.startLine,
552
+ mod.endLine,
553
+ modResult.modifiedCode
554
+ );
555
+ result = { ...modResult, applied: applyResult.success, applyResult };
556
+ } else {
557
+ result = { ...modResult, applied: false };
558
+ }
559
+ } else {
560
+ // 智能修改
561
+ result = await this.smartModifyWithEnhancements(
562
+ mod.filePath,
563
+ mod.description,
564
+ autoApply
565
+ );
566
+ }
567
+
568
+ return {
569
+ filePath: mod.filePath,
570
+ description: mod.description,
571
+ result,
572
+ processedAt: new Date().toISOString()
573
+ };
574
+
575
+ } catch (error) {
576
+ return {
577
+ filePath: mod.filePath,
578
+ description: mod.description,
579
+ result: { success: false, error: error.message },
580
+ processedAt: new Date().toISOString()
581
+ };
582
+ }
583
+ };
584
+
585
+ // 并发处理修改请求
586
+ const chunks = [];
587
+ for (let i = 0; i < modifications.length; i += maxConcurrency) {
588
+ chunks.push(modifications.slice(i, i + maxConcurrency));
589
+ }
590
+
591
+ for (const chunk of chunks) {
592
+ const promises = chunk.map(processModification);
593
+ const chunkResults = await Promise.all(promises);
594
+ results.push(...chunkResults);
595
+ }
596
+
597
+ return {
598
+ success: true,
599
+ results,
600
+ summary: {
601
+ total: results.length,
602
+ successful: results.filter(r => r.result.success).length,
603
+ applied: results.filter(r => r.result.applied).length,
604
+ failed: results.filter(r => !r.result.success).length
605
+ }
606
+ };
607
+ }
608
+
609
+ /**
610
+ * 清理资源
611
+ */
612
+ async cleanup() {
613
+ if (this.vectorStore) {
614
+ // 清理向量存储资源
615
+ this.vectorStore = null;
616
+ }
617
+ console.log('代码修改Agent资源已清理');
618
+ }
619
+ }
620
+
621
+ // 创建增强的代码修改 Agent 实例
622
+ const enhancedCodeAgent = new EnhancedCodeModificationAgent();
623
+
624
+ // 导出功能
625
+ export { enhancedCodeAgent, EnhancedCodeModificationAgent };
626
+
627
+ // 默认导出
628
+ export default enhancedCodeAgent;