docuking-mcp 1.2.3 → 1.2.5
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 +150 -69
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1082,16 +1082,47 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
1082
1082
|
filesToPush.push({ path: filePath, serverPath: serverFilePath, fullPath, fileType });
|
|
1083
1083
|
}
|
|
1084
1084
|
} else {
|
|
1085
|
-
// 전체 파일 - 제외된 파일 목록도 수집
|
|
1086
|
-
collectFiles(workingPath, '', filesToPush, excludedFiles);
|
|
1087
|
-
|
|
1088
|
-
// 서버 경로 추가
|
|
1089
|
-
for (const file of filesToPush) {
|
|
1090
|
-
file.serverPath = serverPathPrefix + file.path;
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
1085
|
if (isCoworker) {
|
|
1086
|
+
// 코워커: 본인 폴더만 수집
|
|
1087
|
+
collectFiles(workingPath, '', filesToPush, excludedFiles);
|
|
1088
|
+
|
|
1089
|
+
// 서버 경로 추가
|
|
1090
|
+
for (const file of filesToPush) {
|
|
1091
|
+
file.serverPath = serverPathPrefix + file.path;
|
|
1092
|
+
}
|
|
1094
1093
|
console.log(`[DocuKing] 코워커 Push: ${filesToPush.length}개 파일 (${coworkerFolderName}/)`);
|
|
1094
|
+
} else {
|
|
1095
|
+
// 오너: z_DocuKing/ + 모든 zz_Coworker_*/ 폴더 수집
|
|
1096
|
+
const docuKingFolderName = findDocuKingFolder(localPath);
|
|
1097
|
+
|
|
1098
|
+
// 1. z_DocuKing/ 폴더 수집
|
|
1099
|
+
if (docuKingFolderName) {
|
|
1100
|
+
const docuKingPath = path.join(localPath, docuKingFolderName);
|
|
1101
|
+
const docuKingFiles = [];
|
|
1102
|
+
collectFiles(docuKingPath, '', docuKingFiles, excludedFiles);
|
|
1103
|
+
|
|
1104
|
+
for (const file of docuKingFiles) {
|
|
1105
|
+
file.serverPath = file.path; // 오너 폴더는 서버 경로 = 파일 경로
|
|
1106
|
+
filesToPush.push(file);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// 2. 모든 zz_Coworker_*/ 폴더 수집
|
|
1111
|
+
const coworkerFolders = findCoworkerFolders(localPath);
|
|
1112
|
+
for (const cwFolder of coworkerFolders) {
|
|
1113
|
+
const cwPath = path.join(localPath, cwFolder);
|
|
1114
|
+
const cwFiles = [];
|
|
1115
|
+
collectFiles(cwPath, '', cwFiles, excludedFiles);
|
|
1116
|
+
|
|
1117
|
+
for (const file of cwFiles) {
|
|
1118
|
+
file.serverPath = `${cwFolder}/${file.path}`; // 코워커 폴더명 포함
|
|
1119
|
+
file.path = `${cwFolder}/${file.path}`; // 로컬 경로도 폴더명 포함
|
|
1120
|
+
file.fullPath = path.join(cwPath, file.path.replace(`${cwFolder}/`, ''));
|
|
1121
|
+
filesToPush.push(file);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
console.log(`[DocuKing] 오너 Push: ${filesToPush.length}개 파일 (z_DocuKing/ + ${coworkerFolders.length}개 코워커 폴더)`);
|
|
1095
1126
|
}
|
|
1096
1127
|
}
|
|
1097
1128
|
|
|
@@ -1106,30 +1137,6 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
1106
1137
|
};
|
|
1107
1138
|
}
|
|
1108
1139
|
|
|
1109
|
-
// 파일 업로드 (진행률 표시)
|
|
1110
|
-
const results = [];
|
|
1111
|
-
const total = filesToPush.length;
|
|
1112
|
-
let current = 0;
|
|
1113
|
-
let skipped = 0;
|
|
1114
|
-
|
|
1115
|
-
// 시작 안내 메시지 출력 (AI가 사용자에게 전달할 수 있도록)
|
|
1116
|
-
console.error(`[DocuKing] Push 시작: ${total}개 파일`);
|
|
1117
|
-
console.error(`[DocuKing] 💡 실시간 진행상황은 DocuKing 웹(https://docuking.ai)에서 확인하세요`);
|
|
1118
|
-
|
|
1119
|
-
// Sync 시작 알림 (웹에서 프로그레스바 표시용)
|
|
1120
|
-
try {
|
|
1121
|
-
await fetch(`${API_ENDPOINT}/projects/${projectId}/sync/start`, {
|
|
1122
|
-
method: 'POST',
|
|
1123
|
-
headers: {
|
|
1124
|
-
'Content-Type': 'application/json',
|
|
1125
|
-
'Authorization': `Bearer ${API_KEY}`,
|
|
1126
|
-
},
|
|
1127
|
-
body: JSON.stringify({ totalFiles: total }),
|
|
1128
|
-
});
|
|
1129
|
-
} catch (e) {
|
|
1130
|
-
console.error('[DocuKing] Sync 시작 알림 실패:', e.message);
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
1140
|
// 서버에서 파일 해시 조회 (변경 감지용)
|
|
1134
1141
|
let serverFileHashes = {};
|
|
1135
1142
|
try {
|
|
@@ -1150,36 +1157,82 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
1150
1157
|
console.error('[DocuKing] 파일 해시 조회 실패:', e.message);
|
|
1151
1158
|
}
|
|
1152
1159
|
|
|
1160
|
+
// ★ 변경된 파일만 필터링 (업로드 전에 미리 계산)
|
|
1161
|
+
const changedFiles = [];
|
|
1162
|
+
const unchangedFiles = [];
|
|
1163
|
+
|
|
1153
1164
|
for (const file of filesToPush) {
|
|
1165
|
+
let fileHash;
|
|
1166
|
+
let content;
|
|
1167
|
+
let encoding = 'utf-8';
|
|
1168
|
+
|
|
1169
|
+
if (file.fileType === 'binary') {
|
|
1170
|
+
const buffer = fs.readFileSync(file.fullPath);
|
|
1171
|
+
fileHash = crypto.createHash('sha256').update(buffer).digest('hex');
|
|
1172
|
+
content = buffer.toString('base64');
|
|
1173
|
+
encoding = 'base64';
|
|
1174
|
+
} else {
|
|
1175
|
+
content = fs.readFileSync(file.fullPath, 'utf-8');
|
|
1176
|
+
fileHash = crypto.createHash('sha256').update(content, 'utf-8').digest('hex');
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
if (serverFileHashes[file.path] === fileHash) {
|
|
1180
|
+
unchangedFiles.push(file.path);
|
|
1181
|
+
} else {
|
|
1182
|
+
changedFiles.push({ ...file, content, encoding, fileHash });
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
const totalLocal = filesToPush.length;
|
|
1187
|
+
const totalChanged = changedFiles.length;
|
|
1188
|
+
const totalUnchanged = unchangedFiles.length;
|
|
1189
|
+
|
|
1190
|
+
// ★ 변경된 파일이 없으면 즉시 반환
|
|
1191
|
+
if (totalChanged === 0) {
|
|
1192
|
+
return {
|
|
1193
|
+
content: [
|
|
1194
|
+
{
|
|
1195
|
+
type: 'text',
|
|
1196
|
+
text: `변경된 파일이 없습니다.
|
|
1197
|
+
|
|
1198
|
+
📊 로컬 파일: ${totalLocal}개
|
|
1199
|
+
✓ 서버와 동일: ${totalUnchanged}개
|
|
1200
|
+
⊘ 변경 필요: 0개
|
|
1201
|
+
|
|
1202
|
+
이미 모든 파일이 서버와 동기화되어 있습니다.`,
|
|
1203
|
+
},
|
|
1204
|
+
],
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// ★ 변경된 파일만 업로드 (AI에게 명확히 알림)
|
|
1209
|
+
console.error(`[DocuKing] 변경 감지 완료: 전체 ${totalLocal}개 중 ${totalChanged}개만 업로드`);
|
|
1210
|
+
console.error(`[DocuKing] 💡 실시간 진행상황은 DocuKing 웹(https://docuking.ai)에서 확인하세요`);
|
|
1211
|
+
|
|
1212
|
+
// Sync 시작 알림 (변경된 파일 수만 전달)
|
|
1213
|
+
try {
|
|
1214
|
+
await fetch(`${API_ENDPOINT}/projects/${projectId}/sync/start`, {
|
|
1215
|
+
method: 'POST',
|
|
1216
|
+
headers: {
|
|
1217
|
+
'Content-Type': 'application/json',
|
|
1218
|
+
'Authorization': `Bearer ${API_KEY}`,
|
|
1219
|
+
},
|
|
1220
|
+
body: JSON.stringify({ totalFiles: totalChanged }),
|
|
1221
|
+
});
|
|
1222
|
+
} catch (e) {
|
|
1223
|
+
console.error('[DocuKing] Sync 시작 알림 실패:', e.message);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// 파일 업로드 (변경된 파일만)
|
|
1227
|
+
const results = [];
|
|
1228
|
+
let current = 0;
|
|
1229
|
+
|
|
1230
|
+
for (const file of changedFiles) {
|
|
1154
1231
|
current++;
|
|
1155
|
-
const progress = `${current}/${
|
|
1232
|
+
const progress = `${current}/${totalChanged}`;
|
|
1156
1233
|
|
|
1157
1234
|
try {
|
|
1158
|
-
|
|
1159
|
-
let fileHash;
|
|
1160
|
-
let content;
|
|
1161
|
-
let encoding = 'utf-8';
|
|
1162
|
-
|
|
1163
|
-
if (file.fileType === 'binary') {
|
|
1164
|
-
// 바이너리 파일은 Base64로 인코딩
|
|
1165
|
-
const buffer = fs.readFileSync(file.fullPath);
|
|
1166
|
-
fileHash = crypto.createHash('sha256').update(buffer).digest('hex');
|
|
1167
|
-
content = buffer.toString('base64');
|
|
1168
|
-
encoding = 'base64';
|
|
1169
|
-
} else {
|
|
1170
|
-
// 텍스트 파일은 UTF-8
|
|
1171
|
-
content = fs.readFileSync(file.fullPath, 'utf-8');
|
|
1172
|
-
fileHash = crypto.createHash('sha256').update(content, 'utf-8').digest('hex');
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
// 변경 감지: 서버에 같은 해시가 있으면 스킵
|
|
1176
|
-
if (serverFileHashes[file.path] === fileHash) {
|
|
1177
|
-
const resultText = `${progress} ⊘ ${file.path} (변경 없음)`;
|
|
1178
|
-
results.push(resultText);
|
|
1179
|
-
console.log(resultText);
|
|
1180
|
-
skipped++;
|
|
1181
|
-
continue;
|
|
1182
|
-
}
|
|
1235
|
+
const { content, encoding, fileHash } = file;
|
|
1183
1236
|
|
|
1184
1237
|
// 재시도 로직 (최대 3회)
|
|
1185
1238
|
let lastError = null;
|
|
@@ -1294,17 +1347,16 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
1294
1347
|
|
|
1295
1348
|
const successCount = results.filter(r => r.includes('✓')).length;
|
|
1296
1349
|
const failCount = results.filter(r => r.includes('✗')).length;
|
|
1297
|
-
const skippedCount = skipped; // 이미 계산된 스킵 개수 사용
|
|
1298
1350
|
const excludedCount = excludedFiles.length;
|
|
1299
1351
|
|
|
1300
|
-
// 요약 정보
|
|
1301
|
-
let summary = `\n📦 커밋 메시지: "${message}"\n\n📊 처리 결과:\n -
|
|
1352
|
+
// 요약 정보 (★ 변경된 파일만 업로드했음을 명확히 표시)
|
|
1353
|
+
let summary = `\n📦 커밋 메시지: "${message}"\n\n📊 처리 결과:\n - 로컬 파일: ${totalLocal}개\n - 변경 없음 (스킵): ${totalUnchanged}개\n - 변경 감지: ${totalChanged}개\n - 업로드 성공: ${successCount}개\n - 업로드 실패: ${failCount}개`;
|
|
1302
1354
|
if (excludedCount > 0) {
|
|
1303
1355
|
summary += `\n - 제외 (압축/설치파일): ${excludedCount}개`;
|
|
1304
1356
|
}
|
|
1305
1357
|
|
|
1306
1358
|
// 상세 결과를 표시 (Git처럼)
|
|
1307
|
-
let resultText = `✓ Push
|
|
1359
|
+
let resultText = `✓ Push 완료! (변경된 ${totalChanged}개 파일만 업로드)${summary}`;
|
|
1308
1360
|
|
|
1309
1361
|
// 업로드된 파일이 있으면 상세 목록 표시
|
|
1310
1362
|
if (successCount > 0) {
|
|
@@ -1312,12 +1364,6 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
1312
1364
|
resultText += `\n\n📤 업로드된 파일 (${successCount}개):\n${uploadedFiles.map(r => ` ${r.replace(/^\d+\/\d+ /, '')}`).join('\n')}`;
|
|
1313
1365
|
}
|
|
1314
1366
|
|
|
1315
|
-
// 스킵된 파일이 있으면 표시
|
|
1316
|
-
if (skippedCount > 0) {
|
|
1317
|
-
const skippedFiles = results.filter(r => r.includes('⊘'));
|
|
1318
|
-
resultText += `\n\n⏭️ 스킵된 파일 (${skippedCount}개, 변경 없음):\n${skippedFiles.map(r => ` ${r.replace(/^\d+\/\d+ /, '')}`).join('\n')}`;
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
1367
|
// 실패한 파일이 있으면 표시
|
|
1322
1368
|
if (failCount > 0) {
|
|
1323
1369
|
const failedFiles = results.filter(r => r.includes('✗'));
|
|
@@ -1612,10 +1658,29 @@ function getFileType(fileName) {
|
|
|
1612
1658
|
return 'binary';
|
|
1613
1659
|
}
|
|
1614
1660
|
|
|
1615
|
-
// 유틸: DocuKing 폴더 찾기
|
|
1661
|
+
// 유틸: DocuKing 폴더 찾기
|
|
1662
|
+
// 우선순위: z_DocuKing > Z_DocuKing > 기타 docuking 포함 폴더
|
|
1616
1663
|
function findDocuKingFolder(projectPath) {
|
|
1617
1664
|
try {
|
|
1618
1665
|
const entries = fs.readdirSync(projectPath, { withFileTypes: true });
|
|
1666
|
+
|
|
1667
|
+
// 1순위: z_DocuKing 또는 Z_DocuKing (정확한 이름)
|
|
1668
|
+
for (const entry of entries) {
|
|
1669
|
+
if (entry.isDirectory() && (entry.name === 'z_DocuKing' || entry.name === 'Z_DocuKing')) {
|
|
1670
|
+
return entry.name;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
// 2순위: z_ 또는 Z_로 시작하고 docuking 포함
|
|
1675
|
+
for (const entry of entries) {
|
|
1676
|
+
if (entry.isDirectory() &&
|
|
1677
|
+
(entry.name.startsWith('z_') || entry.name.startsWith('Z_')) &&
|
|
1678
|
+
entry.name.toLowerCase().includes('docuking')) {
|
|
1679
|
+
return entry.name;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
// 3순위: docuking 포함하는 아무 폴더 (하위 호환)
|
|
1619
1684
|
for (const entry of entries) {
|
|
1620
1685
|
if (entry.isDirectory() && entry.name.toLowerCase().includes('docuking')) {
|
|
1621
1686
|
return entry.name;
|
|
@@ -1627,6 +1692,22 @@ function findDocuKingFolder(projectPath) {
|
|
|
1627
1692
|
return null;
|
|
1628
1693
|
}
|
|
1629
1694
|
|
|
1695
|
+
// 유틸: 모든 zz_Coworker_* 폴더 찾기
|
|
1696
|
+
function findCoworkerFolders(projectPath) {
|
|
1697
|
+
const folders = [];
|
|
1698
|
+
try {
|
|
1699
|
+
const entries = fs.readdirSync(projectPath, { withFileTypes: true });
|
|
1700
|
+
for (const entry of entries) {
|
|
1701
|
+
if (entry.isDirectory() && entry.name.startsWith('zz_Coworker_')) {
|
|
1702
|
+
folders.push(entry.name);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
} catch (e) {
|
|
1706
|
+
// 디렉토리 읽기 실패
|
|
1707
|
+
}
|
|
1708
|
+
return folders;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1630
1711
|
// 유틸: 디렉토리 재귀 탐색
|
|
1631
1712
|
// excludedFiles: 제외된 파일 목록을 수집할 배열 (선택)
|
|
1632
1713
|
function collectFiles(basePath, relativePath, results, excludedFiles = null) {
|