docuking-mcp 2.8.1 → 2.9.1
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/handlers/docs.js +103 -2
- package/handlers/kingcast.js +5 -5
- package/handlers/sync.js +62 -1
- package/handlers/validate.js +544 -544
- package/index.js +830 -762
- package/lib/init.js +5 -5
- package/package.json +1 -1
package/handlers/docs.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DocuKing MCP - 문서 핸들러 모듈
|
|
3
|
-
* todo, talk, plan, done
|
|
3
|
+
* todo, talk, plan, done, urgent
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
|
|
9
|
-
import { getAiBasePath } from '../lib/config.js';
|
|
9
|
+
import { getAiBasePath, parseCoworkerFromApiKey, getApiKey } from '../lib/config.js';
|
|
10
10
|
import { generateDateFileName, generatePlanId, findPlanFiles } from '../lib/utils.js';
|
|
11
11
|
import { handlePush } from './sync.js';
|
|
12
12
|
|
|
@@ -517,3 +517,104 @@ ${artifacts.length > 0 ? `📦 산출물: ${artifacts.length}개` : ''}${pushMes
|
|
|
517
517
|
],
|
|
518
518
|
};
|
|
519
519
|
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* docuking_urgent 구현 - 긴급 보고 (킹어전트)
|
|
523
|
+
* 협업자가 예외 상황 발견 시 xx_Urgent/에 보고
|
|
524
|
+
*/
|
|
525
|
+
export async function handleUrgent(args) {
|
|
526
|
+
const localPath = args.localPath || process.cwd();
|
|
527
|
+
const { title, situation, policyGap, suggestion, priority = 'medium' } = args;
|
|
528
|
+
|
|
529
|
+
if (!title || !situation) {
|
|
530
|
+
return {
|
|
531
|
+
content: [{
|
|
532
|
+
type: 'text',
|
|
533
|
+
text: '오류: title과 situation 파라미터가 필요합니다.',
|
|
534
|
+
}],
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// 보고자 정보 (협업자면 폴더명, 오너면 'Owner')
|
|
539
|
+
const apiKey = getApiKey(localPath);
|
|
540
|
+
let reporter = 'Owner';
|
|
541
|
+
if (apiKey) {
|
|
542
|
+
const { isCoworker, coworkerFolder } = parseCoworkerFromApiKey(apiKey);
|
|
543
|
+
if (isCoworker && coworkerFolder) {
|
|
544
|
+
reporter = coworkerFolder;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// xx_Urgent 폴더 경로
|
|
549
|
+
const urgentBasePath = path.join(localPath, 'xx_Urgent');
|
|
550
|
+
|
|
551
|
+
// 폴더 생성
|
|
552
|
+
if (!fs.existsSync(urgentBasePath)) {
|
|
553
|
+
fs.mkdirSync(urgentBasePath, { recursive: true });
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// 날짜 기반 파일명 생성
|
|
557
|
+
const { fileName, timestamp } = generateDateFileName(`${reporter}_${title}`, 'U');
|
|
558
|
+
const urgentFilePath = path.join(urgentBasePath, fileName);
|
|
559
|
+
|
|
560
|
+
// 우선순위 아이콘
|
|
561
|
+
const priorityIcons = {
|
|
562
|
+
high: '🔴',
|
|
563
|
+
medium: '🟡',
|
|
564
|
+
low: '🟢',
|
|
565
|
+
};
|
|
566
|
+
const priorityIcon = priorityIcons[priority] || '🟡';
|
|
567
|
+
const priorityText = priority === 'high' ? '긴급 (작업 중단 필요)'
|
|
568
|
+
: priority === 'medium' ? '중간 (임시 처리 후 진행)'
|
|
569
|
+
: '낮음 (참고용)';
|
|
570
|
+
|
|
571
|
+
// 마크다운 문서 생성
|
|
572
|
+
const document = `# ${priorityIcon} ${title}
|
|
573
|
+
|
|
574
|
+
> 보고자: ${reporter}
|
|
575
|
+
> 보고 시간: ${timestamp}
|
|
576
|
+
> 우선순위: ${priorityText}
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
## 발생 상황
|
|
581
|
+
${situation}
|
|
582
|
+
|
|
583
|
+
${policyGap ? `## 정책 공백
|
|
584
|
+
${policyGap}
|
|
585
|
+
|
|
586
|
+
` : ''}${suggestion ? `## 제안 사항
|
|
587
|
+
${suggestion}
|
|
588
|
+
|
|
589
|
+
` : ''}---
|
|
590
|
+
|
|
591
|
+
## 오너 처리 (아래 작성)
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
*이 문서는 협업자 AI가 자동 생성한 긴급 보고입니다.*
|
|
597
|
+
*처리 완료 후 오너가 이 파일을 삭제하면 됩니다.*
|
|
598
|
+
`;
|
|
599
|
+
|
|
600
|
+
// 파일 저장
|
|
601
|
+
fs.writeFileSync(urgentFilePath, document, 'utf-8');
|
|
602
|
+
|
|
603
|
+
const relativePath = path.relative(localPath, urgentFilePath).replace(/\\/g, '/');
|
|
604
|
+
|
|
605
|
+
return {
|
|
606
|
+
content: [{
|
|
607
|
+
type: 'text',
|
|
608
|
+
text: `${priorityIcon} 긴급 보고 작성 완료!
|
|
609
|
+
|
|
610
|
+
📋 제목: ${title}
|
|
611
|
+
👤 보고자: ${reporter}
|
|
612
|
+
📁 경로: ${relativePath}
|
|
613
|
+
🕐 시간: ${timestamp}
|
|
614
|
+
⚡ 우선순위: ${priorityText}
|
|
615
|
+
|
|
616
|
+
💡 Push하면 오너가 Pull 시 자동으로 알림을 받습니다.
|
|
617
|
+
💡 오너가 정책을 업데이트하고 해당 파일을 삭제하면 처리 완료입니다.`,
|
|
618
|
+
}],
|
|
619
|
+
};
|
|
620
|
+
}
|
package/handlers/kingcast.js
CHANGED
|
@@ -329,8 +329,8 @@ git checkout -b coworker/${coworkerFolder}
|
|
|
329
329
|
// 인프라 설정 파일 안내 (복사하지 않으므로 원본 경로 표시)
|
|
330
330
|
if (infraConfigFiles.length > 0) {
|
|
331
331
|
indexContent += '\n## 인프라 설정 (xx_Infra_Config/)\n';
|
|
332
|
-
indexContent += '> 아래 파일들은 복사되지
|
|
333
|
-
indexContent += '> 변경 감지 시
|
|
332
|
+
indexContent += '> 아래 파일들은 복사되지 않음. 원본 경로에서 직접 읽을 것.\n';
|
|
333
|
+
indexContent += '> 변경 감지 시 내용을 읽고 로컬 환경에 맞게 적용 필요.\n\n';
|
|
334
334
|
for (const file of infraConfigFiles) {
|
|
335
335
|
const relativePath = file.key.replace('xx_Infra_Config/', '');
|
|
336
336
|
indexContent += `- xx_Infra_Config/${relativePath}\n`;
|
|
@@ -479,12 +479,12 @@ export async function executeKingcast(localPath) {
|
|
|
479
479
|
...changes.modified.filter(k => k.startsWith('xx_Infra_Config/')),
|
|
480
480
|
];
|
|
481
481
|
if (infraChanges.length > 0) {
|
|
482
|
-
message += `\n\n🔧 인프라 설정 변경
|
|
482
|
+
message += `\n\n🔧 인프라 설정 변경 (${infraChanges.length}개):`;
|
|
483
483
|
for (const file of infraChanges) {
|
|
484
484
|
message += `\n - ${file}`;
|
|
485
485
|
}
|
|
486
|
-
message += `\n\n
|
|
487
|
-
message += `\n
|
|
486
|
+
message += `\n\n → 위 파일을 읽고 로컬 환경에 맞게 적용 필요`;
|
|
487
|
+
message += `\n → 예: .env 변수 추가/변경, 배포 설정 확인`;
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
// 정책 변경 시 경고 메시지 추가
|
package/handlers/sync.js
CHANGED
|
@@ -91,6 +91,12 @@ docuking_init 호출 시 apiKey 파라미터를 포함해주세요.`,
|
|
|
91
91
|
// xx_ 시스템 폴더 목록 (오너 전용)
|
|
92
92
|
const systemFolders = ['xx_Infra_Config', 'xx_Policy'];
|
|
93
93
|
|
|
94
|
+
// xx_Urgent/ 폴더는 오너/협업자 모두 생성 (공용 긴급 보고 폴더)
|
|
95
|
+
const urgentFolderPath = path.join(localPath, 'xx_Urgent');
|
|
96
|
+
if (!fs.existsSync(urgentFolderPath)) {
|
|
97
|
+
fs.mkdirSync(urgentFolderPath, { recursive: true });
|
|
98
|
+
}
|
|
99
|
+
|
|
94
100
|
if (isCoworker) {
|
|
95
101
|
// 협업자: yy_Coworker_{폴더명}/ 폴더를 yy_All_Docu/ 밖에 별도 생성
|
|
96
102
|
coworkerFolderName = `yy_Coworker_${coworkerFolder}`;
|
|
@@ -269,7 +275,7 @@ Git처럼 무엇을 변경했는지 명확히 작성해주세요.
|
|
|
269
275
|
console.error(`[DocuKing] Push 권한: isCoworker=${isCoworker}, coworkerFolder=${coworkerFolder}, coworkerFolderName=${coworkerFolderName}`);
|
|
270
276
|
|
|
271
277
|
if (isCoworker) {
|
|
272
|
-
// 협업자: yy_Coworker_{폴더명}/
|
|
278
|
+
// 협업자: yy_Coworker_{폴더명}/ 폴더 + xx_Urgent/ 폴더 Push
|
|
273
279
|
const coworkerPath = path.join(localPath, coworkerFolderName);
|
|
274
280
|
console.error(`[DocuKing] 협업자 Push 대상 폴더: ${coworkerPath}`);
|
|
275
281
|
if (!fs.existsSync(coworkerPath)) {
|
|
@@ -277,6 +283,13 @@ Git처럼 무엇을 변경했는지 명확히 작성해주세요.
|
|
|
277
283
|
}
|
|
278
284
|
pushTargetFolders.push({ localPath: coworkerPath, serverPrefix: coworkerFolderName });
|
|
279
285
|
|
|
286
|
+
// xx_Urgent/ 폴더도 Push 대상에 추가 (협업자도 긴급 보고 가능)
|
|
287
|
+
const urgentPath = path.join(localPath, 'xx_Urgent');
|
|
288
|
+
if (fs.existsSync(urgentPath)) {
|
|
289
|
+
pushTargetFolders.push({ localPath: urgentPath, serverPrefix: 'xx_Urgent' });
|
|
290
|
+
console.error(`[DocuKing] 협업자 xx_Urgent/ 폴더도 Push 대상에 포함`);
|
|
291
|
+
}
|
|
292
|
+
|
|
280
293
|
// 협업자 폴더 내용 디버그 출력
|
|
281
294
|
try {
|
|
282
295
|
const entries = fs.readdirSync(coworkerPath, { withFileTypes: true });
|
|
@@ -1257,6 +1270,54 @@ export async function handlePullInternal(args) {
|
|
|
1257
1270
|
console.error('[DocuKing] 킹캐스트 실행 실패:', e.message);
|
|
1258
1271
|
}
|
|
1259
1272
|
|
|
1273
|
+
// ========================================
|
|
1274
|
+
// 킹어전트 감지 (xx_Urgent/ 폴더의 긴급 보고 확인)
|
|
1275
|
+
// 오너에게만 알림 (협업자는 자기가 작성한 거니까)
|
|
1276
|
+
// ========================================
|
|
1277
|
+
const { isCoworker } = parseCoworkerFromApiKey(apiKey);
|
|
1278
|
+
if (!isCoworker) {
|
|
1279
|
+
const urgentFolderPath = path.join(localPath, 'xx_Urgent');
|
|
1280
|
+
if (fs.existsSync(urgentFolderPath)) {
|
|
1281
|
+
try {
|
|
1282
|
+
const urgentFiles = fs.readdirSync(urgentFolderPath)
|
|
1283
|
+
.filter(f => f.endsWith('.md') && !f.startsWith('.'));
|
|
1284
|
+
|
|
1285
|
+
if (urgentFiles.length > 0) {
|
|
1286
|
+
resultText += `\n\n🚨 **긴급 보고 ${urgentFiles.length}건 있습니다!**`;
|
|
1287
|
+
resultText += `\n\n협업자들이 정책 예외 상황을 보고했습니다. 확인이 필요합니다:`;
|
|
1288
|
+
|
|
1289
|
+
// 각 보고 파일의 제목과 우선순위 표시
|
|
1290
|
+
for (const file of urgentFiles.slice(0, 5)) {
|
|
1291
|
+
const filePath = path.join(urgentFolderPath, file);
|
|
1292
|
+
try {
|
|
1293
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
1294
|
+
// 제목 추출 (# 으로 시작하는 첫 줄)
|
|
1295
|
+
const titleMatch = content.match(/^# (.+)$/m);
|
|
1296
|
+
const title = titleMatch ? titleMatch[1] : file;
|
|
1297
|
+
// 보고자 추출
|
|
1298
|
+
const reporterMatch = content.match(/> 보고자: (.+)$/m);
|
|
1299
|
+
const reporter = reporterMatch ? reporterMatch[1] : '알 수 없음';
|
|
1300
|
+
|
|
1301
|
+
resultText += `\n - ${title} (by ${reporter})`;
|
|
1302
|
+
} catch (e) {
|
|
1303
|
+
resultText += `\n - ${file}`;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
if (urgentFiles.length > 5) {
|
|
1308
|
+
resultText += `\n ... 외 ${urgentFiles.length - 5}건`;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
resultText += `\n\n📁 경로: xx_Urgent/`;
|
|
1312
|
+
resultText += `\n💡 검토 후 xx_Policy/에 정책 추가 → Push (킹캐스트로 전파)`;
|
|
1313
|
+
resultText += `\n💡 처리 완료 후 해당 보고 파일 삭제 → Push`;
|
|
1314
|
+
}
|
|
1315
|
+
} catch (e) {
|
|
1316
|
+
console.error('[DocuKing] xx_Urgent 폴더 읽기 실패:', e.message);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1260
1321
|
// 내부용 객체 반환 (Push에서 사용)
|
|
1261
1322
|
return {
|
|
1262
1323
|
text: resultText,
|