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.
Files changed (2) hide show
  1. package/index.js +200 -22
  2. 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/z_Plan_Result/\`에 자동 저장됨
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: '의미 있는 대화 내용을 자동으로 z_Talk/ 폴더에 기록합니다. AI가 중요한 논의/결정이라고 판단하거나, 사용자가 "이거 기록해줘"라고 요청할 때 사용.',
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: '작업 계획 문서를 z_Plan_Result/ 폴더에 생성하거나 업데이트합니다. 작업 시작 시 계획을 작성하고, 진행하면서 결과를 upsert합니다.',
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
- 작업 계획을 \`z_Plan_Result/\` 폴더에 생성/업데이트합니다.
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/MM/YYYY-MM-DD_HHMM__제목.md\`
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/z_Plan_Result/YYYY/MM/YYYY-MM-DD_HHMM__제목__planId.md\`
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, yearMonth, timestamp } = generateDateFileName(title);
2047
- const talkFolderPath = path.join(talkBasePath, yearMonth);
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(talkFolderPath)) {
2052
- fs.mkdirSync(talkFolderPath, { recursive: true });
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
- // z_Plan_Result 폴더 경로 (z_DocuKing 아래)
2097
- const planBasePath = path.join(localPath, 'z_DocuKing', 'z_Plan_Result');
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, yearMonth, timestamp } = generateDateFileName(title);
2124
- const planFolderPath = path.join(planBasePath, yearMonth);
2302
+ const { fileName, timestamp } = generateDateFileName(title);
2125
2303
 
2126
- if (!fs.existsSync(planFolderPath)) {
2127
- fs.mkdirSync(planFolderPath, { recursive: true });
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(planFolderPath, fileNameWithId);
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
- // z_Plan_Result 폴더 경로 (z_DocuKing 아래)
2248
- const planBasePath = path.join(localPath, 'z_DocuKing', 'z_Plan_Result');
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docuking-mcp",
3
- "version": "1.4.1",
3
+ "version": "1.6.0",
4
4
  "description": "DocuKing MCP Server - AI 시대의 문서 협업 플랫폼",
5
5
  "type": "module",
6
6
  "main": "index.js",