@team-semicolon/semo-cli 4.7.0 → 4.7.2
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/bots.js +6 -216
- package/dist/index.js +117 -1024
- package/package.json +1 -1
package/dist/commands/bots.js
CHANGED
|
@@ -54,7 +54,6 @@ const os = __importStar(require("os"));
|
|
|
54
54
|
const database_1 = require("../database");
|
|
55
55
|
const sessions_1 = require("./sessions");
|
|
56
56
|
const audit_1 = require("./audit");
|
|
57
|
-
const skill_sync_1 = require("./skill-sync");
|
|
58
57
|
const context_1 = require("./context");
|
|
59
58
|
function parseIdentityMd(content) {
|
|
60
59
|
// v1 호환: IDENTITY.md 또는 SOUL.md에서 Name/Emoji/Role 파싱
|
|
@@ -398,19 +397,10 @@ function registerBotsCommands(program) {
|
|
|
398
397
|
botsCmd
|
|
399
398
|
.command("sync")
|
|
400
399
|
.description("bot-workspaces/ 스캔 → semo.bot_status DB upsert")
|
|
401
|
-
.option("--semo-system <path>", "semo-system 경로 (기본: ./semo-system)")
|
|
402
400
|
.option("--dry-run", "실제 upsert 없이 미리보기")
|
|
403
401
|
.action(async (options) => {
|
|
404
|
-
const cwd = process.cwd();
|
|
405
|
-
const semoSystemDir = options.semoSystem
|
|
406
|
-
? path.resolve(options.semoSystem)
|
|
407
|
-
: path.join(cwd, "semo-system");
|
|
408
|
-
if (!fs.existsSync(semoSystemDir)) {
|
|
409
|
-
console.log(chalk_1.default.red(`\n❌ semo-system 디렉토리를 찾을 수 없습니다: ${semoSystemDir}`));
|
|
410
|
-
process.exit(1);
|
|
411
|
-
}
|
|
412
402
|
const spinner = (0, ora_1.default)("bot-workspaces 스캔 중...").start();
|
|
413
|
-
const bots = scanBotWorkspaces(
|
|
403
|
+
const bots = scanBotWorkspaces();
|
|
414
404
|
if (bots.length === 0) {
|
|
415
405
|
spinner.warn("봇 워크스페이스가 없습니다.");
|
|
416
406
|
return;
|
|
@@ -532,23 +522,6 @@ function registerBotsCommands(program) {
|
|
|
532
522
|
catch {
|
|
533
523
|
console.log(chalk_1.default.yellow(" ⚠ audit 저장 실패 (무시)"));
|
|
534
524
|
}
|
|
535
|
-
// Skills piggyback — 스킬 파일 → skill_definitions 동기화
|
|
536
|
-
try {
|
|
537
|
-
console.log(chalk_1.default.gray(" → skills sync 실행 중..."));
|
|
538
|
-
const skillClient = await pool.connect();
|
|
539
|
-
try {
|
|
540
|
-
await skillClient.query("BEGIN");
|
|
541
|
-
const result = await (0, skill_sync_1.syncSkillsToDB)(skillClient, semoSystemDir);
|
|
542
|
-
await skillClient.query("COMMIT");
|
|
543
|
-
console.log(chalk_1.default.green(` → skills sync 완료: ${result.total}개 (봇 전용: ${result.botSpecific})`));
|
|
544
|
-
}
|
|
545
|
-
finally {
|
|
546
|
-
skillClient.release();
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
catch {
|
|
550
|
-
console.log(chalk_1.default.yellow(" ⚠ skills sync 실패 (무시)"));
|
|
551
|
-
}
|
|
552
525
|
// Files piggyback — 워크스페이스 파일 → bot_workspace_files 동기화
|
|
553
526
|
try {
|
|
554
527
|
console.log(chalk_1.default.gray(" → files sync 실행 중..."));
|
|
@@ -725,194 +698,11 @@ function registerBotsCommands(program) {
|
|
|
725
698
|
// ── semo bots seed ──────────────────────────────────────────
|
|
726
699
|
botsCmd
|
|
727
700
|
.command("seed")
|
|
728
|
-
.description("
|
|
729
|
-
.
|
|
730
|
-
.
|
|
731
|
-
.
|
|
732
|
-
.
|
|
733
|
-
const cwd = process.cwd();
|
|
734
|
-
const semoSystemDir = options.semoSystem
|
|
735
|
-
? path.resolve(options.semoSystem)
|
|
736
|
-
: path.join(cwd, "semo-system");
|
|
737
|
-
if (!fs.existsSync(semoSystemDir)) {
|
|
738
|
-
console.log(chalk_1.default.red(`\n❌ semo-system 디렉토리를 찾을 수 없습니다: ${semoSystemDir}`));
|
|
739
|
-
process.exit(1);
|
|
740
|
-
}
|
|
741
|
-
const spinner = (0, ora_1.default)("스킬/에이전트 스캔 중...").start();
|
|
742
|
-
// ─── 1+2. 스킬 스캔 (공통 모듈) ─────────────────────────
|
|
743
|
-
const botSkills = (0, skill_sync_1.scanSkills)(semoSystemDir);
|
|
744
|
-
// ─── 3. 에이전트 스캔 ─────────────────────────────────
|
|
745
|
-
const workspacesDir = path.join(semoSystemDir, "bot-workspaces");
|
|
746
|
-
const agents = [];
|
|
747
|
-
if (fs.existsSync(workspacesDir)) {
|
|
748
|
-
const botEntries = fs.readdirSync(workspacesDir, { withFileTypes: true });
|
|
749
|
-
for (const botEntry of botEntries) {
|
|
750
|
-
if (!botEntry.isDirectory())
|
|
751
|
-
continue;
|
|
752
|
-
const botDir = path.join(workspacesDir, botEntry.name);
|
|
753
|
-
// v2.0: SOUL.md에서 Identity 파싱 (IDENTITY.md fallback)
|
|
754
|
-
const soulPath2 = path.join(botDir, "SOUL.md");
|
|
755
|
-
const identityPath = path.join(botDir, "IDENTITY.md");
|
|
756
|
-
if (!fs.existsSync(soulPath2) && !fs.existsSync(identityPath))
|
|
757
|
-
continue;
|
|
758
|
-
try {
|
|
759
|
-
const identity = fs.existsSync(soulPath2)
|
|
760
|
-
? parseSoulIdentity(fs.readFileSync(soulPath2, "utf-8"), botEntry.name)
|
|
761
|
-
: parseIdentityMd(fs.readFileSync(identityPath, "utf-8"));
|
|
762
|
-
// persona_prompt = SOUL.md + \n\n---\n\n + AGENTS.md
|
|
763
|
-
const parts = [];
|
|
764
|
-
const soulPath = path.join(botDir, "SOUL.md");
|
|
765
|
-
if (fs.existsSync(soulPath)) {
|
|
766
|
-
parts.push(fs.readFileSync(soulPath, "utf-8"));
|
|
767
|
-
}
|
|
768
|
-
const agentsPath = path.join(botDir, "AGENTS.md");
|
|
769
|
-
if (fs.existsSync(agentsPath)) {
|
|
770
|
-
parts.push(fs.readFileSync(agentsPath, "utf-8"));
|
|
771
|
-
}
|
|
772
|
-
const personaPrompt = parts.join("\n\n---\n\n");
|
|
773
|
-
agents.push({
|
|
774
|
-
botId: botEntry.name,
|
|
775
|
-
name: identity.name || botEntry.name,
|
|
776
|
-
emoji: identity.emoji,
|
|
777
|
-
role: (identity.role || "custom").substring(0, 50),
|
|
778
|
-
personaPrompt,
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
|
-
catch { /* skip */ }
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
spinner.stop();
|
|
785
|
-
// ─── 미리보기 출력 ─────────────────────────────────────
|
|
786
|
-
console.log(chalk_1.default.cyan.bold("\n📦 Seed 스캔 결과\n"));
|
|
787
|
-
console.log(chalk_1.default.white(` 봇 전용 스킬 (openclaw): ${botSkills.length}개`));
|
|
788
|
-
console.log(chalk_1.default.white(` 에이전트 (봇): ${agents.length}개`));
|
|
789
|
-
if (agents.length > 0) {
|
|
790
|
-
console.log(chalk_1.default.gray("\n 에이전트:"));
|
|
791
|
-
for (const a of agents) {
|
|
792
|
-
const ownSkills = botSkills.filter(s => s.botId === a.botId);
|
|
793
|
-
console.log(chalk_1.default.gray(` ${a.emoji || "?"} ${a.botId.padEnd(14)}`) +
|
|
794
|
-
chalk_1.default.white(`${a.role}`.substring(0, 40).padEnd(42)) +
|
|
795
|
-
chalk_1.default.gray(`전용 스킬: ${ownSkills.length}`));
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
if (options.dryRun) {
|
|
799
|
-
console.log(chalk_1.default.yellow("\n [dry-run] DB 반영 없이 종료\n"));
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
// ─── DB 반영 ──────────────────────────────────────────
|
|
803
|
-
const spinnerDb = (0, ora_1.default)("DB 반영 중...").start();
|
|
804
|
-
const connected = await (0, database_1.isDbConnected)();
|
|
805
|
-
if (!connected) {
|
|
806
|
-
spinnerDb.fail("DB 연결 실패");
|
|
807
|
-
await (0, database_1.closeConnection)();
|
|
808
|
-
process.exit(1);
|
|
809
|
-
}
|
|
810
|
-
const pool = (0, database_1.getPool)();
|
|
811
|
-
const client = await pool.connect();
|
|
812
|
-
try {
|
|
813
|
-
await client.query("BEGIN");
|
|
814
|
-
// --reset: 기존 데이터 삭제
|
|
815
|
-
if (options.reset) {
|
|
816
|
-
spinnerDb.text = "기존 데이터 삭제 중...";
|
|
817
|
-
await client.query("DELETE FROM agent_definitions");
|
|
818
|
-
await client.query("DELETE FROM semo.skill_definitions WHERE office_id IS NULL");
|
|
819
|
-
}
|
|
820
|
-
// ─── 스킬 시딩 (공통 모듈) ──────────────────────────
|
|
821
|
-
spinnerDb.text = `스킬 ${botSkills.length}개 시딩 중...`;
|
|
822
|
-
await (0, skill_sync_1.syncSkillsToDB)(client, semoSystemDir);
|
|
823
|
-
// ─── 에이전트 시딩 ───────────────────────────────────
|
|
824
|
-
spinnerDb.text = `에이전트 ${agents.length}개 시딩 중...`;
|
|
825
|
-
for (const agent of agents) {
|
|
826
|
-
await client.query(`INSERT INTO agent_definitions (name, role, persona_prompt, package, avatar_config, is_active, office_id)
|
|
827
|
-
VALUES ($1, $2, $3, 'openclaw', $4, true, NULL)
|
|
828
|
-
ON CONFLICT (name, office_id) DO UPDATE SET
|
|
829
|
-
role = EXCLUDED.role,
|
|
830
|
-
persona_prompt = EXCLUDED.persona_prompt,
|
|
831
|
-
avatar_config = EXCLUDED.avatar_config,
|
|
832
|
-
updated_at = NOW()`, [
|
|
833
|
-
agent.botId,
|
|
834
|
-
agent.role,
|
|
835
|
-
agent.personaPrompt,
|
|
836
|
-
JSON.stringify({ emoji: agent.emoji }),
|
|
837
|
-
]);
|
|
838
|
-
}
|
|
839
|
-
// ─── 위임 매트릭스 시딩 ─────────────────────────────
|
|
840
|
-
spinnerDb.text = "위임 매트릭스 시딩 중...";
|
|
841
|
-
const delegationSeeds = [
|
|
842
|
-
{ from: "semiclaw", to: "infraclaw", type: "task", domains: ["infra", "cicd", "deploy", "monitoring"], method: "github_issue" },
|
|
843
|
-
{ from: "semiclaw", to: "designclaw", type: "task", domains: ["ui", "ux", "design", "reference"], method: "github_issue" },
|
|
844
|
-
{ from: "semiclaw", to: "planclaw", type: "task", domains: ["planning", "requirements", "spec"], method: "github_issue" },
|
|
845
|
-
{ from: "semiclaw", to: "reviewclaw", type: "task", domains: ["code_review", "qa", "testing"], method: "github_issue" },
|
|
846
|
-
{ from: "semiclaw", to: "workclaw", type: "task", domains: ["implementation", "dev", "bugfix"], method: "github_issue" },
|
|
847
|
-
{ from: "semiclaw", to: "growthclaw", type: "task", domains: ["marketing", "growth", "analytics", "content"], method: "github_issue" },
|
|
848
|
-
];
|
|
849
|
-
let delegationCount = 0;
|
|
850
|
-
for (const d of delegationSeeds) {
|
|
851
|
-
await client.query(`INSERT INTO semo.bot_delegation
|
|
852
|
-
(from_bot_id, to_bot_id, delegation_type, domains, method)
|
|
853
|
-
VALUES ($1, $2, $3, $4, $5)
|
|
854
|
-
ON CONFLICT (from_bot_id, to_bot_id, delegation_type) DO UPDATE SET
|
|
855
|
-
domains = EXCLUDED.domains,
|
|
856
|
-
method = EXCLUDED.method,
|
|
857
|
-
updated_at = NOW()`, [d.from, d.to, d.type, d.domains, d.method]);
|
|
858
|
-
delegationCount++;
|
|
859
|
-
}
|
|
860
|
-
// ─── 프로토콜 시딩 ──────────────────────────────────
|
|
861
|
-
spinnerDb.text = "프로토콜 메타데이터 시딩 중...";
|
|
862
|
-
const protocolSeeds = [
|
|
863
|
-
{
|
|
864
|
-
key: "task_request_format",
|
|
865
|
-
value: { template: "@{bot} [TASK] {desc}\n[PROJECT] {project}\n[PRIORITY] {priority}\n[ISSUE] {issue}" },
|
|
866
|
-
description: "태스크 요청 메시지 포맷",
|
|
867
|
-
},
|
|
868
|
-
{
|
|
869
|
-
key: "result_format",
|
|
870
|
-
value: { template: "@SemiClaw [DONE] {desc}\n[RESULT] {summary}\n[ARTIFACTS] {urls}" },
|
|
871
|
-
description: "결과 보고 메시지 포맷",
|
|
872
|
-
},
|
|
873
|
-
{
|
|
874
|
-
key: "blocked_format",
|
|
875
|
-
value: { template: "@SemiClaw [BLOCKED] {desc}\n[REASON] {reason}\n[NEED] {need}" },
|
|
876
|
-
description: "블로커 보고 메시지 포맷",
|
|
877
|
-
},
|
|
878
|
-
{
|
|
879
|
-
key: "channel_rules",
|
|
880
|
-
value: { "proj-*": "allowBots", "개발사업팀": "reportOnly" },
|
|
881
|
-
description: "채널별 봇 통신 규칙",
|
|
882
|
-
},
|
|
883
|
-
{
|
|
884
|
-
key: "general",
|
|
885
|
-
value: { max_roundtrips: 5, hub_bot: "semiclaw" },
|
|
886
|
-
description: "일반 프로토콜 설정",
|
|
887
|
-
},
|
|
888
|
-
];
|
|
889
|
-
let protocolCount = 0;
|
|
890
|
-
for (const p of protocolSeeds) {
|
|
891
|
-
await client.query(`INSERT INTO semo.bot_protocol (key, value, description)
|
|
892
|
-
VALUES ($1, $2, $3)
|
|
893
|
-
ON CONFLICT (key) DO UPDATE SET
|
|
894
|
-
value = EXCLUDED.value,
|
|
895
|
-
description = EXCLUDED.description,
|
|
896
|
-
updated_at = NOW()`, [p.key, JSON.stringify(p.value), p.description]);
|
|
897
|
-
protocolCount++;
|
|
898
|
-
}
|
|
899
|
-
await client.query("COMMIT");
|
|
900
|
-
spinnerDb.succeed("seed 완료");
|
|
901
|
-
console.log(chalk_1.default.green(` ✔ 봇 전용 스킬: ${botSkills.length}개 (metadata.bot_ids)`));
|
|
902
|
-
console.log(chalk_1.default.green(` ✔ 에이전트: ${agents.length}개`));
|
|
903
|
-
console.log(chalk_1.default.green(` ✔ 위임 매트릭스: ${delegationCount}개`));
|
|
904
|
-
console.log(chalk_1.default.green(` ✔ 프로토콜: ${protocolCount}개`));
|
|
905
|
-
console.log();
|
|
906
|
-
}
|
|
907
|
-
catch (err) {
|
|
908
|
-
await client.query("ROLLBACK");
|
|
909
|
-
spinnerDb.fail(`seed 실패: ${err}`);
|
|
910
|
-
process.exit(1);
|
|
911
|
-
}
|
|
912
|
-
finally {
|
|
913
|
-
client.release();
|
|
914
|
-
await (0, database_1.closeConnection)();
|
|
915
|
-
}
|
|
701
|
+
.description("[deprecated] 스킬/에이전트 SoT는 DB 직접 관리로 전환됨")
|
|
702
|
+
.action(async () => {
|
|
703
|
+
console.log(chalk_1.default.yellow("\n⚠ 'semo bots seed'는 더 이상 사용되지 않습니다."));
|
|
704
|
+
console.log(chalk_1.default.gray(" 스킬/에이전트 SoT는 DB(skill_definitions, agent_definitions)로 이전되었습니다."));
|
|
705
|
+
console.log(chalk_1.default.gray(" 수정은 직접 DB UPDATE 또는 마이그레이션을 사용하세요.\n"));
|
|
916
706
|
});
|
|
917
707
|
// ── semo bots cron ──────────────────────────────────────────
|
|
918
708
|
const cronCmd = botsCmd
|