@team-semicolon/semo-cli 4.7.2 → 4.7.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/dist/index.js CHANGED
@@ -1508,6 +1508,7 @@ kbCmd
1508
1508
  .option("--service <name>", "서비스(프로젝트) 필터")
1509
1509
  .option("--limit <n>", "최대 결과 수", "10")
1510
1510
  .option("--mode <type>", "검색 모드 (hybrid|semantic|text)", "hybrid")
1511
+ .option("--short", "미리보기 모드 (content를 80자로 잘라서 표시)")
1511
1512
  .action(async (query, options) => {
1512
1513
  const spinner = (0, ora_1.default)(`'${query}' 검색 중...`).start();
1513
1514
  try {
@@ -1525,12 +1526,17 @@ kbCmd
1525
1526
  else {
1526
1527
  console.log(chalk_1.default.cyan.bold(`\n🔍 검색 결과: '${query}' (${results.length}건)\n`));
1527
1528
  for (const entry of results) {
1528
- const preview = entry.content.substring(0, 80).replace(/\n/g, " ");
1529
1529
  const score = entry.score;
1530
1530
  const scoreStr = score ? chalk_1.default.yellow(` (${(score * 100).toFixed(1)}%)`) : "";
1531
1531
  const fullKey = entry.sub_key ? `${entry.key}/${entry.sub_key}` : entry.key;
1532
1532
  console.log(chalk_1.default.cyan(` [${entry.domain}] `) + chalk_1.default.white(fullKey) + scoreStr);
1533
- console.log(chalk_1.default.gray(` ${preview}${entry.content.length > 80 ? "..." : ""}`));
1533
+ if (options.short) {
1534
+ const preview = entry.content.substring(0, 80).replace(/\n/g, " ");
1535
+ console.log(chalk_1.default.gray(` ${preview}${entry.content.length > 80 ? "..." : ""}`));
1536
+ }
1537
+ else {
1538
+ console.log(chalk_1.default.gray(` ${entry.content}`));
1539
+ }
1534
1540
  console.log();
1535
1541
  }
1536
1542
  }
@@ -1699,9 +1705,13 @@ kbCmd
1699
1705
  kbCmd
1700
1706
  .command("ontology")
1701
1707
  .description("온톨로지 조회 — 도메인/타입/스키마/라우팅 테이블")
1702
- .option("--action <type>", "조회 동작 (list|show|services|types|instances|schema|routing-table)", "list")
1703
- .option("--domain <name>", "action=show 시 도메인")
1704
- .option("--type <name>", "action=schema 시 타입 키")
1708
+ .option("--action <type>", "동작 (list|show|services|types|instances|schema|routing-table|register)", "list")
1709
+ .option("--domain <name>", "action=show|register 시 도메인")
1710
+ .option("--type <name>", "action=schema|register 시 타입 키")
1711
+ .option("--description <text>", "action=register 시 설명")
1712
+ .option("--service <name>", "action=register 시 서비스 그룹")
1713
+ .option("--tags <tags>", "action=register 시 태그 (쉼표 구분)")
1714
+ .option("--no-init", "action=register 시 필수 KB entry 자동 생성 건너뛰기")
1705
1715
  .option("--format <type>", "출력 형식 (json|table)", "table")
1706
1716
  .action(async (options) => {
1707
1717
  try {
@@ -1846,8 +1856,48 @@ kbCmd
1846
1856
  console.log();
1847
1857
  }
1848
1858
  }
1859
+ else if (action === "register") {
1860
+ if (!options.domain) {
1861
+ console.log(chalk_1.default.red("--domain 옵션이 필요합니다."));
1862
+ process.exit(1);
1863
+ }
1864
+ if (!options.type) {
1865
+ console.log(chalk_1.default.red("--type 옵션이 필요합니다. (예: --type service, --type team, --type person)"));
1866
+ const types = await (0, kb_1.ontoListTypes)(pool);
1867
+ console.log(chalk_1.default.gray(`사용 가능한 타입: ${types.map(t => t.type_key).join(", ")}`));
1868
+ process.exit(1);
1869
+ }
1870
+ const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : undefined;
1871
+ const result = await (0, kb_1.ontoRegister)(pool, {
1872
+ domain: options.domain,
1873
+ entity_type: options.type,
1874
+ description: options.description,
1875
+ service: options.service,
1876
+ tags,
1877
+ init_required: options.init !== false,
1878
+ });
1879
+ if (options.format === "json") {
1880
+ console.log(JSON.stringify(result, null, 2));
1881
+ }
1882
+ else {
1883
+ if (result.success) {
1884
+ console.log(chalk_1.default.green(`\n✅ 도메인 '${options.domain}' 등록 완료 (타입: ${options.type})`));
1885
+ if (result.created_entries && result.created_entries.length > 0) {
1886
+ console.log(chalk_1.default.gray(` 초기 KB entry ${result.created_entries.length}건 생성:`));
1887
+ for (const e of result.created_entries) {
1888
+ console.log(chalk_1.default.gray(` - ${e.key}`));
1889
+ }
1890
+ }
1891
+ console.log(chalk_1.default.gray(`\n 다음 단계: semo kb upsert ${options.domain} <key> --content "..." 으로 데이터 입력\n`));
1892
+ }
1893
+ else {
1894
+ console.log(chalk_1.default.red(`\n❌ 등록 실패: ${result.error}\n`));
1895
+ process.exit(1);
1896
+ }
1897
+ }
1898
+ }
1849
1899
  else {
1850
- console.log(chalk_1.default.red(`알 수 없는 action: '${action}'. 사용 가능: list, show, services, types, instances, schema, routing-table`));
1900
+ console.log(chalk_1.default.red(`알 수 없는 action: '${action}'. 사용 가능: list, show, services, types, instances, schema, routing-table, register`));
1851
1901
  process.exit(1);
1852
1902
  }
1853
1903
  await (0, database_1.closeConnection)();
@@ -2027,6 +2077,54 @@ ontoCmd
2027
2077
  process.exit(1);
2028
2078
  }
2029
2079
  });
2080
+ ontoCmd
2081
+ .command("register <domain>")
2082
+ .description("새 온톨로지 도메인 등록")
2083
+ .requiredOption("--type <type>", "엔티티 타입 (예: service, team, person, bot)")
2084
+ .option("--description <text>", "도메인 설명")
2085
+ .option("--service <name>", "서비스 그룹")
2086
+ .option("--tags <tags>", "태그 (쉼표 구분)")
2087
+ .option("--no-init", "필수 KB entry 자동 생성 건너뛰기")
2088
+ .option("--format <type>", "출력 형식 (json|table)", "table")
2089
+ .action(async (domain, options) => {
2090
+ try {
2091
+ const pool = (0, database_1.getPool)();
2092
+ const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : undefined;
2093
+ const result = await (0, kb_1.ontoRegister)(pool, {
2094
+ domain,
2095
+ entity_type: options.type,
2096
+ description: options.description,
2097
+ service: options.service,
2098
+ tags,
2099
+ init_required: options.init !== false,
2100
+ });
2101
+ if (options.format === "json") {
2102
+ console.log(JSON.stringify(result, null, 2));
2103
+ }
2104
+ else {
2105
+ if (result.success) {
2106
+ console.log(chalk_1.default.green(`\n✅ 도메인 '${domain}' 등록 완료 (타입: ${options.type})`));
2107
+ if (result.created_entries && result.created_entries.length > 0) {
2108
+ console.log(chalk_1.default.gray(` 초기 KB entry ${result.created_entries.length}건 생성:`));
2109
+ for (const e of result.created_entries) {
2110
+ console.log(chalk_1.default.gray(` - ${e.key}`));
2111
+ }
2112
+ }
2113
+ console.log(chalk_1.default.gray(`\n 다음 단계: semo kb upsert ${domain} <key> --content "..." 으로 데이터 입력\n`));
2114
+ }
2115
+ else {
2116
+ console.log(chalk_1.default.red(`\n❌ 등록 실패: ${result.error}\n`));
2117
+ process.exit(1);
2118
+ }
2119
+ }
2120
+ await (0, database_1.closeConnection)();
2121
+ }
2122
+ catch (err) {
2123
+ console.error(chalk_1.default.red(`등록 실패: ${err}`));
2124
+ await (0, database_1.closeConnection)();
2125
+ process.exit(1);
2126
+ }
2127
+ });
2030
2128
  // === 신규 v4 커맨드 그룹 등록 ===
2031
2129
  (0, context_1.registerContextCommands)(program);
2032
2130
  (0, bots_1.registerBotsCommands)(program);
package/dist/kb.d.ts CHANGED
@@ -198,6 +198,31 @@ export declare function ontoListServices(pool: Pool): Promise<ServiceInfo[]>;
198
198
  * List service instances (entity_type = 'service')
199
199
  */
200
200
  export declare function ontoListInstances(pool: Pool): Promise<ServiceInstance[]>;
201
+ export interface OntoRegisterOptions {
202
+ domain: string;
203
+ entity_type: string;
204
+ description?: string;
205
+ service?: string;
206
+ tags?: string[];
207
+ init_required?: boolean;
208
+ }
209
+ export interface OntoRegisterResult {
210
+ success: boolean;
211
+ error?: string;
212
+ created_entries?: Array<{
213
+ key: string;
214
+ sub_key: string;
215
+ }>;
216
+ }
217
+ /**
218
+ * Register a new ontology domain with optional initial required KB entries.
219
+ *
220
+ * 1. Validate entity_type exists in ontology_types
221
+ * 2. Check domain doesn't already exist
222
+ * 3. INSERT into semo.ontology
223
+ * 4. If init_required (default true), create KB entries for required keys in kb_type_schema
224
+ */
225
+ export declare function ontoRegister(pool: Pool, opts: OntoRegisterOptions): Promise<OntoRegisterResult>;
201
226
  /**
202
227
  * Write ontology schemas to local cache
203
228
  */
package/dist/kb.js CHANGED
@@ -60,6 +60,7 @@ exports.ontoListSchema = ontoListSchema;
60
60
  exports.ontoRoutingTable = ontoRoutingTable;
61
61
  exports.ontoListServices = ontoListServices;
62
62
  exports.ontoListInstances = ontoListInstances;
63
+ exports.ontoRegister = ontoRegister;
63
64
  exports.ontoPullToLocal = ontoPullToLocal;
64
65
  const fs = __importStar(require("fs"));
65
66
  const path = __importStar(require("path"));
@@ -886,6 +887,79 @@ async function ontoListInstances(pool) {
886
887
  client.release();
887
888
  }
888
889
  }
