@team-semicolon/semo-cli 4.9.0 → 4.10.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/dist/database.js +1 -4
- package/dist/env-parser.d.ts +1 -1
- package/dist/env-parser.js +1 -1
- package/dist/index.js +28 -31
- package/dist/kb.d.ts +11 -0
- package/dist/kb.js +31 -0
- package/dist/slack-notify.d.ts +1 -1
- package/dist/slack-notify.js +1 -1
- package/package.json +1 -1
package/dist/database.js
CHANGED
|
@@ -68,11 +68,8 @@ const env_parser_1 = require("./env-parser");
|
|
|
68
68
|
// ~/.claude/semo/.env 자동 로드 — LaunchAgent / Claude Code 앱 / cron 등
|
|
69
69
|
// 인터랙티브 쉘이 아닌 환경에서 환경변수를 공급한다.
|
|
70
70
|
// 이미 설정된 환경변수는 덮어쓰지 않는다 (env var > file).
|
|
71
|
-
// v4.5.0: ~/.semo.env → ~/.claude/semo/.env 이전. 구 경로 폴백 유지.
|
|
72
71
|
function loadSemoEnv() {
|
|
73
|
-
const
|
|
74
|
-
const legacyEnvFile = path.join(os.homedir(), ".semo.env");
|
|
75
|
-
const envFile = fs.existsSync(newEnvFile) ? newEnvFile : legacyEnvFile;
|
|
72
|
+
const envFile = path.join(os.homedir(), ".claude", "semo", ".env");
|
|
76
73
|
if (!fs.existsSync(envFile))
|
|
77
74
|
return;
|
|
78
75
|
try {
|
package/dist/env-parser.d.ts
CHANGED
package/dist/env-parser.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.parseEnvContent = parseEnvContent;
|
|
4
4
|
/**
|
|
5
5
|
* 공용 KEY=VALUE 파서
|
|
6
|
-
* ~/.semo
|
|
6
|
+
* ~/.claude/semo/.env 파일과 GitHub Gist 콘텐츠 모두 이 함수로 파싱한다.
|
|
7
7
|
*/
|
|
8
8
|
function parseEnvContent(content) {
|
|
9
9
|
const result = {};
|
package/dist/index.js
CHANGED
|
@@ -331,8 +331,7 @@ async function showToolsStatus() {
|
|
|
331
331
|
// === 글로벌 설정 체크 ===
|
|
332
332
|
function isGlobalSetupDone() {
|
|
333
333
|
const home = os.homedir();
|
|
334
|
-
const hasEnv = fs.existsSync(path.join(home, ".claude", "semo", ".env"))
|
|
335
|
-
fs.existsSync(path.join(home, ".semo.env")); // 하위 호환
|
|
334
|
+
const hasEnv = fs.existsSync(path.join(home, ".claude", "semo", ".env"));
|
|
336
335
|
const hasSetup = fs.existsSync(path.join(home, ".claude", "semo", "SOUL.md")) ||
|
|
337
336
|
fs.existsSync(path.join(home, ".claude", "skills")); // 하위 호환
|
|
338
337
|
return hasEnv && hasSetup;
|
|
@@ -494,9 +493,7 @@ const BASE_MCP_SERVERS = [
|
|
|
494
493
|
},
|
|
495
494
|
];
|
|
496
495
|
// === ~/.claude/semo/.env 설정 (자동 감지 → Gist → 프롬프트) ===
|
|
497
|
-
// v4.5.0: ~/.semo.env → ~/.claude/semo/.env 이전
|
|
498
496
|
const SEMO_ENV_PATH = path.join(os.homedir(), ".claude", "semo", ".env");
|
|
499
|
-
const LEGACY_ENV_PATH = path.join(os.homedir(), ".semo.env");
|
|
500
497
|
const SEMO_CREDENTIALS = [
|
|
501
498
|
{
|
|
502
499
|
key: "DATABASE_URL",
|
|
@@ -544,27 +541,9 @@ function writeSemoEnvFile(creds) {
|
|
|
544
541
|
}
|
|
545
542
|
lines.push("");
|
|
546
543
|
fs.writeFileSync(SEMO_ENV_PATH, lines.join("\n"), { mode: 0o600 });
|
|
547
|
-
// 하위 호환 심링크: ~/.semo.env → ~/.claude/semo/.env
|
|
548
|
-
try {
|
|
549
|
-
if (fs.existsSync(LEGACY_ENV_PATH)) {
|
|
550
|
-
const stat = fs.lstatSync(LEGACY_ENV_PATH);
|
|
551
|
-
if (!stat.isSymbolicLink()) {
|
|
552
|
-
// 기존 실파일은 백업 후 심링크로 교체
|
|
553
|
-
fs.renameSync(LEGACY_ENV_PATH, LEGACY_ENV_PATH + ".bak");
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
fs.unlinkSync(LEGACY_ENV_PATH);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
fs.symlinkSync(SEMO_ENV_PATH, LEGACY_ENV_PATH);
|
|
560
|
-
}
|
|
561
|
-
catch {
|
|
562
|
-
// 심링크 실패 시 무시 — 새 경로가 원본
|
|
563
|
-
}
|
|
564
544
|
}
|
|
565
545
|
function readSemoEnvCreds() {
|
|
566
|
-
|
|
567
|
-
const envFile = fs.existsSync(SEMO_ENV_PATH) ? SEMO_ENV_PATH : LEGACY_ENV_PATH;
|
|
546
|
+
const envFile = SEMO_ENV_PATH;
|
|
568
547
|
if (!fs.existsSync(envFile))
|
|
569
548
|
return {};
|
|
570
549
|
try {
|
|
@@ -740,7 +719,7 @@ async function buildKbFirstBlock() {
|
|
|
740
719
|
const label = s.desc || s.scheme_key;
|
|
741
720
|
domainGuide += `- 서비스 ${label} → \`domain: {서비스명}\`, key: \`${s.scheme_key}\`\n`;
|
|
742
721
|
}
|
|
743
|
-
domainGuide += `- 서비스 KPI → \`domain: {서비스명}\`, key: \`kpi/
|
|
722
|
+
domainGuide += `- 서비스 KPI → \`domain: {서비스명}\`, key: \`kpi/{YYYY-MM-DD}\` (최신: 가장 최근 날짜)\n`;
|
|
744
723
|
domainGuide += `- 서비스 마일스톤 → \`domain: {서비스명}\`, key: \`milestone/{slug}\`\n`;
|
|
745
724
|
domainGuide += `\n**정확한 경로를 모를 때:**\n`;
|
|
746
725
|
domainGuide += `1. \`semo kb search "검색어"\` → 결과의 \`[domain] key/sub_key\` 경로 확인\n`;
|
|
@@ -853,7 +832,7 @@ async function setupHooks(isUpdate = false) {
|
|
|
853
832
|
hooks: [
|
|
854
833
|
{
|
|
855
834
|
type: "command",
|
|
856
|
-
command: ". ~/.claude/semo/.env 2>/dev/null
|
|
835
|
+
command: ". ~/.claude/semo/.env 2>/dev/null; semo context sync 2>/dev/null || true",
|
|
857
836
|
timeout: 30,
|
|
858
837
|
},
|
|
859
838
|
],
|
|
@@ -865,7 +844,7 @@ async function setupHooks(isUpdate = false) {
|
|
|
865
844
|
hooks: [
|
|
866
845
|
{
|
|
867
846
|
type: "command",
|
|
868
|
-
command: ". ~/.claude/semo/.env 2>/dev/null
|
|
847
|
+
command: ". ~/.claude/semo/.env 2>/dev/null; semo context push 2>/dev/null || true",
|
|
869
848
|
timeout: 30,
|
|
870
849
|
},
|
|
871
850
|
],
|
|
@@ -1119,9 +1098,9 @@ npm run build # 빌드 검증
|
|
|
1119
1098
|
|
|
1120
1099
|
---
|
|
1121
1100
|
|
|
1122
|
-
## 환경변수 (\`~/.semo
|
|
1101
|
+
## 환경변수 (\`~/.claude/semo/.env\`)
|
|
1123
1102
|
|
|
1124
|
-
SEMO는 \`~/.semo
|
|
1103
|
+
SEMO는 \`~/.claude/semo/.env\` 파일에서 팀 공통 환경변수를 로드합니다.
|
|
1125
1104
|
SessionStart 훅과 OpenClaw 게이트웨이 래퍼에서 자동 source됩니다.
|
|
1126
1105
|
|
|
1127
1106
|
| 변수 | 용도 | 필수 |
|
|
@@ -1130,7 +1109,7 @@ SessionStart 훅과 OpenClaw 게이트웨이 래퍼에서 자동 source됩니다
|
|
|
1130
1109
|
| \`OPENAI_API_KEY\` | KB 임베딩용 (text-embedding-3-small) | 선택 |
|
|
1131
1110
|
| \`SLACK_WEBHOOK\` | Slack 알림 | 선택 |
|
|
1132
1111
|
|
|
1133
|
-
키 갱신이 필요하면 \`~/.semo
|
|
1112
|
+
키 갱신이 필요하면 \`~/.claude/semo/.env\`를 직접 편집하거나 \`semo onboarding -f\`를 실행하세요.
|
|
1134
1113
|
|
|
1135
1114
|
---
|
|
1136
1115
|
|
|
@@ -1754,7 +1733,7 @@ kbCmd
|
|
|
1754
1733
|
kbCmd
|
|
1755
1734
|
.command("ontology")
|
|
1756
1735
|
.description("온톨로지 조회 — 도메인/타입/스키마/라우팅 테이블")
|
|
1757
|
-
.option("--action <type>", "동작 (list|show|services|types|instances|schema|routing-table|register|add-key|remove-key)", "list")
|
|
1736
|
+
.option("--action <type>", "동작 (list|show|services|types|instances|schema|routing-table|register|create-type|add-key|remove-key)", "list")
|
|
1758
1737
|
.option("--domain <name>", "action=show|register 시 도메인")
|
|
1759
1738
|
.option("--type <name>", "action=schema|register|add-key|remove-key 시 타입 키")
|
|
1760
1739
|
.option("--key <name>", "action=add-key|remove-key 시 스키마 키")
|
|
@@ -1949,6 +1928,24 @@ kbCmd
|
|
|
1949
1928
|
}
|
|
1950
1929
|
}
|
|
1951
1930
|
}
|
|
1931
|
+
else if (action === "create-type") {
|
|
1932
|
+
if (!options.type) {
|
|
1933
|
+
console.log(chalk_1.default.red("--type 옵션이 필요합니다. (예: --type project)"));
|
|
1934
|
+
process.exit(1);
|
|
1935
|
+
}
|
|
1936
|
+
const result = await (0, kb_1.ontoCreateType)(pool, {
|
|
1937
|
+
type_key: options.type,
|
|
1938
|
+
description: options.description,
|
|
1939
|
+
});
|
|
1940
|
+
if (result.success) {
|
|
1941
|
+
console.log(chalk_1.default.green(`\n✅ 온톨로지 타입 '${options.type}' 생성 완료`));
|
|
1942
|
+
console.log(chalk_1.default.gray(`\n 다음 단계: semo kb ontology --action add-key --type ${options.type} --key <key> --key-type singleton|collection\n`));
|
|
1943
|
+
}
|
|
1944
|
+
else {
|
|
1945
|
+
console.log(chalk_1.default.red(`\n❌ 타입 생성 실패: ${result.error}\n`));
|
|
1946
|
+
process.exit(1);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1952
1949
|
else if (action === "add-key") {
|
|
1953
1950
|
if (!options.type) {
|
|
1954
1951
|
console.log(chalk_1.default.red("--type 옵션이 필요합니다. (예: --type service)"));
|
|
@@ -1993,7 +1990,7 @@ kbCmd
|
|
|
1993
1990
|
}
|
|
1994
1991
|
}
|
|
1995
1992
|
else {
|
|
1996
|
-
console.log(chalk_1.default.red(`알 수 없는 action: '${action}'. 사용 가능: list, show, services, types, instances, schema, routing-table, register, add-key, remove-key`));
|
|
1993
|
+
console.log(chalk_1.default.red(`알 수 없는 action: '${action}'. 사용 가능: list, show, services, types, instances, schema, routing-table, register, create-type, add-key, remove-key`));
|
|
1997
1994
|
process.exit(1);
|
|
1998
1995
|
}
|
|
1999
1996
|
await (0, database_1.closeConnection)();
|
package/dist/kb.d.ts
CHANGED
|
@@ -246,6 +246,17 @@ export declare function ontoAddKey(pool: Pool, opts: {
|
|
|
246
246
|
success: boolean;
|
|
247
247
|
error?: string;
|
|
248
248
|
}>;
|
|
249
|
+
/**
|
|
250
|
+
* Create a new ontology type
|
|
251
|
+
*/
|
|
252
|
+
export declare function ontoCreateType(pool: Pool, opts: {
|
|
253
|
+
type_key: string;
|
|
254
|
+
description?: string;
|
|
255
|
+
schema?: Record<string, unknown>;
|
|
256
|
+
}): Promise<{
|
|
257
|
+
success: boolean;
|
|
258
|
+
error?: string;
|
|
259
|
+
}>;
|
|
249
260
|
/**
|
|
250
261
|
* Remove a key from a type schema
|
|
251
262
|
*/
|
package/dist/kb.js
CHANGED
|
@@ -63,6 +63,7 @@ exports.ontoListServices = ontoListServices;
|
|
|
63
63
|
exports.ontoListInstances = ontoListInstances;
|
|
64
64
|
exports.ontoRegister = ontoRegister;
|
|
65
65
|
exports.ontoAddKey = ontoAddKey;
|
|
66
|
+
exports.ontoCreateType = ontoCreateType;
|
|
66
67
|
exports.ontoRemoveKey = ontoRemoveKey;
|
|
67
68
|
exports.ontoPullToLocal = ontoPullToLocal;
|
|
68
69
|
const fs = __importStar(require("fs"));
|
|
@@ -1057,6 +1058,36 @@ async function ontoAddKey(pool, opts) {
|
|
|
1057
1058
|
client.release();
|
|
1058
1059
|
}
|
|
1059
1060
|
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Create a new ontology type
|
|
1063
|
+
*/
|
|
1064
|
+
async function ontoCreateType(pool, opts) {
|
|
1065
|
+
// kebab-case 검증
|
|
1066
|
+
if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(opts.type_key)) {
|
|
1067
|
+
return { success: false, error: `타입 키 '${opts.type_key}'이(가) 유효하지 않습니다. kebab-case 소문자만 사용 가능 (예: my-type)` };
|
|
1068
|
+
}
|
|
1069
|
+
const client = await pool.connect();
|
|
1070
|
+
try {
|
|
1071
|
+
// 중복 확인
|
|
1072
|
+
const dupCheck = await client.query("SELECT type_key FROM semo.ontology_types WHERE type_key = $1", [opts.type_key]);
|
|
1073
|
+
if (dupCheck.rows.length > 0) {
|
|
1074
|
+
return { success: false, error: `타입 '${opts.type_key}'은(는) 이미 존재합니다.` };
|
|
1075
|
+
}
|
|
1076
|
+
await client.query(`INSERT INTO semo.ontology_types (type_key, schema, description)
|
|
1077
|
+
VALUES ($1, $2, $3)`, [
|
|
1078
|
+
opts.type_key,
|
|
1079
|
+
JSON.stringify(opts.schema || {}),
|
|
1080
|
+
opts.description || opts.type_key,
|
|
1081
|
+
]);
|
|
1082
|
+
return { success: true };
|
|
1083
|
+
}
|
|
1084
|
+
catch (err) {
|
|
1085
|
+
return { success: false, error: String(err) };
|
|
1086
|
+
}
|
|
1087
|
+
finally {
|
|
1088
|
+
client.release();
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1060
1091
|
/**
|
|
1061
1092
|
* Remove a key from a type schema
|
|
1062
1093
|
*/
|
package/dist/slack-notify.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Slack 알림 유틸리티
|
|
3
3
|
*
|
|
4
4
|
* SLACK_WEBHOOK 환경변수에서 URL을 읽어 알림 전송.
|
|
5
|
-
* ~/.semo
|
|
5
|
+
* ~/.claude/semo/.env에서 자동 로드됨 (database.ts loadSemoEnv).
|
|
6
6
|
*/
|
|
7
7
|
export declare function sendSlackNotification(message: string, webhookUrl?: string): Promise<boolean>;
|
|
8
8
|
export declare function formatTestFailureMessage(suiteId: string, runId: string, pass: number, fail: number, warn: number, failedLabels: string[]): string;
|
package/dist/slack-notify.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Slack 알림 유틸리티
|
|
4
4
|
*
|
|
5
5
|
* SLACK_WEBHOOK 환경변수에서 URL을 읽어 알림 전송.
|
|
6
|
-
* ~/.semo
|
|
6
|
+
* ~/.claude/semo/.env에서 자동 로드됨 (database.ts loadSemoEnv).
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.sendSlackNotification = sendSlackNotification;
|