docuking-mcp 1.1.0 → 1.2.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 +108 -107
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -515,7 +515,7 @@ z_DocuKing/
515
515
  **특징:**
516
516
  - 프로젝트에 초대받아 참여한 사람
517
517
  - **읽기**: 전체 문서 Pull 가능 (오너의 문서도 볼 수 있음)
518
- - **쓰기**: 자신의 폴더(\`Co-worker/{이름}/\`)에만 Push 가능
518
+ - **쓰기**: 자신의 폴더(\`zz_Coworker_{이름}/\`)에만 Push 가능
519
519
  - API Key: \`sk_cw_\`로 시작
520
520
  - 프로젝트 설정 변경 불가능
521
521
 
@@ -524,39 +524,37 @@ z_DocuKing/
524
524
  2. MCP 설정 (한 번만)
525
525
  3. 프로젝트 연결 (\`docuking_init\`)
526
526
  4. Pull로 오너의 문서 가져오기 (\`docuking_pull\`)
527
- 5. 내 폴더에 문서 작성 (\`Co-worker/{이름}/\`)
527
+ 5. 내 폴더에 문서 작성 (\`zz_Coworker_{이름}/\`)
528
528
  6. Push (\`docuking_push\`)
529
529
 
530
- **폴더 구조:**
530
+ **폴더 구조 (z_DocuKing과 zz_Coworker는 같은 레벨):**
531
531
  \`\`\`
532
- z_DocuKing/
533
- ├── 정책/
534
- │ └── README.md ← 오너의 파일 (읽기만 가능)
535
- ├── 기획/
536
- │ └── 요구사항.md ← 오너의 파일 (읽기만 가능)
537
- └── Co-worker/
538
- └── 김개발/ 참여자 "김개발"의 폴더
539
- ├── 제안서.md 여기에만 Push 가능
540
- └── 수정안.md ← 여기에만 Push 가능
532
+ 프로젝트/
533
+ ├── src/ ← 소스 코드 (git 관리)
534
+ ├── z_DocuKing/ ← 오너의 문서 공간
535
+ ├── 정책/
536
+ └── README.md ← 오너의 파일 (읽기만 가능)
537
+ └── 기획/
538
+ └── 요구사항.md 오너의 파일 (읽기만 가능)
539
+ └── zz_Coworker_김개발/ 참여자 "김개발"의 폴더 (z_DocuKing과 같은 레벨!)
540
+ ├── 제안서.md ← 여기에만 Push 가능
541
+ └── 수정안.md ← 여기에만 Push 가능
541
542
  \`\`\`
542
543
 
543
544
  **중요 규칙:**
544
- - 참여자가 오너의 폴더를 Pull해서 가지고 있어도, Push할 때는 자동으로 자신의 폴더만 필터링됨
545
- - 참여자가 다른 폴더에 Push 시도 오류 메시지 표시
546
- - \`docuking_status\`로 현재 권한 확인 가능
545
+ - 코워커 폴더(\`zz_Coworker_{이름}/\`)는 z_DocuKing과 같은 레벨에 생성됨
546
+ - 참여자는 Pull로 z_DocuKing/ 폴더의 오너 문서를 있음
547
+ - 참여자는 자신의 폴더에만 Push 가능
548
+ - \`docuking_status\`로 현재 권한과 작업 폴더 확인 가능
547
549
 
548
550
  **참여자가 오너의 파일을 수정하고 싶을 때:**
549
- 1. 오너의 파일을 Pull해서 로컬에 가져옴 (예: \`정책/README.md\`)
550
- 2. 로컬에서 수정함 (로컬 파일이므로 수정 가능)
551
- 3. Push 시도 오류 발생: "협업자는 Co-worker/{이름}/ 폴더에만 Push할 수 있습니다"
552
- 4. **해결 방법**: 수정한 내용을 자신의 폴더에 새 파일로 작성
553
- - 예: \`정책/README.md\`를 수정했다면
554
- - → \`Co-worker/김개발/정책_README_수정제안.md\`로 작성 후 Push
551
+ 1. Pull로 오너의 파일을 로컬에 가져옴 (z_DocuKing/에 저장됨)
552
+ 2. 내용을 참고하여 자신의 폴더에 수정 제안 작성
553
+ - 예: \`zz_Coworker_김개발/정책_README_수정제안.md\`로 작성 Push
555
554
 
556
555
  **AI가 참여자에게 안내해야 할 내용:**
557
- - 참여자가 Push를 요청하면, 파일이 \`Co-worker/{이름}/\` 폴더 안에 있는지 확인
558
- - 다른 폴더에 있는 파일을 Push하려고 하면, 오류 메시지를 명확히 설명하고 해결 방법 제시
559
- - 오너의 파일을 수정하고 싶다면, 자신의 폴더에 제안서 형태로 작성하도록 안내
556
+ - 참여자의 작업 폴더는 \`zz_Coworker_{이름}/\` (z_DocuKing이 아님)
557
+ - 오너의 파일을 직접 수정할 없으므로, 제안서 형태로 작성하도록 안내
560
558
 
561
559
  ## AI 응답 가이드 (중요!)
562
560
 
@@ -942,24 +940,42 @@ Git처럼 무엇을 변경했는지 명확히 작성해주세요.
942
940
  const projectId = projectInfo.projectId;
943
941
  const projectName = projectInfo.projectName;
944
942
 
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
943
  // Co-worker 권한은 API Key 형식에서 판단 (sk_xxx_cw_이름_)
961
944
  const coworkerMatch = API_KEY.match(/^sk_[a-f0-9]+_cw_([^_]+)_/);
962
- const allowedPathPrefix = coworkerMatch ? `Co-worker/${coworkerMatch[1]}/` : null;
945
+ const isCoworker = !!coworkerMatch;
946
+ const coworkerName = coworkerMatch ? coworkerMatch[1] : null;
947
+ const coworkerFolderName = isCoworker ? `zz_Coworker_${coworkerName}` : null;
948
+
949
+ // 작업 폴더 결정: 코워커는 zz_Coworker_{이름}/, 오너는 z_DocuKing/
950
+ let workingPath;
951
+ let serverPathPrefix = ''; // 서버에 저장될 때 경로 접두사
952
+
953
+ if (isCoworker) {
954
+ // 코워커: zz_Coworker_{이름}/ 폴더 사용 (z_DocuKing과 같은 레벨)
955
+ workingPath = path.join(localPath, coworkerFolderName);
956
+ serverPathPrefix = `${coworkerFolderName}/`;
957
+
958
+ if (!fs.existsSync(workingPath)) {
959
+ // 폴더가 없으면 생성
960
+ fs.mkdirSync(workingPath, { recursive: true });
961
+ console.log(`[DocuKing] 코워커 폴더 생성: ${coworkerFolderName}/`);
962
+ }
963
+ } else {
964
+ // 오너: z_DocuKing/ 폴더 사용
965
+ const folderName = findDocuKingFolder(localPath);
966
+ if (!folderName) {
967
+ return {
968
+ content: [
969
+ {
970
+ type: 'text',
971
+ text: `오류: DocuKing 폴더가 없습니다.
972
+ docuking_init을 먼저 실행하세요.`,
973
+ },
974
+ ],
975
+ };
976
+ }
977
+ workingPath = path.join(localPath, folderName);
978
+ }
963
979
 
964
980
  // 파일 목록 수집
965
981
  const filesToPush = [];
@@ -967,7 +983,7 @@ docuking_init을 먼저 실행하세요.`,
967
983
 
968
984
  if (filePath) {
969
985
  // 특정 파일만
970
- const fullPath = path.join(docuKingPath, filePath);
986
+ const fullPath = path.join(workingPath, filePath);
971
987
  if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
972
988
  const fileType = getFileType(filePath);
973
989
  if (fileType === 'excluded') {
@@ -981,52 +997,21 @@ docuking_init을 먼저 실행하세요.`,
981
997
  };
982
998
  }
983
999
 
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 });
1000
+ // 서버 경로: 코워커는 zz_Coworker_{이름}/파일경로, 오너는 파일경로
1001
+ const serverFilePath = serverPathPrefix + filePath;
1002
+ filesToPush.push({ path: filePath, serverPath: serverFilePath, fullPath, fileType });
1001
1003
  }
1002
1004
  } else {
1003
1005
  // 전체 파일 - 제외된 파일 목록도 수집
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
- });
1013
-
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
- }
1006
+ collectFiles(workingPath, '', filesToPush, excludedFiles);
1024
1007
 
1025
- // 필터링된 파일로 교체
1026
- filesToPush.length = 0;
1027
- filesToPush.push(...filteredFiles);
1008
+ // 서버 경로 추가
1009
+ for (const file of filesToPush) {
1010
+ file.serverPath = serverPathPrefix + file.path;
1011
+ }
1028
1012
 
1029
- console.log(`[DocuKing] 협업자 권한: ${filteredFiles.length}개 파일만 Push (${allowedPathPrefix})`);
1013
+ if (isCoworker) {
1014
+ console.log(`[DocuKing] 코워커 Push: ${filesToPush.length}개 파일 (${coworkerFolderName}/)`);
1030
1015
  }
1031
1016
  }
1032
1017
 
@@ -1135,7 +1120,7 @@ docuking_init을 먼저 실행하세요.`,
1135
1120
  },
1136
1121
  body: JSON.stringify({
1137
1122
  projectId,
1138
- path: file.path,
1123
+ path: file.serverPath, // 서버 경로 (코워커는 zz_Coworker_{이름}/파일경로)
1139
1124
  content,
1140
1125
  encoding, // 'utf-8' 또는 'base64'
1141
1126
  message, // 커밋 메시지
@@ -1300,11 +1285,10 @@ async function handlePull(args) {
1300
1285
  const projectId = projectInfo.projectId;
1301
1286
 
1302
1287
  // DocuKing 폴더 찾기 (없으면 기본값으로 생성)
1303
- let folderName = findDocuKingFolder(localPath);
1304
- if (!folderName) {
1305
- folderName = 'z_DocuKing';
1288
+ let ownerFolderName = findDocuKingFolder(localPath);
1289
+ if (!ownerFolderName) {
1290
+ ownerFolderName = 'z_DocuKing';
1306
1291
  }
1307
- const docuKingPath = path.join(localPath, folderName);
1308
1292
 
1309
1293
  // 파일 목록 조회
1310
1294
  let files = [];
@@ -1370,7 +1354,18 @@ async function handlePull(args) {
1370
1354
  }
1371
1355
 
1372
1356
  const data = await response.json();
1373
- const fullPath = path.join(docuKingPath, file.path);
1357
+
1358
+ // 서버 경로에 따라 로컬 저장 경로 결정
1359
+ // zz_Coworker_{이름}/으로 시작하면 해당 폴더에, 아니면 z_DocuKing/에 저장
1360
+ let fullPath;
1361
+ const coworkerPrefixMatch = file.path.match(/^(zz_Coworker_[^/]+)\//);
1362
+ if (coworkerPrefixMatch) {
1363
+ // 코워커 폴더 파일: 프로젝트 루트에 zz_Coworker_{이름}/ 폴더로 저장
1364
+ fullPath = path.join(localPath, file.path);
1365
+ } else {
1366
+ // 오너 폴더 파일: z_DocuKing/ 폴더에 저장
1367
+ fullPath = path.join(localPath, ownerFolderName, file.path);
1368
+ }
1374
1369
 
1375
1370
  // 디렉토리 생성
1376
1371
  fs.mkdirSync(path.dirname(fullPath), { recursive: true });
@@ -1625,20 +1620,20 @@ async function handleStatus(args) {
1625
1620
  const coworkerMatch = API_KEY.match(/^sk_[a-f0-9]+_cw_([^_]+)_/);
1626
1621
  const isCoworker = !!coworkerMatch;
1627
1622
  const coworkerName = coworkerMatch ? coworkerMatch[1] : null;
1628
- const allowedPathPrefix = isCoworker ? `Co-worker/${coworkerName}/` : null;
1623
+ const coworkerFolderName = isCoworker ? `zz_Coworker_${coworkerName}` : null;
1629
1624
 
1630
1625
  // 권한 정보 구성
1631
1626
  let permissionInfo = '';
1632
1627
  if (isCoworker) {
1633
1628
  permissionInfo = `\n\n## 현재 권한: 참여자 (Co-worker)
1634
1629
  - 이름: ${coworkerName}
1635
- - 읽기 권한: 전체 문서 (제한 없음)
1636
- - 쓰기 권한: ${allowedPathPrefix} 폴더만
1637
- - 설명: 다른 폴더의 파일을 Pull해서 가지고 있어도, Push할 때는 자동으로 ${allowedPathPrefix} 폴더의 파일만 Push됩니다.`;
1630
+ - 읽기 권한: 전체 문서 (Pull로 z_DocuKing/ 폴더의 문서 가져오기 가능)
1631
+ - 쓰기 권한: ${coworkerFolderName}/ 폴더만 (z_DocuKing과 같은 레벨)
1632
+ - 설명: 코워커 전용 폴더에서 작업하면 자동으로 서버에 Push됩니다.`;
1638
1633
  } else {
1639
1634
  permissionInfo = `\n\n## 현재 권한: 오너 (Owner)
1640
1635
  - 읽기 권한: 전체 문서
1641
- - 쓰기 권한: 전체 문서 (제한 없음)
1636
+ - 쓰기 권한: z_DocuKing/ 폴더 전체 (제한 없음)
1642
1637
  - 설명: 프로젝트의 모든 폴더에 Push할 수 있습니다.`;
1643
1638
  }
1644
1639
 
@@ -1662,29 +1657,35 @@ async function handleStatus(args) {
1662
1657
  }
1663
1658
 
1664
1659
  // 로컬 파일 목록 조회
1665
- const folderName = findDocuKingFolder(localPath);
1666
- const docuKingPath = folderName ? path.join(localPath, folderName) : null;
1667
1660
  let localFiles = [];
1668
- if (docuKingPath && fs.existsSync(docuKingPath)) {
1669
- collectFiles(docuKingPath, '', localFiles);
1670
- }
1661
+ let pushableFiles = [];
1671
1662
 
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
- });
1663
+ if (isCoworker) {
1664
+ // 코워커: zz_Coworker_{이름}/ 폴더에서 파일 수집
1665
+ const coworkerPath = path.join(localPath, coworkerFolderName);
1666
+ if (fs.existsSync(coworkerPath)) {
1667
+ collectFiles(coworkerPath, '', localFiles);
1668
+ }
1669
+ pushableFiles = localFiles; // 코워커는 자기 폴더의 모든 파일 Push 가능
1670
+ } else {
1671
+ // 오너: z_DocuKing/ 폴더에서 파일 수집
1672
+ const folderName = findDocuKingFolder(localPath);
1673
+ const docuKingPath = folderName ? path.join(localPath, folderName) : null;
1674
+ if (docuKingPath && fs.existsSync(docuKingPath)) {
1675
+ collectFiles(docuKingPath, '', localFiles);
1676
+ }
1677
+ pushableFiles = localFiles; // 오너는 모든 파일 Push 가능
1680
1678
  }
1681
1679
 
1682
1680
  const projectNameInfo = projectName ? ` (${projectName})` : '';
1681
+ const workingFolder = isCoworker ? coworkerFolderName : 'z_DocuKing';
1683
1682
  const statusText = `DocuKing 동기화 상태
1684
1683
 
1685
1684
  **프로젝트**: ${projectId}${projectNameInfo}
1685
+ **작업 폴더**: ${workingFolder}/
1686
1686
  **로컬 파일**: ${localFiles.length}개
1687
- **서버 파일**: ${serverFiles.length}개${allowedPathPrefix ? `\n**Push 가능한 파일**: ${pushableFiles.length} (${allowedPathPrefix})` : ''}${permissionInfo}
1687
+ **서버 파일**: ${serverFiles.length}개
1688
+ **Push 가능한 파일**: ${pushableFiles.length}개${permissionInfo}
1688
1689
 
1689
1690
  ## 사용 가능한 작업
1690
1691
  - **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.0",
4
4
  "description": "DocuKing MCP Server - AI 시대의 문서 협업 플랫폼",
5
5
  "type": "module",
6
6
  "main": "index.js",