890
+ /**
891
+ * Register a new ontology domain with optional initial required KB entries.
892
+ *
893
+ * 1. Validate entity_type exists in ontology_types
894
+ * 2. Check domain doesn't already exist
895
+ * 3. INSERT into semo.ontology
896
+ * 4. If init_required (default true), create KB entries for required keys in kb_type_schema
897
+ */
898
+ async function ontoRegister(pool, opts) {
899
+ const client = await pool.connect();
900
+ try {
901
+ // 1. Validate entity_type
902
+ const typeCheck = await client.query("SELECT type_key FROM semo.ontology_types WHERE type_key = $1", [opts.entity_type]);
903
+ if (typeCheck.rows.length === 0) {
904
+ const known = await client.query("SELECT type_key FROM semo.ontology_types ORDER BY type_key");
905
+ const knownTypes = known.rows.map((r) => r.type_key);
906
+ return {
907
+ success: false,
908
+ error: `타입 '${opts.entity_type}'은(는) 존재하지 않습니다. 사용 가능한 타입: [${knownTypes.join(", ")}]`,
909
+ };
910
+ }
911
+ // 2. Check domain doesn't already exist
912
+ const existCheck = await client.query("SELECT domain FROM semo.ontology WHERE domain = $1", [opts.domain]);
913
+ if (existCheck.rows.length > 0) {
914
+ return { success: false, error: `도메인 '${opts.domain}'은(는) 이미 등록되어 있습니다.` };
915
+ }
916
+ // 3. INSERT into ontology
917
+ await client.query(`INSERT INTO semo.ontology (domain, entity_type, description, service, tags, schema)
918
+ VALUES ($1, $2, $3, $4, $5, $6)`, [
919
+ opts.domain,
920
+ opts.entity_type,
921
+ opts.description || null,
922
+ opts.service || "_global",
923
+ opts.tags || [opts.entity_type],
924
+ JSON.stringify({}),
925
+ ]);
926
+ // 4. Create required KB entries
927
+ const createdEntries = [];
928
+ const initRequired = opts.init_required !== false;
929
+ if (initRequired) {
930
+ const schemaResult = await client.query(`SELECT scheme_key, scheme_description, COALESCE(key_type, 'singleton') as key_type, value_hint
931
+ FROM semo.kb_type_schema
932
+ WHERE type_key = $1 AND required = true
933
+ ORDER BY sort_order`, [opts.entity_type]);
934
+ for (const s of schemaResult.rows) {
935
+ if (s.key_type === "collection")
936
+ continue; // collection은 sub_key가 필요하므로 스킵
937
+ const placeholder = s.value_hint
938
+ ? `(미입력 — hint: ${s.value_hint})`
939
+ : `(미입력)`;
940
+ const text = `${s.scheme_key}: ${placeholder}`;
941
+ const embedding = await generateEmbedding(text);
942
+ const embeddingStr = embedding ? `[${embedding.join(",")}]` : null;
943
+ try {
944
+ await client.query(`INSERT INTO semo.knowledge_base (domain, key, sub_key, content, metadata, created_by, embedding)
945
+ VALUES ($1, $2, '', $3, '{}', 'semo-cli:onto-register', $4::vector)
946
+ ON CONFLICT (domain, key, sub_key) DO NOTHING`, [opts.domain, s.scheme_key, placeholder, embeddingStr]);
947
+ createdEntries.push({ key: s.scheme_key, sub_key: "" });
948
+ }
949
+ catch {
950
+ // 개별 entry 실패는 무시 — 도메인 등록 자체는 성공
951
+ }
952
+ }
953
+ }
954
+ return { success: true, created_entries: createdEntries };
955
+ }
956
+ catch (err) {
957
+ return { success: false, error: String(err) };
958
+ }
959
+ finally {
960
+ client.release();
961
+ }
962
+ }
889
963
  /**
890
964
  * Write ontology schemas to local cache
891
965
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-semicolon/semo-cli",
3
- "version": "4.7.2",
3
+ "version": "4.7.4",
4
4
  "description": "SEMO CLI - AI Agent Orchestration Framework Installer",
5
5
  "main": "dist/index.js",
6
6
  "bin": {