docuking-mcp 2.13.0 → 3.0.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 +49 -109
- 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
|
|
|
@@ -806,82 +803,23 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
806
803
|
}
|
|
807
804
|
|
|
808
805
|
// ========================================
|
|
809
|
-
// 4.
|
|
806
|
+
// 4. 자동 삭제 비활성화 (2026-01-17 버그 수정)
|
|
807
|
+
// ========================================
|
|
808
|
+
// 이전 로직: "서버에 있고 로컬에 없으면 삭제"
|
|
809
|
+
// 문제: 다른 프로젝트에서 Pull하면 서버 파일이 삭제됨
|
|
810
|
+
// 해결: 자동 삭제 제거, docuking_delete로만 명시적 삭제
|
|
811
|
+
//
|
|
812
|
+
// 삭제는 명시적으로만:
|
|
813
|
+
// - 사용자가 "이 파일 삭제해줘" 요청 시
|
|
814
|
+
// - docuking_delete MCP 도구 사용
|
|
810
815
|
// ========================================
|
|
811
|
-
// 단, 협업자 폴더(yy_Coworker_*)는 삭제하지 않음 (오너가 협업자 파일을 삭제하면 안됨)
|
|
812
|
-
// source: 'web' 파일은 MCP에서 삭제 시도하지 않음 (클라이언트에서 필터링)
|
|
813
|
-
// 중요: localPathsBeforePull 사용 (Pull 전에 수집한 목록)
|
|
814
|
-
// processedLocalPaths는 Pull로 내려받은 파일도 포함하므로 사용 금지
|
|
815
816
|
let deleted = 0;
|
|
816
817
|
const deletedFilePaths = [];
|
|
817
|
-
let protectedFiles = [];
|
|
818
|
-
const webProtectedPaths = []; // MCP에서 미리 필터링한 웹 파일
|
|
819
|
-
|
|
820
|
-
if (serverAllPaths.length > 0 && !isCoworker) {
|
|
821
|
-
// 오너만 삭제 수행
|
|
822
|
-
// yy_Coworker_*로 시작하는 경로는 삭제 대상에서 제외
|
|
823
|
-
// localPathsBeforePull: Pull 전에 수집한 로컬 파일 목록 (좀비 파일 방지)
|
|
824
|
-
//
|
|
825
|
-
// ★ 핵심: source: 'web' 파일은 삭제 대상에서 미리 제외!
|
|
826
|
-
// 이렇게 하면 웹에서 생성한 파일이 절대 삭제되지 않음
|
|
827
|
-
const pathsToDelete = serverAllPaths.filter(p => {
|
|
828
|
-
// 1. 로컬에 있으면 제외 (삭제 대상 아님)
|
|
829
|
-
if (localPathsBeforePull.has(p)) return false;
|
|
830
|
-
|
|
831
|
-
// 2. 협업자 폴더는 제외
|
|
832
|
-
if (p.startsWith('yy_Coworker_')) return false;
|
|
833
|
-
|
|
834
|
-
// 3. source: 'web' 파일은 제외 (웹에서 생성된 파일 보호)
|
|
835
|
-
const meta = serverPathToMeta[p];
|
|
836
|
-
if (meta && meta.source === 'web') {
|
|
837
|
-
webProtectedPaths.push(p);
|
|
838
|
-
return false;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
return true;
|
|
842
|
-
});
|
|
843
|
-
|
|
844
|
-
// 웹 보호 파일 로그
|
|
845
|
-
if (webProtectedPaths.length > 0) {
|
|
846
|
-
console.error(`[DocuKing] source:web 파일 ${webProtectedPaths.length}개 삭제에서 제외:`, webProtectedPaths.slice(0, 5));
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
console.error(`[DocuKing] soft-delete 대상: ${pathsToDelete.length}개 (서버에만 있고 Pull 전 로컬에 없던 파일, source:local만)`);
|
|
850
|
-
|
|
851
|
-
if (pathsToDelete.length > 0) {
|
|
852
|
-
try {
|
|
853
|
-
// soft-delete API 호출 (deleted_at 설정, 3일 후 hard delete)
|
|
854
|
-
const deleteResponse = await fetch(`${API_ENDPOINT}/files/soft-delete`, {
|
|
855
|
-
method: 'POST',
|
|
856
|
-
headers: {
|
|
857
|
-
'Content-Type': 'application/json',
|
|
858
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
859
|
-
},
|
|
860
|
-
body: JSON.stringify({
|
|
861
|
-
projectId,
|
|
862
|
-
paths: pathsToDelete,
|
|
863
|
-
}),
|
|
864
|
-
});
|
|
865
|
-
|
|
866
|
-
if (deleteResponse.ok) {
|
|
867
|
-
const deleteResult = await deleteResponse.json();
|
|
868
|
-
deleted = deleteResult.deleted || 0;
|
|
869
|
-
deletedFilePaths.push(...(deleteResult.deletedPaths || []));
|
|
870
|
-
protectedFiles = deleteResult.protected || [];
|
|
818
|
+
let protectedFiles = [];
|
|
871
819
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
} catch (e) {
|
|
878
|
-
console.error('[DocuKing] 파일 soft-delete 실패:', e.message);
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
// MCP에서 미리 제외한 웹 파일도 보호 목록에 추가 (결과 표시용)
|
|
883
|
-
protectedFiles = [...protectedFiles, ...webProtectedPaths];
|
|
884
|
-
}
|
|
820
|
+
// 자동 삭제 로직 비활성화 - 아래 코드는 주석 처리
|
|
821
|
+
// "로컬에 없으면 삭제" 로직은 위험하므로 제거
|
|
822
|
+
console.error(`[DocuKing] 자동 삭제 비활성화됨 (명시적 삭제만 허용: docuking_delete 사용)`);
|
|
885
823
|
|
|
886
824
|
// 5. 빈 폴더 생성
|
|
887
825
|
let createdEmptyFolders = 0;
|
|
@@ -1072,10 +1010,10 @@ export async function handlePullInternal(args) {
|
|
|
1072
1010
|
const commonFolders = ['xx_Urgent'];
|
|
1073
1011
|
|
|
1074
1012
|
// 오너 전용 폴더
|
|
1075
|
-
const ownerFolders = ['xx_Infra_Config', 'xx_Policy', '
|
|
1013
|
+
const ownerFolders = ['xx_Infra_Config', 'xx_Policy', 'xy_TalkTodoPlan'];
|
|
1076
1014
|
|
|
1077
1015
|
// 협업자 전용 폴더 (yy_Coworker_{폴더명}/ 안에)
|
|
1078
|
-
const coworkerSubFolders = ['
|
|
1016
|
+
const coworkerSubFolders = ['zz_TalkTodoPlan', '_Private'];
|
|
1079
1017
|
|
|
1080
1018
|
// 공통 폴더 체크
|
|
1081
1019
|
for (const folder of commonFolders) {
|
|
@@ -1300,11 +1238,24 @@ export async function handlePullInternal(args) {
|
|
|
1300
1238
|
current++;
|
|
1301
1239
|
continue;
|
|
1302
1240
|
}
|
|
1241
|
+
|
|
1242
|
+
// ========================================
|
|
1243
|
+
// [버그 수정] 로컬 파일 우선 정책
|
|
1244
|
+
// 해시가 다르면 → 로컬 수정이 있다는 뜻
|
|
1245
|
+
// 서버 파일로 덮어쓰지 않고 로컬 유지
|
|
1246
|
+
// 다음 Push에서 로컬 → 서버로 업로드됨
|
|
1247
|
+
// ========================================
|
|
1248
|
+
console.error(`[DocuKing] 로컬 우선: ${file.path} (해시 다름 → 로컬 유지, Push 시 업로드됨)`);
|
|
1249
|
+
results.push({ type: 'skip', path: file.path, reason: 'local-modified' });
|
|
1250
|
+
skipped++;
|
|
1251
|
+
current++;
|
|
1252
|
+
continue;
|
|
1303
1253
|
} catch (e) {
|
|
1304
1254
|
// 해시 계산 실패 시 다운로드 대상
|
|
1305
1255
|
}
|
|
1306
1256
|
}
|
|
1307
1257
|
|
|
1258
|
+
// 로컬에 파일이 없는 경우만 다운로드
|
|
1308
1259
|
filesToDownload.push({ ...file, fullPath });
|
|
1309
1260
|
}
|
|
1310
1261
|
|
|
@@ -1437,29 +1388,18 @@ export async function handlePullInternal(args) {
|
|
|
1437
1388
|
}
|
|
1438
1389
|
|
|
1439
1390
|
// ========================================
|
|
1440
|
-
// mark-as-pulled
|
|
1441
|
-
//
|
|
1391
|
+
// mark-as-pulled 비활성화 (2026-01-17 정책 변경)
|
|
1392
|
+
// ========================================
|
|
1393
|
+
// 이전 로직: source를 'web' → 'local'로 변경
|
|
1394
|
+
// 문제: source는 "출생신고"로, 한 번 정해지면 불변이어야 함
|
|
1395
|
+
// 변경하면 "출처 위조"가 됨
|
|
1396
|
+
//
|
|
1397
|
+
// 새 정책: source는 절대 변경하지 않음
|
|
1398
|
+
// - web에서 태어난 파일은 Pull해도 source: 'web' 유지
|
|
1399
|
+
// - local에서 태어난 파일은 Push해도 source: 'local' 유지
|
|
1442
1400
|
// ========================================
|
|
1443
1401
|
if (filesToMarkAsPulled.length > 0) {
|
|
1444
|
-
|
|
1445
|
-
const markResponse = await fetch(`${API_ENDPOINT}/files/mark-as-pulled`, {
|
|
1446
|
-
method: 'POST',
|
|
1447
|
-
headers: {
|
|
1448
|
-
'Content-Type': 'application/json',
|
|
1449
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
1450
|
-
},
|
|
1451
|
-
body: JSON.stringify({
|
|
1452
|
-
projectId,
|
|
1453
|
-
paths: filesToMarkAsPulled,
|
|
1454
|
-
}),
|
|
1455
|
-
});
|
|
1456
|
-
|
|
1457
|
-
if (markResponse.ok) {
|
|
1458
|
-
console.error(`[DocuKing] mark-as-pulled 완료: ${filesToMarkAsPulled.length}개`);
|
|
1459
|
-
}
|
|
1460
|
-
} catch (e) {
|
|
1461
|
-
console.error('[DocuKing] mark-as-pulled 실패:', e.message);
|
|
1462
|
-
}
|
|
1402
|
+
console.error(`[DocuKing] mark-as-pulled 비활성화됨 (source 불변 정책): ${filesToMarkAsPulled.length}개 파일의 source 유지`);
|
|
1463
1403
|
}
|
|
1464
1404
|
|
|
1465
1405
|
// ========================================
|
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 = [];
|