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.
- package/index.js +125 -107
- 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
|
-
- **쓰기**: 자신의 폴더(\`
|
|
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. 내 폴더에 문서 작성 (\`
|
|
544
|
+
5. 내 폴더에 문서 작성 (\`zz_Coworker_{이름}/\`)
|
|
528
545
|
6. Push (\`docuking_push\`)
|
|
529
546
|
|
|
530
|
-
**폴더
|
|
547
|
+
**폴더 구조 (z_DocuKing과 zz_Coworker는 같은 레벨):**
|
|
531
548
|
\`\`\`
|
|
532
|
-
|
|
533
|
-
├──
|
|
534
|
-
|
|
535
|
-
├──
|
|
536
|
-
│ └──
|
|
537
|
-
└──
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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
|
-
-
|
|
545
|
-
-
|
|
546
|
-
-
|
|
562
|
+
- 코워커 폴더(\`zz_Coworker_{이름}/\`)는 z_DocuKing과 같은 레벨에 생성됨
|
|
563
|
+
- 참여자는 Pull로 z_DocuKing/ 폴더의 오너 문서를 볼 수 있음
|
|
564
|
+
- 참여자는 자신의 폴더에만 Push 가능
|
|
565
|
+
- \`docuking_status\`로 현재 권한과 작업 폴더 확인 가능
|
|
547
566
|
|
|
548
567
|
**참여자가 오너의 파일을 수정하고 싶을 때:**
|
|
549
|
-
1. 오너의 파일을
|
|
550
|
-
2.
|
|
551
|
-
|
|
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
|
-
-
|
|
558
|
-
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
986
|
-
|
|
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(
|
|
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
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
1304
|
-
if (!
|
|
1305
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
- 쓰기 권한: ${
|
|
1637
|
-
- 설명:
|
|
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
|
-
|
|
1669
|
-
collectFiles(docuKingPath, '', localFiles);
|
|
1670
|
-
}
|
|
1678
|
+
let pushableFiles = [];
|
|
1671
1679
|
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
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}
|
|
1704
|
+
**서버 파일**: ${serverFiles.length}개
|
|
1705
|
+
**Push 가능한 파일**: ${pushableFiles.length}개${permissionInfo}
|
|
1688
1706
|
|
|
1689
1707
|
## 사용 가능한 작업
|
|
1690
1708
|
- **Push**: docuking_push({ localPath, message: "..." })
|