docuking-mcp 2.7.0 → 2.8.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/handlers/kingcast.js +73 -24
- package/handlers/sync.js +36 -30
- package/index.js +13 -10
- package/lib/init.js +11 -9
- 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,33 @@ function updateLocalRules(localPath, currentFiles, changes, config) {
|
|
|
206
205
|
}
|
|
207
206
|
}
|
|
208
207
|
|
|
208
|
+
// ========================================
|
|
209
|
+
// 1.5. xx_Infra_Config 파일들을 infra/ 서브폴더로 복사
|
|
210
|
+
// ========================================
|
|
211
|
+
const infraLocalPath = path.join(localRulesPath, 'infra');
|
|
212
|
+
if (infraConfigFiles.length > 0) {
|
|
213
|
+
fs.mkdirSync(infraLocalPath, { recursive: true });
|
|
214
|
+
|
|
215
|
+
for (const file of infraConfigFiles) {
|
|
216
|
+
// xx_Infra_Config/deploy.md → infra/deploy.md
|
|
217
|
+
const relativePath = file.key.replace('xx_Infra_Config/', '');
|
|
218
|
+
const targetPath = path.join(infraLocalPath, relativePath);
|
|
219
|
+
|
|
220
|
+
// 디렉토리 생성
|
|
221
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
222
|
+
|
|
223
|
+
// 파일 읽기 및 로컬화
|
|
224
|
+
try {
|
|
225
|
+
const content = fs.readFileSync(file.fullPath, 'utf-8');
|
|
226
|
+
const localizedContent = localizeContent(content, config);
|
|
227
|
+
fs.writeFileSync(targetPath, localizedContent, 'utf-8');
|
|
228
|
+
createdFiles.push(`infra/${relativePath}`);
|
|
229
|
+
} catch (e) {
|
|
230
|
+
console.error(`[KingCast] 인프라 설정 파일 복사 실패: ${file.key} - ${e.message}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
209
235
|
// ========================================
|
|
210
236
|
// 2. 협업자용 설정 파일 생성 (_coworker_config.md)
|
|
211
237
|
// ========================================
|
|
@@ -271,13 +297,13 @@ git checkout -b coworker/${coworkerFolder}
|
|
|
271
297
|
## 환경 정보 (읽기 전용 참고)
|
|
272
298
|
`;
|
|
273
299
|
|
|
274
|
-
//
|
|
300
|
+
// xx_Infra_Config에서 민감하지 않은 정보 추가
|
|
275
301
|
let infraInfo = '';
|
|
276
302
|
for (const file of infraConfigFiles) {
|
|
277
303
|
const fileName = path.basename(file.key);
|
|
278
304
|
// 배포 관련 정보만 참조 (민감 정보 제외)
|
|
279
305
|
if (fileName.includes('deploy') || fileName.includes('README')) {
|
|
280
|
-
infraInfo += `\n참고 문서:
|
|
306
|
+
infraInfo += `\n참고 문서: ${file.key}`;
|
|
281
307
|
}
|
|
282
308
|
}
|
|
283
309
|
|
|
@@ -316,11 +342,20 @@ git checkout -b coworker/${coworkerFolder}
|
|
|
316
342
|
indexContent += '\n(정책 문서가 없습니다)\n';
|
|
317
343
|
} else {
|
|
318
344
|
for (const file of policyFiles) {
|
|
319
|
-
const relativePath = file.key.replace('
|
|
345
|
+
const relativePath = file.key.replace('xx_Policy/', '');
|
|
320
346
|
indexContent += `- [${relativePath}](./${relativePath})\n`;
|
|
321
347
|
}
|
|
322
348
|
}
|
|
323
349
|
|
|
350
|
+
// 인프라 설정 파일 목록 추가
|
|
351
|
+
if (infraConfigFiles.length > 0) {
|
|
352
|
+
indexContent += '\n## 인프라 설정 (참조용)\n';
|
|
353
|
+
for (const file of infraConfigFiles) {
|
|
354
|
+
const relativePath = file.key.replace('xx_Infra_Config/', '');
|
|
355
|
+
indexContent += `- [${relativePath}](./infra/${relativePath})\n`;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
324
359
|
if (isCoworker) {
|
|
325
360
|
indexContent += `\n## 협업자 설정\n- [_coworker_config.md](./_coworker_config.md)\n`;
|
|
326
361
|
}
|
|
@@ -329,12 +364,13 @@ git checkout -b coworker/${coworkerFolder}
|
|
|
329
364
|
createdFiles.push('00_index.md');
|
|
330
365
|
|
|
331
366
|
// ========================================
|
|
332
|
-
// 4. 삭제된
|
|
367
|
+
// 4. 삭제된 정책/인프라에 해당하는 로컬 파일 삭제
|
|
333
368
|
// ========================================
|
|
334
369
|
if (changes.deleted.length > 0) {
|
|
335
370
|
for (const deletedKey of changes.deleted) {
|
|
336
|
-
|
|
337
|
-
|
|
371
|
+
// xx_Policy 삭제
|
|
372
|
+
if (deletedKey.startsWith('xx_Policy/')) {
|
|
373
|
+
const relativePath = deletedKey.replace('xx_Policy/', '');
|
|
338
374
|
const targetPath = path.join(localRulesPath, relativePath);
|
|
339
375
|
if (fs.existsSync(targetPath)) {
|
|
340
376
|
try {
|
|
@@ -345,6 +381,19 @@ git checkout -b coworker/${coworkerFolder}
|
|
|
345
381
|
}
|
|
346
382
|
}
|
|
347
383
|
}
|
|
384
|
+
// xx_Infra_Config 삭제
|
|
385
|
+
if (deletedKey.startsWith('xx_Infra_Config/')) {
|
|
386
|
+
const relativePath = deletedKey.replace('xx_Infra_Config/', '');
|
|
387
|
+
const targetPath = path.join(localRulesPath, 'infra', relativePath);
|
|
388
|
+
if (fs.existsSync(targetPath)) {
|
|
389
|
+
try {
|
|
390
|
+
fs.unlinkSync(targetPath);
|
|
391
|
+
console.error(`[KingCast] 삭제된 인프라 설정 제거: infra/${relativePath}`);
|
|
392
|
+
} catch (e) {
|
|
393
|
+
console.error(`[KingCast] 파일 삭제 실패: infra/${relativePath} - ${e.message}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
348
397
|
}
|
|
349
398
|
}
|
|
350
399
|
|
|
@@ -511,9 +560,9 @@ export async function handleKingcastStatus(args) {
|
|
|
511
560
|
## 킹캐스트 대상 파일
|
|
512
561
|
`;
|
|
513
562
|
|
|
514
|
-
//
|
|
515
|
-
statusText += '\n###
|
|
516
|
-
const infraConfigFiles = Object.keys(currentFiles).filter(k => k.startsWith('
|
|
563
|
+
// xx_Infra_Config 파일
|
|
564
|
+
statusText += '\n### xx_Infra_Config/\n';
|
|
565
|
+
const infraConfigFiles = Object.keys(currentFiles).filter(k => k.startsWith('xx_Infra_Config/'));
|
|
517
566
|
if (infraConfigFiles.length === 0) {
|
|
518
567
|
statusText += '(없음)\n';
|
|
519
568
|
} else {
|
|
@@ -522,9 +571,9 @@ export async function handleKingcastStatus(args) {
|
|
|
522
571
|
}
|
|
523
572
|
}
|
|
524
573
|
|
|
525
|
-
//
|
|
526
|
-
statusText += '\n###
|
|
527
|
-
const policyFiles = Object.keys(currentFiles).filter(k => k.startsWith('
|
|
574
|
+
// xx_Policy 파일
|
|
575
|
+
statusText += '\n### xx_Policy/\n';
|
|
576
|
+
const policyFiles = Object.keys(currentFiles).filter(k => k.startsWith('xx_Policy/'));
|
|
528
577
|
if (policyFiles.length === 0) {
|
|
529
578
|
statusText += '(없음)\n';
|
|
530
579
|
} 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 백업, 배포 정보 등) → .claude/rules/local/infra/에 로컬화
|
|
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 규칙
|
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 행동 지침 → 로컬화 대상
|
|
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/\` 파일들도 \`.claude/rules/local/infra/\`에 복사 (환경변수, 배포 설정 등)
|
|
137
|
+
5. 협업자의 경우 변수 치환 (폴더명, 브랜치명 등 로컬화)
|
|
136
138
|
|
|
137
139
|
### Git 제외 정책
|
|
138
140
|
|