@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.
@@ -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(semoSystemDir);
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("semo-skills + bot-workspaces skill_definitions / agent_definitions 시딩")
729
- .option("--semo-system <path>", "semo-system 경로 (기본: ./semo-system)")
730
- .option("--reset", "시딩 기존 데이터 삭제")
731
- .option("--dry-run", "실제 DB 반영 없이 미리보기")
732
- .action(async (options) => {
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