docuking-mcp 2.7.0 → 2.8.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/kingcast.js +59 -25
- package/handlers/sync.js +36 -30
- package/index.js +16 -11
- package/lib/init.js +14 -10
- package/package.json +1 -1
package/handlers/kingcast.js
CHANGED
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
getAiBasePath,
|
|
13
13
|
} from '../lib/config.js';
|
|
14
14
|
|
|
15
|
-
// 킹캐스트 대상 폴더
|
|
16
|
-
const KINGCAST_FOLDERS = ['
|
|
15
|
+
// 킹캐스트 대상 폴더 (xx_ 접두사로 루트 레벨에 위치)
|
|
16
|
+
const KINGCAST_FOLDERS = ['xx_Infra_Config', 'xx_Policy'];
|
|
17
17
|
|
|
18
18
|
// 킹캐스트 해시 저장 파일
|
|
19
19
|
const KINGCAST_HASH_FILE = '.docuking/kingcast_hashes.json';
|
|
@@ -32,14 +32,13 @@ function calculateFileHash(filePath) {
|
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* 킹캐스트 대상 파일 수집
|
|
35
|
-
*
|
|
35
|
+
* xx_Infra_Config/, xx_Policy/ (루트 레벨) 내의 모든 파일
|
|
36
36
|
*/
|
|
37
37
|
function collectKingcastFiles(localPath) {
|
|
38
|
-
const allDocuPath = path.join(localPath, 'yy_All_Docu');
|
|
39
38
|
const files = {};
|
|
40
39
|
|
|
41
40
|
for (const folder of KINGCAST_FOLDERS) {
|
|
42
|
-
const folderPath = path.join(
|
|
41
|
+
const folderPath = path.join(localPath, folder);
|
|
43
42
|
if (!fs.existsSync(folderPath)) continue;
|
|
44
43
|
|
|
45
44
|
const collectRecursive = (dir, relativePath = '') => {
|
|
@@ -171,25 +170,25 @@ function updateLocalRules(localPath, currentFiles, changes, config) {
|
|
|
171
170
|
const coworkerFolder = config?.coworkerFolder || '';
|
|
172
171
|
const projectName = config?.projectName || 'Unknown Project';
|
|
173
172
|
|
|
174
|
-
//
|
|
173
|
+
// xx_Policy 파일들 분류 (번호순 정렬)
|
|
175
174
|
const policyFiles = Object.entries(currentFiles)
|
|
176
|
-
.filter(([key]) => key.startsWith('
|
|
175
|
+
.filter(([key]) => key.startsWith('xx_Policy/'))
|
|
177
176
|
.map(([key, file]) => ({ key, ...file }))
|
|
178
177
|
.sort((a, b) => a.key.localeCompare(b.key));
|
|
179
178
|
|
|
180
|
-
//
|
|
179
|
+
// xx_Infra_Config 파일들 분류
|
|
181
180
|
const infraConfigFiles = Object.entries(currentFiles)
|
|
182
|
-
.filter(([key]) => key.startsWith('
|
|
181
|
+
.filter(([key]) => key.startsWith('xx_Infra_Config/'))
|
|
183
182
|
.map(([key, file]) => ({ key, ...file }));
|
|
184
183
|
|
|
185
184
|
const createdFiles = [];
|
|
186
185
|
|
|
187
186
|
// ========================================
|
|
188
|
-
// 1.
|
|
187
|
+
// 1. xx_Policy 파일들을 서브파일 구조로 복사
|
|
189
188
|
// ========================================
|
|
190
189
|
for (const file of policyFiles) {
|
|
191
|
-
//
|
|
192
|
-
const relativePath = file.key.replace('
|
|
190
|
+
// xx_Policy/00_project_overview.md → 00_project_overview.md
|
|
191
|
+
const relativePath = file.key.replace('xx_Policy/', '');
|
|
193
192
|
const targetPath = path.join(localRulesPath, relativePath);
|
|
194
193
|
|
|
195
194
|
// 디렉토리 생성
|
|
@@ -206,6 +205,12 @@ function updateLocalRules(localPath, currentFiles, changes, config) {
|
|
|
206
205
|
}
|
|
207
206
|
}
|
|
208
207
|
|
|
208
|
+
// ========================================
|
|
209
|
+
// 1.5. xx_Infra_Config는 복사하지 않음
|
|
210
|
+
// 이미 Pull 후 xx_Infra_Config/에 존재함
|
|
211
|
+
// AI가 변경 감지 시 직접 읽고 로컬 환경에 맞게 적용
|
|
212
|
+
// ========================================
|
|
213
|
+
|
|
209
214
|
// ========================================
|
|
210
215
|
// 2. 협업자용 설정 파일 생성 (_coworker_config.md)
|
|
211
216
|
// ========================================
|
|
@@ -271,13 +276,13 @@ git checkout -b coworker/${coworkerFolder}
|
|
|
271
276
|
## 환경 정보 (읽기 전용 참고)
|
|
272
277
|
`;
|
|
273
278
|
|
|
274
|
-
//
|
|
279
|
+
// xx_Infra_Config에서 민감하지 않은 정보 추가
|
|
275
280
|
let infraInfo = '';
|
|
276
281
|
for (const file of infraConfigFiles) {
|
|
277
282
|
const fileName = path.basename(file.key);
|
|
278
283
|
// 배포 관련 정보만 참조 (민감 정보 제외)
|
|
279
284
|
if (fileName.includes('deploy') || fileName.includes('README')) {
|
|
280
|
-
infraInfo += `\n참고 문서:
|
|
285
|
+
infraInfo += `\n참고 문서: ${file.key}`;
|
|
281
286
|
}
|
|
282
287
|
}
|
|
283
288
|
|
|
@@ -316,11 +321,22 @@ git checkout -b coworker/${coworkerFolder}
|
|
|
316
321
|
indexContent += '\n(정책 문서가 없습니다)\n';
|
|
317
322
|
} else {
|
|
318
323
|
for (const file of policyFiles) {
|
|
319
|
-
const relativePath = file.key.replace('
|
|
324
|
+
const relativePath = file.key.replace('xx_Policy/', '');
|
|
320
325
|
indexContent += `- [${relativePath}](./${relativePath})\n`;
|
|
321
326
|
}
|
|
322
327
|
}
|
|
323
328
|
|
|
329
|
+
// 인프라 설정 파일 안내 (복사하지 않으므로 원본 경로 표시)
|
|
330
|
+
if (infraConfigFiles.length > 0) {
|
|
331
|
+
indexContent += '\n## 인프라 설정 (xx_Infra_Config/)\n';
|
|
332
|
+
indexContent += '> 아래 파일들은 복사되지 않습니다. 원본 경로에서 직접 읽으세요.\n';
|
|
333
|
+
indexContent += '> 변경 감지 시 AI가 내용을 읽고 로컬 환경에 맞게 적용해야 합니다.\n\n';
|
|
334
|
+
for (const file of infraConfigFiles) {
|
|
335
|
+
const relativePath = file.key.replace('xx_Infra_Config/', '');
|
|
336
|
+
indexContent += `- xx_Infra_Config/${relativePath}\n`;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
324
340
|
if (isCoworker) {
|
|
325
341
|
indexContent += `\n## 협업자 설정\n- [_coworker_config.md](./_coworker_config.md)\n`;
|
|
326
342
|
}
|
|
@@ -329,12 +345,13 @@ git checkout -b coworker/${coworkerFolder}
|
|
|
329
345
|
createdFiles.push('00_index.md');
|
|
330
346
|
|
|
331
347
|
// ========================================
|
|
332
|
-
// 4. 삭제된
|
|
348
|
+
// 4. 삭제된 정책/인프라에 해당하는 로컬 파일 삭제
|
|
333
349
|
// ========================================
|
|
334
350
|
if (changes.deleted.length > 0) {
|
|
335
351
|
for (const deletedKey of changes.deleted) {
|
|
336
|
-
|
|
337
|
-
|
|
352
|
+
// xx_Policy 삭제
|
|
353
|
+
if (deletedKey.startsWith('xx_Policy/')) {
|
|
354
|
+
const relativePath = deletedKey.replace('xx_Policy/', '');
|
|
338
355
|
const targetPath = path.join(localRulesPath, relativePath);
|
|
339
356
|
if (fs.existsSync(targetPath)) {
|
|
340
357
|
try {
|
|
@@ -345,6 +362,8 @@ git checkout -b coworker/${coworkerFolder}
|
|
|
345
362
|
}
|
|
346
363
|
}
|
|
347
364
|
}
|
|
365
|
+
// xx_Infra_Config는 복사하지 않으므로 삭제 처리 불필요
|
|
366
|
+
// Pull 시 자동으로 원본 폴더에서 제거됨
|
|
348
367
|
}
|
|
349
368
|
}
|
|
350
369
|
|
|
@@ -454,8 +473,23 @@ export async function executeKingcast(localPath) {
|
|
|
454
473
|
message += `\n 📄 정책 파일 ${result.policyCount}개 → .claude/rules/local/`;
|
|
455
474
|
}
|
|
456
475
|
|
|
476
|
+
// xx_Infra_Config 변경 감지 시 안내
|
|
477
|
+
const infraChanges = [
|
|
478
|
+
...changes.added.filter(k => k.startsWith('xx_Infra_Config/')),
|
|
479
|
+
...changes.modified.filter(k => k.startsWith('xx_Infra_Config/')),
|
|
480
|
+
];
|
|
481
|
+
if (infraChanges.length > 0) {
|
|
482
|
+
message += `\n\n🔧 인프라 설정 변경 감지 (${infraChanges.length}개):`;
|
|
483
|
+
for (const file of infraChanges) {
|
|
484
|
+
message += `\n - ${file}`;
|
|
485
|
+
}
|
|
486
|
+
message += `\n\n 👉 AI는 위 파일들을 읽고 로컬 환경에 맞게 적용하세요.`;
|
|
487
|
+
message += `\n 👉 예: .env 변수 추가/변경, 배포 설정 확인 등`;
|
|
488
|
+
}
|
|
489
|
+
|
|
457
490
|
// 정책 변경 시 경고 메시지 추가
|
|
458
|
-
|
|
491
|
+
const policyModified = changes.modified.filter(k => k.startsWith('xx_Policy/'));
|
|
492
|
+
if (policyModified.length > 0) {
|
|
459
493
|
message += `\n\n⚠️ 중요: 정책이 변경되었습니다!`;
|
|
460
494
|
message += `\n 기존 코드가 새 정책과 맞지 않을 수 있습니다.`;
|
|
461
495
|
message += `\n 변경된 정책 파일을 읽고, 기존 코드에도 새 정책을 적용하세요.`;
|
|
@@ -511,9 +545,9 @@ export async function handleKingcastStatus(args) {
|
|
|
511
545
|
## 킹캐스트 대상 파일
|
|
512
546
|
`;
|
|
513
547
|
|
|
514
|
-
//
|
|
515
|
-
statusText += '\n###
|
|
516
|
-
const infraConfigFiles = Object.keys(currentFiles).filter(k => k.startsWith('
|
|
548
|
+
// xx_Infra_Config 파일
|
|
549
|
+
statusText += '\n### xx_Infra_Config/\n';
|
|
550
|
+
const infraConfigFiles = Object.keys(currentFiles).filter(k => k.startsWith('xx_Infra_Config/'));
|
|
517
551
|
if (infraConfigFiles.length === 0) {
|
|
518
552
|
statusText += '(없음)\n';
|
|
519
553
|
} else {
|
|
@@ -522,9 +556,9 @@ export async function handleKingcastStatus(args) {
|
|
|
522
556
|
}
|
|
523
557
|
}
|
|
524
558
|
|
|
525
|
-
//
|
|
526
|
-
statusText += '\n###
|
|
527
|
-
const policyFiles = Object.keys(currentFiles).filter(k => k.startsWith('
|
|
559
|
+
// xx_Policy 파일
|
|
560
|
+
statusText += '\n### xx_Policy/\n';
|
|
561
|
+
const policyFiles = Object.keys(currentFiles).filter(k => k.startsWith('xx_Policy/'));
|
|
528
562
|
if (policyFiles.length === 0) {
|
|
529
563
|
statusText += '(없음)\n';
|
|
530
564
|
} else {
|
package/handlers/sync.js
CHANGED
|
@@ -88,6 +88,9 @@ docuking_init 호출 시 apiKey 파라미터를 포함해주세요.`,
|
|
|
88
88
|
// zz_ai_* 폴더 목록
|
|
89
89
|
const aiFolders = ['zz_ai_1_Talk', 'zz_ai_2_Todo', 'zz_ai_3_Plan'];
|
|
90
90
|
|
|
91
|
+
// xx_ 시스템 폴더 목록 (오너 전용)
|
|
92
|
+
const systemFolders = ['xx_Infra_Config', 'xx_Policy'];
|
|
93
|
+
|
|
91
94
|
if (isCoworker) {
|
|
92
95
|
// 협업자: yy_Coworker_{폴더명}/ 폴더를 yy_All_Docu/ 밖에 별도 생성
|
|
93
96
|
coworkerFolderName = `yy_Coworker_${coworkerFolder}`;
|
|
@@ -108,15 +111,14 @@ docuking_init 호출 시 apiKey 파라미터를 포함해주세요.`,
|
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
113
|
} else {
|
|
111
|
-
// 오너:
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
fs.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (!fs.existsSync(policyPath)) {
|
|
118
|
-
fs.mkdirSync(policyPath, { recursive: true });
|
|
114
|
+
// 오너: xx_ 시스템 폴더 (루트에 생성)
|
|
115
|
+
for (const folder of systemFolders) {
|
|
116
|
+
const folderPath = path.join(localPath, folder);
|
|
117
|
+
if (!fs.existsSync(folderPath)) {
|
|
118
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
119
|
+
}
|
|
119
120
|
}
|
|
121
|
+
// 오너: yy_All_Docu/ 안에 _Private/ 생성
|
|
120
122
|
const ownerPrivatePath = path.join(mainFolderPath, '_Private');
|
|
121
123
|
if (!fs.existsSync(ownerPrivatePath)) {
|
|
122
124
|
fs.mkdirSync(ownerPrivatePath, { recursive: true });
|
|
@@ -296,27 +298,27 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
296
298
|
};
|
|
297
299
|
}
|
|
298
300
|
|
|
299
|
-
// 오너 Push 대상: yy_All_Docu/ + zz_ai_*/ 폴더들
|
|
301
|
+
// 오너 Push 대상: xx_*/ + yy_All_Docu/ + zz_ai_*/ 폴더들
|
|
300
302
|
pushTargetFolders.push({ localPath: mainFolderPath, serverPrefix: 'yy_All_Docu' });
|
|
301
303
|
|
|
302
|
-
// zz_ai_* 폴더들 찾기
|
|
304
|
+
// xx_*, zz_ai_* 폴더들 찾기
|
|
303
305
|
const rootEntries = fs.readdirSync(localPath, { withFileTypes: true });
|
|
304
306
|
for (const entry of rootEntries) {
|
|
305
|
-
if (entry.isDirectory() && entry.name.startsWith('zz_ai_')) {
|
|
306
|
-
const
|
|
307
|
-
pushTargetFolders.push({ localPath:
|
|
307
|
+
if (entry.isDirectory() && (entry.name.startsWith('xx_') || entry.name.startsWith('zz_ai_'))) {
|
|
308
|
+
const folderPath = path.join(localPath, entry.name);
|
|
309
|
+
pushTargetFolders.push({ localPath: folderPath, serverPrefix: entry.name });
|
|
308
310
|
}
|
|
309
311
|
}
|
|
310
312
|
}
|
|
311
313
|
|
|
312
|
-
// .env 파일 자동 백업:
|
|
313
|
-
// 오너만 가능 (협업자는
|
|
314
|
+
// .env 파일 자동 백업: xx_Infra_Config/ 폴더에 복사
|
|
315
|
+
// 오너만 가능 (협업자는 xx_Infra_Config/ 에 접근 불가)
|
|
314
316
|
if (!isCoworker) {
|
|
315
317
|
const envFilePath = path.join(localPath, '.env');
|
|
316
|
-
const infraConfigPath = path.join(
|
|
318
|
+
const infraConfigPath = path.join(localPath, 'xx_Infra_Config');
|
|
317
319
|
|
|
318
320
|
if (fs.existsSync(envFilePath)) {
|
|
319
|
-
//
|
|
321
|
+
// xx_Infra_Config 폴더 생성
|
|
320
322
|
if (!fs.existsSync(infraConfigPath)) {
|
|
321
323
|
fs.mkdirSync(infraConfigPath, { recursive: true });
|
|
322
324
|
}
|
|
@@ -325,7 +327,7 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
325
327
|
const envBackupPath = path.join(infraConfigPath, '.env');
|
|
326
328
|
try {
|
|
327
329
|
fs.copyFileSync(envFilePath, envBackupPath);
|
|
328
|
-
console.error(`[DocuKing] .env →
|
|
330
|
+
console.error(`[DocuKing] .env → xx_Infra_Config/.env 자동 백업 완료`);
|
|
329
331
|
} catch (err) {
|
|
330
332
|
console.error(`[DocuKing] .env 백업 실패: ${err.message}`);
|
|
331
333
|
}
|
|
@@ -341,7 +343,7 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
341
343
|
const backendEnvBackupPath = path.join(infraConfigPath, 'backend.env');
|
|
342
344
|
try {
|
|
343
345
|
fs.copyFileSync(backendEnvPath, backendEnvBackupPath);
|
|
344
|
-
console.error(`[DocuKing] backend/.env →
|
|
346
|
+
console.error(`[DocuKing] backend/.env → xx_Infra_Config/backend.env 자동 백업 완료`);
|
|
345
347
|
} catch (err) {
|
|
346
348
|
console.error(`[DocuKing] backend/.env 백업 실패: ${err.message}`);
|
|
347
349
|
}
|
|
@@ -357,7 +359,7 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
357
359
|
const frontendEnvBackupPath = path.join(infraConfigPath, 'frontend.env.local');
|
|
358
360
|
try {
|
|
359
361
|
fs.copyFileSync(frontendEnvPath, frontendEnvBackupPath);
|
|
360
|
-
console.error(`[DocuKing] frontend/.env.local →
|
|
362
|
+
console.error(`[DocuKing] frontend/.env.local → xx_Infra_Config/frontend.env.local 자동 백업 완료`);
|
|
361
363
|
} catch (err) {
|
|
362
364
|
console.error(`[DocuKing] frontend/.env.local 백업 실패: ${err.message}`);
|
|
363
365
|
}
|
|
@@ -373,7 +375,7 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
373
375
|
const packageJsonBackupPath = path.join(infraConfigPath, 'package.json');
|
|
374
376
|
try {
|
|
375
377
|
fs.copyFileSync(packageJsonPath, packageJsonBackupPath);
|
|
376
|
-
console.error(`[DocuKing] package.json →
|
|
378
|
+
console.error(`[DocuKing] package.json → xx_Infra_Config/package.json 자동 백업 완료`);
|
|
377
379
|
} catch (err) {
|
|
378
380
|
console.error(`[DocuKing] package.json 백업 실패: ${err.message}`);
|
|
379
381
|
}
|
|
@@ -389,7 +391,7 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
389
391
|
const backendPackageJsonBackupPath = path.join(infraConfigPath, 'backend.package.json');
|
|
390
392
|
try {
|
|
391
393
|
fs.copyFileSync(backendPackageJsonPath, backendPackageJsonBackupPath);
|
|
392
|
-
console.error(`[DocuKing] backend/package.json →
|
|
394
|
+
console.error(`[DocuKing] backend/package.json → xx_Infra_Config/backend.package.json 자동 백업 완료`);
|
|
393
395
|
} catch (err) {
|
|
394
396
|
console.error(`[DocuKing] backend/package.json 백업 실패: ${err.message}`);
|
|
395
397
|
}
|
|
@@ -405,7 +407,7 @@ docuking_init을 먼저 실행하세요.`,
|
|
|
405
407
|
const frontendPackageJsonBackupPath = path.join(infraConfigPath, 'frontend.package.json');
|
|
406
408
|
try {
|
|
407
409
|
fs.copyFileSync(frontendPackageJsonPath, frontendPackageJsonBackupPath);
|
|
408
|
-
console.error(`[DocuKing] frontend/package.json →
|
|
410
|
+
console.error(`[DocuKing] frontend/package.json → xx_Infra_Config/frontend.package.json 자동 백업 완료`);
|
|
409
411
|
} catch (err) {
|
|
410
412
|
console.error(`[DocuKing] frontend/package.json 백업 실패: ${err.message}`);
|
|
411
413
|
}
|
|
@@ -1026,11 +1028,14 @@ export async function handlePullInternal(args) {
|
|
|
1026
1028
|
|
|
1027
1029
|
// 서버 경로에 따라 로컬 저장 위치 결정
|
|
1028
1030
|
// 프로젝트 루트 기준 절대경로 그대로 사용
|
|
1031
|
+
// - xx_Infra_Config/xxx → xx_Infra_Config/xxx (시스템/인프라)
|
|
1032
|
+
// - xx_Policy/xxx → xx_Policy/xxx (정책)
|
|
1029
1033
|
// - yy_All_Docu/xxx → yy_All_Docu/xxx (오너 문서)
|
|
1030
1034
|
// - yy_Coworker_xxx/yyy → yy_Coworker_xxx/yyy (협업자 폴더)
|
|
1031
1035
|
// - zz_ai_xxx/yyy → zz_ai_xxx/yyy (AI 폴더)
|
|
1032
1036
|
let fullPath;
|
|
1033
|
-
if (file.path.startsWith('
|
|
1037
|
+
if (file.path.startsWith('xx_') ||
|
|
1038
|
+
file.path.startsWith('yy_All_Docu/') ||
|
|
1034
1039
|
file.path.startsWith('yy_Coworker_') ||
|
|
1035
1040
|
file.path.startsWith('zz_ai_')) {
|
|
1036
1041
|
// 서버 경로 그대로 로컬에 저장
|
|
@@ -1168,7 +1173,8 @@ export async function handlePullInternal(args) {
|
|
|
1168
1173
|
try {
|
|
1169
1174
|
// 서버 경로에 따라 로컬 경로 결정
|
|
1170
1175
|
let fullPath;
|
|
1171
|
-
if (deletedFile.path.startsWith('
|
|
1176
|
+
if (deletedFile.path.startsWith('xx_') ||
|
|
1177
|
+
deletedFile.path.startsWith('yy_All_Docu/') ||
|
|
1172
1178
|
deletedFile.path.startsWith('yy_Coworker_') ||
|
|
1173
1179
|
deletedFile.path.startsWith('zz_ai_')) {
|
|
1174
1180
|
fullPath = path.join(localPath, deletedFile.path);
|
|
@@ -1460,16 +1466,16 @@ export async function handleStatus(args) {
|
|
|
1460
1466
|
}
|
|
1461
1467
|
pushableFiles = localFiles; // 협업자는 자기 폴더의 모든 파일 Push 가능
|
|
1462
1468
|
} else {
|
|
1463
|
-
// 오너: yy_All_Docu/ + zz_ai_*/ 폴더에서 파일 수집
|
|
1469
|
+
// 오너: xx_*/ + yy_All_Docu/ + zz_ai_*/ 폴더에서 파일 수집
|
|
1464
1470
|
if (fs.existsSync(mainFolderPath)) {
|
|
1465
1471
|
collectFilesSimple(mainFolderPath, '', localFiles);
|
|
1466
1472
|
}
|
|
1467
|
-
// zz_ai_* 폴더들도 수집
|
|
1473
|
+
// xx_*, zz_ai_* 폴더들도 수집
|
|
1468
1474
|
const rootEntries = fs.readdirSync(localPath, { withFileTypes: true });
|
|
1469
1475
|
for (const entry of rootEntries) {
|
|
1470
|
-
if (entry.isDirectory() && entry.name.startsWith('zz_ai_')) {
|
|
1471
|
-
const
|
|
1472
|
-
collectFilesSimple(
|
|
1476
|
+
if (entry.isDirectory() && (entry.name.startsWith('xx_') || entry.name.startsWith('zz_ai_'))) {
|
|
1477
|
+
const folderPath = path.join(localPath, entry.name);
|
|
1478
|
+
collectFilesSimple(folderPath, '', localFiles);
|
|
1473
1479
|
}
|
|
1474
1480
|
}
|
|
1475
1481
|
pushableFiles = localFiles; // 오너는 모든 파일 Push 가능
|
package/index.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* AI 시대의 문서 협업 플랫폼 - AI가 문서를 Push/Pull 할 수 있게 해주는 MCP 서버
|
|
7
7
|
*
|
|
8
8
|
* 폴더 구조:
|
|
9
|
+
* - xx_Infra_Config/ : 시스템 설정 (.env 백업, 배포 정보 등) - Push 대상
|
|
10
|
+
* - xx_Policy/ : 정책 문서 (AI 행동 지침) - Push 대상, 킹캐스트 대상
|
|
9
11
|
* - yy_All_Docu/ : 문서 동기화 폴더 (Push/Pull 대상)
|
|
10
|
-
* - yy_All_Docu/_Infra_Config/: 민감 정보 백업 (.env 자동 복사, 팀 공유)
|
|
11
12
|
* - yy_All_Docu/_Private/ : 오너 비공개 폴더 (오너만 접근)
|
|
12
13
|
* - yy_Coworker_{폴더명}/ : 협업자 폴더 (yy_All_Docu와 별도, 동기화 대상)
|
|
13
14
|
* - zz_ai_1_Talk/ : AI 대화록 (킹톡, Push 대상)
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
* - zz_ai_3_Plan/ : AI 플랜 (킹플랜, Push 대상)
|
|
16
17
|
*
|
|
17
18
|
* 접두사 규칙:
|
|
19
|
+
* - xx_ : 시스템용 폴더 (인프라, 정책) - Push 대상
|
|
18
20
|
* - yy_ : 사람용 폴더 (문서, 협업자) - Push 대상
|
|
19
21
|
* - zz_ai_ : AI용 폴더 (Talk, Todo, Plan) - Push 대상
|
|
20
22
|
* - _ : 비공개 폴더 (본인만 접근)
|
|
@@ -87,7 +89,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
87
89
|
- localPath: 로컬 프로젝트 경로
|
|
88
90
|
|
|
89
91
|
**오너 초기화 시:**
|
|
90
|
-
-
|
|
92
|
+
- xx_Infra_Config/, xx_Policy/ 폴더 생성 (시스템 설정)
|
|
93
|
+
- yy_All_Docu/, yy_All_Docu/_Private/ 폴더 생성
|
|
91
94
|
- zz_ai_1_Talk/, zz_ai_2_Todo/, zz_ai_3_Plan/ 폴더 생성
|
|
92
95
|
- CLAUDE.md에 DocuKing 연결 안내 추가
|
|
93
96
|
- 초기화 완료 후 정책 문서 작성 가이드 제공
|
|
@@ -97,7 +100,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
97
100
|
- CLAUDE.md에 DocuKing 연결 안내 추가
|
|
98
101
|
- "Pull을 실행하세요" 안내 (오너 정책이 킹캐스트로 로컬화됨)
|
|
99
102
|
|
|
100
|
-
**정책 문서 템플릿 (오너용,
|
|
103
|
+
**정책 문서 템플릿 (오너용, xx_Policy/ 폴더에 작성):**
|
|
101
104
|
|
|
102
105
|
[필수]
|
|
103
106
|
- 00_project_overview.md - 프로젝트 개요, 기술 스택, 팀 구성, 주요 URL
|
|
@@ -188,7 +191,7 @@ init 완료 후 오너에게 "정책 문서를 작성하시겠습니까?" 제안
|
|
|
188
191
|
},
|
|
189
192
|
{
|
|
190
193
|
name: 'docuking_pull',
|
|
191
|
-
description: '서버에서 문서를
|
|
194
|
+
description: '서버에서 문서를 다운로드합니다. "DocuKing에서 가져와" 요청 시 사용.\n\n**킹캐스트**: Pull 후 xx_Policy/, xx_Infra_Config/ 폴더 변경이 감지되면 자동으로 .claude/rules/local/에 로컬화됩니다.',
|
|
192
195
|
inputSchema: {
|
|
193
196
|
type: 'object',
|
|
194
197
|
properties: {
|
|
@@ -451,7 +454,7 @@ init 완료 후 오너에게 "정책 문서를 작성하시겠습니까?" 제안
|
|
|
451
454
|
|
|
452
455
|
**검증 대상:**
|
|
453
456
|
- .claude/rules/local/ 의 정책 파일 기준 (킹캐스트로 로컬화된 정책)
|
|
454
|
-
-
|
|
457
|
+
- xx_Policy/ 정책 파일도 참조
|
|
455
458
|
- 폴더 구조, 명명 규칙, API 규칙, DB 스키마 등
|
|
456
459
|
|
|
457
460
|
**출력:**
|
|
@@ -577,7 +580,7 @@ zz_ai_3_Plan/
|
|
|
577
580
|
|
|
578
581
|
### 3. docuking_pull
|
|
579
582
|
서버 문서를 로컬로 다운로드합니다.
|
|
580
|
-
**킹캐스트**: Pull 후
|
|
583
|
+
**킹캐스트**: Pull 후 xx_Policy/, xx_Infra_Config/ 폴더 변경이 감지되면 자동으로 .claude/rules/local/에 로컬화됩니다.
|
|
581
584
|
|
|
582
585
|
### 4. docuking_list
|
|
583
586
|
서버 파일 목록을 조회합니다.
|
|
@@ -653,7 +656,7 @@ DocuKing 협업의 핵심 원칙입니다. **각자 자기 영역만 수정할
|
|
|
653
656
|
|
|
654
657
|
## 킹캐스트 (KingCast) - 정책 자동 배포
|
|
655
658
|
|
|
656
|
-
오너가 \`
|
|
659
|
+
오너가 \`xx_Policy/\` 폴더에 정책을 작성하고 Push하면,
|
|
657
660
|
협업자가 Pull할 때 자동으로 \`.claude/rules/local/\`에 서브파일 구조로 로컬화됩니다.
|
|
658
661
|
|
|
659
662
|
### 킹캐스트 동작 원리
|
|
@@ -662,12 +665,12 @@ DocuKing 협업의 핵심 원칙입니다. **각자 자기 영역만 수정할
|
|
|
662
665
|
\`\`\`
|
|
663
666
|
|
|
664
667
|
### 킹캐스트 대상 폴더
|
|
665
|
-
- \`
|
|
666
|
-
- \`
|
|
668
|
+
- \`xx_Infra_Config/\` - 환경 설정 (.env 백업, 배포 정보 등) → 변경 감지만 (복사X)
|
|
669
|
+
- \`xx_Policy/\` - AI 행동 지침 → .claude/rules/local/에 복사
|
|
667
670
|
|
|
668
671
|
### 정책 파일 작성 템플릿 (오너용)
|
|
669
672
|
\`\`\`
|
|
670
|
-
|
|
673
|
+
xx_Policy/
|
|
671
674
|
├── 00_project_overview.md # 프로젝트 개요
|
|
672
675
|
├── 01_folder_structure.md # 폴더 구조
|
|
673
676
|
├── 02_api_convention.md # API 규칙
|
|
@@ -681,9 +684,11 @@ _Policy/
|
|
|
681
684
|
- \`.claude/rules/local/_coworker_config.md\` - 협업자 설정 (협업자만)
|
|
682
685
|
|
|
683
686
|
### AI 필독: 킹캐스트 후 행동
|
|
684
|
-
1. Pull 후 "📢 킹캐스트" 메시지가 표시되면,
|
|
687
|
+
1. Pull 후 "📢 킹캐스트" 메시지가 표시되면, 정책/환경 변경이 감지된 것
|
|
685
688
|
2. \`.claude/rules/local/00_index.md\`를 읽고 정책 목록 확인
|
|
686
689
|
3. 각 정책 파일을 **읽어서** 내용을 숙지하고 규칙을 따라 작업
|
|
690
|
+
4. **xx_Infra_Config 변경 시**: 원본 경로(xx_Infra_Config/)에서 직접 읽고 로컬 환경에 맞게 적용
|
|
691
|
+
- 예: .env 변수 추가/수정, 배포 설정 확인 등
|
|
687
692
|
|
|
688
693
|
웹 탐색기: https://docuking.ai
|
|
689
694
|
`;
|
package/lib/init.js
CHANGED
|
@@ -64,7 +64,7 @@ ${marker}
|
|
|
64
64
|
- 의미 있는 작업 시작 시 (추적용)
|
|
65
65
|
|
|
66
66
|
### 규칙
|
|
67
|
-
1. 동기화 대상: yy_All_Docu/ + zz_ai_*/ 폴더 모두
|
|
67
|
+
1. 동기화 대상: xx_*/ + yy_All_Docu/ + zz_ai_*/ 폴더 모두
|
|
68
68
|
2. 킹푸시는 Plan 완료(done) 시 자동 실행
|
|
69
69
|
3. **Talk → Todo → Plan → Done** 순서 준수
|
|
70
70
|
4. Plan 전에 반드시 Talk로 배경 기록
|
|
@@ -85,7 +85,7 @@ ${marker}
|
|
|
85
85
|
|
|
86
86
|
| 역할 | Push (올리기) | Pull (내리기) | 삭제 |
|
|
87
87
|
|------|---------------|---------------|------|
|
|
88
|
-
| **오너** | \`yy_All_Docu/\` + \`zz_ai_*/\` | 합집합 전체 | 자기 영역만 |
|
|
88
|
+
| **오너** | \`xx_*/\` + \`yy_All_Docu/\` + \`zz_ai_*/\` | 합집합 전체 | 자기 영역만 |
|
|
89
89
|
| **협업자** | \`yy_Coworker_{본인}/\` (안에 zz_ai_* 포함) | 합집합 전체 | 자기 영역만 |
|
|
90
90
|
|
|
91
91
|
### 합집합이란?
|
|
@@ -103,6 +103,8 @@ ${marker}
|
|
|
103
103
|
### 폴더 구조 (Pull 후)
|
|
104
104
|
\`\`\`
|
|
105
105
|
project/
|
|
106
|
+
├── xx_Infra_Config/ ← 시스템 설정 (오너 관리)
|
|
107
|
+
├── xx_Policy/ ← 정책 문서 (오너 관리, 킹캐스트 대상)
|
|
106
108
|
├── yy_All_Docu/ ← 오너 문서 (읽기만)
|
|
107
109
|
├── yy_Coworker_a_Kim/ ← 협업자 Kim
|
|
108
110
|
│ ├── 작업문서.md
|
|
@@ -120,19 +122,19 @@ project/
|
|
|
120
122
|
|
|
121
123
|
## 킹캐스트 (KingCast) - 정책 자동 배포
|
|
122
124
|
|
|
123
|
-
오너가 \`
|
|
125
|
+
오너가 \`xx_Policy/\` 폴더에 정책을 작성하고 Push하면,
|
|
124
126
|
협업자가 Pull할 때 자동으로 \`.claude/rules/local/\`에 로컬화됩니다.
|
|
125
127
|
|
|
126
128
|
### 킹캐스트 대상 폴더
|
|
127
|
-
- \`
|
|
128
|
-
- \`
|
|
129
|
+
- \`xx_Infra_Config/\` - 환경 설정 (.env 백업, 배포 정보 등) → 변경 감지만
|
|
130
|
+
- \`xx_Policy/\` - AI 행동 지침 → .claude/rules/local/에 복사
|
|
129
131
|
|
|
130
132
|
### 킹캐스트 동작
|
|
131
133
|
1. Pull 실행
|
|
132
|
-
2.
|
|
133
|
-
3. \`
|
|
134
|
-
4.
|
|
135
|
-
5.
|
|
134
|
+
2. xx_Infra_Config/, xx_Policy/ 폴더 변경 감지 (해시 비교)
|
|
135
|
+
3. \`xx_Policy/\` 파일들을 \`.claude/rules/local/\`에 서브파일 구조로 복사
|
|
136
|
+
4. \`xx_Infra_Config/\` 변경 시 AI에게 알림 (복사X, 원본 경로에서 직접 읽음)
|
|
137
|
+
5. 협업자의 경우 AI가 내용을 읽고 로컬 환경에 맞게 적용
|
|
136
138
|
|
|
137
139
|
### Git 제외 정책
|
|
138
140
|
|
|
@@ -144,9 +146,11 @@ project/
|
|
|
144
146
|
- \`.claude/rules/local/*.md\` - 정책 파일 (폴더구조, API규칙 등 민감정보 없음)
|
|
145
147
|
|
|
146
148
|
### AI 필독
|
|
147
|
-
- Pull 후 "📢 킹캐스트" 메시지가
|
|
149
|
+
- Pull 후 "📢 킹캐스트" 메시지가 표시되면 반드시 확인하세요.
|
|
148
150
|
- \`.claude/rules/local/00_index.md\`를 읽고 정책 문서 목록을 확인하세요.
|
|
149
151
|
- 각 정책 파일의 내용을 읽고 규칙을 따라 작업하세요.
|
|
152
|
+
- **xx_Infra_Config 변경 감지 시**: 해당 파일을 직접 읽고 로컬 환경에 맞게 적용하세요.
|
|
153
|
+
- 예: .env 변수 추가/수정, 배포 설정 확인 등
|
|
150
154
|
`;
|
|
151
155
|
|
|
152
156
|
try {
|