docuking-mcp 1.4.1 → 1.6.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/index.js +200 -22
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -130,7 +130,7 @@ docuking_done({
|
|
|
130
130
|
### 절대 규칙
|
|
131
131
|
- **작업 시작 전 반드시 \`docuking_plan\` 호출**
|
|
132
132
|
- **작업 완료 후 반드시 \`docuking_done\` 호출**
|
|
133
|
-
- 결과는 \`z_DocuKing/
|
|
133
|
+
- 결과는 \`z_DocuKing/zz_Plan_Result/\`에 자동 저장됨 (플랫 구조)
|
|
134
134
|
`;
|
|
135
135
|
|
|
136
136
|
try {
|
|
@@ -357,7 +357,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
357
357
|
},
|
|
358
358
|
{
|
|
359
359
|
name: 'docuking_talk',
|
|
360
|
-
description: '의미 있는 대화 내용을
|
|
360
|
+
description: '의미 있는 대화 내용을 z_DocuKing/z_Talk/ 폴더에 기록합니다 (플랫 구조). AI가 중요한 논의/결정이라고 판단하거나, 사용자가 "이거 기록해줘"라고 요청할 때 사용.',
|
|
361
361
|
inputSchema: {
|
|
362
362
|
type: 'object',
|
|
363
363
|
properties: {
|
|
@@ -384,7 +384,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
384
384
|
},
|
|
385
385
|
{
|
|
386
386
|
name: 'docuking_plan',
|
|
387
|
-
description: '작업 계획 문서를
|
|
387
|
+
description: '작업 계획 문서를 z_DocuKing/zz_Plan_Result/ 폴더에 생성/업데이트합니다 (플랫 구조). 작업 시작 시 계획을 작성하고, 진행하면서 결과를 upsert합니다.',
|
|
388
388
|
inputSchema: {
|
|
389
389
|
type: 'object',
|
|
390
390
|
properties: {
|
|
@@ -451,6 +451,33 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
451
451
|
required: ['localPath', 'planId', 'summary'],
|
|
452
452
|
},
|
|
453
453
|
},
|
|
454
|
+
{
|
|
455
|
+
name: 'docuking_todo',
|
|
456
|
+
description: '할일을 z_DocuKing/z_Todo/z_Todo.md 파일에 추가하거나 완료 표시합니다. 단일 파일에 계속 누적되어 전체 작업 히스토리가 됩니다. 논의 후 할일이 생기면 바로 등록하세요.',
|
|
457
|
+
inputSchema: {
|
|
458
|
+
type: 'object',
|
|
459
|
+
properties: {
|
|
460
|
+
localPath: {
|
|
461
|
+
type: 'string',
|
|
462
|
+
description: '로컬 프로젝트 경로',
|
|
463
|
+
},
|
|
464
|
+
action: {
|
|
465
|
+
type: 'string',
|
|
466
|
+
enum: ['add', 'done', 'list'],
|
|
467
|
+
description: 'add: 할일 추가, done: 완료 표시, list: 목록 조회',
|
|
468
|
+
},
|
|
469
|
+
todo: {
|
|
470
|
+
type: 'string',
|
|
471
|
+
description: '할일 내용 (add 시 필수)',
|
|
472
|
+
},
|
|
473
|
+
todoId: {
|
|
474
|
+
type: 'number',
|
|
475
|
+
description: '할일 번호 (done 시 필수)',
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
required: ['localPath', 'action'],
|
|
479
|
+
},
|
|
480
|
+
},
|
|
454
481
|
],
|
|
455
482
|
};
|
|
456
483
|
});
|
|
@@ -553,12 +580,12 @@ zz_Coworker_*/
|
|
|
553
580
|
특정 커밋으로 되돌립니다. (웹 탐색기에서 사용 가능)
|
|
554
581
|
|
|
555
582
|
### 9. docuking_talk
|
|
556
|
-
의미 있는 대화 내용을 \`z_Talk/\` 폴더에
|
|
583
|
+
의미 있는 대화 내용을 \`z_DocuKing/z_Talk/\` 폴더에 기록합니다 (플랫 구조).
|
|
557
584
|
- AI가 중요한 논의/결정이라고 판단할 때
|
|
558
585
|
- 사용자가 "이거 기록해줘"라고 요청할 때
|
|
559
586
|
|
|
560
587
|
### 10. docuking_plan
|
|
561
|
-
작업 계획을 \`
|
|
588
|
+
작업 계획을 \`z_DocuKing/zz_Plan_Result/\` 폴더에 생성/업데이트합니다 (플랫 구조).
|
|
562
589
|
- 작업 시작 시 계획 생성
|
|
563
590
|
- 진행하면서 단계별 결과 upsert
|
|
564
591
|
- planId로 기존 계획 찾아서 업데이트
|
|
@@ -766,7 +793,7 @@ AI: (결정이 내려졌으므로 docuking_talk 호출)
|
|
|
766
793
|
})
|
|
767
794
|
\`\`\`
|
|
768
795
|
|
|
769
|
-
**저장 위치:** \`z_DocuKing/z_Talk/YYYY
|
|
796
|
+
**저장 위치:** \`z_DocuKing/z_Talk/YYYY-MM-DD_HHMM__제목.md\` (플랫 구조)
|
|
770
797
|
|
|
771
798
|
### 작업 계획 관리 (docuking_plan, docuking_done)
|
|
772
799
|
|
|
@@ -817,7 +844,7 @@ AI: docuking_done({
|
|
|
817
844
|
})
|
|
818
845
|
\`\`\`
|
|
819
846
|
|
|
820
|
-
**저장 위치:** \`z_DocuKing/
|
|
847
|
+
**저장 위치:** \`z_DocuKing/zz_Plan_Result/YYYY-MM-DD_HHMM__제목__planId.md\` (플랫 구조)
|
|
821
848
|
|
|
822
849
|
**핵심 가치:**
|
|
823
850
|
- AI 세션이 끊겨도 (컴팩션, 세션 종료) 다음 AI가 계획 문서를 보고 이어서 작업 가능
|
|
@@ -958,6 +985,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
958
985
|
return await handlePlan(args);
|
|
959
986
|
case 'docuking_done':
|
|
960
987
|
return await handleDone(args);
|
|
988
|
+
case 'docuking_todo':
|
|
989
|
+
return await handleTodo(args);
|
|
961
990
|
default:
|
|
962
991
|
throw new Error(`Unknown tool: ${name}`);
|
|
963
992
|
}
|
|
@@ -2035,21 +2064,171 @@ function generatePlanId() {
|
|
|
2035
2064
|
return id;
|
|
2036
2065
|
}
|
|
2037
2066
|
|
|
2067
|
+
// docuking_todo 구현 - 할일 추가/완료/조회 (단일 파일 누적)
|
|
2068
|
+
async function handleTodo(args) {
|
|
2069
|
+
const { localPath, action, todo, todoId } = args;
|
|
2070
|
+
|
|
2071
|
+
// z_Todo 폴더 경로
|
|
2072
|
+
const todoBasePath = path.join(localPath, 'z_DocuKing', 'z_Todo');
|
|
2073
|
+
const todoFilePath = path.join(todoBasePath, 'z_Todo.md');
|
|
2074
|
+
|
|
2075
|
+
// 폴더 생성
|
|
2076
|
+
if (!fs.existsSync(todoBasePath)) {
|
|
2077
|
+
fs.mkdirSync(todoBasePath, { recursive: true });
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
// 파일이 없으면 헤더와 함께 생성
|
|
2081
|
+
if (!fs.existsSync(todoFilePath)) {
|
|
2082
|
+
const header = `# TODO 목록
|
|
2083
|
+
|
|
2084
|
+
> 이 파일은 모든 할일을 누적 기록합니다.
|
|
2085
|
+
> - [ ] 미완료 / - [x] 완료 (완료 시 날짜 추가)
|
|
2086
|
+
|
|
2087
|
+
---
|
|
2088
|
+
|
|
2089
|
+
`;
|
|
2090
|
+
fs.writeFileSync(todoFilePath, header, 'utf-8');
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
// 파일 읽기
|
|
2094
|
+
let content = fs.readFileSync(todoFilePath, 'utf-8');
|
|
2095
|
+
|
|
2096
|
+
// 현재 TODO 번호 찾기 (가장 큰 번호)
|
|
2097
|
+
const todoPattern = /^(\d+)\. /gm;
|
|
2098
|
+
let maxId = 0;
|
|
2099
|
+
let match;
|
|
2100
|
+
while ((match = todoPattern.exec(content)) !== null) {
|
|
2101
|
+
const id = parseInt(match[1], 10);
|
|
2102
|
+
if (id > maxId) maxId = id;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
const now = new Date();
|
|
2106
|
+
const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
|
2107
|
+
const timeStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
|
|
2108
|
+
|
|
2109
|
+
if (action === 'add') {
|
|
2110
|
+
if (!todo) {
|
|
2111
|
+
return {
|
|
2112
|
+
content: [{ type: 'text', text: '오류: todo 파라미터가 필요합니다.' }],
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
const newId = maxId + 1;
|
|
2117
|
+
const newTodo = `${newId}. - [ ] ${todo} (${dateStr})\n`;
|
|
2118
|
+
|
|
2119
|
+
// 파일 끝에 추가
|
|
2120
|
+
fs.appendFileSync(todoFilePath, newTodo, 'utf-8');
|
|
2121
|
+
|
|
2122
|
+
return {
|
|
2123
|
+
content: [{
|
|
2124
|
+
type: 'text',
|
|
2125
|
+
text: `✓ TODO 추가 완료!
|
|
2126
|
+
|
|
2127
|
+
📝 #${newId}: ${todo}
|
|
2128
|
+
📅 등록: ${dateStr} ${timeStr}
|
|
2129
|
+
|
|
2130
|
+
💡 완료 시: docuking_todo(action: "done", todoId: ${newId})`,
|
|
2131
|
+
}],
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
if (action === 'done') {
|
|
2136
|
+
if (!todoId) {
|
|
2137
|
+
return {
|
|
2138
|
+
content: [{ type: 'text', text: '오류: todoId 파라미터가 필요합니다.' }],
|
|
2139
|
+
};
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
// 해당 번호의 TODO 찾아서 완료 표시
|
|
2143
|
+
const todoLinePattern = new RegExp(`^(${todoId}\\. - \\[)( )(\\].*)$`, 'm');
|
|
2144
|
+
const matched = content.match(todoLinePattern);
|
|
2145
|
+
|
|
2146
|
+
if (!matched) {
|
|
2147
|
+
return {
|
|
2148
|
+
content: [{ type: 'text', text: `오류: TODO #${todoId}를 찾을 수 없습니다.` }],
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
// [ ] -> [x] 변경 + 완료 날짜 추가
|
|
2153
|
+
const updatedContent = content.replace(
|
|
2154
|
+
todoLinePattern,
|
|
2155
|
+
`$1x$3 ✓${dateStr}`
|
|
2156
|
+
);
|
|
2157
|
+
|
|
2158
|
+
fs.writeFileSync(todoFilePath, updatedContent, 'utf-8');
|
|
2159
|
+
|
|
2160
|
+
// 완료된 TODO 내용 추출
|
|
2161
|
+
const todoContent = matched[0].replace(/^\d+\. - \[ \] /, '').replace(/ \(\d{4}-\d{2}-\d{2}\)$/, '');
|
|
2162
|
+
|
|
2163
|
+
return {
|
|
2164
|
+
content: [{
|
|
2165
|
+
type: 'text',
|
|
2166
|
+
text: `✓ TODO #${todoId} 완료!
|
|
2167
|
+
|
|
2168
|
+
✅ ${todoContent}
|
|
2169
|
+
📅 완료: ${dateStr} ${timeStr}`,
|
|
2170
|
+
}],
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
if (action === 'list') {
|
|
2175
|
+
// 미완료 TODO만 추출
|
|
2176
|
+
const pendingPattern = /^(\d+)\. - \[ \] (.+)$/gm;
|
|
2177
|
+
const pendingTodos = [];
|
|
2178
|
+
let listMatch;
|
|
2179
|
+
while ((listMatch = pendingPattern.exec(content)) !== null) {
|
|
2180
|
+
pendingTodos.push({ id: listMatch[1], content: listMatch[2] });
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
// 완료된 TODO 수 세기
|
|
2184
|
+
const completedCount = (content.match(/- \[x\]/g) || []).length;
|
|
2185
|
+
|
|
2186
|
+
if (pendingTodos.length === 0) {
|
|
2187
|
+
return {
|
|
2188
|
+
content: [{
|
|
2189
|
+
type: 'text',
|
|
2190
|
+
text: `📋 미완료 TODO: 없음
|
|
2191
|
+
|
|
2192
|
+
✅ 완료된 TODO: ${completedCount}개
|
|
2193
|
+
📁 전체 기록: z_DocuKing/z_Todo/z_Todo.md`,
|
|
2194
|
+
}],
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
const listText = pendingTodos.map(t => ` #${t.id}: ${t.content}`).join('\n');
|
|
2199
|
+
|
|
2200
|
+
return {
|
|
2201
|
+
content: [{
|
|
2202
|
+
type: 'text',
|
|
2203
|
+
text: `📋 미완료 TODO: ${pendingTodos.length}개
|
|
2204
|
+
|
|
2205
|
+
${listText}
|
|
2206
|
+
|
|
2207
|
+
✅ 완료된 TODO: ${completedCount}개
|
|
2208
|
+
📁 전체 기록: z_DocuKing/z_Todo/z_Todo.md`,
|
|
2209
|
+
}],
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
return {
|
|
2214
|
+
content: [{ type: 'text', text: '오류: action은 add, done, list 중 하나여야 합니다.' }],
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2038
2218
|
// docuking_talk 구현 - 대화록 자동 저장
|
|
2039
2219
|
async function handleTalk(args) {
|
|
2040
2220
|
const { localPath, title, content, tags = [] } = args;
|
|
2041
2221
|
|
|
2042
|
-
// z_Talk 폴더 경로 (z_DocuKing 아래)
|
|
2222
|
+
// z_Talk 폴더 경로 (z_DocuKing 아래) - 플랫 구조
|
|
2043
2223
|
const talkBasePath = path.join(localPath, 'z_DocuKing', 'z_Talk');
|
|
2044
2224
|
|
|
2045
2225
|
// 날짜 기반 파일명 생성
|
|
2046
|
-
const { fileName,
|
|
2047
|
-
const
|
|
2048
|
-
const talkFilePath = path.join(talkFolderPath, fileName);
|
|
2226
|
+
const { fileName, timestamp } = generateDateFileName(title);
|
|
2227
|
+
const talkFilePath = path.join(talkBasePath, fileName);
|
|
2049
2228
|
|
|
2050
2229
|
// 폴더 생성
|
|
2051
|
-
if (!fs.existsSync(
|
|
2052
|
-
fs.mkdirSync(
|
|
2230
|
+
if (!fs.existsSync(talkBasePath)) {
|
|
2231
|
+
fs.mkdirSync(talkBasePath, { recursive: true });
|
|
2053
2232
|
}
|
|
2054
2233
|
|
|
2055
2234
|
// 태그 문자열
|
|
@@ -2093,8 +2272,8 @@ ${content}
|
|
|
2093
2272
|
async function handlePlan(args) {
|
|
2094
2273
|
const { localPath, planId, title, goal, steps = [], notes } = args;
|
|
2095
2274
|
|
|
2096
|
-
//
|
|
2097
|
-
const planBasePath = path.join(localPath, 'z_DocuKing', '
|
|
2275
|
+
// zz_Plan_Result 폴더 경로 (z_DocuKing 아래) - 플랫 구조
|
|
2276
|
+
const planBasePath = path.join(localPath, 'z_DocuKing', 'zz_Plan_Result');
|
|
2098
2277
|
|
|
2099
2278
|
// 기존 계획 업데이트 또는 새 계획 생성
|
|
2100
2279
|
let targetPlanId = planId;
|
|
@@ -2120,16 +2299,15 @@ async function handlePlan(args) {
|
|
|
2120
2299
|
} else {
|
|
2121
2300
|
// 새 계획 생성
|
|
2122
2301
|
targetPlanId = generatePlanId();
|
|
2123
|
-
const { fileName,
|
|
2124
|
-
const planFolderPath = path.join(planBasePath, yearMonth);
|
|
2302
|
+
const { fileName, timestamp } = generateDateFileName(title);
|
|
2125
2303
|
|
|
2126
|
-
if (!fs.existsSync(
|
|
2127
|
-
fs.mkdirSync(
|
|
2304
|
+
if (!fs.existsSync(planBasePath)) {
|
|
2305
|
+
fs.mkdirSync(planBasePath, { recursive: true });
|
|
2128
2306
|
}
|
|
2129
2307
|
|
|
2130
2308
|
// 파일명에 planId 포함
|
|
2131
2309
|
const fileNameWithId = fileName.replace('.md', `__${targetPlanId}.md`);
|
|
2132
|
-
planFilePath = path.join(
|
|
2310
|
+
planFilePath = path.join(planBasePath, fileNameWithId);
|
|
2133
2311
|
}
|
|
2134
2312
|
|
|
2135
2313
|
// 현재 시간
|
|
@@ -2244,8 +2422,8 @@ function findPlanFiles(basePath, planId) {
|
|
|
2244
2422
|
async function handleDone(args) {
|
|
2245
2423
|
const { localPath, planId, summary, artifacts = [] } = args;
|
|
2246
2424
|
|
|
2247
|
-
//
|
|
2248
|
-
const planBasePath = path.join(localPath, 'z_DocuKing', '
|
|
2425
|
+
// zz_Plan_Result 폴더 경로 (z_DocuKing 아래) - 플랫 구조
|
|
2426
|
+
const planBasePath = path.join(localPath, 'z_DocuKing', 'zz_Plan_Result');
|
|
2249
2427
|
|
|
2250
2428
|
// 계획 파일 찾기
|
|
2251
2429
|
const planFiles = findPlanFiles(planBasePath, planId);
|