docuking-mcp 2.9.1 → 2.9.2
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/handlers/docs.js +44 -2
- package/handlers/sync.js +275 -27
- package/index.js +37 -1
- package/package.json +1 -1
package/handlers/docs.js
CHANGED
|
@@ -406,6 +406,28 @@ ${notes || '(없음)'}
|
|
|
406
406
|
|
|
407
407
|
const relativePath = path.relative(localPath, planFilePath).replace(/\\/g, '/');
|
|
408
408
|
|
|
409
|
+
// 새 Plan 생성 시 Talk/Todo 자동 생성 지시
|
|
410
|
+
let autoCreateHint = '';
|
|
411
|
+
if (isNew) {
|
|
412
|
+
autoCreateHint = `
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
📝 **AI 작업 흐름 (자동 체크)**
|
|
416
|
+
|
|
417
|
+
아래 항목이 아직 없다면 생성하세요:
|
|
418
|
+
|
|
419
|
+
1. **Talk 기록** (이 작업의 배경/논의)
|
|
420
|
+
- 이미 작성했으면 스킵
|
|
421
|
+
- 없으면: docuking_talk 호출 (왜 이 작업을 하게 됐는지)
|
|
422
|
+
|
|
423
|
+
2. **킹투두 등록** (이 작업 추적용)
|
|
424
|
+
- 이미 등록했으면 스킵
|
|
425
|
+
- 없으면: docuking_todo(action: "add", todo: "[태그] ${title}")
|
|
426
|
+
|
|
427
|
+
3. **작업 완료 후**
|
|
428
|
+
- docuking_done 호출 (planId: "${targetPlanId}")`;
|
|
429
|
+
}
|
|
430
|
+
|
|
409
431
|
return {
|
|
410
432
|
content: [
|
|
411
433
|
{
|
|
@@ -418,7 +440,7 @@ ${notes || '(없음)'}
|
|
|
418
440
|
📊 진행률: ${progress}% (${doneCount}/${totalCount})
|
|
419
441
|
|
|
420
442
|
💡 이 planId를 기억해두세요. 나중에 업데이트하거나 완료 처리할 때 필요합니다.
|
|
421
|
-
💡 DocuKing에 Push하면 웹에서도 확인할 수
|
|
443
|
+
💡 DocuKing에 Push하면 웹에서도 확인할 수 있습니다.${autoCreateHint}`,
|
|
422
444
|
},
|
|
423
445
|
],
|
|
424
446
|
};
|
|
@@ -501,6 +523,26 @@ ${artifactsMarkdown}
|
|
|
501
523
|
pushMessage = `\n\n⚠️ DocuKing 동기화 실패: ${e.message}`;
|
|
502
524
|
}
|
|
503
525
|
|
|
526
|
+
// 검증 지시 메시지
|
|
527
|
+
const validateHint = artifacts.length > 0 ? `
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
🔍 **정책 검증 필요**
|
|
531
|
+
|
|
532
|
+
산출물이 xx_Policy/ 정책을 준수하는지 검증하세요:
|
|
533
|
+
${artifacts.slice(0, 5).map(a => `- ${a}`).join('\n')}${artifacts.length > 5 ? `\n... 외 ${artifacts.length - 5}개` : ''}
|
|
534
|
+
|
|
535
|
+
**검증 방법:**
|
|
536
|
+
1. Task 도구로 서브에이전트 실행:
|
|
537
|
+
- prompt: "위 파일들이 xx_Policy/ 정책을 준수하는지 확인. 위반 시 수정하거나 docuking_urgent로 보고"
|
|
538
|
+
- subagent_type: "Explore"
|
|
539
|
+
|
|
540
|
+
2. 또는 직접 xx_Policy/ 읽고 비교
|
|
541
|
+
|
|
542
|
+
위반 발견 시:
|
|
543
|
+
- 수정 가능하면 → 직접 수정
|
|
544
|
+
- 정책 불명확하면 → docuking_urgent 호출` : '';
|
|
545
|
+
|
|
504
546
|
return {
|
|
505
547
|
content: [
|
|
506
548
|
{
|
|
@@ -512,7 +554,7 @@ ${artifactsMarkdown}
|
|
|
512
554
|
🕐 완료 시간: ${timestamp}
|
|
513
555
|
|
|
514
556
|
📝 요약: ${summary}
|
|
515
|
-
${artifacts.length > 0 ? `📦 산출물: ${artifacts.length}개` : ''}${pushMessage}`,
|
|
557
|
+
${artifacts.length > 0 ? `📦 산출물: ${artifacts.length}개` : ''}${pushMessage}${validateHint}`,
|
|
516
558
|
},
|
|
517
559
|
],
|
|
518
560
|
};
|
package/handlers/sync.js
CHANGED
|
@@ -234,31 +234,6 @@ Git처럼 무엇을 변경했는지 명확히 작성해주세요.
|
|
|
234
234
|
const projectId = projectInfo.projectId;
|
|
235
235
|
const projectName = projectInfo.projectName;
|
|
236
236
|
|
|
237
|
-
// ========================================
|
|
238
|
-
// 1단계: Pull 먼저 실행 (git pull && git push 패턴)
|
|
239
|
-
// ========================================
|
|
240
|
-
let pullResultText = '';
|
|
241
|
-
try {
|
|
242
|
-
const pullResult = await handlePullInternal({ localPath, filePath });
|
|
243
|
-
pullResultText = pullResult.text;
|
|
244
|
-
} catch (e) {
|
|
245
|
-
return {
|
|
246
|
-
content: [
|
|
247
|
-
{
|
|
248
|
-
type: 'text',
|
|
249
|
-
text: `❌ Pull 실패로 Push를 중단합니다.
|
|
250
|
-
오류: ${e.message}
|
|
251
|
-
|
|
252
|
-
먼저 Pull 문제를 해결한 후 다시 시도하세요.`,
|
|
253
|
-
},
|
|
254
|
-
],
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// ========================================
|
|
259
|
-
// 2단계: Push 진행
|
|
260
|
-
// ========================================
|
|
261
|
-
|
|
262
237
|
// Co-worker 권한은 API Key 형식에서 판단
|
|
263
238
|
const { isCoworker, coworkerFolder } = parseCoworkerFromApiKey(apiKey);
|
|
264
239
|
const coworkerFolderName = isCoworker ? `yy_Coworker_${coworkerFolder}` : null;
|
|
@@ -498,6 +473,35 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
498
473
|
}
|
|
499
474
|
}
|
|
500
475
|
|
|
476
|
+
// ========================================
|
|
477
|
+
// 로컬 파일 경로 목록 저장 (Pull 전에!)
|
|
478
|
+
// soft-delete에서 "로컬에 없는 파일" 판단에 사용
|
|
479
|
+
// Pull이 파일을 내려받아도 이 목록은 변하지 않음
|
|
480
|
+
// ========================================
|
|
481
|
+
const localPathsBeforePull = new Set(filesToPush.map(f => f.serverPath));
|
|
482
|
+
console.error(`[DocuKing] Pull 전 로컬 파일 ${localPathsBeforePull.size}개 기록됨`);
|
|
483
|
+
|
|
484
|
+
// ========================================
|
|
485
|
+
// Pull 실행 (git pull && git push 패턴)
|
|
486
|
+
// ========================================
|
|
487
|
+
let pullResultText = '';
|
|
488
|
+
try {
|
|
489
|
+
const pullResult = await handlePullInternal({ localPath, filePath });
|
|
490
|
+
pullResultText = pullResult.text;
|
|
491
|
+
} catch (e) {
|
|
492
|
+
return {
|
|
493
|
+
content: [
|
|
494
|
+
{
|
|
495
|
+
type: 'text',
|
|
496
|
+
text: `❌ Pull 실패로 Push를 중단합니다.
|
|
497
|
+
오류: ${e.message}
|
|
498
|
+
|
|
499
|
+
먼저 Pull 문제를 해결한 후 다시 시도하세요.`,
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
501
505
|
if (filesToPush.length === 0 && emptyFolders.length === 0) {
|
|
502
506
|
return {
|
|
503
507
|
content: [
|
|
@@ -734,16 +738,21 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
734
738
|
// 단, 협업자 폴더(yy_Coworker_*)는 삭제하지 않음 (오너가 협업자 파일을 삭제하면 안됨)
|
|
735
739
|
// 기존 hard delete(delete-batch) → soft delete(soft-delete)로 변경
|
|
736
740
|
// 3일 후 크론잡에서 hard delete 수행
|
|
741
|
+
// 중요: localPathsBeforePull 사용 (Pull 전에 수집한 목록)
|
|
742
|
+
// processedLocalPaths는 Pull로 내려받은 파일도 포함하므로 사용 금지
|
|
737
743
|
let deleted = 0;
|
|
738
744
|
const deletedFilePaths = [];
|
|
739
745
|
let protectedFiles = []; // source: 'web' 파일 (보호됨)
|
|
740
746
|
if (serverAllPaths.length > 0 && !isCoworker) {
|
|
741
747
|
// 코워커가 아닌 경우에만 삭제 수행 (오너 전용)
|
|
742
748
|
// yy_Coworker_*로 시작하는 경로는 삭제 대상에서 제외
|
|
749
|
+
// localPathsBeforePull: Pull 전에 수집한 로컬 파일 목록 (좀비 파일 방지)
|
|
743
750
|
const pathsToDelete = serverAllPaths.filter(p =>
|
|
744
|
-
!
|
|
751
|
+
!localPathsBeforePull.has(p) && !p.startsWith('yy_Coworker_')
|
|
745
752
|
);
|
|
746
753
|
|
|
754
|
+
console.error(`[DocuKing] soft-delete 대상: ${pathsToDelete.length}개 (서버에만 있고 Pull 전 로컬에 없던 파일)`);
|
|
755
|
+
|
|
747
756
|
if (pathsToDelete.length > 0) {
|
|
748
757
|
try {
|
|
749
758
|
// soft-delete API 호출 (deleted_at 설정, 3일 후 hard delete)
|
|
@@ -943,6 +952,65 @@ export async function handlePullInternal(args) {
|
|
|
943
952
|
|
|
944
953
|
const projectId = projectInfo.projectId;
|
|
945
954
|
|
|
955
|
+
// Co-worker 권한은 API Key 형식에서 판단
|
|
956
|
+
const { isCoworker, coworkerFolder } = parseCoworkerFromApiKey(apiKey);
|
|
957
|
+
|
|
958
|
+
// ========================================
|
|
959
|
+
// 필수 폴더 체크 & 생성 (init 이후 추가된 폴더들)
|
|
960
|
+
// ========================================
|
|
961
|
+
const createdFolders = [];
|
|
962
|
+
|
|
963
|
+
// 공통 필수 폴더
|
|
964
|
+
const commonFolders = ['xx_Urgent'];
|
|
965
|
+
|
|
966
|
+
// 오너 전용 폴더
|
|
967
|
+
const ownerFolders = ['xx_Infra_Config', 'xx_Policy', 'zz_ai_1_Talk', 'zz_ai_2_Todo', 'zz_ai_3_Plan'];
|
|
968
|
+
|
|
969
|
+
// 협업자 전용 폴더 (yy_Coworker_{폴더명}/ 안에)
|
|
970
|
+
const coworkerSubFolders = ['zz_ai_1_Talk', 'zz_ai_2_Todo', 'zz_ai_3_Plan', '_Private'];
|
|
971
|
+
|
|
972
|
+
// 공통 폴더 체크
|
|
973
|
+
for (const folder of commonFolders) {
|
|
974
|
+
const folderPath = path.join(localPath, folder);
|
|
975
|
+
if (!fs.existsSync(folderPath)) {
|
|
976
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
977
|
+
createdFolders.push(folder);
|
|
978
|
+
console.error(`[DocuKing] 누락 폴더 생성: ${folder}/`);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
if (isCoworker) {
|
|
983
|
+
// 협업자: yy_Coworker_{폴더명}/ 안에 하위 폴더 체크
|
|
984
|
+
const coworkerFolderName = `yy_Coworker_${coworkerFolder}`;
|
|
985
|
+
const coworkerBasePath = path.join(localPath, coworkerFolderName);
|
|
986
|
+
|
|
987
|
+
// 협업자 루트 폴더
|
|
988
|
+
if (!fs.existsSync(coworkerBasePath)) {
|
|
989
|
+
fs.mkdirSync(coworkerBasePath, { recursive: true });
|
|
990
|
+
createdFolders.push(coworkerFolderName);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// 협업자 하위 폴더
|
|
994
|
+
for (const subFolder of coworkerSubFolders) {
|
|
995
|
+
const subFolderPath = path.join(coworkerBasePath, subFolder);
|
|
996
|
+
if (!fs.existsSync(subFolderPath)) {
|
|
997
|
+
fs.mkdirSync(subFolderPath, { recursive: true });
|
|
998
|
+
createdFolders.push(`${coworkerFolderName}/${subFolder}`);
|
|
999
|
+
console.error(`[DocuKing] 누락 폴더 생성: ${coworkerFolderName}/${subFolder}/`);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
} else {
|
|
1003
|
+
// 오너: 루트에 폴더 체크
|
|
1004
|
+
for (const folder of ownerFolders) {
|
|
1005
|
+
const folderPath = path.join(localPath, folder);
|
|
1006
|
+
if (!fs.existsSync(folderPath)) {
|
|
1007
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
1008
|
+
createdFolders.push(folder);
|
|
1009
|
+
console.error(`[DocuKing] 누락 폴더 생성: ${folder}/`);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
946
1014
|
// yy_All_Docu 폴더 (없으면 생성)
|
|
947
1015
|
const mainFolderName = 'yy_All_Docu';
|
|
948
1016
|
const mainFolderPath = path.join(localPath, mainFolderName);
|
|
@@ -1127,6 +1195,14 @@ export async function handlePullInternal(args) {
|
|
|
1127
1195
|
- 스킵 (변경 없음): ${skipped}개
|
|
1128
1196
|
- 실패: ${failed}개`;
|
|
1129
1197
|
|
|
1198
|
+
// 누락 폴더 생성 알림
|
|
1199
|
+
if (createdFolders.length > 0) {
|
|
1200
|
+
resultText += `\n\n📁 누락 폴더 생성됨 (${createdFolders.length}개):`;
|
|
1201
|
+
createdFolders.forEach(f => {
|
|
1202
|
+
resultText += `\n + ${f}/`;
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1130
1206
|
// 다운로드된 파일 목록
|
|
1131
1207
|
const downloadedFiles = results.filter(r => r.type === 'download');
|
|
1132
1208
|
if (downloadedFiles.length > 0) {
|
|
@@ -1274,7 +1350,6 @@ export async function handlePullInternal(args) {
|
|
|
1274
1350
|
// 킹어전트 감지 (xx_Urgent/ 폴더의 긴급 보고 확인)
|
|
1275
1351
|
// 오너에게만 알림 (협업자는 자기가 작성한 거니까)
|
|
1276
1352
|
// ========================================
|
|
1277
|
-
const { isCoworker } = parseCoworkerFromApiKey(apiKey);
|
|
1278
1353
|
if (!isCoworker) {
|
|
1279
1354
|
const urgentFolderPath = path.join(localPath, 'xx_Urgent');
|
|
1280
1355
|
if (fs.existsSync(urgentFolderPath)) {
|
|
@@ -1566,3 +1641,176 @@ export async function handleStatus(args) {
|
|
|
1566
1641
|
],
|
|
1567
1642
|
};
|
|
1568
1643
|
}
|
|
1644
|
+
|
|
1645
|
+
/**
|
|
1646
|
+
* docuking_delete 구현 - 서버 파일 삭제
|
|
1647
|
+
*/
|
|
1648
|
+
export async function handleDelete(args) {
|
|
1649
|
+
const localPath = args.localPath || process.cwd();
|
|
1650
|
+
const { paths, force = false } = args;
|
|
1651
|
+
|
|
1652
|
+
if (!paths || paths.length === 0) {
|
|
1653
|
+
return {
|
|
1654
|
+
content: [{
|
|
1655
|
+
type: 'text',
|
|
1656
|
+
text: '오류: 삭제할 파일 경로(paths)가 필요합니다.',
|
|
1657
|
+
}],
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// 로컬 config에서 API 키 읽기
|
|
1662
|
+
const apiKey = getApiKey(localPath);
|
|
1663
|
+
if (!apiKey) {
|
|
1664
|
+
return {
|
|
1665
|
+
content: [{
|
|
1666
|
+
type: 'text',
|
|
1667
|
+
text: '오류: API 키를 찾을 수 없습니다. 먼저 docuking_init을 실행하세요.',
|
|
1668
|
+
}],
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
// 프로젝트 정보 조회
|
|
1673
|
+
const projectInfo = getProjectInfo(localPath);
|
|
1674
|
+
if (projectInfo.error) {
|
|
1675
|
+
return {
|
|
1676
|
+
content: [{
|
|
1677
|
+
type: 'text',
|
|
1678
|
+
text: projectInfo.error,
|
|
1679
|
+
}],
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
const projectId = projectInfo.projectId;
|
|
1684
|
+
|
|
1685
|
+
// Co-worker 권한 체크
|
|
1686
|
+
const { isCoworker, coworkerFolder } = parseCoworkerFromApiKey(apiKey);
|
|
1687
|
+
|
|
1688
|
+
// 협업자는 자기 폴더만 삭제 가능
|
|
1689
|
+
if (isCoworker) {
|
|
1690
|
+
const coworkerPrefix = `yy_Coworker_${coworkerFolder}/`;
|
|
1691
|
+
const invalidPaths = paths.filter(p => !p.startsWith(coworkerPrefix) && !p.startsWith('xx_Urgent/'));
|
|
1692
|
+
if (invalidPaths.length > 0) {
|
|
1693
|
+
return {
|
|
1694
|
+
content: [{
|
|
1695
|
+
type: 'text',
|
|
1696
|
+
text: `오류: 협업자는 자기 폴더(${coworkerPrefix})와 xx_Urgent/만 삭제할 수 있습니다.
|
|
1697
|
+
삭제 불가: ${invalidPaths.join(', ')}`,
|
|
1698
|
+
}],
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
// 오너도 협업자 폴더는 삭제 금지
|
|
1704
|
+
if (!isCoworker) {
|
|
1705
|
+
const coworkerPaths = paths.filter(p => p.startsWith('yy_Coworker_'));
|
|
1706
|
+
if (coworkerPaths.length > 0 && !force) {
|
|
1707
|
+
return {
|
|
1708
|
+
content: [{
|
|
1709
|
+
type: 'text',
|
|
1710
|
+
text: `오류: 협업자 폴더는 삭제할 수 없습니다.
|
|
1711
|
+
대상: ${coworkerPaths.join(', ')}
|
|
1712
|
+
|
|
1713
|
+
정말 삭제하려면 force: true 옵션을 사용하세요. (권장하지 않음)`,
|
|
1714
|
+
}],
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
// soft-delete API 호출
|
|
1720
|
+
try {
|
|
1721
|
+
const deleteResponse = await fetch(`${API_ENDPOINT}/files/soft-delete`, {
|
|
1722
|
+
method: 'POST',
|
|
1723
|
+
headers: {
|
|
1724
|
+
'Content-Type': 'application/json',
|
|
1725
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
1726
|
+
},
|
|
1727
|
+
body: JSON.stringify({
|
|
1728
|
+
projectId,
|
|
1729
|
+
paths,
|
|
1730
|
+
source: 'mcp-delete', // 명시적 삭제 요청 표시
|
|
1731
|
+
}),
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
if (!deleteResponse.ok) {
|
|
1735
|
+
const error = await deleteResponse.text();
|
|
1736
|
+
return {
|
|
1737
|
+
content: [{
|
|
1738
|
+
type: 'text',
|
|
1739
|
+
text: `오류: 삭제 실패 - ${error}`,
|
|
1740
|
+
}],
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
const result = await deleteResponse.json();
|
|
1745
|
+
const deleted = result.deleted || 0;
|
|
1746
|
+
const deletedPaths = result.deletedPaths || [];
|
|
1747
|
+
const protectedFiles = result.protected || [];
|
|
1748
|
+
|
|
1749
|
+
let resultText = `✓ 삭제 완료!
|
|
1750
|
+
|
|
1751
|
+
📊 처리 결과:
|
|
1752
|
+
- 삭제됨: ${deleted}개 (3일 후 영구 삭제)
|
|
1753
|
+
- 보호됨: ${protectedFiles.length}개`;
|
|
1754
|
+
|
|
1755
|
+
if (deletedPaths.length > 0) {
|
|
1756
|
+
resultText += `\n\n🗑️ 삭제된 파일:`;
|
|
1757
|
+
deletedPaths.slice(0, 20).forEach(p => {
|
|
1758
|
+
resultText += `\n ✗ ${p}`;
|
|
1759
|
+
});
|
|
1760
|
+
if (deletedPaths.length > 20) {
|
|
1761
|
+
resultText += `\n ... 외 ${deletedPaths.length - 20}개`;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
if (protectedFiles.length > 0) {
|
|
1766
|
+
resultText += `\n\n🛡️ 보호된 파일 (웹에서 생성됨):`;
|
|
1767
|
+
protectedFiles.slice(0, 10).forEach(p => {
|
|
1768
|
+
resultText += `\n 🔒 ${p}`;
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
resultText += `\n\n💡 삭제된 파일은 3일 후 영구 삭제됩니다.
|
|
1773
|
+
💡 복구가 필요하면 3일 이내에 docuking_rollback을 사용하세요.`;
|
|
1774
|
+
|
|
1775
|
+
// 로컬 파일도 삭제
|
|
1776
|
+
let localDeleted = 0;
|
|
1777
|
+
for (const serverPath of deletedPaths) {
|
|
1778
|
+
try {
|
|
1779
|
+
let localFilePath;
|
|
1780
|
+
if (serverPath.startsWith('xx_') ||
|
|
1781
|
+
serverPath.startsWith('yy_All_Docu/') ||
|
|
1782
|
+
serverPath.startsWith('yy_Coworker_') ||
|
|
1783
|
+
serverPath.startsWith('zz_ai_')) {
|
|
1784
|
+
localFilePath = path.join(localPath, serverPath);
|
|
1785
|
+
} else {
|
|
1786
|
+
localFilePath = path.join(localPath, 'yy_All_Docu', serverPath);
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
if (fs.existsSync(localFilePath)) {
|
|
1790
|
+
fs.unlinkSync(localFilePath);
|
|
1791
|
+
localDeleted++;
|
|
1792
|
+
}
|
|
1793
|
+
} catch (e) {
|
|
1794
|
+
console.error(`[DocuKing] 로컬 파일 삭제 실패: ${serverPath} - ${e.message}`);
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
if (localDeleted > 0) {
|
|
1799
|
+
resultText += `\n\n📁 로컬 파일도 ${localDeleted}개 삭제됨`;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
return {
|
|
1803
|
+
content: [{
|
|
1804
|
+
type: 'text',
|
|
1805
|
+
text: resultText,
|
|
1806
|
+
}],
|
|
1807
|
+
};
|
|
1808
|
+
} catch (e) {
|
|
1809
|
+
return {
|
|
1810
|
+
content: [{
|
|
1811
|
+
type: 'text',
|
|
1812
|
+
text: `오류: ${e.message}`,
|
|
1813
|
+
}],
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
}
|
package/index.js
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
handlePull,
|
|
44
44
|
handleList,
|
|
45
45
|
handleStatus,
|
|
46
|
+
handleDelete,
|
|
46
47
|
} from './handlers/sync.js';
|
|
47
48
|
|
|
48
49
|
import {
|
|
@@ -192,7 +193,11 @@ init 완료 후 오너에게 "정책 문서를 작성하시겠습니까?" 제안
|
|
|
192
193
|
},
|
|
193
194
|
{
|
|
194
195
|
name: 'docuking_pull',
|
|
195
|
-
description:
|
|
196
|
+
description: `서버에서 문서를 다운로드합니다. "DocuKing에서 가져와" 요청 시 사용.
|
|
197
|
+
|
|
198
|
+
**킹캐스트 포함**: Pull 후 xx_Policy/, xx_Infra_Config/ 폴더 변경이 감지되면 자동으로 .claude/rules/local/에 로컬화됩니다. 오너가 정책을 수정하고 Push하면, 협업자가 Pull할 때 자동으로 정책이 전파됩니다.
|
|
199
|
+
|
|
200
|
+
**긴급 보고 알림**: Pull 후 xx_Urgent/ 폴더에 파일이 있으면 "긴급 보고 N건 있습니다" 알림을 표시합니다.`,
|
|
196
201
|
inputSchema: {
|
|
197
202
|
type: 'object',
|
|
198
203
|
properties: {
|
|
@@ -509,6 +514,35 @@ init 완료 후 오너에게 "정책 문서를 작성하시겠습니까?" 제안
|
|
|
509
514
|
required: ['localPath', 'title', 'situation'],
|
|
510
515
|
},
|
|
511
516
|
},
|
|
517
|
+
{
|
|
518
|
+
name: 'docuking_delete',
|
|
519
|
+
description: `서버에서 파일/폴더를 직접 삭제합니다.
|
|
520
|
+
|
|
521
|
+
**사용 시점:**
|
|
522
|
+
- 로컬에서 삭제한 파일이 Push 후에도 서버에 남아있을 때
|
|
523
|
+
- 서버에만 존재하는 불필요한 파일을 정리할 때
|
|
524
|
+
- "서버에서 삭제해줘", "킹폴더에서 지워줘" 요청 시
|
|
525
|
+
|
|
526
|
+
**권한:**
|
|
527
|
+
- 오너: xx_*/, yy_All_Docu/, zz_ai_*/ 삭제 가능
|
|
528
|
+
- 협업자: yy_Coworker_{본인}/ 내부만 삭제 가능
|
|
529
|
+
|
|
530
|
+
**주의:** 삭제는 soft-delete로 처리되며, 3일 후 완전 삭제됩니다.`,
|
|
531
|
+
inputSchema: {
|
|
532
|
+
type: 'object',
|
|
533
|
+
properties: {
|
|
534
|
+
localPath: {
|
|
535
|
+
type: 'string',
|
|
536
|
+
description: '로컬 프로젝트 경로 (설정 파일에서 projectId 찾음)',
|
|
537
|
+
},
|
|
538
|
+
path: {
|
|
539
|
+
type: 'string',
|
|
540
|
+
description: '삭제할 파일/폴더 경로 (서버 기준). 예: "yy_All_Docu/old_file.md"',
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
required: ['localPath', 'path'],
|
|
544
|
+
},
|
|
545
|
+
},
|
|
512
546
|
{
|
|
513
547
|
name: 'docuking_validate',
|
|
514
548
|
description: `코드가 정책을 준수하는지 검증합니다 (킹밸리데이트).
|
|
@@ -803,6 +837,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
803
837
|
return await handleTodo(args);
|
|
804
838
|
case 'docuking_urgent':
|
|
805
839
|
return await handleUrgent(args);
|
|
840
|
+
case 'docuking_delete':
|
|
841
|
+
return await handleDelete(args);
|
|
806
842
|
case 'docuking_validate':
|
|
807
843
|
return await handleValidate(args);
|
|
808
844
|
default:
|