@dusky-bluehour/agent-service 0.6.3 → 0.6.4

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/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # tri-agent-manager
2
2
 
3
+ ```bash
4
+ pnpm dlx --package=@dusky-bluehour/agent-service tri-agent-manager --interactive
5
+ ```
6
+
3
7
  Claude Code, Antigravity, Codex에서 공통으로 사용하실 수 있는 서비스 운영형 스킬/워크플로우 패키지입니다.
4
8
 
5
9
  ## 목적
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dusky-bluehour/agent-service",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/scripts/init.mjs CHANGED
@@ -1146,32 +1146,82 @@ function statusLabel(status) {
1146
1146
  }
1147
1147
  }
1148
1148
 
1149
- function printPlan(catalog, selection, mode, targetDir) {
1150
- console.log('');
1151
- console.log('실행 계획');
1152
- console.log(`- 모드: ${mode}`);
1153
- console.log(`- 프리셋: ${selection.presetId ?? '수동/없음'}`);
1154
- console.log(`- 대상 경로: ${targetDir}`);
1149
+ function statusIcon(status) {
1150
+ switch (status) {
1151
+ case 'copied':
1152
+ return '✅';
1153
+ case 'overwritten':
1154
+ return '♻️';
1155
+ case 'skipped':
1156
+ return '⏭️';
1157
+ case 'would-copy':
1158
+ return '📝';
1159
+ case 'would-overwrite':
1160
+ return '🛠️';
1161
+ default:
1162
+ return '•';
1163
+ }
1164
+ }
1155
1165
 
1156
- for (const toolId of selection.selectedToolIds) {
1166
+ function normalizeChoiceInput(value) {
1167
+ return value
1168
+ .trim()
1169
+ .toLowerCase()
1170
+ .replaceAll('1', '1')
1171
+ .replaceAll('2', '2')
1172
+ .replaceAll('3', '3')
1173
+ .replaceAll('4', '4')
1174
+ .replaceAll('5', '5');
1175
+ }
1176
+
1177
+ function printPlan(catalog, selection, mode, targetDir, dryRun = false) {
1178
+ console.log('');
1179
+ console.log('━━━━━━━━ 실행 계획 ━━━━━━━━');
1180
+ console.log(`모드 : ${mode}`);
1181
+ console.log(`실행 타입 : ${dryRun ? 'dry-run (실제 파일 변경 없음)' : '실행 (파일 변경 반영)'}`);
1182
+ console.log(`프리셋 : ${selection.presetId ?? '수동/없음'}`);
1183
+ console.log(`대상 경로 : ${targetDir}`);
1184
+ console.log(`선택 도구 : ${selection.selectedToolIds.length}개`);
1185
+
1186
+ selection.selectedToolIds.forEach((toolId, index) => {
1157
1187
  const tool = findTool(catalog, toolId);
1188
+ if (!tool) {
1189
+ return;
1190
+ }
1158
1191
  const installRoot = getEffectiveInstallRoot(tool, selection);
1159
1192
  const installReferences = getToolInstallReferences(tool);
1160
1193
  const componentIds = selection.componentSelection[toolId] ?? [];
1161
- const labels = tool.components
1162
- .filter((c) => componentIds.includes(c.id))
1163
- .map((c) => `${c.title}(${c.id} → ${path.join(installRoot, getComponentInstallPath(c))})`)
1164
- .join(', ');
1165
- console.log(
1166
- `- ${tool.title}(${tool.id}) [설치 루트: ${installRoot}]: ${labels || '선택된 항목 없음'}`
1167
- );
1194
+
1195
+ console.log('');
1196
+ console.log(`${index + 1}. ${tool.title} (${tool.id})`);
1197
+ console.log(` 설치 루트 : ${installRoot}`);
1198
+ console.log(' 구성요소 :');
1199
+
1200
+ if (componentIds.length === 0) {
1201
+ console.log(' - 선택된 항목 없음');
1202
+ } else {
1203
+ for (const componentId of componentIds) {
1204
+ const component = tool.components.find((candidate) => candidate.id === componentId);
1205
+ if (!component) continue;
1206
+ console.log(
1207
+ ` - ${component.title} (${component.id}) -> ${path.join(
1208
+ installRoot,
1209
+ getComponentInstallPath(component)
1210
+ )}`
1211
+ );
1212
+ }
1213
+ }
1214
+
1168
1215
  if (tool.install_root_basis) {
1169
- console.log(` 경로 기준: ${tool.install_root_basis}`);
1216
+ console.log(` 경로 기준 : ${tool.install_root_basis}`);
1170
1217
  }
1171
1218
  if (installReferences.length > 0) {
1172
- console.log(` 참고 문서: ${installReferences.join(', ')}`);
1219
+ console.log(' 참고 문서 :');
1220
+ installReferences.forEach((ref) => {
1221
+ console.log(` - ${ref}`);
1222
+ });
1173
1223
  }
1174
- }
1224
+ });
1175
1225
  }
1176
1226
 
1177
1227
  async function confirmProceed(yesFlag) {
@@ -1180,9 +1230,23 @@ async function confirmProceed(yesFlag) {
1180
1230
 
1181
1231
  const rl = createInterface({ input, output });
1182
1232
  try {
1183
- const answer = await rl.question('계속 진행할까요? (y/N)\n> ');
1184
- const normalized = answer.trim().toLowerCase();
1185
- return normalized === 'y' || normalized === 'yes';
1233
+ while (true) {
1234
+ console.log('');
1235
+ console.log('진행 여부를 선택해 주세요.');
1236
+ console.log('1) 진행');
1237
+ console.log('2) 취소 (기본값)');
1238
+ const answer = await rl.question('번호를 입력하세요. [기본: 2]\n> ');
1239
+ const normalized = normalizeChoiceInput(answer);
1240
+
1241
+ if (!normalized || normalized === '2' || normalized === '취소' || normalized === '아니오') {
1242
+ return false;
1243
+ }
1244
+ if (normalized === '1' || normalized === '진행' || normalized === '예') {
1245
+ return true;
1246
+ }
1247
+
1248
+ console.log('잘못된 입력입니다. 1 또는 2를 입력해 주세요.');
1249
+ }
1186
1250
  } finally {
1187
1251
  rl.close();
1188
1252
  }
@@ -1289,12 +1353,40 @@ async function runInstallOrUpdate({ catalog, packageData, mode, selection, force
1289
1353
  }
1290
1354
 
1291
1355
  console.log('');
1292
- console.log('적용 결과');
1356
+ console.log('━━━━━━━━ 적용 결과 ━━━━━━━━');
1357
+ const summaryCounter = {
1358
+ copied: 0,
1359
+ overwritten: 0,
1360
+ skipped: 0,
1361
+ 'would-copy': 0,
1362
+ 'would-overwrite': 0
1363
+ };
1293
1364
  for (const item of results) {
1294
- console.log(
1295
- `- [${item.toolId}] ${item.source} -> ${item.destination}: ${statusLabel(item.status)}`
1296
- );
1365
+ if (item.status in summaryCounter) {
1366
+ summaryCounter[item.status] += 1;
1367
+ }
1368
+ }
1369
+
1370
+ const groupedResults = new Map();
1371
+ for (const item of results) {
1372
+ const list = groupedResults.get(item.toolId) ?? [];
1373
+ list.push(item);
1374
+ groupedResults.set(item.toolId, list);
1375
+ }
1376
+ for (const toolId of selection.selectedToolIds) {
1377
+ const items = groupedResults.get(toolId) ?? [];
1378
+ if (items.length === 0) continue;
1379
+ console.log(`[${toolId}]`);
1380
+ for (const item of items) {
1381
+ console.log(
1382
+ ` - ${statusIcon(item.status)} ${statusLabel(item.status)} | ${item.source} -> ${item.destination}`
1383
+ );
1384
+ }
1297
1385
  }
1386
+ console.log('');
1387
+ console.log(
1388
+ `요약: 복사 ${summaryCounter.copied} · 업데이트 ${summaryCounter.overwritten} · 건너뜀 ${summaryCounter.skipped} · 복사예정 ${summaryCounter['would-copy']} · 업데이트예정 ${summaryCounter['would-overwrite']}`
1389
+ );
1298
1390
 
1299
1391
  const usageGuidePath = path.join(targetDir, STATE_DIR_NAME, GUIDE_FILE_NAME);
1300
1392
  if (dryRun) {
@@ -1313,46 +1405,61 @@ async function runInstallOrUpdate({ catalog, packageData, mode, selection, force
1313
1405
 
1314
1406
  function printList(catalog) {
1315
1407
  console.log('');
1316
- console.log(`${CLI_NAME} 카탈로그 (한글)`);
1317
- console.log('');
1318
- for (const tool of catalog.tools) {
1408
+ console.log('━━━━━━━━ 도구 카탈로그 ━━━━━━━━');
1409
+ console.log(`CLI: ${CLI_NAME}`);
1410
+
1411
+ catalog.tools.forEach((tool, toolIndex) => {
1319
1412
  const installRoot = getToolInstallRoot(tool);
1320
1413
  const installReferences = getToolInstallReferences(tool);
1321
- console.log(`- ${tool.title} (${tool.id})`);
1322
- console.log(` 설명: ${tool.description}`);
1323
- console.log(` 설치 루트: ${installRoot}`);
1414
+ console.log('');
1415
+ console.log(`${toolIndex + 1}. ${tool.title} (${tool.id})`);
1416
+ console.log(` 설명 : ${tool.description}`);
1417
+ console.log(` 설치 루트 : ${installRoot}`);
1324
1418
  if (tool.install_root_basis) {
1325
- console.log(` 경로 기준: ${tool.install_root_basis}`);
1419
+ console.log(` 경로 기준 : ${tool.install_root_basis}`);
1326
1420
  }
1327
1421
  if (installReferences.length > 0) {
1328
- console.log(` 참고 문서: ${installReferences.join(', ')}`);
1422
+ console.log(' 참고 문서 :');
1423
+ installReferences.forEach((ref) => {
1424
+ console.log(` - ${ref}`);
1425
+ });
1329
1426
  }
1330
- console.log(' 구성요소:');
1331
- for (const component of tool.components) {
1427
+ console.log(' 구성요소 :');
1428
+ tool.components.forEach((component) => {
1332
1429
  console.log(
1333
- ` - ${component.title} (${component.id}): ${component.description} -> ${path.join(
1430
+ ` - ${component.title} (${component.id}) -> ${path.join(
1334
1431
  installRoot,
1335
1432
  getComponentInstallPath(component)
1336
1433
  )}`
1337
1434
  );
1338
- }
1339
- console.log('');
1340
- }
1435
+ if (component.description) {
1436
+ console.log(` ${component.description}`);
1437
+ }
1438
+ });
1439
+ });
1341
1440
 
1342
1441
  const presets = Array.isArray(catalog.presets) ? catalog.presets : [];
1343
1442
  if (presets.length > 0) {
1344
- console.log('프리셋 목록');
1345
- for (const preset of presets) {
1346
- console.log(`- ${preset.title} (${preset.id})`);
1347
- console.log(` 설명: ${preset.description}`);
1348
- }
1443
+ console.log('');
1444
+ console.log('━━━━━━━━ 프리셋 목록 ━━━━━━━━');
1445
+ presets.forEach((preset, index) => {
1446
+ const toolCount = Array.isArray(preset.tools) ? preset.tools.length : 0;
1447
+ console.log(`${index + 1}. ${preset.title} (${preset.id})`);
1448
+ console.log(` 설명 : ${preset.description}`);
1449
+ console.log(` 포함 도구 : ${toolCount}개 (${(preset.tools ?? []).join(', ')})`);
1450
+ });
1451
+ console.log('');
1452
+ console.log('프리셋 사용 예시:');
1453
+ console.log(
1454
+ ` ${CLI_NAME} install --preset balanced-core --target /path/to/project --yes --non-interactive`
1455
+ );
1349
1456
  console.log('');
1350
1457
  }
1351
1458
 
1352
- console.log('권장 UX 흐름');
1353
- console.log('1) pnpm dlx tri-agent-manager --interactive 로 시작합니다.');
1354
- console.log('2) 설치 후 state.json 기준으로 update를 수행한다.');
1355
- console.log('3) update 시 필요한 도구만 부분 갱신한다.');
1459
+ console.log('━━━━━━━━ 권장 흐름 ━━━━━━━━');
1460
+ console.log(`1) pnpm dlx ${CLI_NAME} --interactive`);
1461
+ console.log('2) 설치 후 .tri-agent-manager/state.json 기준으로 update');
1462
+ console.log('3) update 시 필요한 도구만 부분 갱신');
1356
1463
  }
1357
1464
 
1358
1465
  async function main() {
@@ -1427,7 +1534,7 @@ async function main() {
1427
1534
  })
1428
1535
  };
1429
1536
 
1430
- printPlan(catalog, selection, command, selection.targetDir);
1537
+ printPlan(catalog, selection, command, selection.targetDir, options.dryRun);
1431
1538
 
1432
1539
  const proceed = await confirmProceed(options.yes);
1433
1540
  if (!proceed) {
@@ -1445,7 +1552,12 @@ async function main() {
1445
1552
  });
1446
1553
 
1447
1554
  console.log('');
1448
- console.log('완료: 선택한 운영팩 반영이 끝났습니다.');
1555
+ if (options.dryRun) {
1556
+ console.log('완료: dry-run 계획 확인이 끝났습니다. 실제 반영하려면 --dry-run 없이 다시 실행해 주세요.');
1557
+ } else {
1558
+ console.log('완료: 선택한 운영팩 반영이 끝났습니다.');
1559
+ console.log(`가이드 확인: ${path.join(selection.targetDir, STATE_DIR_NAME, GUIDE_FILE_NAME)}`);
1560
+ }
1449
1561
  }
1450
1562
 
1451
1563
  main().catch((error) => {