docuking-mcp 1.1.0 → 1.2.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.
Files changed (2) hide show
  1. package/index.js +125 -107
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -396,6 +396,23 @@ DocuKing은 문서 버전 관리 시스템입니다. Git이 코드를 관리하
396
396
  2. 프로젝트 연결은 각 폴더마다 (폴더별로 다른 프로젝트 ID)
397
397
  3. 이미 MCP가 작동 중이면 재시작 없이 바로 프로젝트 연결 가능
398
398
 
399
+ ## .gitignore 설정 (중요!)
400
+
401
+ DocuKing 문서 폴더는 git에서 제외해야 합니다. 코드는 git으로, 문서는 DocuKing으로 분리 관리합니다.
402
+
403
+ \`\`\`gitignore
404
+ # DocuKing 문서 폴더 (문서는 DocuKing으로 관리)
405
+ *_DocuKing_*
406
+ *_Docuking_*
407
+ z_DocuKing/
408
+ zz_Coworker_*/
409
+ \`\`\`
410
+
411
+ **왜 gitignore에 등록해야 하나요?**
412
+ - 문서와 코드의 버전 관리를 분리
413
+ - git 저장소 크기 최적화 (대용량 문서 제외)
414
+ - DocuKing이 문서 버전 관리를 전담
415
+
399
416
  ## MCP 도구 목록
400
417
 
401
418
  ### 1. docuking_init
@@ -515,7 +532,7 @@ z_DocuKing/
515
532
  **특징:**
516
533
  - 프로젝트에 초대받아 참여한 사람
517
534
  - **읽기**: 전체 문서 Pull 가능 (오너의 문서도 볼 수 있음)
518
- - **쓰기**: 자신의 폴더(\`Co-worker/{이름}/\`)에만 Push 가능
535
+ - **쓰기**: 자신의 폴더(\`zz_Coworker_{이름}/\`)에만 Push 가능
519
536
  - API Key: \`sk_cw_\`로 시작
520
537
  - 프로젝트 설정 변경 불가능
521
538
 
@@ -524,39 +541,37 @@ z_DocuKing/
524
541
  2. MCP 설정 (한 번만)
525
542
  3. 프로젝트 연결 (\`docuking_init\`)
526
543
  4. Pull로 오너의 문서 가져오기 (\`docuking_pull\`)
527
- 5. 내 폴더에 문서 작성 (\`Co-worker/{이름}/\`)
544
+ 5. 내 폴더에 문서 작성 (\`zz_Coworker_{이름}/\`)
528
545
  6. Push (\`docuking_push\`)
529
546
 
530
- **폴더 구조:**
547
+ **폴더 구조 (z_DocuKing과 zz_Coworker는 같은 레벨):**
531
548
  \`\`\`
532
- z_DocuKing/
533
- ├── 정책/
534
- │ └── README.md ← 오너의 파일 (읽기만 가능)
535
- ├── 기획/
536
- │ └── 요구사항.md ← 오너의 파일 (읽기만 가능)
537
- └── Co-worker/
538
- └── 김개발/ 참여자 "김개발"의 폴더
539
- ├── 제안서.md 여기에만 Push 가능
540
- └── 수정안.md ← 여기에만 Push 가능
549
+ 프로젝트/
550
+ ├── src/ ← 소스 코드 (git 관리)
551
+ ├── z_DocuKing/ ← 오너의 문서 공간
552
+ ├── 정책/
553
+ └── README.md ← 오너의 파일 (읽기만 가능)
554
+ └── 기획/
555
+ └── 요구사항.md 오너의 파일 (읽기만 가능)
556
+ └── zz_Coworker_김개발/ 참여자 "김개발"의 폴더 (z_DocuKing과 같은 레벨!)
557
+ ├── 제안서.md ← 여기에만 Push 가능
558
+ └── 수정안.md ← 여기에만 Push 가능
541
559
  \`\`\`
542
560
 
543
561
  **중요 규칙:**
544
- - 참여자가 오너의 폴더를 Pull해서 가지고 있어도, Push할 때는 자동으로 자신의 폴더만 필터링됨
545
- - 참여자가 다른 폴더에 Push 시도 오류 메시지 표시
546
- - \`docuking_status\`로 현재 권한 확인 가능
562
+ - 코워커 폴더(\`zz_Coworker_{이름}/\`)는 z_DocuKing과 같은 레벨에 생성됨
563
+ - 참여자는 Pull로 z_DocuKing/ 폴더의 오너 문서를 있음
564
+ - 참여자는 자신의 폴더에만 Push 가능
565
+ - \`docuking_status\`로 현재 권한과 작업 폴더 확인 가능
547
566
 
548
567
  **참여자가 오너의 파일을 수정하고 싶을 때:**
549
- 1. 오너의 파일을 Pull해서 로컬에 가져옴 (예: \`정책/README.md\`)
550
- 2. 로컬에서 수정함 (로컬 파일이므로 수정 가능)
551
- 3. Push 시도 오류 발생: "협업자는 Co-worker/{이름}/ 폴더에만 Push할 수 있습니다"
552
- 4. **해결 방법**: 수정한 내용을 자신의 폴더에 새 파일로 작성
553
- - 예: \`정책/README.md\`를 수정했다면
554
- - → \`Co-worker/김개발/정책_README_수정제안.md\`로 작성 후 Push
568
+ 1. Pull로 오너의 파일을 로컬에 가져옴 (z_DocuKing/에 저장됨)
569
+ 2. 내용을 참고하여 자신의 폴더에 수정 제안 작성
570
+ - 예: \`zz_Coworker_김개발/정책_README_수정제안.md\`로 작성 Push
555
571
 
556
572
  **AI가 참여자에게 안내해야 할 내용:**
557
- - 참여자가 Push를 요청하면, 파일이 \`Co-worker/{이름}/\` 폴더 안에 있는지 확인
558
- - 다른 폴더에 있는 파일을 Push하려고 하면, 오류 메시지를 명확히 설명하고 해결 방법 제시
559
- - 오너의 파일을 수정하고 싶다면, 자신의 폴더에 제안서 형태로 작성하도록 안내
573
+ - 참여자의 작업 폴더는 \`zz_Coworker_{이름}/\` (z_DocuKing이 아님)
574
+ - 오너의 파일을 직접 수정할 없으므로, 제안서 형태로 작성하도록 안내
560
575
 
561
576
  ## AI 응답 가이드 (중요!)
562
577
 
@@ -942,24 +957,42 @@ Git처럼 무엇을 변경했는지 명확히 작성해주세요.
942
957
  const projectId = projectInfo.projectId;
943
958
  const projectName = projectInfo.projectName;
944
959
 
945
- // DocuKing 폴더 찾기
946
- const folderName = findDocuKingFolder(localPath);
947
- if (!folderName) {
948
- return {
949
- content: [
950
- {
951
- type: 'text',
952
- text: `오류: DocuKing 폴더가 없습니다.
953
- docuking_init을 먼저 실행하세요.`,
954
- },
955
- ],
956
- };
957
- }
958
- const docuKingPath = path.join(localPath, folderName);
959
-
960
960
  // Co-worker 권한은 API Key 형식에서 판단 (sk_xxx_cw_이름_)
961
961
  const coworkerMatch = API_KEY.match(/^sk_[a-f0-9]+_cw_([^_]+)_/);
962
- const allowedPathPrefix = coworkerMatch ? `Co-worker/${coworkerMatch[1]}/` : null;
962
+ const isCoworker = !!coworkerMatch;
963
+ const coworkerName = coworkerMatch ? coworkerMatch[1] : null;
964
+ const coworkerFolderName = isCoworker ? `zz_Coworker_${coworkerName}` : null;
965
+
966
+ // 작업 폴더 결정: 코워커는 zz_Coworker_{이름}/, 오너는 z_DocuKing/
967
+ let workingPath;
968
+ let serverPathPrefix = ''; // 서버에 저장될 때 경로 접두사
969
+
970
+ if (isCoworker) {
971
+ // 코워커: zz_Coworker_{이름}/ 폴더 사용 (z_DocuKing과 같은 레벨)
972
+ workingPath = path.join(localPath, coworkerFolderName);
973
+ serverPathPrefix = `${coworkerFolderName}/`;
974
+
975
+ if (!fs.existsSync(workingPath)) {
976
+ // 폴더가 없으면 생성
977
+ fs.mkdirSync(workingPath, { recursive: true });
978
+ console.log(`[DocuKing] 코워커 폴더 생성: ${coworkerFolderName}/`);
979
+ }
980
+ } else {
981
+ // 오너: z_DocuKing/ 폴더 사용
982
+ const folderName = findDocuKingFolder(localPath);
983
+ if (!folderName) {
984
+ return {
985
+ content: [
986
+ {
987
+ type: 'text',
988
+ text: `오류: DocuKing 폴더가 없습니다.
989
+ docuking_init을 먼저 실행하세요.`,
990
+ },
991
+ ],
992
+ };
993
+ }
994
+ workingPath = path.join(localPath, folderName);
995
+ }
963
996
 
964
997
  // 파일 목록 수집
965
998
  const filesToPush = [];
@@ -967,7 +1000,7 @@ docuking_init을 먼저 실행하세요.`,
967
1000
 
