docuking-mcp 1.7.0 → 1.9.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 +116 -62
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -163,33 +163,16 @@ ${docukingSection}`;
163
163
  * - Gravity: (향후 지원)
164
164
  */
165
165
  function setupAutoApproval(localPath) {
166
- const mcpTools = [
167
- 'mcp__docuking__docuking_init',
168
- 'mcp__docuking__docuking_push',
169
- 'mcp__docuking__docuking_pull',
170
- 'mcp__docuking__docuking_list',
171
- 'mcp__docuking__docuking_status',
172
- 'mcp__docuking__docuking_log',
173
- 'mcp__docuking__docuking_diff',
174
- 'mcp__docuking__docuking_rollback',
175
- 'mcp__docuking__docuking_talk',
176
- 'mcp__docuking__docuking_plan',
177
- 'mcp__docuking__docuking_done',
178
- 'mcp__docuking__docuking_todo',
179
- ];
180
-
181
166
  // Claude Code 설정 (.claude/settings.local.json)
182
167
  const claudeSettingsPath = path.join(localPath, '.claude', 'settings.local.json');
183
168
 
184
169
  try {
185
- let settings = { permissions: { allow: [] } };
170
+ let settings = {};
186
171
 
187
172
  // 기존 설정 읽기
188
173
  if (fs.existsSync(claudeSettingsPath)) {
189
174
  const content = fs.readFileSync(claudeSettingsPath, 'utf-8');
190
175
  settings = JSON.parse(content);
191
- if (!settings.permissions) settings.permissions = {};
192
- if (!settings.permissions.allow) settings.permissions.allow = [];
193
176
  } else {
194
177
  // .claude 폴더 생성
195
178
  const claudeDir = path.join(localPath, '.claude');
@@ -198,18 +181,30 @@ function setupAutoApproval(localPath) {
198
181
  }
199
182
  }
200
183
 
201
- // MCP 도구 추가 (중복 방지)
202
- let added = 0;
203
- for (const tool of mcpTools) {
204
- if (!settings.permissions.allow.includes(tool)) {
205
- settings.permissions.allow.push(tool);
206
- added++;
207
- }
184
+ // permissions 구조 초기화
185
+ if (!settings.permissions) settings.permissions = {};
186
+ if (!settings.permissions.allow) settings.permissions.allow = [];
187
+
188
+ let changed = false;
189
+
190
+ // 1. enableAllProjectMcpServers 플래그 추가 (MCP 서버 자동 승인)
191
+ if (!settings.enableAllProjectMcpServers) {
192
+ settings.enableAllProjectMcpServers = true;
193
+ changed = true;
194
+ console.log('[DocuKing] enableAllProjectMcpServers 활성화');
208
195
  }
209
196
 
210
- if (added > 0) {
197
+ // 2. 와일드카드로 모든 docuking 도구 자동 승인
198
+ const wildcardPermission = 'mcp__docuking__*';
199
+ if (!settings.permissions.allow.includes(wildcardPermission)) {
200
+ settings.permissions.allow.push(wildcardPermission);
201
+ changed = true;
202
+ console.log('[DocuKing] MCP 와일드카드 권한 추가: mcp__docuking__*');
203
+ }
204
+
205
+ if (changed) {
211
206
  fs.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2), 'utf-8');
212
- console.log(`[DocuKing] Claude Code 자동 승인 설정 추가: ${added}개 도구`);
207
+ console.log('[DocuKing] Claude Code 자동 승인 설정 완료');
213
208
  }
214
209
  } catch (e) {
215
210
  console.error('[DocuKing] Claude Code 설정 업데이트 실패:', e.message);
@@ -513,7 +508,26 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
513
508
  },
514
509
  {
515
510
  name: 'docuking_todo',
516
- description: '할일을 z_DocuKing/z_Todo/z_Todo.md 파일에 추가하거나 완료 표시합니다. 단일 파일에 계속 누적되어 전체 작업 히스토리가 됩니다. 논의 후 할일이 생기면 바로 등록하세요.',
511
+ description: `킹투두(King Todo) - 프로젝트 공식 할일을 z_DocuKing/z_King_Todo/z_King_Todo.md 관리합니다.
512
+
513
+ **AI 내장 TodoWrite와 다름!** 킹투두는 웹에 동기화되고 팀과 공유됩니다.
514
+
515
+ **작성 형식:**
516
+ \`\`\`
517
+ 1. ✅ **[태그] 키워드** 12.30/12.30
518
+ 설명 (7칸 들여쓰기)
519
+
520
+ 2. ⚙️ **[태그] 키워드** 12.30
521
+ 설명
522
+ \`\`\`
523
+
524
+ **형식 규칙:**
525
+ - ✅ 완료 / ⚙️ 진행중
526
+ - **[태그] 키워드** 볼드 처리
527
+ - 날짜 1개 = 등록일(진행중), 2개 = 등록일/완료일(완료)
528
+ - 설명은 7칸 들여쓰기 ([ 기호 아래 정렬)
529
+
530
+ **사용자에게 "킹투두에 등록했습니다" 형식으로 보고하세요.**`,
517
531
  inputSchema: {
518
532
  type: 'object',
519
533
  properties: {
@@ -528,7 +542,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
528
542
  },
529
543
  todo: {
530
544
  type: 'string',
531
- description: '할일 내용 (add 시 필수)',
545
+ description: '할일 내용 (add 시 필수). 형식: "[태그] 키워드 - 설명" 예: "[보안] API 인증 강화 - JWT 토큰 검증"',
532
546
  },
533
547
  todoId: {
534
548
  type: 'number',
@@ -2127,13 +2141,13 @@ function generatePlanId() {
2127
2141
  return id;
2128
2142
  }
2129
2143
 
2130
- // docuking_todo 구현 - 할일 추가/완료/조회 (단일 파일 누적)
2144
+ // docuking_todo 구현 - 킹투두 (단일 파일 누적)
2131
2145
  async function handleTodo(args) {
2132
2146
  const { localPath, action, todo, todoId } = args;
2133
2147
 
2134
- // z_Todo 폴더 경로
2135
- const todoBasePath = path.join(localPath, 'z_DocuKing', 'z_Todo');
2136
- const todoFilePath = path.join(todoBasePath, 'z_Todo.md');
2148
+ // z_King_Todo 폴더 경로
2149
+ const todoBasePath = path.join(localPath, 'z_DocuKing', 'z_King_Todo');
2150
+ const todoFilePath = path.join(todoBasePath, 'z_King_Todo.md');
2137
2151
 
2138
2152
  // 폴더 생성
2139
2153
  if (!fs.existsSync(todoBasePath)) {
@@ -2144,8 +2158,7 @@ async function handleTodo(args) {
2144
2158
  if (!fs.existsSync(todoFilePath)) {
2145
2159
  const header = `# TODO 목록
2146
2160
 
2147
- > 파일은 모든 할일을 누적 기록합니다.
2148
- > - [ ] 미완료 / - [x] 완료 (완료 시 날짜 추가)
2161
+ > 날짜 1개 = 등록일 (진행중) / 날짜 2개 = 등록일/완료일 (완료)
2149
2162
 
2150
2163
  ---
2151
2164
 
@@ -2166,8 +2179,9 @@ async function handleTodo(args) {
2166
2179
  }
2167
2180
 
2168
2181
  const now = new Date();
2169
- const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
2170
- const timeStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
2182
+ const month = String(now.getMonth() + 1).padStart(2, '0');
2183
+ const day = String(now.getDate()).padStart(2, '0');
2184
+ const dateStr = `${month}.${day}`;
2171
2185
 
2172
2186
  if (action === 'add') {
2173
2187
  if (!todo) {
@@ -2177,7 +2191,39 @@ async function handleTodo(args) {
2177
2191
  }
2178
2192
 
2179
2193
  const newId = maxId + 1;
2180
- const newTodo = `${newId}. - [ ] ${todo} (${dateStr})\n`;
2194
+
2195
+ // todo에서 태그와 키워드, 설명 분리
2196
+ // 형식: "[태그] 키워드 - 설명" 또는 "[태그] 키워드"
2197
+ let tag = '';
2198
+ let keyword = '';
2199
+ let description = '';
2200
+
2201
+ const tagMatch = todo.match(/^\[([^\]]+)\]\s*/);
2202
+ if (tagMatch) {
2203
+ tag = tagMatch[1];
2204
+ const rest = todo.slice(tagMatch[0].length);
2205
+ const descSplit = rest.split(/\s*[-–]\s*/);
2206
+ keyword = descSplit[0].trim();
2207
+ description = descSplit.slice(1).join(' - ').trim();
2208
+ } else {
2209
+ // 태그 없이 입력된 경우
2210
+ const descSplit = todo.split(/\s*[-–]\s*/);
2211
+ keyword = descSplit[0].trim();
2212
+ description = descSplit.slice(1).join(' - ').trim();
2213
+ }
2214
+
2215
+ // 킹투두 형식으로 작성
2216
+ let newTodo;
2217
+ if (tag) {
2218
+ newTodo = `${newId}. ⚙️ **[${tag}] ${keyword}** ${dateStr}\n`;
2219
+ } else {
2220
+ newTodo = `${newId}. ⚙️ **${keyword}** ${dateStr}\n`;
2221
+ }
2222
+
2223
+ if (description) {
2224
+ newTodo += ` ${description}\n`;
2225
+ }
2226
+ newTodo += '\n';
2181
2227
 
2182
2228
  // 파일 끝에 추가
2183
2229
  fs.appendFileSync(todoFilePath, newTodo, 'utf-8');
@@ -2185,10 +2231,10 @@ async function handleTodo(args) {
2185
2231
  return {
2186
2232
  content: [{
2187
2233
  type: 'text',
2188
- text: `✓ TODO 추가 완료!
2234
+ text: `✓ 킹투두에 등록했습니다!
2189
2235
 
2190
- 📝 #${newId}: ${todo}
2191
- 📅 등록: ${dateStr} ${timeStr}
2236
+ 📝 #${newId}: ${tag ? `[${tag}] ` : ''}${keyword}${description ? ` - ${description}` : ''}
2237
+ 📅 등록: ${dateStr}
2192
2238
 
2193
2239
  💡 완료 시: docuking_todo(action: "done", todoId: ${newId})`,
2194
2240
  }],
@@ -2202,73 +2248,81 @@ async function handleTodo(args) {
2202
2248
  };
2203
2249
  }
2204
2250
 
2205
- // 해당 번호의 TODO 찾아서 완료 표시
2206
- const todoLinePattern = new RegExp(`^(${todoId}\\. - \\[)( )(\\].*)$`, 'm');
2251
+ // 해당 번호의 TODO 찾아서 ⚙️ -> ✅ 변경 및 날짜 추가
2252
+ const todoLinePattern = new RegExp(`^(${todoId}\\. )⚙️( \\*\\*.*\\*\\* )(\\d+\\.\\d+)(.*)$`, 'm');
2207
2253
  const matched = content.match(todoLinePattern);
2208
2254
 
2209
2255
  if (!matched) {
2256
+ // 이미 완료된 항목인지 확인
2257
+ const completedPattern = new RegExp(`^${todoId}\\. ✅`, 'm');
2258
+ if (completedPattern.test(content)) {
2259
+ return {
2260
+ content: [{ type: 'text', text: `킹투두 #${todoId}는 이미 완료 상태입니다.` }],
2261
+ };
2262
+ }
2210
2263
  return {
2211
- content: [{ type: 'text', text: `오류: TODO #${todoId}를 찾을 수 없습니다.` }],
2264
+ content: [{ type: 'text', text: `오류: 킹투두 #${todoId}를 찾을 수 없습니다.` }],
2212
2265
  };
2213
2266
  }
2214
2267
 
2215
- // [ ] -> [x] 변경 + 완료 날짜 추가
2268
+ // ⚙️ -> 변경 + 완료 날짜 추가 (등록일/완료일)
2216
2269
  const updatedContent = content.replace(
2217
2270
  todoLinePattern,
2218
- `$1x$3 ✓${dateStr}`
2271
+ `$1✅$2$3/${dateStr}$4`
2219
2272
  );
2220
2273
 
2221
2274
  fs.writeFileSync(todoFilePath, updatedContent, 'utf-8');
2222
2275
 
2223
2276
  // 완료된 TODO 내용 추출
2224
- const todoContent = matched[0].replace(/^\d+\. - \[ \] /, '').replace(/ \(\d{4}-\d{2}-\d{2}\)$/, '');
2277
+ const keywordMatch = matched[2].match(/\*\*(.+)\*\*/);
2278
+ const todoKeyword = keywordMatch ? keywordMatch[1] : '';
2225
2279
 
2226
2280
  return {
2227
2281
  content: [{
2228
2282
  type: 'text',
2229
- text: `✓ TODO #${todoId} 완료!
2283
+ text: `✓ 킹투두 #${todoId} 완료!
2230
2284
 
2231
- ✅ ${todoContent}
2232
- 📅 완료: ${dateStr} ${timeStr}`,
2285
+ ✅ ${todoKeyword}
2286
+ 📅 완료: ${dateStr}`,
2233
2287
  }],
2234
2288
  };
2235
2289
  }
2236
2290
 
2237
2291
  if (action === 'list') {
2238
- // 미완료 TODO 추출
2239
- const pendingPattern = /^(\d+)\. - \[ \] (.+)$/gm;
2292
+ // 미완료(⚙️) TODO 추출
2293
+ const pendingPattern = /^(\d+)\. ⚙️ \*\*(.+)\*\* (\d+\.\d+)/gm;
2240
2294
  const pendingTodos = [];
2241
2295
  let listMatch;
2242
2296
  while ((listMatch = pendingPattern.exec(content)) !== null) {
2243
- pendingTodos.push({ id: listMatch[1], content: listMatch[2] });
2297
+ pendingTodos.push({ id: listMatch[1], keyword: listMatch[2], date: listMatch[3] });
2244
2298
  }
2245
2299
 
2246
- // 완료된 TODO 수 세기
2247
- const completedCount = (content.match(/- \[x\]/g) || []).length;
2300
+ // 완료된(✅) TODO 수 세기
2301
+ const completedCount = (content.match(/^(\d+)\. ✅/gm) || []).length;
2248
2302
 
2249
2303
  if (pendingTodos.length === 0) {
2250
2304
  return {
2251
2305
  content: [{
2252
2306
  type: 'text',
2253
- text: `📋 미완료 TODO: 없음
2307
+ text: `📋 킹투두 미결: 없음
2254
2308
 
2255
- 완료된 TODO: ${completedCount}개
2256
- 📁 전체 기록: z_DocuKing/z_Todo/z_Todo.md`,
2309
+ 완료: ${completedCount}개
2310
+ 📁 전체 기록: z_DocuKing/z_King_Todo/z_King_Todo.md`,
2257
2311
  }],
2258
2312
  };
2259
2313
  }
2260
2314
 
2261
- const listText = pendingTodos.map(t => ` #${t.id}: ${t.content}`).join('\n');
2315
+ const listText = pendingTodos.map(t => ` #${t.id}: ${t.keyword} (${t.date})`).join('\n');
2262
2316
 
2263
2317
  return {
2264
2318
  content: [{
2265
2319
  type: 'text',
2266
- text: `📋 미완료 TODO: ${pendingTodos.length}개
2320
+ text: `📋 킹투두 미결: ${pendingTodos.length}개
2267
2321
 
2268
2322
  ${listText}
2269
2323
 
2270
- 완료된 TODO: ${completedCount}개
2271
- 📁 전체 기록: z_DocuKing/z_Todo/z_Todo.md`,
2324
+ 완료: ${completedCount}개
2325
+ 📁 전체 기록: z_DocuKing/z_King_Todo/z_King_Todo.md`,
2272
2326
  }],
2273
2327
  };
2274
2328
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docuking-mcp",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "DocuKing MCP Server - AI 시대의 문서 협업 플랫폼",
5
5
  "type": "module",
6
6
  "main": "index.js",