@team-semicolon/semicolony-cli 4.18.51

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.
Files changed (164) hide show
  1. package/LICENSE +200 -0
  2. package/NOTICE +8 -0
  3. package/README.md +152 -0
  4. package/dist/bundle.js +2887 -0
  5. package/dist/templates/launchd/ai.semicolony.host-signals-sidecar.plist +40 -0
  6. package/migrations/001_initial.sql +280 -0
  7. package/migrations/002_add_indexes.sql +11 -0
  8. package/migrations/003_add_fk.sql +32 -0
  9. package/migrations/004_session_count_trigger.sql +35 -0
  10. package/migrations/005_bot_workspace_audits.sql +16 -0
  11. package/migrations/006_bot_kb_subscriptions.sql +24 -0
  12. package/migrations/007_flatten_skill_names.sql +16 -0
  13. package/migrations/008_bot_workspace_files.sql +14 -0
  14. package/migrations/009_enforce_flat_skill_names.sql +53 -0
  15. package/migrations/010_bot_delegation.sql +27 -0
  16. package/migrations/011_drop_legacy_tables.sql +35 -0
  17. package/migrations/012_drop_bot_knowledge.sql +16 -0
  18. package/migrations/013_kb_workspace_domains.sql +88 -0
  19. package/migrations/014_timestamp_to_timestamptz.sql +33 -0
  20. package/migrations/015_kb_domain_enforcement.sql +83 -0
  21. package/migrations/016_ontology_redesign.sql +108 -0
  22. package/migrations/017_ontology_instance_model.sql +151 -0
  23. package/migrations/018_type_schema.sql +11 -0
  24. package/migrations/019_test_management.sql +69 -0
  25. package/migrations/020_bot_workspace_standard.sql +174 -0
  26. package/migrations/021_drop_dead_tables.sql +8 -0
  27. package/migrations/021_test_declarative.sql +19 -0
  28. package/migrations/022_bot_ontology_type.sql +38 -0
  29. package/migrations/022_kb_schema_enforcement.sql +77 -0
  30. package/migrations/023_decision_key_cleanup.ts +154 -0
  31. package/migrations/024_embedding_backfill.ts +137 -0
  32. package/migrations/025_kb_flat_keys.sql +33 -0
  33. package/migrations/026_drop_session_log.sql +19 -0
  34. package/migrations/027_kb_manager_skill.sql +22 -0
  35. package/migrations/028_skill_definitions_to_semicolony.sql +19 -0
  36. package/migrations/029_agents_citation_max_lines.sql +6 -0
  37. package/migrations/030_team_ontology.sql +54 -0
  38. package/migrations/031_gfp_tables.sql +101 -0
  39. package/migrations/031_kb_manager_domain_register.sql +12 -0
  40. package/migrations/032_gfp_skill_and_callback.sql +40 -0
  41. package/migrations/032_kebab_case_keys.sql +33 -0
  42. package/migrations/033_skill_workspace_rules.sql +10 -0
  43. package/migrations/034_current_to_date_keys.sql +13 -0
  44. package/migrations/034_current_to_date_keys.ts +104 -0
  45. package/migrations/035_bot_budgets.sql +51 -0
  46. package/migrations/036_gfp_stitch_design.sql +35 -0
  47. package/migrations/037_gfp_qa_sections.sql +13 -0
  48. package/migrations/038_gfp_phase_split.sql +26 -0
  49. package/migrations/039_meeting_audio_data.sql +2 -0
  50. package/migrations/040_bot_commitments.sql +68 -0
  51. package/migrations/041_palette_previews.sql +13 -0
  52. package/migrations/042_gfp_parallel_tracks.sql +37 -0
  53. package/migrations/043_blocker_key.sql +13 -0
  54. package/migrations/044_gfp_stitch_screenshots.sql +9 -0
  55. package/migrations/045_service_table_rename_and_projection.sql +150 -0
  56. package/migrations/046_service_ops_tables.sql +75 -0
  57. package/migrations/047_update_gfp_manager_skill.sql +16 -0
  58. package/migrations/048_service_migration_skill.sql +23 -0
  59. package/migrations/049_service_features.sql +39 -0
  60. package/migrations/050_service_kpi_action_items.sql +87 -0
  61. package/migrations/051_update_service_migration_skill_prompt.sql +9 -0
  62. package/migrations/052_feature_iteration_link.sql +11 -0
  63. package/migrations/052_services_table_rename.sql +149 -0
  64. package/migrations/053_feature_discovery.sql +47 -0
  65. package/migrations/054_deploy_verification.sql +17 -0
  66. package/migrations/055_feature_lifecycle.sql +37 -0
  67. package/migrations/056_service_incident_schema.sql +16 -0
  68. package/migrations/057_decision_date_to_metadata.ts +173 -0
  69. package/migrations/058_incubator_kb_schema.sql +8 -0
  70. package/migrations/059_incubator_sessions.sql +29 -0
  71. package/migrations/060_skill_reference_files.sql +8 -0
  72. package/migrations/061_cron_trigger_bridge.sql +7 -0
  73. package/migrations/062_bot_slack_profiles.sql +21 -0
  74. package/migrations/063_rename_gfp_skills.sql +7 -0
  75. package/migrations/064_bot_cost_log.sql +11 -0
  76. package/migrations/065_hub_spoke_orchestration.sql +35 -0
  77. package/migrations/066_cost_log_commitment.sql +9 -0
  78. package/migrations/067_module_ontology_type.sql +44 -0
  79. package/migrations/068_migrate_incubator_data.sql +32 -0
  80. package/migrations/069_action_items_sot.sql +156 -0
  81. package/migrations/069_cost_log_tokens.sql +45 -0
  82. package/migrations/070_services_sot_columns.sql +78 -0
  83. package/migrations/071_cleanup_deprecated_service_kb.sql +38 -0
  84. package/migrations/072_incubator_heartbeat.sql +12 -0
  85. package/migrations/072_service_role_schema.sql +57 -0
  86. package/migrations/073_service_type_and_platform.sql +8 -0
  87. package/migrations/074_schema_ref_type.sql +16 -0
  88. package/migrations/075_routing_delegation.sql +34 -0
  89. package/migrations/076_meeting_service_link.sql +5 -0
  90. package/migrations/077_agents_ontology_type.sql +93 -0
  91. package/migrations/078_incubator_agent.sql +45 -0
  92. package/migrations/079_drop_audio_blob_url.sql +4 -0
  93. package/migrations/080_kb_metadata_indexing.sql +9 -0
  94. package/migrations/081_person_unification.sql +41 -0
  95. package/migrations/082_discord_channel.sql +3 -0
  96. package/migrations/084_deprecate_kpi_metrics_table.sql +5 -0
  97. package/migrations/085_meeting_target_domain.sql +37 -0
  98. package/migrations/086_skill_routing_new_skills.sql +23 -0
  99. package/migrations/087_skill_routing_second_opinion.sql +8 -0
  100. package/migrations/088_meeting_notion_sync.sql +10 -0
  101. package/migrations/089_bot_commitments_dedup.sql +17 -0
  102. package/migrations/090_incubator_discord_guild.sql +8 -0
  103. package/migrations/091_drop_deprecated_gfp_kpi_views.sql +65 -0
  104. package/migrations/092_ontology_channel_mapping.sql +28 -0
  105. package/migrations/093_update_bot_workspace_paths.sql +15 -0
  106. package/migrations/094_cron_run_tracking.sql +28 -0
  107. package/migrations/095_drop_bot_cron_runs.sql +7 -0
  108. package/migrations/096_kb_pipeline_keys.sql +66 -0
  109. package/migrations/097_drop_empty_service_tables.sql +83 -0
  110. package/migrations/098_migrate_features_materials_drop.sql +119 -0
  111. package/migrations/099_migrate_sections_drop.sql +52 -0
  112. package/migrations/099_seed_cron_poller_tick.sql +14 -0
  113. package/migrations/100_migrate_services_drop.sql +57 -0
  114. package/migrations/101_drop_services.sql +5 -0
  115. package/migrations/102_cost_log_session_dedup.sql +13 -0
  116. package/migrations/103_board.sql +57 -0
  117. package/migrations/104_agent_factory.sql +284 -0
  118. package/migrations/105_kb_notify.sql +44 -0
  119. package/migrations/106_office_id_agents_commands.sql +73 -0
  120. package/migrations/107_agent_service_credentials.sql +79 -0
  121. package/migrations/108_drop_l2_ontology_seeds.sql +16 -0
  122. package/migrations/109_generic_ontology_descriptions.sql +21 -0
  123. package/migrations/110_kb_manager_skill_l2_cleanup.sql +34 -0
  124. package/migrations/111_drop_l2_team_ontology_seeds.sql +20 -0
  125. package/migrations/112_fix_pipeline_schema_and_role_trigger.sql +83 -0
  126. package/migrations/113_kb_history.sql +53 -0
  127. package/migrations/114_runtime_target_columns.sql +26 -0
  128. package/migrations/115_runtime_source_columns.sql +52 -0
  129. package/migrations/116_commitment_pattern_health.sql +81 -0
  130. package/migrations/117_commitment_pattern_alert_claims.sql +33 -0
  131. package/migrations/118_retire_semiclaw_semicolonybot_alias.sql +30 -0
  132. package/migrations/119_commitment_events.sql +51 -0
  133. package/migrations/120_host_signals.sql +51 -0
  134. package/migrations/121_agent_spec_derived_runtime_metadata.sql +68 -0
  135. package/migrations/122_agent_spec_derived_fk_policy.sql +18 -0
  136. package/migrations/123_agent_spec_projection_skip_targets.sql +24 -0
  137. package/migrations/124_bot_commitments_notify.sql +26 -0
  138. package/migrations/125_agent_personas.sql +36 -0
  139. package/migrations/128_entity_relations.sql +125 -0
  140. package/migrations/129_kb_multitenant_gateway.sql +86 -0
  141. package/migrations/130_jewelry_relation_types.sql +36 -0
  142. package/migrations/131_concierge_requests.sql +38 -0
  143. package/migrations/132_kb_supersession.sql +27 -0
  144. package/migrations/133_kb_claims.sql +52 -0
  145. package/migrations/134_migration_registry.sql +18 -0
  146. package/migrations/135_sc_roles.sql +102 -0
  147. package/migrations/136_runtime_tenant_columns.sql +71 -0
  148. package/migrations/137_runtime_backfill_team_semicolon.sql +131 -0
  149. package/migrations/138_runtime_constraints_not_valid.sql +56 -0
  150. package/migrations/139_tenant_vms.sql +31 -0
  151. package/migrations/140_fleet_slots.sql +23 -0
  152. package/migrations/141_provisioning_jobs.sql +29 -0
  153. package/migrations/142_rls_enable_semicolony.sql +129 -0
  154. package/migrations/143_rls_enable_public.sql +54 -0
  155. package/migrations/144_worker_device_registry.sql +211 -0
  156. package/migrations/145_workspace_profile_workstation_policy.sql +39 -0
  157. package/migrations/146_worker_profile_repo_scope.sql +24 -0
  158. package/migrations/147_worker_profile_semo_ops_repo_rename.sql +18 -0
  159. package/migrations/148_momo_runtime_convergence.sql +92 -0
  160. package/migrations/README.md +88 -0
  161. package/migrations-sqlite/kb/001_initial.sql +56 -0
  162. package/migrations-sqlite/ops/001_initial.sql +53 -0
  163. package/migrations-sqlite/ops/002_action_items.sql +48 -0
  164. package/package.json +78 -0
