@team-semicolon/semo-cli 4.0.3 → 4.0.5
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/commands/context.js +16 -9
- package/dist/database.js +67 -0
- package/dist/index.js +35 -15
- package/package.json +1 -1
package/dist/commands/context.js
CHANGED
|
@@ -53,6 +53,14 @@ const kb_1 = require("../kb");
|
|
|
53
53
|
// Memory file mapping
|
|
54
54
|
// ============================================================
|
|
55
55
|
const MEMORY_DIR = ".claude/memory";
|
|
56
|
+
// --out-dir 로 override 가능 (OpenClaw 봇 workspace 경로 지원)
|
|
57
|
+
function resolveMemoryDir(outDir) {
|
|
58
|
+
if (outDir) {
|
|
59
|
+
// 절대경로 또는 ~ 경로 처리
|
|
60
|
+
return outDir.replace(/^~/, require("os").homedir());
|
|
61
|
+
}
|
|
62
|
+
return path.join(process.cwd(), MEMORY_DIR);
|
|
63
|
+
}
|
|
56
64
|
const KB_DOMAIN_MAP = {
|
|
57
65
|
team: "team.md",
|
|
58
66
|
project: "projects.md",
|
|
@@ -63,10 +71,9 @@ const KB_DOMAIN_MAP = {
|
|
|
63
71
|
// ============================================================
|
|
64
72
|
// Helpers
|
|
65
73
|
// ============================================================
|
|
66
|
-
function ensureMemoryDir(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return memDir;
|
|
74
|
+
function ensureMemoryDir(resolvedDir) {
|
|
75
|
+
fs.mkdirSync(resolvedDir, { recursive: true });
|
|
76
|
+
return resolvedDir;
|
|
70
77
|
}
|
|
71
78
|
function kbEntriesToMarkdown(domain, entries) {
|
|
72
79
|
if (entries.length === 0) {
|
|
@@ -176,8 +183,8 @@ function registerContextCommands(program) {
|
|
|
176
183
|
.option("--domain <name>", "특정 KB 도메인만")
|
|
177
184
|
.option("--no-bots", "bot_status 동기화 건너뜀")
|
|
178
185
|
.option("--no-ontology", "ontology 동기화 건너뜀")
|
|
186
|
+
.option("--out-dir <path>", "메모리 파일 출력 경로 (기본: .claude/memory/). OpenClaw 봇 workspace 지원용")
|
|
179
187
|
.action(async (options) => {
|
|
180
|
-
const cwd = process.cwd();
|
|
181
188
|
const spinner = (0, ora_1.default)("context sync 시작...").start();
|
|
182
189
|
const connected = await (0, database_1.isDbConnected)();
|
|
183
190
|
if (!connected) {
|
|
@@ -186,7 +193,7 @@ function registerContextCommands(program) {
|
|
|
186
193
|
return;
|
|
187
194
|
}
|
|
188
195
|
const pool = (0, database_1.getPool)();
|
|
189
|
-
const memDir = ensureMemoryDir(
|
|
196
|
+
const memDir = ensureMemoryDir(resolveMemoryDir(options.outDir));
|
|
190
197
|
let written = 0;
|
|
191
198
|
try {
|
|
192
199
|
// 1. KB domains → memory/*.md
|
|
@@ -221,7 +228,7 @@ function registerContextCommands(program) {
|
|
|
221
228
|
written++;
|
|
222
229
|
}
|
|
223
230
|
spinner.succeed(`context sync 완료 — ${written}개 파일 업데이트`);
|
|
224
|
-
console.log(chalk_1.default.gray(` 저장 위치: ${
|
|
231
|
+
console.log(chalk_1.default.gray(` 저장 위치: ${memDir}`));
|
|
225
232
|
}
|
|
226
233
|
catch (err) {
|
|
227
234
|
spinner.fail(`context sync 실패: ${err}`);
|
|
@@ -236,9 +243,9 @@ function registerContextCommands(program) {
|
|
|
236
243
|
.description(".claude/memory/decisions.md → Core DB (semo.knowledge_base)")
|
|
237
244
|
.option("--domain <name>", "push할 도메인 (기본: decision)", "decision")
|
|
238
245
|
.option("--dry-run", "실제 push 없이 변경사항만 미리보기")
|
|
246
|
+
.option("--out-dir <path>", "메모리 파일 경로 (기본: .claude/memory/). OpenClaw 봇 workspace 지원용")
|
|
239
247
|
.action(async (options) => {
|
|
240
|
-
const
|
|
241
|
-
const memDir = path.join(cwd, MEMORY_DIR);
|
|
248
|
+
const memDir = resolveMemoryDir(options.outDir);
|
|
242
249
|
const filename = KB_DOMAIN_MAP[options.domain] || `${options.domain}.md`;
|
|
243
250
|
const filePath = path.join(memDir, filename);
|
|
244
251
|
if (!fs.existsSync(filePath)) {
|
package/dist/database.js
CHANGED
|
@@ -9,6 +9,39 @@
|
|
|
9
9
|
* - semo-system 의존성 제거
|
|
10
10
|
* - 문서 내용을 DB에서 직접 조회
|
|
11
11
|
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
12
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
46
|
exports.getPool = getPool;
|
|
14
47
|
exports.getActiveSkills = getActiveSkills;
|
|
@@ -20,6 +53,40 @@ exports.getSkillCountByCategory = getSkillCountByCategory;
|
|
|
20
53
|
exports.closeConnection = closeConnection;
|
|
21
54
|
exports.isDbConnected = isDbConnected;
|
|
22
55
|
const pg_1 = require("pg");
|
|
56
|
+
const fs = __importStar(require("fs"));
|
|
57
|
+
const os = __importStar(require("os"));
|
|
58
|
+
const path = __importStar(require("path"));
|
|
59
|
+
// ~/.semo.env 자동 로드 — LaunchAgent / Claude Code 앱 / cron 등
|
|
60
|
+
// 인터랙티브 쉘이 아닌 환경에서 환경변수를 공급한다.
|
|
61
|
+
// 이미 설정된 환경변수는 덮어쓰지 않는다 (env var > file).
|
|
62
|
+
function loadSemoEnv() {
|
|
63
|
+
const envFile = path.join(os.homedir(), ".semo.env");
|
|
64
|
+
if (!fs.existsSync(envFile))
|
|
65
|
+
return;
|
|
66
|
+
try {
|
|
67
|
+
const lines = fs.readFileSync(envFile, "utf8").split("\n");
|
|
68
|
+
for (const raw of lines) {
|
|
69
|
+
const line = raw.trim().replace(/^export\s+/, "");
|
|
70
|
+
if (!line || line.startsWith("#"))
|
|
71
|
+
continue;
|
|
72
|
+
const eqIdx = line.indexOf("=");
|
|
73
|
+
if (eqIdx === -1)
|
|
74
|
+
continue;
|
|
75
|
+
const key = line.slice(0, eqIdx).trim();
|
|
76
|
+
let val = line.slice(eqIdx + 1).trim();
|
|
77
|
+
if ((val.startsWith("'") && val.endsWith("'")) || (val.startsWith('"') && val.endsWith('"'))) {
|
|
78
|
+
val = val.slice(1, -1);
|
|
79
|
+
}
|
|
80
|
+
if (key && !process.env[key])
|
|
81
|
+
process.env[key] = val;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// 파일 읽기 실패 시 무시 — 환경변수가 없으면 이후 buildDbConfig에서 에러
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// 최초 import 시 즉시 실행
|
|
89
|
+
loadSemoEnv();
|
|
23
90
|
// PostgreSQL 연결 정보 (팀 코어)
|
|
24
91
|
// DATABASE_URL 우선, 없으면 개별 SEMO_DB_* 변수 사용
|
|
25
92
|
function buildDbConfig() {
|
package/dist/index.js
CHANGED
|
@@ -822,6 +822,8 @@ program
|
|
|
822
822
|
}
|
|
823
823
|
// 7. Hooks 설치 (대화 로깅)
|
|
824
824
|
await setupHooks(cwd, false);
|
|
825
|
+
// 7.5. ~/.semo.env 템플릿 생성 (없는 경우)
|
|
826
|
+
setupSemoEnvTemplate();
|
|
825
827
|
// 8. CLAUDE.md 생성
|
|
826
828
|
await setupClaudeMd(cwd, [], options.force);
|
|
827
829
|
// 9. 설치 검증
|
|
@@ -847,9 +849,11 @@ program
|
|
|
847
849
|
console.log(chalk_1.default.gray(" ✓ semo-agents (14개 페르소나 Agent)"));
|
|
848
850
|
console.log(chalk_1.default.gray(" ✓ semo-scripts (자동화 스크립트)"));
|
|
849
851
|
console.log(chalk_1.default.cyan("\n다음 단계:"));
|
|
850
|
-
console.log(chalk_1.default.gray(" 1.
|
|
851
|
-
console.log(chalk_1.default.gray("
|
|
852
|
-
console.log(chalk_1.default.gray("
|
|
852
|
+
console.log(chalk_1.default.gray(" 1. ~/.semo.env에 팀 DB 접속 정보 입력 (이미 존재하면 생략)"));
|
|
853
|
+
console.log(chalk_1.default.gray(" DATABASE_URL='postgres://user:pass@host:5432/appdb'"));
|
|
854
|
+
console.log(chalk_1.default.gray(" 2. Claude Code에서 프로젝트 열기 (SessionStart 훅이 자동 sync)"));
|
|
855
|
+
console.log(chalk_1.default.gray(" 3. 자연어로 요청하기 (예: \"댓글 기능 구현해줘\")"));
|
|
856
|
+
console.log(chalk_1.default.gray(" 4. /SEMO:help로 도움말 확인"));
|
|
853
857
|
console.log();
|
|
854
858
|
});
|
|
855
859
|
// === Standard 설치 (DB 기반) ===
|
|
@@ -1360,16 +1364,6 @@ function printVerificationResult(result) {
|
|
|
1360
1364
|
}
|
|
1361
1365
|
}
|
|
1362
1366
|
const BASE_MCP_SERVERS = [
|
|
1363
|
-
{
|
|
1364
|
-
name: "semo-integrations",
|
|
1365
|
-
command: "npx",
|
|
1366
|
-
args: ["-y", "@team-semicolon/semo-mcp"],
|
|
1367
|
-
env: {
|
|
1368
|
-
// Slack/GitHub/DB 토큰은 패키지에 암호화 포함됨 (설정 불필요)
|
|
1369
|
-
SUPABASE_URL: "${SUPABASE_URL}",
|
|
1370
|
-
SUPABASE_KEY: "${SUPABASE_KEY}",
|
|
1371
|
-
},
|
|
1372
|
-
},
|
|
1373
1367
|
{
|
|
1374
1368
|
name: "context7",
|
|
1375
1369
|
command: "npx",
|
|
@@ -1391,6 +1385,32 @@ const BASE_MCP_SERVERS = [
|
|
|
1391
1385
|
args: ["-y", "@modelcontextprotocol/server-github"],
|
|
1392
1386
|
},
|
|
1393
1387
|
];
|
|
1388
|
+
// === ~/.semo.env 템플릿 생성 ===
|
|
1389
|
+
function setupSemoEnvTemplate() {
|
|
1390
|
+
const envFile = path.join(os.homedir(), ".semo.env");
|
|
1391
|
+
if (fs.existsSync(envFile))
|
|
1392
|
+
return; // 이미 존재하면 건너뜀
|
|
1393
|
+
const template = `# SEMO 환경변수 — 모든 컨텍스트에서 자동 로드됨
|
|
1394
|
+
# (Claude Code 앱, OpenClaw LaunchAgent, cron 등 비인터랙티브 환경 포함)
|
|
1395
|
+
#
|
|
1396
|
+
# 팀 Core DB 접속 정보를 여기에 입력하세요.
|
|
1397
|
+
# 설정 후 Claude Code 세션을 재시작하면 자동으로 context sync가 동작합니다.
|
|
1398
|
+
|
|
1399
|
+
DATABASE_URL=''
|
|
1400
|
+
|
|
1401
|
+
# Slack 알림 Webhook (선택 — bot-ops 채널 dead-letter 감지용)
|
|
1402
|
+
SLACK_WEBHOOK=''
|
|
1403
|
+
`;
|
|
1404
|
+
try {
|
|
1405
|
+
fs.writeFileSync(envFile, template, { mode: 0o600 });
|
|
1406
|
+
console.log(chalk_1.default.cyan("\n📄 ~/.semo.env 템플릿 생성됨"));
|
|
1407
|
+
console.log(chalk_1.default.yellow(" ⚠️ DATABASE_URL을 팀 Core DB 접속 정보로 채워주세요!"));
|
|
1408
|
+
console.log(chalk_1.default.gray(` 파일: ${envFile}`));
|
|
1409
|
+
}
|
|
1410
|
+
catch {
|
|
1411
|
+
// silent — 권한 등으로 실패해도 무시
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1394
1414
|
// === Claude MCP 서버 존재 여부 확인 ===
|
|
1395
1415
|
function isMCPServerRegistered(serverName) {
|
|
1396
1416
|
try {
|
|
@@ -1597,7 +1617,7 @@ async function setupHooks(cwd, isUpdate = false) {
|
|
|
1597
1617
|
hooks: [
|
|
1598
1618
|
{
|
|
1599
1619
|
type: "command",
|
|
1600
|
-
command: "semo context sync 2>/dev/null || true",
|
|
1620
|
+
command: ". ~/.semo.env 2>/dev/null; semo context sync 2>/dev/null || true",
|
|
1601
1621
|
timeout: 30,
|
|
1602
1622
|
},
|
|
1603
1623
|
],
|
|
@@ -1631,7 +1651,7 @@ async function setupHooks(cwd, isUpdate = false) {
|
|
|
1631
1651
|
hooks: [
|
|
1632
1652
|
{
|
|
1633
1653
|
type: "command",
|
|
1634
|
-
command: "semo context push 2>/dev/null || true",
|
|
1654
|
+
command: ". ~/.semo.env 2>/dev/null; semo context push 2>/dev/null || true",
|
|
1635
1655
|
timeout: 30,
|
|
1636
1656
|
},
|
|
1637
1657
|
],
|