@team-semicolon/semo-cli 3.13.1 → 3.14.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/dist/supabase.js DELETED
@@ -1,602 +0,0 @@
1
- "use strict";
2
- /**
3
- * SEMO CLI - Supabase 클라이언트
4
- *
5
- * package_definitions 테이블에서 패키지 정보를 조회합니다.
6
- * DB 연결 실패 시 하드코딩된 폴백 데이터를 사용합니다.
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.getPackages = getPackages;
10
- exports.getStandardPackages = getStandardPackages;
11
- exports.getExtensionPackages = getExtensionPackages;
12
- exports.resolvePackageName = resolvePackageName;
13
- exports.getPackageVersion = getPackageVersion;
14
- exports.getPackagesByGroup = getPackagesByGroup;
15
- exports.getDetectablePackages = getDetectablePackages;
16
- exports.toExtensionPackageFormat = toExtensionPackageFormat;
17
- exports.buildShortnameMappingFromDb = buildShortnameMappingFromDb;
18
- exports.buildExtensionPackagesFromDb = buildExtensionPackagesFromDb;
19
- exports.getActiveSkills = getActiveSkills;
20
- exports.getActiveSkillNames = getActiveSkillNames;
21
- exports.getSkillCountByCategory = getSkillCountByCategory;
22
- const supabase_js_1 = require("@supabase/supabase-js");
23
- // Supabase 연결 정보 (공개 프로젝트용)
24
- const SUPABASE_URL = process.env.SEMO_SUPABASE_URL || "https://vdrllieckyeumbhyclkc.supabase.co";
25
- const SUPABASE_ANON_KEY = process.env.SEMO_SUPABASE_ANON_KEY ||
26
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZkcmxsaWVja3lldW1iaHljbGtjIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Njc5NzgxMTEsImV4cCI6MjA4MzU1NDExMX0.SrruG4y9geH1bCWE4uLzMHiUA38UcGMQwbAxxaaa718";
27
- // Supabase 클라이언트 (싱글톤)
28
- let supabaseClient = null;
29
- function getSupabaseClient() {
30
- if (!supabaseClient) {
31
- supabaseClient = (0, supabase_js_1.createClient)(SUPABASE_URL, SUPABASE_ANON_KEY);
32
- }
33
- return supabaseClient;
34
- }
35
- // DB 연결 가능 여부 확인
36
- let dbAvailable = null;
37
- async function checkDbConnection() {
38
- if (dbAvailable !== null)
39
- return dbAvailable;
40
- try {
41
- const supabase = getSupabaseClient();
42
- const { error } = await supabase
43
- .from("package_definitions")
44
- .select("id")
45
- .limit(1);
46
- dbAvailable = !error;
47
- }
48
- catch {
49
- dbAvailable = false;
50
- }
51
- return dbAvailable;
52
- }
53
- /**
54
- * 모든 패키지 목록 조회
55
- */
56
- async function getPackages(layer) {
57
- const isConnected = await checkDbConnection();
58
- if (!isConnected) {
59
- console.warn("⚠️ DB 연결 실패, 폴백 데이터 사용");
60
- return getFallbackPackages(layer);
61
- }
62
- const supabase = getSupabaseClient();
63
- let query = supabase
64
- .from("package_definitions")
65
- .select("*")
66
- .eq("is_active", true);
67
- if (layer) {
68
- query = query.eq("layer", layer);
69
- }
70
- const { data, error } = await query.order("install_order");
71
- if (error) {
72
- console.warn("⚠️ 패키지 조회 실패, 폴백 데이터 사용");
73
- return getFallbackPackages(layer);
74
- }
75
- return data || [];
76
- }
77
- /**
78
- * Standard 패키지 목록 조회
79
- */
80
- async function getStandardPackages() {
81
- const isConnected = await checkDbConnection();
82
- if (!isConnected) {
83
- return getFallbackPackages().filter((p) => p.package_type === "standard");
84
- }
85
- const supabase = getSupabaseClient();
86
- const { data, error } = await supabase
87
- .from("package_definitions")
88
- .select("*")
89
- .eq("package_type", "standard")
90
- .eq("is_active", true)
91
- .order("install_order");
92
- if (error) {
93
- return getFallbackPackages().filter((p) => p.package_type === "standard");
94
- }
95
- return data || [];
96
- }
97
- /**
98
- * Extension 패키지 목록 조회
99
- */
100
- async function getExtensionPackages(layer) {
101
- const isConnected = await checkDbConnection();
102
- if (!isConnected) {
103
- const fallback = getFallbackPackages(layer);
104
- return fallback.filter((p) => p.package_type === "extension");
105
- }
106
- const supabase = getSupabaseClient();
107
- let query = supabase
108
- .from("package_definitions")
109
- .select("*")
110
- .eq("package_type", "extension")
111
- .eq("is_active", true);
112
- if (layer) {
113
- query = query.eq("layer", layer);
114
- }
115
- const { data, error } = await query.order("install_order");
116
- if (error) {
117
- const fallback = getFallbackPackages(layer);
118
- return fallback.filter((p) => p.package_type === "extension");
119
- }
120
- return data || [];
121
- }
122
- /**
123
- * 패키지명 또는 별칭으로 패키지 찾기
124
- */
125
- async function resolvePackageName(input) {
126
- const isConnected = await checkDbConnection();
127
- if (!isConnected) {
128
- return resolveFallbackPackageName(input);
129
- }
130
- const supabase = getSupabaseClient();
131
- // 1. 정확한 이름 매칭
132
- const { data: exactMatch } = await supabase
133
- .from("package_definitions")
134
- .select("name")
135
- .eq("name", input)
136
- .eq("is_active", true)
137
- .single();
138
- if (exactMatch)
139
- return exactMatch.name;
140
- // 2. 별칭 매칭 (aliases 배열에 포함)
141
- const { data: aliasMatch } = await supabase
142
- .from("package_definitions")
143
- .select("name")
144
- .contains("aliases", [input])
145
- .eq("is_active", true)
146
- .single();
147
- if (aliasMatch)
148
- return aliasMatch.name;
149
- return null;
150
- }
151
- /**
152
- * 패키지 버전 조회
153
- */
154
- async function getPackageVersion(name) {
155
- const isConnected = await checkDbConnection();
156
- if (!isConnected) {
157
- const pkg = getFallbackPackages().find((p) => p.name === name);
158
- return pkg?.version || null;
159
- }
160
- const supabase = getSupabaseClient();
161
- const { data } = await supabase
162
- .from("package_definitions")
163
- .select("version")
164
- .eq("name", name)
165
- .single();
166
- return data?.version || null;
167
- }
168
- /**
169
- * 그룹별 패키지 목록 조회
170
- */
171
- async function getPackagesByGroup(group) {
172
- return getExtensionPackages(group);
173
- }
174
- /**
175
- * 프로젝트 타입 감지용 패키지 조회
176
- */
177
- async function getDetectablePackages() {
178
- const packages = await getExtensionPackages();
179
- return packages.filter((p) => p.detect_files && p.detect_files.length > 0);
180
- }
181
- // ============================================================
182
- // 폴백 데이터 (DB 연결 실패 시 사용)
183
- // ============================================================
184
- const FALLBACK_PACKAGES = [
185
- // Standard
186
- {
187
- id: "std-core",
188
- name: "semo-core",
189
- display_name: "SEMO Core",
190
- description: "원칙, 오케스트레이터, 워크플로우 커맨드",
191
- layer: "standard",
192
- package_type: "standard",
193
- version: "3.12.0",
194
- repo_url: "https://github.com/semicolon-devteam/semo.git",
195
- source_path: "semo-system/semo-core",
196
- detect_files: [],
197
- depends_on: [],
198
- aliases: [],
199
- is_active: true,
200
- is_required: true,
201
- install_order: 10,
202
- },
203
- {
204
- id: "std-skills",
205
- name: "semo-skills",
206
- display_name: "SEMO Skills",
207
- description: "18개 통합 스킬 (워크플로우 포함)",
208
- layer: "standard",
209
- package_type: "standard",
210
- version: "3.12.0",
211
- repo_url: "https://github.com/semicolon-devteam/semo.git",
212
- source_path: "semo-system/semo-skills",
213
- detect_files: [],
214
- depends_on: [],
215
- aliases: [],
216
- is_active: true,
217
- is_required: true,
218
- install_order: 20,
219
- },
220
- {
221
- id: "std-scripts",
222
- name: "semo-scripts",
223
- display_name: "SEMO Scripts",
224
- description: "자동화 스크립트",
225
- layer: "standard",
226
- package_type: "standard",
227
- version: "3.12.0",
228
- repo_url: "https://github.com/semicolon-devteam/semo.git",
229
- source_path: "semo-system/semo-scripts",
230
- detect_files: [],
231
- depends_on: [],
232
- aliases: [],
233
- is_active: true,
234
- is_required: true,
235
- install_order: 30,
236
- },
237
- // Business Layer
238
- {
239
- id: "biz-discovery",
240
- name: "biz/discovery",
241
- display_name: "Discovery",
242
- description: "아이템 발굴, 시장 조사, Epic/Task",
243
- layer: "biz",
244
- package_type: "extension",
245
- version: "1.0.0",
246
- repo_url: "https://github.com/semicolon-devteam/semo.git",
247
- source_path: "semo-system/biz/discovery",
248
- detect_files: [],
249
- depends_on: [],
250
- aliases: ["discovery"],
251
- is_active: true,
252
- is_required: false,
253
- install_order: 100,
254
- },
255
- {
256
- id: "biz-design",
257
- name: "biz/design",
258
- display_name: "Design",
259
- description: "컨셉 설계, 목업, UX",
260
- layer: "biz",
261
- package_type: "extension",
262
- version: "1.0.0",
263
- repo_url: "https://github.com/semicolon-devteam/semo.git",
264
- source_path: "semo-system/biz/design",
265
- detect_files: [],
266
- depends_on: [],
267
- aliases: ["design"],
268
- is_active: true,
269
- is_required: false,
270
- install_order: 101,
271
- },
272
- {
273
- id: "biz-management",
274
- name: "biz/management",
275
- display_name: "Management",
276
- description: "일정/인력/스프린트 관리",
277
- layer: "biz",
278
- package_type: "extension",
279
- version: "1.0.0",
280
- repo_url: "https://github.com/semicolon-devteam/semo.git",
281
- source_path: "semo-system/biz/management",
282
- detect_files: [],
283
- depends_on: [],
284
- aliases: ["management"],
285
- is_active: true,
286
- is_required: false,
287
- install_order: 102,
288
- },
289
- {
290
- id: "biz-poc",
291
- name: "biz/poc",
292
- display_name: "PoC",
293
- description: "빠른 PoC, 패스트트랙",
294
- layer: "biz",
295
- package_type: "extension",
296
- version: "1.0.0",
297
- repo_url: "https://github.com/semicolon-devteam/semo.git",
298
- source_path: "semo-system/biz/poc",
299
- detect_files: [],
300
- depends_on: [],
301
- aliases: ["poc", "mvp"],
302
- is_active: true,
303
- is_required: false,
304
- install_order: 103,
305
- },
306
- // Engineering Layer
307
- {
308
- id: "eng-nextjs",
309
- name: "eng/nextjs",
310
- display_name: "Next.js",
311
- description: "Next.js 프론트엔드 개발",
312
- layer: "eng",
313
- package_type: "extension",
314
- version: "1.0.0",
315
- repo_url: "https://github.com/semicolon-devteam/semo.git",
316
- source_path: "semo-system/eng/nextjs",
317
- detect_files: ["next.config.js", "next.config.mjs", "next.config.ts"],
318
- depends_on: [],
319
- aliases: ["nextjs", "next"],
320
- is_active: true,
321
- is_required: false,
322
- install_order: 110,
323
- },
324
- {
325
- id: "eng-spring",
326
- name: "eng/spring",
327
- display_name: "Spring",
328
- description: "Spring Boot 백엔드 개발",
329
- layer: "eng",
330
- package_type: "extension",
331
- version: "1.0.0",
332
- repo_url: "https://github.com/semicolon-devteam/semo.git",
333
- source_path: "semo-system/eng/spring",
334
- detect_files: ["pom.xml", "build.gradle"],
335
- depends_on: [],
336
- aliases: ["spring", "backend"],
337
- is_active: true,
338
- is_required: false,
339
- install_order: 111,
340
- },
341
- {
342
- id: "eng-ms",
343
- name: "eng/ms",
344
- display_name: "Microservice",
345
- description: "마이크로서비스 아키텍처",
346
- layer: "eng",
347
- package_type: "extension",
348
- version: "1.0.0",
349
- repo_url: "https://github.com/semicolon-devteam/semo.git",
350
- source_path: "semo-system/eng/ms",
351
- detect_files: [],
352
- depends_on: [],
353
- aliases: ["ms"],
354
- is_active: true,
355
- is_required: false,
356
- install_order: 112,
357
- },
358
- {
359
- id: "eng-infra",
360
- name: "eng/infra",
361
- display_name: "Infra",
362
- description: "인프라/배포 관리",
363
- layer: "eng",
364
- package_type: "extension",
365
- version: "1.0.0",
366
- repo_url: "https://github.com/semicolon-devteam/semo.git",
367
- source_path: "semo-system/eng/infra",
368
- detect_files: ["docker-compose.yml", "Dockerfile"],
369
- depends_on: [],
370
- aliases: ["infra"],
371
- is_active: true,
372
- is_required: false,
373
- install_order: 113,
374
- },
375
- // Operations Layer
376
- {
377
- id: "ops-qa",
378
- name: "ops/qa",
379
- display_name: "QA",
380
- description: "테스트/품질 관리",
381
- layer: "ops",
382
- package_type: "extension",
383
- version: "1.0.0",
384
- repo_url: "https://github.com/semicolon-devteam/semo.git",
385
- source_path: "semo-system/ops/qa",
386
- detect_files: [],
387
- depends_on: [],
388
- aliases: ["qa"],
389
- is_active: true,
390
- is_required: false,
391
- install_order: 120,
392
- },
393
- {
394
- id: "ops-monitor",
395
- name: "ops/monitor",
396
- display_name: "Monitor",
397
- description: "서비스 현황 모니터링",
398
- layer: "ops",
399
- package_type: "extension",
400
- version: "1.0.0",
401
- repo_url: "https://github.com/semicolon-devteam/semo.git",
402
- source_path: "semo-system/ops/monitor",
403
- detect_files: [],
404
- depends_on: [],
405
- aliases: ["monitor"],
406
- is_active: true,
407
- is_required: false,
408
- install_order: 121,
409
- },
410
- {
411
- id: "ops-improve",
412
- name: "ops/improve",
413
- display_name: "Improve",
414
- description: "개선 제안",
415
- layer: "ops",
416
- package_type: "extension",
417
- version: "1.0.0",
418
- repo_url: "https://github.com/semicolon-devteam/semo.git",
419
- source_path: "semo-system/ops/improve",
420
- detect_files: [],
421
- depends_on: [],
422
- aliases: ["improve"],
423
- is_active: true,
424
- is_required: false,
425
- install_order: 122,
426
- },
427
- // Meta Layer
428
- {
429
- id: "meta",
430
- name: "meta",
431
- display_name: "Meta",
432
- description: "SEMO 프레임워크 자체 개발/관리",
433
- layer: "meta",
434
- package_type: "extension",
435
- version: "3.12.0",
436
- repo_url: "https://github.com/semicolon-devteam/semo.git",
437
- source_path: "semo-system/meta",
438
- detect_files: ["semo-core", "semo-skills"],
439
- depends_on: [],
440
- aliases: [],
441
- is_active: true,
442
- is_required: false,
443
- install_order: 200,
444
- },
445
- // System Layer
446
- {
447
- id: "sys-hooks",
448
- name: "semo-hooks",
449
- display_name: "Hooks",
450
- description: "Claude Code Hooks 기반 로깅 시스템",
451
- layer: "system",
452
- package_type: "extension",
453
- version: "1.0.0",
454
- repo_url: "https://github.com/semicolon-devteam/semo.git",
455
- source_path: "semo-system/semo-hooks",
456
- detect_files: [],
457
- depends_on: [],
458
- aliases: ["hooks"],
459
- is_active: true,
460
- is_required: false,
461
- install_order: 210,
462
- },
463
- {
464
- id: "sys-remote",
465
- name: "semo-remote",
466
- display_name: "Remote",
467
- description: "Claude Code 원격 제어 (모바일 PWA)",
468
- layer: "system",
469
- package_type: "extension",
470
- version: "1.0.0",
471
- repo_url: "https://github.com/semicolon-devteam/semo.git",
472
- source_path: "semo-system/semo-remote",
473
- detect_files: [],
474
- depends_on: [],
475
- aliases: ["remote"],
476
- is_active: true,
477
- is_required: false,
478
- install_order: 211,
479
- },
480
- ];
481
- function getFallbackPackages(layer) {
482
- if (layer) {
483
- return FALLBACK_PACKAGES.filter((p) => p.layer === layer);
484
- }
485
- return FALLBACK_PACKAGES;
486
- }
487
- function resolveFallbackPackageName(input) {
488
- // 1. 정확한 이름 매칭
489
- const exactMatch = FALLBACK_PACKAGES.find((p) => p.name === input);
490
- if (exactMatch)
491
- return exactMatch.name;
492
- // 2. 별칭 매칭
493
- const aliasMatch = FALLBACK_PACKAGES.find((p) => p.aliases.includes(input));
494
- if (aliasMatch)
495
- return aliasMatch.name;
496
- return null;
497
- }
498
- // ============================================================
499
- // 유틸리티
500
- // ============================================================
501
- /**
502
- * 패키지 정보를 CLI 포맷으로 변환
503
- */
504
- function toExtensionPackageFormat(pkg) {
505
- return {
506
- name: pkg.display_name,
507
- desc: pkg.description || "",
508
- detect: pkg.detect_files,
509
- layer: pkg.layer,
510
- };
511
- }
512
- /**
513
- * 별칭 매핑 객체 생성 (SHORTNAME_MAPPING 대체)
514
- */
515
- async function buildShortnameMappingFromDb() {
516
- const packages = await getPackages();
517
- const mapping = {};
518
- for (const pkg of packages) {
519
- for (const alias of pkg.aliases) {
520
- mapping[alias] = pkg.name;
521
- }
522
- }
523
- return mapping;
524
- }
525
- /**
526
- * EXTENSION_PACKAGES 형태의 객체 생성 (호환성용)
527
- */
528
- async function buildExtensionPackagesFromDb() {
529
- const packages = await getExtensionPackages();
530
- const result = {};
531
- for (const pkg of packages) {
532
- result[pkg.name] = toExtensionPackageFormat(pkg);
533
- }
534
- return result;
535
- }
536
- // 폴백 스킬 목록 (DB 연결 실패 시 사용)
537
- const FALLBACK_SKILLS = [
538
- // Workflow Management (3개)
539
- { id: "sk-1", name: "workflow-start", display_name: "워크플로우 시작", description: "워크플로우 인스턴스 생성 및 시작", category: "workflow", source_path: "semo-skills/workflow-start", is_active: true, is_required: true, install_order: 1, version: "1.0.0" },
540
- { id: "sk-2", name: "workflow-progress", display_name: "워크플로우 진행", description: "워크플로우 진행 상황 조회", category: "workflow", source_path: "semo-skills/workflow-progress", is_active: true, is_required: true, install_order: 2, version: "1.0.0" },
541
- { id: "sk-3", name: "workflow-resume", display_name: "워크플로우 재개", description: "중단된 워크플로우 재개", category: "workflow", source_path: "semo-skills/workflow-resume", is_active: true, is_required: true, install_order: 3, version: "1.0.0" },
542
- // Discovery (1개)
543
- { id: "sk-10", name: "ideate", display_name: "아이디에이션", description: "아이디어 발굴 및 분석", category: "discovery", source_path: "semo-skills/ideate", is_active: true, is_required: true, install_order: 10, version: "1.0.0" },
544
- // Planning (3개)
545
- { id: "sk-20", name: "create-epic", display_name: "Epic 생성", description: "Epic 이슈 생성", category: "planning", source_path: "semo-skills/create-epic", is_active: true, is_required: true, install_order: 20, version: "1.0.0" },
546
- { id: "sk-21", name: "design-user-flow", display_name: "사용자 흐름 설계", description: "UX 사용자 흐름 다이어그램 설계", category: "planning", source_path: "semo-skills/design-user-flow", is_active: true, is_required: true, install_order: 21, version: "1.0.0" },
547
- { id: "sk-22", name: "generate-mockup", display_name: "목업 생성", description: "UI 목업 생성", category: "planning", source_path: "semo-skills/generate-mockup", is_active: true, is_required: true, install_order: 22, version: "1.0.0" },
548
- // Solutioning (4개)
549
- { id: "sk-30", name: "scaffold-domain", display_name: "도메인 스캐폴딩", description: "DDD 4-layer 도메인 구조 생성", category: "solutioning", source_path: "semo-skills/scaffold-domain", is_active: true, is_required: true, install_order: 30, version: "1.0.0" },
550
- { id: "sk-31", name: "validate-architecture", display_name: "아키텍처 검증", description: "DDD 4-layer 아키텍처 준수 검증", category: "solutioning", source_path: "semo-skills/validate-architecture", is_active: true, is_required: true, install_order: 31, version: "1.0.0" },
551
- { id: "sk-32", name: "generate-spec", display_name: "명세 생성", description: "Speckit 워크플로우 통합 실행", category: "solutioning", source_path: "semo-skills/generate-spec", is_active: true, is_required: true, install_order: 32, version: "1.0.0" },
552
- { id: "sk-33", name: "design-tests", display_name: "테스트 설계", description: "구현 전 테스트 케이스 설계 (TDD)", category: "solutioning", source_path: "semo-skills/design-tests", is_active: true, is_required: true, install_order: 33, version: "1.0.0" },
553
- // Implementation (6개)
554
- { id: "sk-40", name: "create-sprint", display_name: "스프린트 생성", description: "Sprint 목표 설정 및 시작", category: "implementation", source_path: "semo-skills/create-sprint", is_active: true, is_required: true, install_order: 40, version: "1.0.0" },
555
- { id: "sk-41", name: "start-task", display_name: "태스크 시작", description: "작업 시작 (이슈 상태 변경, 브랜치 생성)", category: "implementation", source_path: "semo-skills/start-task", is_active: true, is_required: true, install_order: 41, version: "1.0.0" },
556
- { id: "sk-42", name: "review-task", display_name: "태스크 리뷰", description: "태스크 이슈 기반 구현 완료 리뷰", category: "implementation", source_path: "semo-skills/review-task", is_active: true, is_required: true, install_order: 42, version: "1.0.0" },
557
- { id: "sk-43", name: "write-code", display_name: "코드 작성", description: "코드 작성, 수정, 구현", category: "implementation", source_path: "semo-skills/write-code", is_active: true, is_required: true, install_order: 43, version: "1.0.0" },
558
- { id: "sk-44", name: "run-code-review", display_name: "코드 리뷰", description: "프로젝트 통합 코드 리뷰", category: "implementation", source_path: "semo-skills/run-code-review", is_active: true, is_required: true, install_order: 44, version: "1.0.0" },
559
- { id: "sk-45", name: "close-sprint", display_name: "스프린트 종료", description: "Sprint 종료 및 회고 정리", category: "implementation", source_path: "semo-skills/close-sprint", is_active: true, is_required: true, install_order: 45, version: "1.0.0" },
560
- // Supporting (2개)
561
- { id: "sk-50", name: "git-workflow", display_name: "Git 워크플로우", description: "Git 커밋/푸시/PR 자동화", category: "supporting", source_path: "semo-skills/git-workflow", is_active: true, is_required: true, install_order: 50, version: "1.0.0" },
562
- { id: "sk-51", name: "notify-slack", display_name: "Slack 알림", description: "Slack 채널에 메시지 전송", category: "supporting", source_path: "semo-skills/notify-slack", is_active: true, is_required: true, install_order: 51, version: "1.0.0" },
563
- ];
564
- /**
565
- * 활성 스킬 목록 조회 (설치할 스킬)
566
- */
567
- async function getActiveSkills() {
568
- const isConnected = await checkDbConnection();
569
- if (!isConnected) {
570
- console.warn("⚠️ DB 연결 실패, 폴백 스킬 목록 사용 (19개)");
571
- return FALLBACK_SKILLS.filter(s => s.is_active);
572
- }
573
- const supabase = getSupabaseClient();
574
- const { data, error } = await supabase
575
- .from("skill_definitions")
576
- .select("*")
577
- .eq("is_active", true)
578
- .order("install_order");
579
- if (error) {
580
- console.warn("⚠️ 스킬 조회 실패, 폴백 데이터 사용");
581
- return FALLBACK_SKILLS.filter(s => s.is_active);
582
- }
583
- return data || FALLBACK_SKILLS.filter(s => s.is_active);
584
- }
585
- /**
586
- * 스킬 이름 목록만 조회
587
- */
588
- async function getActiveSkillNames() {
589
- const skills = await getActiveSkills();
590
- return skills.map(s => s.name);
591
- }
592
- /**
593
- * 카테고리별 스킬 개수 조회
594
- */
595
- async function getSkillCountByCategory() {
596
- const skills = await getActiveSkills();
597
- const counts = {};
598
- for (const skill of skills) {
599
- counts[skill.category] = (counts[skill.category] || 0) + 1;
600
- }
601
- return counts;
602
- }