autosnippet 3.1.2 → 3.1.4

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/bin/mcp-server.js CHANGED
File without changes
@@ -60,8 +60,6 @@ const __dirname = dirname(__filename);
60
60
 
61
61
  /** AutoSnippet 源码仓库根目录(定位 templates/ 等资源) */
62
62
  const REPO_ROOT = resolve(__dirname, '..', '..');
63
- /** V2 子项目根目录(定位 bin/mcp-server.js 等) */
64
- const V2_ROOT = resolve(__dirname, '..', '..');
65
63
 
66
64
  // ─────────────────────────────────────────────────────
67
65
 
@@ -434,10 +432,8 @@ export class SetupService {
434
432
  /* ═══ Step 3: IDE 集成 ═══════════════════════════════ */
435
433
 
436
434
  stepIDE() {
437
- const mcpServerPath = join(V2_ROOT, 'bin', 'mcp-server.js');
438
-
439
- this._configureVSCodeMCP(mcpServerPath);
440
- this._configureCursorMCP(mcpServerPath);
435
+ this._configureVSCodeMCP();
436
+ this._configureCursorMCP();
441
437
  this._copyCopilotInstructions();
442
438
  this._generateAgentInstructionFiles();
443
439
  this._copyCursorRules();
@@ -649,7 +645,7 @@ export class SetupService {
649
645
  }
650
646
 
651
647
  /** @private .vscode/mcp.json → VSCode MCP (新标准格式) */
652
- _configureVSCodeMCP(mcpServerPath) {
648
+ _configureVSCodeMCP() {
653
649
  const vscodeDir = join(this.projectRoot, '.vscode');
654
650
  const mcpConfigPath = join(vscodeDir, 'mcp.json');
655
651
  mkdirSync(vscodeDir, { recursive: true });
@@ -668,11 +664,9 @@ export class SetupService {
668
664
  }
669
665
  config.servers.autosnippet = {
670
666
  type: 'stdio',
671
- command: 'node',
672
- args: [mcpServerPath],
667
+ command: 'asd-mcp',
673
668
  env: {
674
669
  ASD_PROJECT_DIR: this.projectRoot,
675
- NODE_PATH: join(V2_ROOT, 'node_modules'),
676
670
  },
677
671
  };
678
672
 
@@ -680,7 +674,7 @@ export class SetupService {
680
674
  }
681
675
 
682
676
  /** @private .cursor/mcp.json */
683
- _configureCursorMCP(mcpServerPath) {
677
+ _configureCursorMCP() {
684
678
  const cursorDir = join(this.projectRoot, '.cursor');
685
679
  const configPath = join(cursorDir, 'mcp.json');
686
680
  mkdirSync(cursorDir, { recursive: true });
@@ -698,11 +692,9 @@ export class SetupService {
698
692
  existing.mcpServers = {};
699
693
  }
700
694
  existing.mcpServers.autosnippet = {
701
- command: 'node',
702
- args: [mcpServerPath],
695
+ command: 'asd-mcp',
703
696
  env: {
704
697
  ASD_PROJECT_DIR: this.projectRoot,
705
- NODE_PATH: join(V2_ROOT, 'node_modules'),
706
698
  },
707
699
  };
708
700
 
@@ -63,16 +63,13 @@ export class UpgradeService {
63
63
  /* ═══ MCP 配置 ══════════════════════════════════════ */
64
64
 
65
65
  _upgradeMCP() {
66
- const mcpServerPath = join(REPO_ROOT, 'bin', 'mcp-server.js');
67
- const nodePath = join(REPO_ROOT, 'node_modules');
68
-
69
66
  // Cursor
70
- this._updateCursorMCP(mcpServerPath, nodePath);
67
+ this._updateCursorMCP();
71
68
  // VSCode
72
- this._updateVSCodeMCP(mcpServerPath, nodePath);
69
+ this._updateVSCodeMCP();
73
70
  }
74
71
 
75
- _updateCursorMCP(mcpServerPath, nodePath) {
72
+ _updateCursorMCP() {
76
73
  const configPath = join(this.projectRoot, '.cursor', 'mcp.json');
77
74
  if (!existsSync(configPath)) {
78
75
  return;
@@ -89,18 +86,16 @@ export class UpgradeService {
89
86
  }
90
87
 
91
88
  config.mcpServers.autosnippet = {
92
- command: 'node',
93
- args: [mcpServerPath],
89
+ command: 'asd-mcp',
94
90
  env: {
95
91
  ASD_PROJECT_DIR: this.projectRoot,
96
- NODE_PATH: nodePath,
97
92
  },
98
93
  };
99
94
 
100
95
  writeFileSync(configPath, JSON.stringify(config, null, 2));
101
96
  }
102
97
 
103
- _updateVSCodeMCP(mcpServerPath, nodePath) {
98
+ _updateVSCodeMCP() {
104
99
  const vscodeDir = join(this.projectRoot, '.vscode');
105
100
  const mcpConfigPath = join(vscodeDir, 'mcp.json');
106
101
 
@@ -119,11 +114,9 @@ export class UpgradeService {
119
114
 
120
115
  config.servers.autosnippet = {
121
116
  type: 'stdio',
122
- command: 'node',
123
- args: [mcpServerPath],
117
+ command: 'asd-mcp',
124
118
  env: {
125
119
  ASD_PROJECT_DIR: this.projectRoot,
126
- NODE_PATH: nodePath,
127
120
  },
128
121
  };
129
122
 
@@ -9,6 +9,7 @@
9
9
  import fs from 'node:fs/promises';
10
10
  import path from 'node:path';
11
11
  import Logger from '../../../../../infrastructure/logging/Logger.js';
12
+ import pathGuard from '../../../../../shared/PathGuard.js';
12
13
 
13
14
  const logger = Logger.getInstance();
14
15
 
@@ -79,8 +80,12 @@ export async function loadCheckpoints(projectRoot) {
79
80
  export async function clearCheckpoints(projectRoot) {
80
81
  try {
81
82
  const checkpointDir = path.join(projectRoot, '.autosnippet', 'bootstrap-checkpoint');
83
+ pathGuard.assertSafe(checkpointDir);
82
84
  await fs.rm(checkpointDir, { recursive: true, force: true });
83
- } catch {
84
- /* ignore */
85
+ } catch (err) {
86
+ if (err?.name === 'PathGuardError') {
87
+ throw err;
88
+ }
89
+ /* ignore other errors */
85
90
  }
86
91
  }
@@ -309,6 +309,7 @@ export class KnowledgeFileWriter {
309
309
  if (entry.sourceFile) {
310
310
  const fullPath = path.join(this.projectRoot, entry.sourceFile);
311
311
  if (fs.existsSync(fullPath)) {
312
+ pathGuard.assertSafe(fullPath);
312
313
  fs.unlinkSync(fullPath);
313
314
  this.logger.info('Knowledge entry file removed', {
314
315
  entryId: entry.id,
@@ -328,6 +329,7 @@ export class KnowledgeFileWriter {
328
329
  for (const dir of searchDirs) {
329
330
  const fp = path.join(dir, filename);
330
331
  if (fs.existsSync(fp)) {
332
+ pathGuard.assertSafe(fp);
331
333
  fs.unlinkSync(fp);
332
334
  this.logger.info('Knowledge entry file removed', { entryId: entry.id, path: fp });
333
335
  return true;
@@ -358,6 +360,7 @@ export class KnowledgeFileWriter {
358
360
 
359
361
  // 删除旧文件
360
362
  if (oldPath && fs.existsSync(oldPath)) {
363
+ pathGuard.assertSafe(oldPath);
361
364
  fs.unlinkSync(oldPath);
362
365
  this.logger.info('Removed old knowledge entry file on lifecycle change', {
363
366
  entryId: entry.id,
@@ -392,6 +395,7 @@ export class KnowledgeFileWriter {
392
395
  }
393
396
  const oldPath = path.join(this.projectRoot, entry.sourceFile);
394
397
  if (oldPath !== newPath && fs.existsSync(oldPath)) {
398
+ pathGuard.assertSafe(oldPath);
395
399
  fs.unlinkSync(oldPath);
396
400
  this.logger.info('Cleaned up old knowledge entry file', {
397
401
  entryId: entry.id,
@@ -675,6 +679,7 @@ function _walkAndRemoveById(dir, id) {
675
679
  } else if (entry.name.endsWith('.md') && !entry.name.startsWith('_')) {
676
680
  const head = fs.readFileSync(full, 'utf8').slice(0, 500);
677
681
  if (head.includes(`id: ${id}`)) {
682
+ pathGuard.assertSafe(full);
678
683
  fs.unlinkSync(full);
679
684
  return true;
680
685
  }
@@ -332,6 +332,10 @@ export function dedup(files, wikiDir, emit) {
332
332
  // 完全相同 hash → 移除后来的
333
333
  if (existing.hash === file.hash) {
334
334
  const fullPath = path.join(wikiDir, file.path);
335
+ if (!fullPath.startsWith(path.resolve(wikiDir) + path.sep)) {
336
+ logger.warn(`[WikiGenerator] Dedup: path escape blocked — ${file.path}`);
337
+ continue;
338
+ }
335
339
  try {
336
340
  fs.unlinkSync(fullPath);
337
341
  } catch {
@@ -361,6 +365,10 @@ export function dedup(files, wikiDir, emit) {
361
365
  if (isCurrentSynced && !isFirstSynced) {
362
366
  // 当前是 synced,first 是 codegen → 删除 synced
363
367
  const fullPath = path.join(wikiDir, file.path);
368
+ if (!fullPath.startsWith(path.resolve(wikiDir) + path.sep)) {
369
+ logger.warn(`[WikiGenerator] Dedup: path escape blocked — ${file.path}`);
370
+ continue;
371
+ }
364
372
  try {
365
373
  fs.unlinkSync(fullPath);
366
374
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autosnippet",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "description": "Extract code patterns into a knowledge base for AI coding assistants",
5
5
  "type": "module",
6
6
  "main": "lib/bootstrap.js",
@@ -40,7 +40,8 @@
40
40
  "author": "gaoxuefeng",
41
41
  "license": "MIT",
42
42
  "bin": {
43
- "asd": "bin/cli.js"
43
+ "asd": "bin/cli.js",
44
+ "asd-mcp": "bin/mcp-server.js"
44
45
  },
45
46
  "keywords": [
46
47
  "snippet",