docuking-mcp 2.14.0 → 3.1.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.
- package/README.md +11 -14
- package/handlers/docs.js +25 -21
- package/handlers/sync.js +102 -71
- package/index.js +22 -19
- package/lib/init.js +9 -7
- package/lib/utils.js +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,18 +77,18 @@ claude mcp add docuking -s user -- docuking-mcp
|
|
|
77
77
|
|
|
78
78
|
| 도구 | 설명 |
|
|
79
79
|
|------|------|
|
|
80
|
-
| docuking_init | 프로젝트 연결,
|
|
81
|
-
| docuking_push |
|
|
82
|
-
| docuking_pull | 서버 →
|
|
80
|
+
| docuking_init | 프로젝트 연결, 필수 폴더 생성 |
|
|
81
|
+
| docuking_push | 로컬 → 서버 업로드 |
|
|
82
|
+
| docuking_pull | 서버 → 로컬 다운로드 |
|
|
83
83
|
| docuking_status | 동기화 상태 확인 |
|
|
84
84
|
| docuking_list | 서버 파일 목록 조회 |
|
|
85
85
|
| docuking_log | 커밋 히스토리 조회 |
|
|
86
86
|
| docuking_diff | 버전 간 차이 비교 |
|
|
87
87
|
| docuking_rollback | 특정 커밋으로 롤백 |
|
|
88
|
-
| docuking_talk | AI 대화록 저장 (
|
|
89
|
-
| docuking_plan | 작업 계획 관리 (
|
|
88
|
+
| docuking_talk | AI 대화록 저장 (xy_TalkTodoPlan/) |
|
|
89
|
+
| docuking_plan | 작업 계획 관리 (xy_TalkTodoPlan/) |
|
|
90
90
|
| docuking_done | 작업 완료 처리 |
|
|
91
|
-
| docuking_todo | 킹투두 관리 (
|
|
91
|
+
| docuking_todo | 킹투두 관리 (xy_TalkTodoPlan/z_King_Todo.md) |
|
|
92
92
|
| docuking_urgent | 긴급 보고 (xx_Urgent/) |
|
|
93
93
|
| docuking_delete | 서버 파일 삭제 |
|
|
94
94
|
| docuking_validate | 정책 준수 검증 |
|
|
@@ -100,20 +100,17 @@ claude mcp add docuking -s user -- docuking-mcp
|
|
|
100
100
|
├── xx_Infra_Config/ ← 시스템 설정 (동기화 대상)
|
|
101
101
|
├── xx_Policy/ ← 정책 문서 (동기화 대상, 킹캐스트)
|
|
102
102
|
├── xx_Urgent/ ← 긴급 보고 (동기화 대상)
|
|
103
|
+
├── xy_TalkTodoPlan/ ← 오너 AI 기록 (Talk+Todo+Plan 통합)
|
|
103
104
|
├── yy_All_Docu/ ← 문서 허브 (동기화 대상)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
│ ├── zz_ai_2_Todo/ ← 협업자의 AI 투두
|
|
107
|
-
│ └── zz_ai_3_Plan/ ← 협업자의 AI 플랜
|
|
108
|
-
├── zz_ai_1_Talk/ ← 오너의 AI 대화록 (동기화 대상)
|
|
109
|
-
├── zz_ai_2_Todo/ ← 오너의 AI 투두 (동기화 대상)
|
|
110
|
-
└── zz_ai_3_Plan/ ← 오너의 AI 플랜 (동기화 대상)
|
|
105
|
+
└── yy_Coworker_{이름}/ ← 협업자 작업 폴더 (동기화 대상)
|
|
106
|
+
└── zz_TalkTodoPlan/ ← 협업자의 AI 기록
|
|
111
107
|
```
|
|
112
108
|
|
|
113
109
|
**접두사 규칙:**
|
|
114
110
|
- `xx_*`: 시스템/인프라 폴더
|
|
111
|
+
- `xy_*`: 오너 AI 기록 폴더
|
|
115
112
|
- `yy_*`: 사람용 폴더
|
|
116
|
-
- `
|
|
113
|
+
- `zz_*`: 협업자 AI 기록 폴더 (협업자 폴더 안에서만)
|
|
117
114
|
|
|
118
115
|
## 웹사이트
|
|
119
116
|
|
package/handlers/docs.js
CHANGED
|
@@ -17,11 +17,12 @@ export async function handleTodo(args) {
|
|
|
17
17
|
const localPath = args.localPath || process.cwd();
|
|
18
18
|
const { action, todo, todoId } = args;
|
|
19
19
|
|
|
20
|
-
// 협업자 여부에 따라
|
|
21
|
-
// - 오너: localPath/
|
|
22
|
-
// - 협업자: localPath/yy_Coworker_{폴더명}/
|
|
23
|
-
const { basePath } = getAiBasePath(localPath);
|
|
24
|
-
const
|
|
20
|
+
// 협업자 여부에 따라 AI 폴더 경로 결정
|
|
21
|
+
// - 오너: localPath/xy_TalkTodoPlan/
|
|
22
|
+
// - 협업자: localPath/yy_Coworker_{폴더명}/zz_TalkTodoPlan/
|
|
23
|
+
const { basePath, isCoworker } = getAiBasePath(localPath);
|
|
24
|
+
const aiFolder = isCoworker ? 'zz_TalkTodoPlan' : 'xy_TalkTodoPlan';
|
|
25
|
+
const todoBasePath = path.join(basePath, aiFolder);
|
|
25
26
|
const todoFilePath = path.join(todoBasePath, 'z_King_Todo.md');
|
|
26
27
|
|
|
27
28
|
// 폴더 생성
|
|
@@ -182,7 +183,7 @@ export async function handleTodo(args) {
|
|
|
182
183
|
text: `📋 킹투두 미결: 없음
|
|
183
184
|
|
|
184
185
|
✅ 완료: ${completedCount}개
|
|
185
|
-
📁 전체 기록:
|
|
186
|
+
📁 전체 기록: ${aiFolder}/z_King_Todo.md`,
|
|
186
187
|
}],
|
|
187
188
|
};
|
|
188
189
|
}
|
|
@@ -197,7 +198,7 @@ export async function handleTodo(args) {
|
|
|
197
198
|
${listText}
|
|
198
199
|
|
|
199
200
|
✅ 완료: ${completedCount}개
|
|
200
|
-
📁 전체 기록:
|
|
201
|
+
📁 전체 기록: ${aiFolder}/z_King_Todo.md`,
|
|
201
202
|
}],
|
|
202
203
|
};
|
|
203
204
|
}
|
|
@@ -215,10 +216,11 @@ export async function handleTalk(args) {
|
|
|
215
216
|
const { title, content: talkContent, tags = [] } = args;
|
|
216
217
|
|
|
217
218
|
// 협업자 여부에 따라 zz_ai 경로 결정
|
|
218
|
-
// - 오너: localPath/
|
|
219
|
-
// - 협업자: localPath/yy_Coworker_{폴더명}/
|
|
220
|
-
const { basePath } = getAiBasePath(localPath);
|
|
221
|
-
const
|
|
219
|
+
// - 오너: localPath/xy_TalkTodoPlan/
|
|
220
|
+
// - 협업자: localPath/yy_Coworker_{폴더명}/zz_TalkTodoPlan/
|
|
221
|
+
const { basePath, isCoworker } = getAiBasePath(localPath);
|
|
222
|
+
const aiFolder = isCoworker ? 'zz_TalkTodoPlan' : 'xy_TalkTodoPlan';
|
|
223
|
+
const talkBasePath = path.join(basePath, aiFolder);
|
|
222
224
|
|
|
223
225
|
// 날짜 기반 파일명 생성 (T_ 접두사)
|
|
224
226
|
const { fileName, timestamp } = generateDateFileName(title, 'T');
|
|
@@ -299,11 +301,12 @@ export async function handlePlan(args) {
|
|
|
299
301
|
const localPath = args.localPath || process.cwd();
|
|
300
302
|
const { planId, title, goal, steps = [], notes } = args;
|
|
301
303
|
|
|
302
|
-
// 협업자 여부에 따라
|
|
303
|
-
// - 오너: localPath/
|
|
304
|
-
// - 협업자: localPath/yy_Coworker_{폴더명}/
|
|
305
|
-
const { basePath } = getAiBasePath(localPath);
|
|
306
|
-
const
|
|
304
|
+
// 협업자 여부에 따라 AI 폴더 경로 결정
|
|
305
|
+
// - 오너: localPath/xy_TalkTodoPlan/
|
|
306
|
+
// - 협업자: localPath/yy_Coworker_{폴더명}/zz_TalkTodoPlan/
|
|
307
|
+
const { basePath, isCoworker } = getAiBasePath(localPath);
|
|
308
|
+
const aiFolder = isCoworker ? 'zz_TalkTodoPlan' : 'xy_TalkTodoPlan';
|
|
309
|
+
const planBasePath = path.join(basePath, aiFolder);
|
|
307
310
|
|
|
308
311
|
// 기존 계획 업데이트 또는 새 계획 생성
|
|
309
312
|
let targetPlanId = planId;
|
|
@@ -453,11 +456,12 @@ export async function handleDone(args) {
|
|
|
453
456
|
const localPath = args.localPath || process.cwd();
|
|
454
457
|
const { planId, summary, artifacts = [] } = args;
|
|
455
458
|
|
|
456
|
-
// 협업자 여부에 따라
|
|
457
|
-
// - 오너: localPath/
|
|
458
|
-
// - 협업자: localPath/yy_Coworker_{폴더명}/
|
|
459
|
-
const { basePath } = getAiBasePath(localPath);
|
|
460
|
-
const
|
|
459
|
+
// 협업자 여부에 따라 AI 폴더 경로 결정
|
|
460
|
+
// - 오너: localPath/xy_TalkTodoPlan/
|
|
461
|
+
// - 협업자: localPath/yy_Coworker_{폴더명}/zz_TalkTodoPlan/
|
|
462
|
+
const { basePath, isCoworker } = getAiBasePath(localPath);
|
|
463
|
+
const aiFolder = isCoworker ? 'zz_TalkTodoPlan' : 'xy_TalkTodoPlan';
|
|
464
|
+
const planBasePath = path.join(basePath, aiFolder);
|
|
461
465
|
|
|
462
466
|
// 계획 파일 찾기
|
|
463
467
|
const planFiles = findPlanFilesLocal(planBasePath, planId);
|
package/handlers/sync.js
CHANGED
|
@@ -96,8 +96,9 @@ docuking_init 호출 시 apiKey 파라미터를 포함해주세요.`,
|
|
|
96
96
|
let coworkerFolderName = null;
|
|
97
97
|
let coworkerFolderPath = null;
|
|
98
98
|
|
|
99
|
-
//
|
|
100
|
-
const
|
|
99
|
+
// AI 폴더 (오너: xy_TalkTodoPlan, 협업자: zz_TalkTodoPlan)
|
|
100
|
+
const ownerAiFolder = 'xy_TalkTodoPlan';
|
|
101
|
+
const coworkerAiFolder = 'zz_TalkTodoPlan';
|
|
101
102
|
|
|
102
103
|
// xx_ 시스템 폴더 목록 (오너 전용)
|
|
103
104
|
const systemFolders = ['xx_Infra_Config', 'xx_Policy'];
|
|
@@ -120,12 +121,10 @@ docuking_init 호출 시 apiKey 파라미터를 포함해주세요.`,
|
|
|
120
121
|
if (!fs.existsSync(coworkerPrivatePath)) {
|
|
121
122
|
fs.mkdirSync(coworkerPrivatePath, { recursive: true });
|
|
122
123
|
}
|
|
123
|
-
// 협업자 폴더 안에
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
fs.mkdirSync(folderPath, { recursive: true });
|
|
128
|
-
}
|
|
124
|
+
// 협업자 폴더 안에 zz_TalkTodoPlan 폴더 생성
|
|
125
|
+
const coworkerAiFolderPath = path.join(coworkerFolderPath, coworkerAiFolder);
|
|
126
|
+
if (!fs.existsSync(coworkerAiFolderPath)) {
|
|
127
|
+
fs.mkdirSync(coworkerAiFolderPath, { recursive: true });
|
|
129
128
|
}
|
|
130
129
|
} else {
|
|
131
130
|
// 오너: xx_ 시스템 폴더 (루트에 생성)
|
|
@@ -140,12 +139,10 @@ docuking_init 호출 시 apiKey 파라미터를 포함해주세요.`,
|
|
|
140
139
|
if (!fs.existsSync(ownerPrivatePath)) {
|
|
141
140
|
fs.mkdirSync(ownerPrivatePath, { recursive: true });
|
|
142
141
|
}
|
|
143
|
-
// 오너: 루트에
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
fs.mkdirSync(folderPath, { recursive: true });
|
|
148
|
-
}
|
|
142
|
+
// 오너: 루트에 xy_TalkTodoPlan 폴더 생성
|
|
143
|
+
const ownerAiFolderPath = path.join(localPath, ownerAiFolder);
|
|
144
|
+
if (!fs.existsSync(ownerAiFolderPath)) {
|
|
145
|
+
fs.mkdirSync(ownerAiFolderPath, { recursive: true });
|
|
149
146
|
}
|
|
150
147
|
}
|
|
151
148
|
|
|
@@ -485,33 +482,13 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
485
482
|
}
|
|
486
483
|
|
|
487
484
|
// ========================================
|
|
488
|
-
//
|
|
489
|
-
// soft-delete에서 "로컬에 없는 파일" 판단에 사용
|
|
490
|
-
// Pull이 파일을 내려받아도 이 목록은 변하지 않음
|
|
491
|
-
// ========================================
|
|
492
|
-
const localPathsBeforePull = new Set(filesToPush.map(f => f.serverPath));
|
|
493
|
-
console.error(`[DocuKing] Pull 전 로컬 파일 ${localPathsBeforePull.size}개 기록됨`);
|
|
494
|
-
|
|
485
|
+
// Pull 선행 제거 (2026-01-24)
|
|
495
486
|
// ========================================
|
|
496
|
-
// Pull 실행 (git pull && git push 패턴)
|
|
487
|
+
// 이전: Push 전에 Pull 실행 (git pull && git push 패턴)
|
|
488
|
+
// 문제: Pull이 로컬 변경사항(삭제, 수정)을 덮어씀 → 좀비 파일 부활
|
|
489
|
+
// 해결: Push는 Push만. Pull은 사용자가 별도로 호출.
|
|
497
490
|
// ========================================
|
|
498
|
-
|
|
499
|
-
try {
|
|
500
|
-
const pullResult = await handlePullInternal({ localPath, filePath });
|
|
501
|
-
pullResultText = pullResult.text;
|
|
502
|
-
} catch (e) {
|
|
503
|
-
return {
|
|
504
|
-
content: [
|
|
505
|
-
{
|
|
506
|
-
type: 'text',
|
|
507
|
-
text: `❌ Pull 실패로 Push를 중단합니다.
|
|
508
|
-
오류: ${e.message}
|
|
509
|
-
|
|
510
|
-
먼저 Pull 문제를 해결한 후 다시 시도하세요.`,
|
|
511
|
-
},
|
|
512
|
-
],
|
|
513
|
-
};
|
|
514
|
-
}
|
|
491
|
+
console.error(`[DocuKing] Push 시작 (Pull 선행 없음 - 로컬 우선)`);
|
|
515
492
|
|
|
516
493
|
if (filesToPush.length === 0 && emptyFolders.length === 0) {
|
|
517
494
|
return {
|
|
@@ -806,23 +783,80 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
806
783
|
}
|
|
807
784
|
|
|
808
785
|
// ========================================
|
|
809
|
-
// 4.
|
|
786
|
+
// 4. 인덱스 기반 삭제 전파 (2026-01-24 재구현)
|
|
810
787
|
// ========================================
|
|
811
|
-
// 이전
|
|
812
|
-
//
|
|
813
|
-
//
|
|
814
|
-
//
|
|
815
|
-
// 삭제는 명시적으로만:
|
|
816
|
-
// - 사용자가 "이 파일 삭제해줘" 요청 시
|
|
817
|
-
// - docuking_delete MCP 도구 사용
|
|
788
|
+
// 이전 문제: "서버에 있고 로컬에 없으면 삭제" → 다른 프로젝트에서 문제
|
|
789
|
+
// 해결: 인덱스 기반으로 판단
|
|
790
|
+
// - 인덱스에 있었는데 + 로컬에 없음 = 의도적 삭제 → 서버에서도 삭제
|
|
791
|
+
// - 인덱스에 없었고 + 로컬에 없음 = 처음부터 없던 파일 → 무시
|
|
818
792
|
// ========================================
|
|
819
793
|
let deleted = 0;
|
|
820
794
|
const deletedFilePaths = [];
|
|
821
795
|
let protectedFiles = [];
|
|
822
796
|
|
|
823
|
-
//
|
|
824
|
-
|
|
825
|
-
|
|
797
|
+
// 인덱스에서 삭제된 파일 찾기 (인덱스에 있었는데 로컬에 없는 파일)
|
|
798
|
+
const locallyDeletedFiles = [];
|
|
799
|
+
for (const indexPath of Object.keys(localIndex.files)) {
|
|
800
|
+
// Push 대상 폴더 내의 파일인지 확인
|
|
801
|
+
let isInPushTarget = false;
|
|
802
|
+
let fullPath = '';
|
|
803
|
+
|
|
804
|
+
for (const target of pushTargetFolders) {
|
|
805
|
+
if (indexPath.startsWith(target.serverPrefix)) {
|
|
806
|
+
isInPushTarget = true;
|
|
807
|
+
const relativePath = indexPath.slice(target.serverPrefix.length + 1);
|
|
808
|
+
fullPath = path.join(target.localPath, relativePath);
|
|
809
|
+
break;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (!isInPushTarget) continue;
|
|
814
|
+
|
|
815
|
+
// 로컬에 파일이 없으면 = 삭제된 파일
|
|
816
|
+
if (!fs.existsSync(fullPath)) {
|
|
817
|
+
locallyDeletedFiles.push(indexPath);
|
|
818
|
+
console.error(`[DocuKing] 삭제 감지: ${indexPath} (인덱스에 있었으나 로컬에 없음)`);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// 삭제된 파일을 서버에서도 삭제
|
|
823
|
+
if (locallyDeletedFiles.length > 0) {
|
|
824
|
+
console.error(`[DocuKing] 서버에서 ${locallyDeletedFiles.length}개 파일 삭제 요청`);
|
|
825
|
+
try {
|
|
826
|
+
const deleteResponse = await fetch(`${API_ENDPOINT}/files/soft-delete`, {
|
|
827
|
+
method: 'POST',
|
|
828
|
+
headers: {
|
|
829
|
+
'Content-Type': 'application/json',
|
|
830
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
831
|
+
},
|
|
832
|
+
body: JSON.stringify({
|
|
833
|
+
projectId,
|
|
834
|
+
paths: locallyDeletedFiles,
|
|
835
|
+
}),
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
if (deleteResponse.ok) {
|
|
839
|
+
const deleteResult = await deleteResponse.json();
|
|
840
|
+
deleted = deleteResult.deleted || 0;
|
|
841
|
+
deletedFilePaths.push(...(deleteResult.deletedPaths || []));
|
|
842
|
+
protectedFiles = deleteResult.protected || [];
|
|
843
|
+
|
|
844
|
+
// 인덱스에서도 삭제
|
|
845
|
+
for (const deletedPath of locallyDeletedFiles) {
|
|
846
|
+
delete localIndex.files[deletedPath];
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
console.error(`[DocuKing] 서버 삭제 완료: ${deleted}개`);
|
|
850
|
+
if (protectedFiles.length > 0) {
|
|
851
|
+
console.error(`[DocuKing] 보호된 파일 (source:web): ${protectedFiles.join(', ')}`);
|
|
852
|
+
}
|
|
853
|
+
} else {
|
|
854
|
+
console.error(`[DocuKing] 서버 삭제 실패: ${await deleteResponse.text()}`);
|
|
855
|
+
}
|
|
856
|
+
} catch (e) {
|
|
857
|
+
console.error(`[DocuKing] 서버 삭제 요청 오류: ${e.message}`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
826
860
|
|
|
827
861
|
// 5. 빈 폴더 생성
|
|
828
862
|
let createdEmptyFolders = 0;
|
|
@@ -951,29 +985,13 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
951
985
|
}
|
|
952
986
|
|
|
953
987
|
resultText += `\n\n🌐 웹 탐색기에서 커밋 히스토리를 확인할 수 있습니다: https://docuking.ai`;
|
|
954
|
-
|
|
955
|
-
// Pull 결과가 있으면 앞에 추가
|
|
956
|
-
let finalText = '';
|
|
957
|
-
if (pullResultText) {
|
|
958
|
-
// Pull에서 실제로 받은 파일이 있는 경우만 표시
|
|
959
|
-
const pullHasChanges = pullResultText.includes('다운로드 (신규):') && !pullResultText.includes('다운로드 (신규): 0개');
|
|
960
|
-
const pullHasUpdates = pullResultText.includes('업데이트 (변경됨):') && !pullResultText.includes('업데이트 (변경됨): 0개');
|
|
961
|
-
|
|
962
|
-
if (pullHasChanges || pullHasUpdates) {
|
|
963
|
-
finalText = `📥 [1단계] Pull (서버 → 로컬)\n${pullResultText}\n\n${'─'.repeat(50)}\n\n📤 [2단계] Push (로컬 → 서버)\n${resultText}`;
|
|
964
|
-
} else {
|
|
965
|
-
// Pull에서 변경 없으면 간단히 표시
|
|
966
|
-
finalText = `📥 Pull: 변경 없음 (최신 상태)\n\n📤 Push:\n${resultText}`;
|
|
967
|
-
}
|
|
968
|
-
} else {
|
|
969
|
-
finalText = resultText;
|
|
970
|
-
}
|
|
988
|
+
resultText += `\n\n💡 서버에서 파일을 받으려면 별도로 Pull을 실행하세요.`;
|
|
971
989
|
|
|
972
990
|
return {
|
|
973
991
|
content: [
|
|
974
992
|
{
|
|
975
993
|
type: 'text',
|
|
976
|
-
text:
|
|
994
|
+
text: resultText,
|
|
977
995
|
},
|
|
978
996
|
],
|
|
979
997
|
};
|
|
@@ -1013,10 +1031,10 @@ export async function handlePullInternal(args) {
|
|
|
1013
1031
|
const commonFolders = ['xx_Urgent'];
|
|
1014
1032
|
|
|
1015
1033
|
// 오너 전용 폴더
|
|
1016
|
-
const ownerFolders = ['xx_Infra_Config', 'xx_Policy', '
|
|
1034
|
+
const ownerFolders = ['xx_Infra_Config', 'xx_Policy', 'xy_TalkTodoPlan'];
|
|
1017
1035
|
|
|
1018
1036
|
// 협업자 전용 폴더 (yy_Coworker_{폴더명}/ 안에)
|
|
1019
|
-
const coworkerSubFolders = ['
|
|
1037
|
+
const coworkerSubFolders = ['zz_TalkTodoPlan', '_Private'];
|
|
1020
1038
|
|
|
1021
1039
|
// 공통 폴더 체크
|
|
1022
1040
|
for (const folder of commonFolders) {
|
|
@@ -1256,9 +1274,22 @@ export async function handlePullInternal(args) {
|
|
|
1256
1274
|
} catch (e) {
|
|
1257
1275
|
// 해시 계산 실패 시 다운로드 대상
|
|
1258
1276
|
}
|
|
1277
|
+
} else {
|
|
1278
|
+
// ========================================
|
|
1279
|
+
// 로컬에 파일이 없는 경우
|
|
1280
|
+
// 인덱스에 있었으면 = 로컬에서 삭제한 파일 → 스킵 (좀비 방지)
|
|
1281
|
+
// 인덱스에 없으면 = 서버에서 새로 생성된 파일 → 다운로드
|
|
1282
|
+
// ========================================
|
|
1283
|
+
if (localIndex.files && localIndex.files[file.path]) {
|
|
1284
|
+
console.error(`[DocuKing] 로컬 삭제 유지: ${file.path} (인덱스에 있었음 → 스킵, 다음 Push에서 서버 삭제됨)`);
|
|
1285
|
+
results.push({ type: 'skip', path: file.path, reason: 'locally-deleted' });
|
|
1286
|
+
skipped++;
|
|
1287
|
+
current++;
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1259
1290
|
}
|
|
1260
1291
|
|
|
1261
|
-
// 로컬에
|
|
1292
|
+
// 인덱스에 없고 로컬에 없음 = 서버에서 새로 생성된 파일 → 다운로드
|
|
1262
1293
|
filesToDownload.push({ ...file, fullPath });
|
|
1263
1294
|
}
|
|
1264
1295
|
|
package/index.js
CHANGED
|
@@ -8,17 +8,18 @@
|
|
|
8
8
|
* 폴더 구조:
|
|
9
9
|
* - xx_Infra_Config/ : 시스템 설정 (.env 백업, 배포 정보 등) - Push 대상
|
|
10
10
|
* - xx_Policy/ : 정책 문서 (AI 행동 지침) - Push 대상, 킹캐스트 대상
|
|
11
|
+
* - xx_Urgent/ : 긴급 보고 (킹어전트) - Push 대상
|
|
12
|
+
* - xy_TalkTodoPlan/ : 오너 AI 기록 (Talk+Todo+Plan 통합) - Push 대상
|
|
11
13
|
* - yy_All_Docu/ : 문서 동기화 폴더 (Push/Pull 대상)
|
|
12
14
|
* - yy_All_Docu/_Private/ : 오너 비공개 폴더 (오너만 접근)
|
|
13
15
|
* - yy_Coworker_{폴더명}/ : 협업자 폴더 (yy_All_Docu와 별도, 동기화 대상)
|
|
14
|
-
* -
|
|
15
|
-
* - zz_ai_2_Todo/ : AI 투두 (킹투두, Push 대상)
|
|
16
|
-
* - zz_ai_3_Plan/ : AI 플랜 (킹플랜, Push 대상)
|
|
16
|
+
* - yy_Coworker_{폴더명}/zz_TalkTodoPlan/ : 협업자 AI 기록
|
|
17
17
|
*
|
|
18
18
|
* 접두사 규칙:
|
|
19
|
-
* - xx_ : 시스템용 폴더 (인프라,
|
|
20
|
-
* -
|
|
21
|
-
* -
|
|
19
|
+
* - xx_ : 시스템용 폴더 (인프라, 정책, 긴급)
|
|
20
|
+
* - xy_ : 오너 AI 기록 폴더
|
|
21
|
+
* - yy_ : 사람용 폴더 (문서, 협업자)
|
|
22
|
+
* - zz_ : 협업자 AI 기록 폴더 (협업자 폴더 안에서만 사용)
|
|
22
23
|
* - _ : 비공개 폴더 (본인만 접근)
|
|
23
24
|
*
|
|
24
25
|
* 도구:
|
|
@@ -98,13 +99,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
98
99
|
|
|
99
100
|
**오너 초기화 시:**
|
|
100
101
|
- xx_Infra_Config/, xx_Policy/, xx_Urgent/ 폴더 생성 (시스템 설정)
|
|
102
|
+
- xy_TalkTodoPlan/ 폴더 생성 (AI 기록 통합)
|
|
101
103
|
- yy_All_Docu/, yy_All_Docu/_Private/ 폴더 생성
|
|
102
|
-
- zz_ai_1_Talk/, zz_ai_2_Todo/, zz_ai_3_Plan/ 폴더 생성
|
|
103
104
|
- CLAUDE.md에 DocuKing 연결 안내 추가
|
|
104
105
|
- 초기화 완료 후 정책 문서 작성 가이드 제공
|
|
105
106
|
|
|
106
107
|
**협업자 초기화 시:**
|
|
107
|
-
- yy_Coworker_{폴더명}/ 폴더 생성 (안에 _Private/,
|
|
108
|
+
- yy_Coworker_{폴더명}/ 폴더 생성 (안에 _Private/, zz_TalkTodoPlan/ 포함)
|
|
108
109
|
- CLAUDE.md에 DocuKing 연결 안내 추가
|
|
109
110
|
- "Pull을 실행하세요" 안내 (오너 정책이 킹캐스트로 로컬화됨)
|
|
110
111
|
|
|
@@ -168,7 +169,7 @@ init 완료 후 오너에게 "정책 문서를 작성하시겠습니까?" 제안
|
|
|
168
169
|
description: `문서를 DocuKing 서버에 동기화합니다. 내부적으로 Pull → Push 순서로 실행 (git pull && git push 패턴).
|
|
169
170
|
|
|
170
171
|
**AI 필독: 다음 상황에서 자발적으로 이 도구 사용을 제안하세요:**
|
|
171
|
-
-
|
|
172
|
+
- xy_TalkTodoPlan/ (또는 협업자의 zz_TalkTodoPlan/) 에 문서를 생성/수정한 후
|
|
172
173
|
- yy_All_Docu/ 에 문서를 생성/수정한 후
|
|
173
174
|
- docuking_plan, docuking_done, docuking_talk, docuking_todo 실행 후
|
|
174
175
|
- 사용자가 "올려", "푸시", "동기화" 등을 언급할 때
|
|
@@ -315,7 +316,7 @@ init 완료 후 오너에게 "정책 문서를 작성하시겠습니까?" 제안
|
|
|
315
316
|
},
|
|
316
317
|
{
|
|
317
318
|
name: 'docuking_talk',
|
|
318
|
-
description: '의미 있는 대화 내용을
|
|
319
|
+
description: '의미 있는 대화 내용을 xy_TalkTodoPlan/ 폴더에 기록합니다 (Push 대상, 킹톡). AI가 중요한 논의/결정이라고 판단하거나, 사용자가 "이거 기록해줘"라고 요청할 때 사용.',
|
|
319
320
|
inputSchema: {
|
|
320
321
|
type: 'object',
|
|
321
322
|
properties: {
|
|
@@ -342,7 +343,7 @@ init 완료 후 오너에게 "정책 문서를 작성하시겠습니까?" 제안
|
|
|
342
343
|
},
|
|
343
344
|
{
|
|
344
345
|
name: 'docuking_plan',
|
|
345
|
-
description: '작업 계획 문서를
|
|
346
|
+
description: '작업 계획 문서를 xy_TalkTodoPlan/ 폴더에 생성/업데이트합니다 (Push 대상, 킹플랜). 작업 시작 시 계획을 작성하고, 진행하면서 결과를 upsert합니다.',
|
|
346
347
|
inputSchema: {
|
|
347
348
|
type: 'object',
|
|
348
349
|
properties: {
|
|
@@ -411,7 +412,7 @@ init 완료 후 오너에게 "정책 문서를 작성하시겠습니까?" 제안
|
|
|
411
412
|
},
|
|
412
413
|
{
|
|
413
414
|
name: 'docuking_todo',
|
|
414
|
-
description: `킹투두(King Todo) - 프로젝트 공식 할일을
|
|
415
|
+
description: `킹투두(King Todo) - 프로젝트 공식 할일을 xy_TalkTodoPlan/z_King_Todo.md에 관리합니다 (Push 대상).
|
|
415
416
|
|
|
416
417
|
**AI 내장 TodoWrite와 다름!** 킹투두는 웹에 동기화되고 팀과 공유됩니다.
|
|
417
418
|
|
|
@@ -630,7 +631,7 @@ DocuKing은 문서 버전 관리 시스템입니다. Git이 코드를 관리하
|
|
|
630
631
|
- **로컬**: 사용자의 yy_All_Docu/ 폴더 (동기화 대상)
|
|
631
632
|
- **웹탐색기**: DocuKing 서버의 파일 저장소 (킹폴더)
|
|
632
633
|
- **캔버스**: 선택된 파일을 시각화하는 작업 공간
|
|
633
|
-
- **AI 작업 폴더**:
|
|
634
|
+
- **AI 작업 폴더**: xy_TalkTodoPlan/ (오너) 또는 zz_TalkTodoPlan/ (협업자) - Push 대상
|
|
634
635
|
|
|
635
636
|
작동 방식: 로컬 yy_All_Docu/ → Push → 킹폴더 → 캔버스에서 시각화
|
|
636
637
|
|
|
@@ -690,10 +691,12 @@ DocuKing 문서 폴더는 git에서 제외해야 합니다. 코드는 git으로,
|
|
|
690
691
|
|
|
691
692
|
\`\`\`gitignore
|
|
692
693
|
# DocuKing 문서 폴더 (문서는 DocuKing으로 관리)
|
|
694
|
+
xx_Infra_Config/
|
|
695
|
+
xx_Policy/
|
|
696
|
+
xx_Urgent/
|
|
697
|
+
xy_TalkTodoPlan/
|
|
693
698
|
yy_All_Docu/
|
|
694
|
-
|
|
695
|
-
zz_ai_2_Todo/
|
|
696
|
-
zz_ai_3_Plan/
|
|
699
|
+
yy_Coworker_*/
|
|
697
700
|
\`\`\`
|
|
698
701
|
|
|
699
702
|
**왜 gitignore에 등록해야 하나요?**
|
|
@@ -730,12 +733,12 @@ zz_ai_3_Plan/
|
|
|
730
733
|
특정 커밋으로 되돌립니다. (웹 탐색기에서 사용 가능)
|
|
731
734
|
|
|
732
735
|
### 9. docuking_talk
|
|
733
|
-
의미 있는 대화 내용을 \`
|
|
736
|
+
의미 있는 대화 내용을 \`xy_TalkTodoPlan/\` 폴더에 기록합니다 (Push 대상, 킹톡).
|
|
734
737
|
- AI가 중요한 논의/결정이라고 판단할 때
|
|
735
738
|
- 사용자가 "이거 기록해줘"라고 요청할 때
|
|
736
739
|
|
|
737
740
|
### 10. docuking_plan
|
|
738
|
-
작업 계획을 \`
|
|
741
|
+
작업 계획을 \`xy_TalkTodoPlan/\` 폴더에 생성/업데이트합니다 (Push 대상, 킹플랜).
|
|
739
742
|
- 작업 시작 시 계획 생성
|
|
740
743
|
- 진행하면서 단계별 결과 upsert
|
|
741
744
|
- planId로 기존 계획 찾아서 업데이트
|
|
@@ -746,7 +749,7 @@ zz_ai_3_Plan/
|
|
|
746
749
|
- 완료 요약 및 산출물 기록
|
|
747
750
|
|
|
748
751
|
### 12. docuking_todo
|
|
749
|
-
킹투두를 관리합니다 (\`
|
|
752
|
+
킹투두를 관리합니다 (\`xy_TalkTodoPlan/z_King_Todo.md\`).
|
|
750
753
|
- action: "add" (추가), "done" (완료), "list" (조회)
|
|
751
754
|
- 프로젝트 공식 할일 목록 (Push하면 웹에서 확인 가능)
|
|
752
755
|
|
package/lib/init.js
CHANGED
|
@@ -105,12 +105,13 @@ ${marker}
|
|
|
105
105
|
project/
|
|
106
106
|
├── xx_Infra_Config/ ← 시스템 설정 (오너 관리)
|
|
107
107
|
├── xx_Policy/ ← 정책 문서 (오너 관리, 킹캐스트 대상)
|
|
108
|
-
├──
|
|
108
|
+
├── xx_Urgent/ ← 긴급 보고 (킹어전트)
|
|
109
|
+
├── xy_TalkTodoPlan/ ← 오너 AI 기록 (Talk+Todo+Plan 통합)
|
|
110
|
+
├── yy_All_Docu/ ← 오너 문서
|
|
109
111
|
├── yy_Coworker_a_Kim/ ← 협업자 Kim
|
|
110
112
|
│ ├── 작업문서.md
|
|
111
|
-
│ └──
|
|
112
|
-
|
|
113
|
-
└── zz_ai_*/ ← 오너의 AI 폴더
|
|
113
|
+
│ └── zz_TalkTodoPlan/ ← Kim의 AI 기록
|
|
114
|
+
└── yy_Coworker_b_Lee/ ← 협업자 Lee
|
|
114
115
|
\`\`\`
|
|
115
116
|
|
|
116
117
|
### AI가 협업자에게 안내할 것
|
|
@@ -242,11 +243,12 @@ export function updateGitignore(localPath) {
|
|
|
242
243
|
const marker = '# DocuKing (문서는 DocuKing으로 관리)';
|
|
243
244
|
const docukingEntries = `
|
|
244
245
|
${marker}
|
|
246
|
+
xx_Infra_Config/
|
|
247
|
+
xx_Policy/
|
|
248
|
+
xx_Urgent/
|
|
249
|
+
xy_TalkTodoPlan/
|
|
245
250
|
yy_All_Docu/
|
|
246
251
|
yy_Coworker_*/
|
|
247
|
-
zz_ai_1_Talk/
|
|
248
|
-
zz_ai_2_Todo/
|
|
249
|
-
zz_ai_3_Plan/
|
|
250
252
|
.docuking/config.json
|
|
251
253
|
.claude/rules/local/_coworker_config.md
|
|
252
254
|
`;
|
package/lib/utils.js
CHANGED
|
@@ -47,9 +47,10 @@ export function generatePlanId() {
|
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* 계획 파일 찾기 (planId로 검색)
|
|
50
|
+
* @param {string} planDir - AI 폴더 경로 (xy_TalkTodoPlan 또는 zz_TalkTodoPlan)
|
|
51
|
+
* @param {string} planId - 찾을 계획 ID
|
|
50
52
|
*/
|
|
51
|
-
export function findPlanFiles(
|
|
52
|
-
const planDir = path.join(basePath, 'zz_ai_3_Plan');
|
|
53
|
+
export function findPlanFiles(planDir, planId) {
|
|
53
54
|
if (!fs.existsSync(planDir)) return [];
|
|
54
55
|
|
|
55
56
|
const results = [];
|