docuking-mcp 2.3.0 → 2.4.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 (2) hide show
  1. package/index.js +111 -35
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1388,22 +1388,23 @@ Git처럼 무엇을 변경했는지 명확히 작성해주세요.
1388
1388
  const { isCoworker, coworkerFolder } = parseCoworkerFromApiKey(apiKey);
1389
1389
  const coworkerFolderName = isCoworker ? `yy_Coworker_${coworkerFolder}` : null;
1390
1390
 
1391
- // 작업 폴더 결정: 오너는 yy_All_Docu/, 협업자는 yy_Coworker_{폴더명}/ (별도 폴더)
1391
+ // 작업 폴더 결정: 프로젝트 루트 기준 절대경로 사용
1392
+ // 오너: yy_All_Docu/, zz_ai_*/ 폴더들 Push
1393
+ // 협업자: yy_Coworker_{폴더명}/ 폴더만 Push
1392
1394
  const mainFolderPath = path.join(localPath, 'yy_All_Docu');
1393
- let workingPath;
1394
- let serverPathPrefix = ''; // 서버에 저장될 때 경로 접두사
1395
1395
 
1396
- if (isCoworker) {
1397
- // 협업자: yy_Coworker_{폴더명}/ 폴더에서 Push (yy_All_Docu/ 밖에 별도)
1398
- workingPath = path.join(localPath, coworkerFolderName);
1399
- serverPathPrefix = `${coworkerFolderName}/`;
1396
+ // Push 대상 폴더 목록 (프로젝트 루트 기준)
1397
+ let pushTargetFolders = [];
1400
1398
 
1401
- if (!fs.existsSync(workingPath)) {
1402
- // 폴더가 없으면 생성
1403
- fs.mkdirSync(workingPath, { recursive: true });
1399
+ if (isCoworker) {
1400
+ // 협업자: yy_Coworker_{폴더명}/ 폴더만 Push
1401
+ const coworkerPath = path.join(localPath, coworkerFolderName);
1402
+ if (!fs.existsSync(coworkerPath)) {
1403
+ fs.mkdirSync(coworkerPath, { recursive: true });
1404
1404
  }
1405
+ pushTargetFolders.push({ localPath: coworkerPath, serverPrefix: coworkerFolderName });
1405
1406
  } else {
1406
- // 오너: yy_All_Docu/ 폴더 사용
1407
+ // 오너: yy_All_Docu/ 폴더 확인
1407
1408
  if (!fs.existsSync(mainFolderPath)) {
1408
1409
  return {
1409
1410
  content: [
@@ -1415,8 +1416,18 @@ docuking_init을 먼저 실행하세요.`,
1415
1416
  ],
1416
1417
  };
1417
1418
  }
1418
- workingPath = mainFolderPath;
1419
- serverPathPrefix = 'yy_All_Docu/'; // 오너도 yy_All_Docu/ 접두사 필요
1419
+
1420
+ // 오너 Push 대상: yy_All_Docu/ + zz_ai_*/ 폴더들
1421
+ pushTargetFolders.push({ localPath: mainFolderPath, serverPrefix: 'yy_All_Docu' });
1422
+
1423
+ // zz_ai_* 폴더들 찾기
1424
+ const rootEntries = fs.readdirSync(localPath, { withFileTypes: true });
1425
+ for (const entry of rootEntries) {
1426
+ if (entry.isDirectory() && entry.name.startsWith('zz_ai_')) {
1427
+ const zzPath = path.join(localPath, entry.name);
1428
+ pushTargetFolders.push({ localPath: zzPath, serverPrefix: entry.name });
1429
+ }
1430
+ }
1420
1431
  }
1421
1432
 
1422
1433
  // .env 파일 자동 백업: _Infra_Config/ 폴더에 복사
@@ -1525,10 +1536,12 @@ docuking_init을 먼저 실행하세요.`,
1525
1536
  // 파일 목록 수집
1526
1537
  const filesToPush = [];
1527
1538
  const excludedFiles = []; // 제외된 파일 목록
1539
+ const largeFiles = [];
1528
1540
 
1529
1541
  if (filePath) {
1530
- // 특정 파일만
1531
- const fullPath = path.join(workingPath, filePath);
1542
+ // 특정 파일만 - 첫 번째 대상 폴더에서 찾기
1543
+ const targetFolder = pushTargetFolders[0];
1544
+ const fullPath = path.join(targetFolder.localPath, filePath);
1532
1545
  if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
1533
1546
  const fileType = getFileType(filePath);
1534
1547
  if (fileType === 'excluded') {
@@ -1542,20 +1555,24 @@ docuking_init을 먼저 실행하세요.`,
1542
1555
  };
1543
1556
  }
1544
1557
 
1545
- // 서버 경로: 코워커는 yy_Coworker_{폴더명}/파일경로, 오너는 파일경로
1546
- const serverFilePath = serverPathPrefix + filePath;
1558
+ // 서버 경로: 폴더prefix/파일경로 (예: yy_All_Docu/회의록/test.md)
1559
+ const serverFilePath = `${targetFolder.serverPrefix}/${filePath}`;
1547
1560
  filesToPush.push({ path: filePath, serverPath: serverFilePath, fullPath, fileType });
1548
1561
  }
1549
1562
  } else {
1550
- // 전체 파일 - 제외된 파일 대용량 파일 목록도 수집
1551
- const largeFiles = [];
1552
- collectFiles(workingPath, '', filesToPush, excludedFiles, largeFiles);
1563
+ // 전체 파일 - 모든 대상 폴더에서 수집
1564
+ for (const targetFolder of pushTargetFolders) {
1565
+ if (!fs.existsSync(targetFolder.localPath)) continue;
1553
1566
 
1554
- // 서버 경로 추가
1555
- for (const file of filesToPush) {
1556
- file.serverPath = serverPathPrefix + file.path;
1557
- }
1567
+ const folderFiles = [];
1568
+ collectFilesSimple(targetFolder.localPath, '', folderFiles, excludedFiles, largeFiles);
1558
1569
 
1570
+ // 서버 경로 추가: prefix/상대경로 (예: yy_All_Docu/회의록/test.md)
1571
+ for (const file of folderFiles) {
1572
+ file.serverPath = `${targetFolder.serverPrefix}/${file.path}`;
1573
+ filesToPush.push(file);
1574
+ }
1575
+ }
1559
1576
 
1560
1577
  // 대용량 파일 경고
1561
1578
  if (largeFiles.length > 0) {
@@ -2018,14 +2035,15 @@ async function handlePull(args) {
2018
2035
  const data = await response.json();
2019
2036
 
2020
2037
  // 서버 경로에 따라 로컬 저장 위치 결정
2038
+ // 프로젝트 루트 기준 절대경로 그대로 사용
2021
2039
  // - yy_All_Docu/xxx → yy_All_Docu/xxx (오너 문서)
2022
- // - yy_Coworker_xxx/yyy → yy_Coworker_xxx/yyy (협업자 폴더, 루트에 별도)
2040
+ // - yy_Coworker_xxx/yyy → yy_Coworker_xxx/yyy (협업자 폴더)
2041
+ // - zz_ai_xxx/yyy → zz_ai_xxx/yyy (AI 폴더)
2023
2042
  let fullPath;
2024
- if (file.path.startsWith('yy_All_Docu/')) {
2025
- // 오너 문서: yy_All_Docu/ 접두사 포함된 채로 로컬에 저장
2026
- fullPath = path.join(localPath, file.path);
2027
- } else if (file.path.startsWith('yy_Coworker_')) {
2028
- // 협업자 폴더: 루트에 별도 폴더로 저장
2043
+ if (file.path.startsWith('yy_All_Docu/') ||
2044
+ file.path.startsWith('yy_Coworker_') ||
2045
+ file.path.startsWith('zz_ai_')) {
2046
+ // 서버 경로 그대로 로컬에 저장
2029
2047
  fullPath = path.join(localPath, file.path);
2030
2048
  } else {
2031
2049
  // 기타 (구버전 호환): yy_All_Docu/ 안에 저장
@@ -2229,7 +2247,57 @@ function hasAllDocuFolder(projectPath) {
2229
2247
  return fs.existsSync(allDocuPath);
2230
2248
  }
2231
2249
 
2232
- // 유틸: 디렉토리 재귀 탐색
2250
+ // 유틸: 디렉토리 재귀 탐색 (zz_* 제외 없음, Push용)
2251
+ // excludedFiles: 제외된 파일 목록을 수집할 배열 (선택)
2252
+ // largeFiles: 대용량 파일 목록을 수집할 배열 (선택)
2253
+ function collectFilesSimple(basePath, relativePath, results, excludedFiles = null, largeFiles = null) {
2254
+ const fullPath = path.join(basePath, relativePath);
2255
+ if (!fs.existsSync(fullPath)) return;
2256
+
2257
+ const entries = fs.readdirSync(fullPath, { withFileTypes: true });
2258
+
2259
+ for (const entry of entries) {
2260
+ const entryRelPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
2261
+
2262
+ if (entry.isDirectory()) {
2263
+ collectFilesSimple(basePath, entryRelPath, results, excludedFiles, largeFiles);
2264
+ } else if (entry.isFile()) {
2265
+ const fileType = getFileType(entry.name);
2266
+
2267
+ // 제외 파일은 건너뜀
2268
+ if (fileType === 'excluded') {
2269
+ console.error(`[DocuKing] 제외됨: ${entryRelPath} (지원하지 않는 파일 형식)`);
2270
+ if (excludedFiles) {
2271
+ excludedFiles.push(entryRelPath);
2272
+ }
2273
+ continue;
2274
+ }
2275
+
2276
+ // 파일 크기 확인
2277
+ const fileFullPath = path.join(fullPath, entry.name);
2278
+ const stats = fs.statSync(fileFullPath);
2279
+ const fileSizeMB = stats.size / (1024 * 1024);
2280
+
2281
+ // 대용량 파일은 별도 추적
2282
+ if (stats.size > MAX_FILE_SIZE_BYTES) {
2283
+ console.error(`[DocuKing] ⚠️ 대용량 파일 제외: ${entryRelPath} (${fileSizeMB.toFixed(1)}MB > ${MAX_FILE_SIZE_MB}MB)`);
2284
+ if (largeFiles) {
2285
+ largeFiles.push({ path: entryRelPath, sizeMB: fileSizeMB.toFixed(1) });
2286
+ }
2287
+ continue;
2288
+ }
2289
+
2290
+ results.push({
2291
+ path: entryRelPath,
2292
+ fullPath: fileFullPath,
2293
+ fileType,
2294
+ sizeMB: fileSizeMB,
2295
+ });
2296
+ }
2297
+ }
2298
+ }
2299
+
2300
+ // 유틸: 디렉토리 재귀 탐색 (기존 호환용)
2233
2301
  // excludedFiles: 제외된 파일 목록을 수집할 배열 (선택)
2234
2302
  // largeFiles: 대용량 파일 목록을 수집할 배열 (선택)
2235
2303
  // zz_* 폴더는 로컬 전용이므로 동기화에서 제외
@@ -2378,16 +2446,24 @@ async function handleStatus(args) {
2378
2446
  const mainFolderPath = path.join(localPath, 'yy_All_Docu');
2379
2447
 
2380
2448
  if (isCoworker) {
2381
- // 협업자: yy_Coworker_{폴더명}/ 폴더에서 파일 수집 (yy_All_Docu/ 밖에 별도)
2449
+ // 협업자: yy_Coworker_{폴더명}/ 폴더에서 파일 수집
2382
2450
  const coworkerPath = path.join(localPath, coworkerFolderName);
2383
2451
  if (fs.existsSync(coworkerPath)) {
2384
- collectFiles(coworkerPath, '', localFiles);
2452
+ collectFilesSimple(coworkerPath, '', localFiles);
2385
2453
  }
2386
2454
  pushableFiles = localFiles; // 협업자는 자기 폴더의 모든 파일 Push 가능
2387
2455
  } else {
2388
- // 오너: yy_All_Docu/ 폴더에서 파일 수집
2456
+ // 오너: yy_All_Docu/ + zz_ai_*/ 폴더에서 파일 수집
2389
2457
  if (fs.existsSync(mainFolderPath)) {
2390
- collectFiles(mainFolderPath, '', localFiles);
2458
+ collectFilesSimple(mainFolderPath, '', localFiles);
2459
+ }
2460
+ // zz_ai_* 폴더들도 수집
2461
+ const rootEntries = fs.readdirSync(localPath, { withFileTypes: true });
2462
+ for (const entry of rootEntries) {
2463
+ if (entry.isDirectory() && entry.name.startsWith('zz_ai_')) {
2464
+ const zzPath = path.join(localPath, entry.name);
2465
+ collectFilesSimple(zzPath, '', localFiles);
2466
+ }
2391
2467
  }
2392
2468
  pushableFiles = localFiles; // 오너는 모든 파일 Push 가능
2393
2469
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docuking-mcp",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "DocuKing MCP Server - AI 시대의 문서 협업 플랫폼",
5
5
  "type": "module",
6
6
  "main": "index.js",