@clazic/urban 0.2.6 → 0.2.8

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/package.json +2 -2
  2. package/src/cli.js +34 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clazic/urban",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "도시계획연구 보고서 자동 수집·지식베이스 데몬",
5
5
  "type": "module",
6
6
  "engines": {
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
- "@clazic/kordoc": "^2.4.18",
20
+ "@clazic/kordoc": "^2.4.19",
21
21
  "@modelcontextprotocol/sdk": "^1.12.0",
22
22
  "@rhwp/core": "^0.7.2",
23
23
  "better-sqlite3": "^12.9.0",
package/src/cli.js CHANGED
@@ -51,8 +51,21 @@ async function run() {
51
51
  break;
52
52
  }
53
53
  case 'start': {
54
- const result = await startDaemon();
55
- console.log(result.ok ? `✅ ${result.message}` : `❌ ${result.message}`);
54
+ let result = await startDaemon();
55
+ if (!result.ok) {
56
+ // 태스크 미등록 → 자동 install 후 재시도
57
+ console.log('데몬이 등록되지 않았습니다. 자동 등록을 시도합니다...');
58
+ const installResult = await installDaemon();
59
+ if (installResult.ok) {
60
+ console.log(`✅ ${installResult.message}`);
61
+ result = await startDaemon();
62
+ } else {
63
+ console.log(`❌ 등록 실패: ${installResult.message}`);
64
+ console.log(" 수동 등록: urban install && urban start");
65
+ break;
66
+ }
67
+ }
68
+ console.log(result.ok ? `✅ ${result.message}` : `❌ 데몬 시작 실패: ${result.message}`);
56
69
  break;
57
70
  }
58
71
  case 'stop': {
@@ -499,21 +512,31 @@ async function runDoctor() {
499
512
  const urbanHome = process.env.URBAN_HOME ?? join(homedir(), '.urban');
500
513
  checks.push({ name: 'URBAN_HOME', ok: existsSync(urbanHome), detail: urbanHome });
501
514
 
502
- // LLM CLI
503
- for (const cli of ['claude', 'gemini', 'codex']) {
515
+ // LLM CLI — claude가 있으면 OK, 나머지는 경고만
516
+ const { execSync } = await import('node:child_process');
517
+ const isCliInstalled = (cli) => {
504
518
  try {
505
- const { execSync } = await import('node:child_process');
506
- // Windows는 which 없음 where 사용 (CLAUDE.md 크로스플랫폼 규칙)
507
- const whichCmd = process.platform === 'win32' ? `where ${cli}` : `which ${cli}`;
508
- execSync(whichCmd, { stdio: 'ignore', shell: process.platform === 'win32' });
519
+ const cmd = process.platform === 'win32' ? `where ${cli}` : `which ${cli}`;
520
+ execSync(cmd, { stdio: 'ignore', shell: process.platform === 'win32' });
521
+ return true;
522
+ } catch { return false; }
523
+ };
524
+ const hasAnyCli = ['claude', 'gemini', 'codex', 'ollama'].some(isCliInstalled);
525
+ for (const cli of ['claude', 'gemini', 'codex']) {
526
+ const installed = isCliInstalled(cli);
527
+ if (installed) {
509
528
  checks.push({ name: `LLM (${cli})`, ok: true, detail: '설치됨' });
510
- } catch {
511
- checks.push({ name: `LLM (${cli})`, ok: false, detail: '미설치' });
529
+ } else if (cli === 'claude' && !hasAnyCli) {
530
+ // 아무 CLI도 없으면 claude를 필수로 표시
531
+ checks.push({ name: `LLM (${cli})`, ok: false, detail: '미설치 (최소 1개 필요)' });
532
+ } else {
533
+ checks.push({ name: `LLM (${cli})`, ok: null, detail: '미설치 (선택)' });
512
534
  }
513
535
  }
514
536
 
515
537
  for (const check of checks) {
516
- console.log(`${check.ok ? '✅' : '❌'} ${check.name}: ${check.detail}`);
538
+ const icon = check.ok === true ? '✅' : check.ok === false ? '❌' : '⚠️';
539
+ console.log(`${icon} ${check.name}: ${check.detail}`);
517
540
  }
518
541
  }
519
542