968
1001
  if (filePath) {
969
1002
  // 특정 파일만
970
- const fullPath = path.join(docuKingPath, filePath);
1003
+ const fullPath = path.join(workingPath, filePath);
971
1004
  if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
972
1005
  const fileType = getFileType(filePath);
973
1006
  if (fileType === 'excluded') {
@@ -981,52 +1014,21 @@ docuking_init을 먼저 실행하세요.`,
981
1014
  };
982
1015
  }
983
1016
 
984
- // 참여자인 경우 경로 체크
985
- if (allowedPathPrefix) {
986
- const normalizedPath = filePath.toLowerCase();
987
- const normalizedPrefix = allowedPathPrefix.toLowerCase();
988
- if (!normalizedPath.startsWith(normalizedPrefix)) {
989
- return {
990
- content: [
991
- {
992
- type: 'text',
993
- text: `오류: 협업자는 ${allowedPathPrefix} 폴더에만 Push할 수 있습니다.\n\n요청한 파일: ${filePath}\n허용된 경로: ${allowedPathPrefix}\n\n💡 참고: 오너의 파일을 로컬에서 수정하셨다면, 수정 내용을 ${allowedPathPrefix} 폴더에 새 파일로 작성해주세요.\n예: 정책/README.md를 수정했다면 → ${allowedPathPrefix}정책_README_수정제안.md`,
994
- },
995
- ],
996
- };
997
- }
998
- }
999
-
1000
- filesToPush.push({ path: filePath, fullPath, fileType });
1017
+ // 서버 경로: 코워커는 zz_Coworker_{이름}/파일경로, 오너는 파일경로
1018
+ const serverFilePath = serverPathPrefix + filePath;
1019
+ filesToPush.push({ path: filePath, serverPath: serverFilePath, fullPath, fileType });
1001
1020
  }
1002
1021
  } else {
1003
1022
  // 전체 파일 - 제외된 파일 목록도 수집
1004
- collectFiles(docuKingPath, '', filesToPush, excludedFiles);
1005
-
1006
- // 참여자인 경우 허용된 경로의 파일만 필터링
1007
- if (allowedPathPrefix) {
1008
- const normalizedPrefix = allowedPathPrefix.toLowerCase();
1009
- const filteredFiles = filesToPush.filter(file => {
1010
- const normalizedPath = file.path.toLowerCase();
1011
- return normalizedPath.startsWith(normalizedPrefix);
1012
- });
1023
+ collectFiles(workingPath, '', filesToPush, excludedFiles);
1013
1024
 
1014
- if (filteredFiles.length === 0) {
1015
- return {
1016
- content: [
1017
- {
1018
- type: 'text',
1019
- text: `Push할 파일이 없습니다.\n\n협업자는 ${allowedPathPrefix} 폴더에만 Push할 수 있습니다.\n\n현재 폴더 구조를 확인하고, 해당 폴더에 파일을 배치한 후 다시 시도해주세요.`,
1020
- },
1021
- ],
1022
- };
1023
- }
1024
-
1025
- // 필터링된 파일로 교체
1026
- filesToPush.length = 0;
1027
- filesToPush.push(...filteredFiles);
1025
+ // 서버 경로 추가
1026
+ for (const file of filesToPush) {
1027
+ file.serverPath = serverPathPrefix + file.path;
1028
+ }
1028
1029
 
1029
- console.log(`[DocuKing] 협업자 권한: ${filteredFiles.length}개 파일만 Push (${allowedPathPrefix})`);
1030
+ if (isCoworker) {
1031
+ console.log(`[DocuKing] 코워커 Push: ${filesToPush.length}개 파일 (${coworkerFolderName}/)`);
1030
1032
  }
1031
1033
  }
1032
1034
 
@@ -1135,7 +1137,7 @@ docuking_init을 먼저 실행하세요.`,
1135
1137
  },
1136
1138
  body: JSON.stringify({
1137
1139
  projectId,
1138
- path: file.path,
1140
+ path: file.serverPath, // 서버 경로 (코워커는 zz_Coworker_{이름}/파일경로)
1139
1141
  content,
1140
1142
  encoding, // 'utf-8' 또는 'base64'
1141
1143
  message, // 커밋 메시지
@@ -1300,11 +1302,10 @@ async function handlePull(args) {
1300
1302
  const projectId = projectInfo.projectId;
1301
1303
 
1302
1304
  // DocuKing 폴더 찾기 (없으면 기본값으로 생성)
1303
- let folderName = findDocuKingFolder(localPath);
1304
- if (!folderName) {
1305
- folderName = 'z_DocuKing';
1305
+ let ownerFolderName = findDocuKingFolder(localPath);
1306
+ if (!ownerFolderName) {
1307
+ ownerFolderName = 'z_DocuKing';
1306
1308
  }
1307
- const docuKingPath = path.join(localPath, folderName);
1308
1309
 
1309
1310
  // 파일 목록 조회
1310
1311
  let files = [];
@@ -1370,7 +1371,18 @@ async function handlePull(args) {
1370
1371
  }
1371
1372
 
1372
1373
  const data = await response.json();
1373
- const fullPath = path.join(docuKingPath, file.path);
1374
+
1375
+ // 서버 경로에 따라 로컬 저장 경로 결정
1376
+ // zz_Coworker_{이름}/으로 시작하면 해당 폴더에, 아니면 z_DocuKing/에 저장
1377
+ let fullPath;
1378
+ const coworkerPrefixMatch = file.path.match(/^(zz_Coworker_[^/]+)\//);
1379
+ if (coworkerPrefixMatch) {
1380
+ // 코워커 폴더 파일: 프로젝트 루트에 zz_Coworker_{이름}/ 폴더로 저장
1381
+ fullPath = path.join(localPath, file.path);
1382
+ } else {
1383
+ // 오너 폴더 파일: z_DocuKing/ 폴더에 저장
1384
+ fullPath = path.join(localPath, ownerFolderName, file.path);
1385
+ }
1374
1386
 
1375
1387
  // 디렉토리 생성
1376
1388
  fs.mkdirSync(path.dirname(fullPath), { recursive: true });
@@ -1625,20 +1637,20 @@ async function handleStatus(args) {
1625
1637
  const coworkerMatch = API_KEY.match(/^sk_[a-f0-9]+_cw_([^_]+)_/);
1626
1638
  const isCoworker = !!coworkerMatch;
1627
1639
  const coworkerName = coworkerMatch ? coworkerMatch[1] : null;
1628
- const allowedPathPrefix = isCoworker ? `Co-worker/${coworkerName}/` : null;
1640
+ const coworkerFolderName = isCoworker ? `zz_Coworker_${coworkerName}` : null;
1629
1641
 
1630
1642
  // 권한 정보 구성
1631
1643
  let permissionInfo = '';
1632
1644
  if (isCoworker) {
1633
1645
  permissionInfo = `\n\n## 현재 권한: 참여자 (Co-worker)
1634
1646
  - 이름: ${coworkerName}
1635
- - 읽기 권한: 전체 문서 (제한 없음)
1636
- - 쓰기 권한: ${allowedPathPrefix} 폴더만
1637
- - 설명: 다른 폴더의 파일을 Pull해서 가지고 있어도, Push할 때는 자동으로 ${allowedPathPrefix} 폴더의 파일만 Push됩니다.`;
1647
+ - 읽기 권한: 전체 문서 (Pull로 z_DocuKing/ 폴더의 문서 가져오기 가능)
1648
+ - 쓰기 권한: ${coworkerFolderName}/ 폴더만 (z_DocuKing과 같은 레벨)
1649
+ - 설명: 코워커 전용 폴더에서 작업하면 자동으로 서버에 Push됩니다.`;
1638
1650
  } else {
1639
1651
  permissionInfo = `\n\n## 현재 권한: 오너 (Owner)
1640
1652
  - 읽기 권한: 전체 문서
1641
- - 쓰기 권한: 전체 문서 (제한 없음)
1653
+ - 쓰기 권한: z_DocuKing/ 폴더 전체 (제한 없음)
1642
1654
  - 설명: 프로젝트의 모든 폴더에 Push할 수 있습니다.`;
1643
1655
  }
1644
1656
 
@@ -1662,29 +1674,35 @@ async function handleStatus(args) {
1662
1674
  }
1663
1675
 
1664
1676
  // 로컬 파일 목록 조회
1665
- const folderName = findDocuKingFolder(localPath);
1666
- const docuKingPath = folderName ? path.join(localPath, folderName) : null;
1667
1677
  let localFiles = [];
1668
- if (docuKingPath && fs.existsSync(docuKingPath)) {
1669
- collectFiles(docuKingPath, '', localFiles);
1670
- }
1678
+ let pushableFiles = [];
1671
1679
 
1672
- // 참여자인 경우 Push 가능한 파일만 필터링
1673
- let pushableFiles = localFiles;
1674
- if (allowedPathPrefix) {
1675
- const normalizedPrefix = allowedPathPrefix.toLowerCase();
1676
- pushableFiles = localFiles.filter(file => {
1677
- const normalizedPath = file.path.toLowerCase();
1678
- return normalizedPath.startsWith(normalizedPrefix);
1679
- });
1680
+ if (isCoworker) {
1681
+ // 코워커: zz_Coworker_{이름}/ 폴더에서 파일 수집
1682
+ const coworkerPath = path.join(localPath, coworkerFolderName);
1683
+ if (fs.existsSync(coworkerPath)) {
1684
+ collectFiles(coworkerPath, '', localFiles);
1685
+ }
1686
+ pushableFiles = localFiles; // 코워커는 자기 폴더의 모든 파일 Push 가능
1687
+ } else {
1688
+ // 오너: z_DocuKing/ 폴더에서 파일 수집
1689
+ const folderName = findDocuKingFolder(localPath);
1690
+ const docuKingPath = folderName ? path.join(localPath, folderName) : null;
1691
+ if (docuKingPath && fs.existsSync(docuKingPath)) {
1692
+ collectFiles(docuKingPath, '', localFiles);
1693
+ }
1694
+ pushableFiles = localFiles; // 오너는 모든 파일 Push 가능
1680
1695
  }
1681
1696
 
1682
1697
  const projectNameInfo = projectName ? ` (${projectName})` : '';
1698
+ const workingFolder = isCoworker ? coworkerFolderName : 'z_DocuKing';
1683
1699
  const statusText = `DocuKing 동기화 상태
1684
1700
 
1685
1701
  **프로젝트**: ${projectId}${projectNameInfo}
1702
+ **작업 폴더**: ${workingFolder}/
1686
1703
  **로컬 파일**: ${localFiles.length}개
1687
- **서버 파일**: ${serverFiles.length}개${allowedPathPrefix ? `\n**Push 가능한 파일**: ${pushableFiles.length} (${allowedPathPrefix})` : ''}${permissionInfo}
1704
+ **서버 파일**: ${serverFiles.length}개
1705
+ **Push 가능한 파일**: ${pushableFiles.length}개${permissionInfo}
1688
1706
 
1689
1707
  ## 사용 가능한 작업
1690
1708
  - **Push**: docuking_push({ localPath, message: "..." })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docuking-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "DocuKing MCP Server - AI 시대의 문서 협업 플랫폼",
5
5
  "type": "module",
6
6
  "main": "index.js",