@@ -0,0 +1,40 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ SemiColony host-signals sidecar — pushes pgrep/PID/auth-profile signals to semicolony.host_signals.
4
+
5
+ Spec: KB semicolony decision/dashboard-host-signals-sidecar-2026-05-07
6
+ Action item: 4ddac18f
7
+
8
+ Install:
9
+ cp packages/cli/templates/launchd/ai.semicolony.host-signals-sidecar.plist \
10
+ ~/Library/LaunchAgents/
11
+ launchctl load ~/Library/LaunchAgents/ai.semicolony.host-signals-sidecar.plist
12
+
13
+ Verify:
14
+ launchctl list | grep host-signals
15
+ semicolony host-signals show --limit 20
16
+
17
+ Polling: every 90 seconds (StartInterval). 5-minute dashboard freshness window
18
+ means missing 3 cycles → stale_count increments → daemon detection alert.
19
+ -->
20
+ <plist version="1.0">
21
+ <dict>
22
+ <key>Label</key><string>ai.semicolony.host-signals-sidecar</string>
23
+ <key>ProgramArguments</key>
24
+ <array>
25
+ <string>/usr/local/bin/semicolony</string>
26
+ <string>host-signals</string>
27
+ <string>push</string>
28
+ <string>--quiet</string>
29
+ </array>
30
+ <key>StartInterval</key><integer>90</integer>
31
+ <key>StandardOutPath</key><string>/Users/reus/.semicolony/logs/host-signals-sidecar.log</string>
32
+ <key>StandardErrorPath</key><string>/Users/reus/.semicolony/logs/host-signals-sidecar.log</string>
33
+ <key>EnvironmentVariables</key>
34
+ <dict>
35
+ <key>HOME</key><string>/Users/reus</string>
36
+ <key>PATH</key><string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
37
+ </dict>
38
+ <key>RunAtLoad</key><true/>
39
+ </dict>
40
+ </plist>
@@ -0,0 +1,280 @@
1
+ -- 001_initial.sql
2
+ -- 기존 semicolony 스키마 문서화 (모든 테이블은 이미 존재)
3
+ -- IF NOT EXISTS로 안전하게 작성 — 새 환경에서도 적용 가능
4
+
5
+ CREATE SCHEMA IF NOT EXISTS semicolony;
6
+
7
+ -- pgvector
8
+ CREATE EXTENSION IF NOT EXISTS vector;
9
+
10
+ -- ============================================================
11
+ -- 1. skills
12
+ -- ============================================================
13
+ CREATE TABLE IF NOT EXISTS semicolony.skills (
14
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
15
+ name VARCHAR(100) NOT NULL UNIQUE,
16
+ display_name VARCHAR(100),
17
+ description TEXT,
18
+ content TEXT NOT NULL,
19
+ category VARCHAR(50),
20
+ package VARCHAR(50) DEFAULT 'core',
21
+ is_active BOOLEAN DEFAULT true,
22
+ is_required BOOLEAN DEFAULT false,
23
+ install_order INT DEFAULT 100,
24
+ version VARCHAR(20) DEFAULT '1.0.0',
25
+ metadata JSONB DEFAULT '{}',
26
+ created_at TIMESTAMPTZ DEFAULT now(),
27
+ updated_at TIMESTAMPTZ DEFAULT now()
28
+ );
29
+
30
+ -- ============================================================
31
+ -- 2. commands
32
+ -- ============================================================
33
+ CREATE TABLE IF NOT EXISTS semicolony.commands (
34
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
35
+ name VARCHAR(100) NOT NULL,
36
+ folder VARCHAR(50) NOT NULL,
37
+ content TEXT NOT NULL,
38
+ description TEXT,
39
+ is_active BOOLEAN DEFAULT true,
40
+ created_at TIMESTAMPTZ DEFAULT now(),
41
+ updated_at TIMESTAMPTZ DEFAULT now(),
42
+ UNIQUE(folder, name)
43
+ );
44
+
45
+ -- ============================================================
46
+ -- 3. agents
47
+ -- ============================================================
48
+ CREATE TABLE IF NOT EXISTS semicolony.agents (
49
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
50
+ name VARCHAR(100) NOT NULL UNIQUE,
51
+ display_name VARCHAR(100),
52
+ content TEXT NOT NULL,
53
+ package VARCHAR(50) DEFAULT 'core',
54
+ is_active BOOLEAN DEFAULT true,
55
+ install_order INT DEFAULT 100,
56
+ created_at TIMESTAMPTZ DEFAULT now(),
57
+ updated_at TIMESTAMPTZ DEFAULT now()
58
+ );
59
+
60
+ -- ============================================================
61
+ -- 4. packages
62
+ -- ============================================================
63
+ CREATE TABLE IF NOT EXISTS semicolony.packages (
64
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
65
+ name VARCHAR(100) NOT NULL UNIQUE,
66
+ display_name VARCHAR(100),
67
+ description TEXT,
68
+ layer VARCHAR(50),
69
+ package_type VARCHAR(50),
70
+ version VARCHAR(20) DEFAULT '1.0.0',
71
+ is_active BOOLEAN DEFAULT true,
72
+ is_required BOOLEAN DEFAULT false,
73
+ install_order INT DEFAULT 100,
74
+ created_at TIMESTAMPTZ DEFAULT now(),
75
+ updated_at TIMESTAMPTZ DEFAULT now()
76
+ );
77
+
78
+ -- ============================================================
79
+ -- 5. knowledge_base (공통 KB)
80
+ -- ============================================================
81
+ CREATE TABLE IF NOT EXISTS semicolony.knowledge_base (
82
+ kb_id BIGSERIAL PRIMARY KEY,
83
+ domain VARCHAR(100) NOT NULL,
84
+ key VARCHAR(255) NOT NULL,
85
+ content TEXT NOT NULL,
86
+ embedding vector(1024),
87
+ metadata JSONB DEFAULT '{}',
88
+ version INT DEFAULT 1,
89
+ created_by VARCHAR(50),
90
+ created_at TIMESTAMPTZ DEFAULT NOW(),
91
+ updated_at TIMESTAMPTZ DEFAULT NOW(),
92
+ UNIQUE(domain, key)
93
+ );
94
+
95
+ -- ============================================================
96
+ -- 6. bot_knowledge (봇별 KB)
97
+ -- ============================================================
98
+ CREATE TABLE IF NOT EXISTS semicolony.bot_knowledge (
99
+ id BIGSERIAL PRIMARY KEY,
100
+ bot_id VARCHAR(50) NOT NULL,
101
+ domain VARCHAR(100) NOT NULL,
102
+ key VARCHAR(255) NOT NULL,
103
+ content TEXT NOT NULL,
104
+ embedding vector(1024),
105
+ metadata JSONB DEFAULT '{}',
106
+ version INT DEFAULT 1,
107
+ synced_at TIMESTAMPTZ,
108
+ created_at TIMESTAMPTZ DEFAULT NOW(),
109
+ updated_at TIMESTAMPTZ DEFAULT NOW(),
110
+ UNIQUE(bot_id, domain, key)
111
+ );
112
+
113
+ -- ============================================================
114
+ -- 7. ontology (도메인 스키마)
115
+ -- ============================================================
116
+ CREATE TABLE IF NOT EXISTS semicolony.ontology (
117
+ id BIGSERIAL PRIMARY KEY,
118
+ domain VARCHAR(100) NOT NULL UNIQUE,
119
+ schema JSONB NOT NULL,
120
+ description TEXT,
121
+ version INT DEFAULT 1,
122
+ created_at TIMESTAMPTZ DEFAULT NOW(),
123
+ updated_at TIMESTAMPTZ DEFAULT NOW()
124
+ );
125
+
126
+ -- ============================================================
127
+ -- 8. bot_status
128
+ -- ============================================================
129
+ CREATE TABLE IF NOT EXISTS semicolony.bot_status (
130
+ bot_id TEXT PRIMARY KEY,
131
+ name TEXT,
132
+ emoji TEXT,
133
+ role TEXT,
134
+ last_active TIMESTAMP,
135
+ session_count INTEGER DEFAULT 0,
136
+ workspace_path TEXT,
137
+ status TEXT,
138
+ synced_at TIMESTAMP DEFAULT NOW()
139
+ );
140
+
141
+ -- ============================================================
142
+ -- 9. bot_sessions
143
+ -- ============================================================
144
+ CREATE TABLE IF NOT EXISTS semicolony.bot_sessions (
145
+ bot_id TEXT NOT NULL,
146
+ session_key TEXT NOT NULL,
147
+ label TEXT,
148
+ kind TEXT,
149
+ chat_type TEXT,
150
+ last_activity TIMESTAMP,
151
+ message_count INTEGER DEFAULT 0,
152
+ synced_at TIMESTAMP DEFAULT NOW(),
153
+ PRIMARY KEY (bot_id, session_key)
154
+ );
155
+
156
+ -- ============================================================
157
+ -- 10. bot_cron_jobs
158
+ -- ============================================================
159
+ CREATE TABLE IF NOT EXISTS semicolony.bot_cron_jobs (
160
+ bot_id TEXT NOT NULL,
161
+ job_id TEXT NOT NULL,
162
+ name TEXT,
163
+ schedule JSONB,
164
+ enabled BOOLEAN DEFAULT TRUE,
165
+ last_run TIMESTAMP,
166
+ next_run TIMESTAMP,
167
+ session_target TEXT,
168
+ synced_at TIMESTAMP DEFAULT NOW(),
169
+ PRIMARY KEY (bot_id, job_id)
170
+ );
171
+
172
+ -- ============================================================
173
+ -- 11. bot_query_logs
174
+ -- ============================================================
175
+ CREATE TABLE IF NOT EXISTS semicolony.bot_query_logs (
176
+ id BIGSERIAL PRIMARY KEY,
177
+ bot_id TEXT NOT NULL,
178
+ user_id TEXT,
179
+ user_name TEXT,
180
+ channel TEXT,
181
+ channel_id TEXT,
182
+ thread_id TEXT,
183
+ query TEXT NOT NULL,
184
+ response TEXT,
185
+ model TEXT,
186
+ latency_ms INTEGER,
187
+ token_input INTEGER,
188
+ token_output INTEGER,
189
+ metadata JSONB DEFAULT '{}',
190
+ created_at TIMESTAMPTZ DEFAULT NOW()
191
+ );
192
+
193
+ -- ============================================================
194
+ -- 기존 인덱스 (이미 존재하면 skip)
195
+ -- ============================================================
196
+ CREATE INDEX IF NOT EXISTS idx_semicolony_skills_name ON semicolony.skills(name);
197
+ CREATE INDEX IF NOT EXISTS idx_semicolony_skills_active ON semicolony.skills(is_active);
198
+ CREATE INDEX IF NOT EXISTS idx_semicolony_skills_category ON semicolony.skills(category);
199
+ CREATE INDEX IF NOT EXISTS idx_semicolony_commands_folder ON semicolony.commands(folder);
200
+ CREATE INDEX IF NOT EXISTS idx_semicolony_commands_active ON semicolony.commands(is_active);
201
+ CREATE INDEX IF NOT EXISTS idx_semicolony_agents_name ON semicolony.agents(name);
202
+ CREATE INDEX IF NOT EXISTS idx_semicolony_agents_active ON semicolony.agents(is_active);
203
+ CREATE INDEX IF NOT EXISTS idx_semicolony_packages_name ON semicolony.packages(name);
204
+ CREATE INDEX IF NOT EXISTS idx_semicolony_packages_layer ON semicolony.packages(layer);
205
+ CREATE INDEX IF NOT EXISTS idx_semicolony_packages_active ON semicolony.packages(is_active);
206
+ CREATE INDEX IF NOT EXISTS idx_kb_domain ON semicolony.knowledge_base(domain);
207
+ CREATE INDEX IF NOT EXISTS idx_kb_created_by ON semicolony.knowledge_base(created_by);
208
+ CREATE INDEX IF NOT EXISTS idx_kb_updated_at ON semicolony.knowledge_base(updated_at DESC);
209
+ CREATE INDEX IF NOT EXISTS idx_kb_embedding ON semicolony.knowledge_base USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);
210
+ CREATE INDEX IF NOT EXISTS idx_botk_bot_id ON semicolony.bot_knowledge(bot_id);
211
+ CREATE INDEX IF NOT EXISTS idx_botk_domain ON semicolony.bot_knowledge(bot_id, domain);
212
+ CREATE INDEX IF NOT EXISTS idx_botk_updated_at ON semicolony.bot_knowledge(updated_at DESC);
213
+ CREATE INDEX IF NOT EXISTS idx_botk_embedding ON semicolony.bot_knowledge USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);
214
+ CREATE INDEX IF NOT EXISTS idx_bot_status_synced_at ON semicolony.bot_status(synced_at);
215
+ CREATE INDEX IF NOT EXISTS idx_bot_sessions_bot_id ON semicolony.bot_sessions(bot_id);
216
+ CREATE INDEX IF NOT EXISTS idx_bot_sessions_last_activity ON semicolony.bot_sessions(last_activity DESC);
217
+ CREATE INDEX IF NOT EXISTS idx_bot_cron_jobs_bot_id ON semicolony.bot_cron_jobs(bot_id);
218
+ CREATE INDEX IF NOT EXISTS idx_bot_cron_jobs_next_run ON semicolony.bot_cron_jobs(next_run);
219
+
220
+ -- ============================================================
221
+ -- Helper: updated_at trigger function
222
+ -- ============================================================
223
+ CREATE OR REPLACE FUNCTION semicolony.update_updated_at()
224
+ RETURNS TRIGGER AS $$
225
+ BEGIN
226
+ NEW.updated_at = NOW();
227
+ NEW.version = COALESCE(OLD.version, 0) + 1;
228
+ RETURN NEW;
229
+ END;
230
+ $$ LANGUAGE plpgsql;
231
+
232
+ DO $$
233
+ BEGIN
234
+ IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_kb_updated_at') THEN
235
+ CREATE TRIGGER trg_kb_updated_at BEFORE UPDATE ON semicolony.knowledge_base
236
+ FOR EACH ROW EXECUTE FUNCTION semicolony.update_updated_at();
237
+ END IF;
238
+ IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_botk_updated_at') THEN
239
+ CREATE TRIGGER trg_botk_updated_at BEFORE UPDATE ON semicolony.bot_knowledge
240
+ FOR EACH ROW EXECUTE FUNCTION semicolony.update_updated_at();
241
+ END IF;
242
+ IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'trg_onto_updated_at') THEN
243
+ CREATE TRIGGER trg_onto_updated_at BEFORE UPDATE ON semicolony.ontology
244
+ FOR EACH ROW EXECUTE FUNCTION semicolony.update_updated_at();
245
+ END IF;
246
+ END $$;
247
+
248
+ -- ============================================================
249
+ -- Helper functions
250
+ -- ============================================================
251
+ CREATE OR REPLACE FUNCTION semicolony.get_active_skills()
252
+ RETURNS TABLE (
253
+ name VARCHAR(100),
254
+ content TEXT,
255
+ category VARCHAR(50),
256
+ install_order INT
257
+ ) AS $$
258
+ BEGIN
259
+ RETURN QUERY
260
+ SELECT s.name, s.content, s.category, s.install_order
261
+ FROM semicolony.skills s
262
+ WHERE s.is_active = true
263
+ ORDER BY s.install_order;
264
+ END;
265
+ $$ LANGUAGE plpgsql;
266
+
267
+ CREATE OR REPLACE FUNCTION semicolony.get_skill_count_by_category()
268
+ RETURNS TABLE (
269
+ category VARCHAR(50),
270
+ count BIGINT
271
+ ) AS $$
272
+ BEGIN
273
+ RETURN QUERY
274
+ SELECT s.category, COUNT(*)::BIGINT
275
+ FROM semicolony.skills s
276
+ WHERE s.is_active = true
277
+ GROUP BY s.category
278
+ ORDER BY MIN(s.install_order);
279
+ END;
280
+ $$ LANGUAGE plpgsql;
@@ -0,0 +1,11 @@
1
+ -- 002_add_indexes.sql
2
+ -- P1-2: 누락된 성능 인덱스 추가
3
+
4
+ -- bot_status.status 필터용
5
+ CREATE INDEX IF NOT EXISTS idx_bot_status_status ON semicolony.bot_status(status);
6
+
7
+ -- bot_query_logs 조회 최적화
8
+ CREATE INDEX IF NOT EXISTS idx_bql_bot_id ON semicolony.bot_query_logs(bot_id);
9
+ CREATE INDEX IF NOT EXISTS idx_bql_user_id ON semicolony.bot_query_logs(user_id);
10
+ CREATE INDEX IF NOT EXISTS idx_bql_created_at ON semicolony.bot_query_logs(created_at DESC);
11
+ CREATE INDEX IF NOT EXISTS idx_bql_channel_id ON semicolony.bot_query_logs(channel_id);
@@ -0,0 +1,32 @@
1
+ -- 003_add_fk.sql
2
+ -- P1-3: FK 제약 추가
3
+
4
+ -- bot_sessions.bot_id → bot_status.bot_id
5
+ DO $$
6
+ BEGIN
7
+ IF NOT EXISTS (
8
+ SELECT 1 FROM information_schema.table_constraints
9
+ WHERE constraint_name = 'fk_sessions_bot'
10
+ AND table_schema = 'semicolony'
11
+ ) THEN
12
+ ALTER TABLE semicolony.bot_sessions
13
+ ADD CONSTRAINT fk_sessions_bot
14
+ FOREIGN KEY (bot_id) REFERENCES semicolony.bot_status(bot_id) ON DELETE CASCADE;
15
+ END IF;
16
+ END $$;
17
+
18
+ -- bot_cron_jobs.bot_id → bot_status.bot_id
19
+ DO $$
20
+ BEGIN
21
+ IF NOT EXISTS (
22
+ SELECT 1 FROM information_schema.table_constraints
23
+ WHERE constraint_name = 'fk_cron_jobs_bot'
24
+ AND table_schema = 'semicolony'
25
+ ) THEN
26
+ ALTER TABLE semicolony.bot_cron_jobs
27
+ ADD CONSTRAINT fk_cron_jobs_bot
28
+ FOREIGN KEY (bot_id) REFERENCES semicolony.bot_status(bot_id) ON DELETE CASCADE;
29
+ END IF;
30
+ END $$;
31
+
32
+ -- bot_query_logs는 FK 제외 — 로그 테이블은 참조 무결성보다 적재 안정성 우선
@@ -0,0 +1,35 @@
1
+ -- 004_session_count_trigger.sql
2
+ -- P0-3: session_count 경쟁 조건 해결
3
+ -- bot_sessions INSERT/DELETE 시 bot_status.session_count 자동 갱신 트리거
4
+
5
+ CREATE OR REPLACE FUNCTION semicolony.update_session_count()
6
+ RETURNS TRIGGER AS $$
7
+ DECLARE
8
+ target_bot_id TEXT;
9
+ BEGIN
10
+ IF TG_OP = 'DELETE' THEN
11
+ target_bot_id := OLD.bot_id;
12
+ ELSE
13
+ target_bot_id := NEW.bot_id;
14
+ END IF;
15
+
16
+ UPDATE semicolony.bot_status
17
+ SET session_count = (
18
+ SELECT COUNT(*) FROM semicolony.bot_sessions WHERE bot_id = target_bot_id
19
+ )
20
+ WHERE bot_id = target_bot_id;
21
+
22
+ IF TG_OP = 'DELETE' THEN
23
+ RETURN OLD;
24
+ ELSE
25
+ RETURN NEW;
26
+ END IF;
27
+ END;
28
+ $$ LANGUAGE plpgsql;
29
+
30
+ -- 기존 트리거 있으면 교체
31
+ DROP TRIGGER IF EXISTS trg_session_count ON semicolony.bot_sessions;
32
+
33
+ CREATE TRIGGER trg_session_count
34
+ AFTER INSERT OR DELETE ON semicolony.bot_sessions
35
+ FOR EACH ROW EXECUTE FUNCTION semicolony.update_session_count();
@@ -0,0 +1,16 @@
1
+ -- Bot Workspace Audits
2
+ -- 봇 워크스페이스 표준 구조 compliance 감사 결과 저장
3
+
4
+ CREATE TABLE IF NOT EXISTS semicolony.bot_workspace_audits (
5
+ id BIGSERIAL PRIMARY KEY,
6
+ bot_id TEXT NOT NULL,
7
+ run_id TEXT NOT NULL,
8
+ rating TEXT NOT NULL, -- 'GOOD' | 'NEEDS-WORK' | 'POOR'
9
+ score INT NOT NULL DEFAULT 0, -- 0-100
10
+ checks JSONB NOT NULL, -- [{ name, passed, detail }]
11
+ created_at TIMESTAMPTZ DEFAULT NOW()
12
+ );
13
+
14
+ CREATE INDEX IF NOT EXISTS idx_bwa_bot_id ON semicolony.bot_workspace_audits(bot_id);
15
+ CREATE INDEX IF NOT EXISTS idx_bwa_run_id ON semicolony.bot_workspace_audits(run_id);
16
+ CREATE INDEX IF NOT EXISTS idx_bwa_created_at ON semicolony.bot_workspace_audits(created_at DESC);
@@ -0,0 +1,24 @@
1
+ -- 006: Bot KB Subscriptions
2
+ -- 봇별 KB 도메인 구독 + 변경 다이제스트 워터마크
3
+
4
+ CREATE TABLE IF NOT EXISTS semicolony.bot_kb_subscriptions (
5
+ bot_id VARCHAR(50) NOT NULL,
6
+ domain VARCHAR(100) NOT NULL,
7
+ last_synced_at TIMESTAMPTZ DEFAULT '1970-01-01T00:00:00Z',
8
+ created_at TIMESTAMPTZ DEFAULT NOW(),
9
+ PRIMARY KEY (bot_id, domain)
10
+ );
11
+
12
+ CREATE INDEX IF NOT EXISTS idx_bot_kb_subs_bot_id
13
+ ON semicolony.bot_kb_subscriptions(bot_id);
14
+
15
+ -- Seed: 봇별 구독 도메인
16
+ INSERT INTO semicolony.bot_kb_subscriptions (bot_id, domain) VALUES
17
+ ('semiclaw', 'team'), ('semiclaw', 'project'), ('semiclaw', 'decision'), ('semiclaw', 'process'), ('semiclaw', 'infra'),
18
+ ('workclaw', 'team'), ('workclaw', 'project'), ('workclaw', 'decision'), ('workclaw', 'process'),
19
+ ('reviewclaw', 'team'), ('reviewclaw', 'project'), ('reviewclaw', 'decision'),
20
+ ('planclaw', 'team'), ('planclaw', 'project'), ('planclaw', 'decision'), ('planclaw', 'process'),
21
+ ('designclaw', 'team'), ('designclaw', 'project'), ('designclaw', 'decision'),
22
+ ('infraclaw', 'team'), ('infraclaw', 'infra'), ('infraclaw', 'decision'),
23
+ ('growthclaw', 'team'), ('growthclaw', 'project'), ('growthclaw', 'decision')
24
+ ON CONFLICT DO NOTHING;
@@ -0,0 +1,16 @@
1
+ -- 007_flatten_skill_names.sql
2
+ -- Flat skill naming: botId/skillName → skillName + metadata.bot_ids
3
+ --
4
+ -- 1. metadata.bot_id (string) → metadata.bot_ids (array) 변환
5
+ -- 2. name에서 botId/ 프리픽스 제거
6
+
7
+ -- Step 1: metadata.bot_id → metadata.bot_ids 배열로 변환
8
+ UPDATE skill_definitions
9
+ SET metadata = (metadata - 'bot_id') || jsonb_build_object('bot_ids', jsonb_build_array(metadata->>'bot_id'))
10
+ WHERE metadata ? 'bot_id';
11
+
12
+ -- Step 2: name에서 botId/ 프리픽스 제거
13
+ UPDATE skill_definitions
14
+ SET name = SUBSTRING(name FROM POSITION('/' IN name) + 1)
15
+ WHERE name LIKE '%/%'
16
+ AND office_id IS NULL;
@@ -0,0 +1,14 @@
1
+ -- Bot workspace file storage for Dashboard DB-based access
2
+ -- Replaces local filesystem reads with DB queries
3
+
4
+ CREATE TABLE IF NOT EXISTS semicolony.bot_workspace_files (
5
+ bot_id TEXT NOT NULL,
6
+ file_path TEXT NOT NULL,
7
+ content TEXT NOT NULL,
8
+ file_size INTEGER,
9
+ file_hash TEXT, -- SHA-256, 변경 감지용
10
+ synced_at TIMESTAMPTZ DEFAULT NOW(),
11
+ PRIMARY KEY (bot_id, file_path)
12
+ );
13
+
14
+ CREATE INDEX IF NOT EXISTS idx_bwf_bot ON semicolony.bot_workspace_files(bot_id);
@@ -0,0 +1,53 @@
1
+ -- 009_enforce_flat_skill_names.sql
2
+ -- Enforce flat skill names (strip any remaining slashes)
3
+ -- Also add CHECK constraint to prevent future slashes
4
+
5
+ -- Step 1: flat 이름이 이미 존재하면 slash 버전의 metadata.bot_ids를 flat 버전에 병합 후 삭제
6
+ -- (slash 버전에만 있는 bot_ids를 보존)
7
+ WITH slash_skills AS (
8
+ SELECT id, name,
9
+ SUBSTRING(name FROM POSITION('/' IN name) + 1) AS flat_name,
10
+ metadata
11
+ FROM skill_definitions
12
+ WHERE name LIKE '%/%' AND office_id IS NULL
13
+ ),
14
+ has_flat AS (
15
+ SELECT s.id AS slash_id, s.flat_name, s.metadata AS slash_meta,
16
+ f.id AS flat_id, f.metadata AS flat_meta
17
+ FROM slash_skills s
18
+ JOIN skill_definitions f ON f.name = s.flat_name AND f.office_id IS NULL
19
+ )
20
+ -- Merge bot_ids from slash version into flat version
21
+ UPDATE skill_definitions f
22
+ SET metadata = CASE
23
+ WHEN f.metadata ? 'bot_ids' AND hf.slash_meta ? 'bot_ids'
24
+ THEN jsonb_set(f.metadata, '{bot_ids}',
25
+ (SELECT jsonb_agg(DISTINCT val)
26
+ FROM (
27
+ SELECT val FROM jsonb_array_elements(f.metadata->'bot_ids') val
28
+ UNION
29
+ SELECT val FROM jsonb_array_elements(hf.slash_meta->'bot_ids') val
30
+ ) combined))
31
+ WHEN hf.slash_meta ? 'bot_ids'
32
+ THEN COALESCE(f.metadata, '{}'::jsonb) || jsonb_build_object('bot_ids', hf.slash_meta->'bot_ids')
33
+ ELSE f.metadata
34
+ END
35
+ FROM has_flat hf
36
+ WHERE f.id = hf.flat_id;
37
+
38
+ -- Step 2: Delete slash versions that have a flat counterpart
39
+ DELETE FROM skill_definitions
40
+ WHERE name LIKE '%/%'
41
+ AND office_id IS NULL
42
+ AND SUBSTRING(name FROM POSITION('/' IN name) + 1) IN (
43
+ SELECT name FROM skill_definitions WHERE name NOT LIKE '%/%' AND office_id IS NULL
44
+ );
45
+
46
+ -- Step 3: Rename remaining slash versions (no flat counterpart)
47
+ UPDATE skill_definitions
48
+ SET name = SUBSTRING(name FROM POSITION('/' IN name) + 1)
49
+ WHERE name LIKE '%/%' AND office_id IS NULL;
50
+
51
+ -- Step 4: Prevent future slashes
52
+ ALTER TABLE skill_definitions
53
+ ADD CONSTRAINT skill_name_no_slash CHECK (name NOT LIKE '%/%');
@@ -0,0 +1,27 @@
1
+ -- 010_bot_delegation.sql
2
+ -- Bot delegation matrix: who can delegate what to whom
3
+
4
+ CREATE TABLE IF NOT EXISTS semicolony.bot_delegation (
5
+ id SERIAL PRIMARY KEY,
6
+ from_bot_id TEXT NOT NULL,
7
+ to_bot_id TEXT NOT NULL,
8
+ delegation_type TEXT NOT NULL DEFAULT 'task',
9
+ domains TEXT[] NOT NULL DEFAULT '{}',
10
+ method TEXT NOT NULL DEFAULT 'github_issue',
11
+ channel TEXT,
12
+ max_roundtrips INT DEFAULT 5,
13
+ priority TEXT DEFAULT 'medium',
14
+ is_active BOOLEAN DEFAULT true,
15
+ created_at TIMESTAMPTZ DEFAULT NOW(),
16
+ updated_at TIMESTAMPTZ DEFAULT NOW(),
17
+ UNIQUE (from_bot_id, to_bot_id, delegation_type)
18
+ );
19
+
20
+ -- Protocol metadata (structured version of PROTOCOL.md)
21
+ CREATE TABLE IF NOT EXISTS semicolony.bot_protocol (
22
+ id SERIAL PRIMARY KEY,
23
+ key TEXT NOT NULL UNIQUE,
24
+ value JSONB NOT NULL,
25
+ description TEXT,
26
+ updated_at TIMESTAMPTZ DEFAULT NOW()
27
+ );
@@ -0,0 +1,35 @@
1
+ -- 011_drop_legacy_tables.sql
2
+ -- 레거시 정규화 테이블 정리: KB(domain='project')가 SoT로 확정됨
3
+ -- FK 의존관계 순서: 자식 → 부모
4
+
5
+ BEGIN;
6
+
7
+ -- Group A: 프로젝트 관리 계층 (KB domain='project'로 대체됨)
8
+ DROP TABLE IF EXISTS semicolony.kpi_snapshots;
9
+ DROP TABLE IF EXISTS semicolony.project_kpi;
10
+ DROP TABLE IF EXISTS semicolony.tasks; -- FK: projects, epics, sprints
11
+ DROP TABLE IF EXISTS semicolony.sprints; -- FK: projects
12
+ DROP TABLE IF EXISTS semicolony.epics; -- FK: projects
13
+ DROP TABLE IF EXISTS semicolony.projects; -- 루트 — KB가 SoT
14
+
15
+ -- Group B: 워크플로우 엔진 (생성만 되고 사용 안 됨)
16
+ DROP TABLE IF EXISTS semicolony.workflow_node_executions; -- FK: instances, nodes
17
+ DROP TABLE IF EXISTS semicolony.workflow_instances; -- FK: definitions, nodes
18
+ DROP TABLE IF EXISTS semicolony.workflow_nodes; -- FK: definitions
19
+ DROP TABLE IF EXISTS semicolony.workflow_definitions; -- 루트
20
+
21
+ -- Group C: 아카이브 코드에서만 참조 (mcp-server/_archived)
22
+ DROP TABLE IF EXISTS semicolony.sessions;
23
+ DROP TABLE IF EXISTS semicolony.interaction_logs;
24
+ DROP TABLE IF EXISTS semicolony.embedding_requests;
25
+ DROP TABLE IF EXISTS semicolony.user_facts;
26
+ DROP TABLE IF EXISTS semicolony.memory_stats;
27
+ DROP VIEW IF EXISTS semicolony.index_usage;
28
+ DROP TABLE IF EXISTS semicolony.audit_logs;
29
+
30
+ -- 마이그레이션 기록
31
+ INSERT INTO semicolony.schema_migrations (version)
32
+ VALUES ('011_drop_legacy_tables')
33
+ ON CONFLICT (version) DO NOTHING;
34
+
35
+ COMMIT;
@@ -0,0 +1,16 @@
1
+ -- Migration 012: Drop bot_knowledge and bot_kb_subscriptions
2
+ -- bot_knowledge 데이터를 knowledge_base로 이관 후 테이블 DROP.
3
+ -- 봇 프라이빗 KB를 폐기하고 knowledge_base 단일 테이블로 통일.
4
+
5
+ -- 1. bot_knowledge → knowledge_base 이관 (중복 시 skip)
6
+ INSERT INTO semicolony.knowledge_base (domain, key, content, metadata, embedding, created_by)
7
+ SELECT domain, key, content, metadata, embedding, bot_id AS created_by
8
+ FROM semicolony.bot_knowledge
9
+ ON CONFLICT (domain, key) DO NOTHING;
10
+
11
+ -- 2. 테이블 DROP
12
+ DROP TABLE IF EXISTS semicolony.bot_knowledge;
13
+ DROP TABLE IF EXISTS semicolony.bot_kb_subscriptions;
14
+
15
+ -- 3. bot:* 온톨로지 제거 (봇 프라이빗 KB 폐기로 불필요)
16
+ DELETE FROM semicolony.ontology WHERE domain LIKE 'bot:%';