@rkarim08/sia 1.0.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.
Files changed (355) hide show
  1. package/.claude-plugin/marketplace.json +35 -0
  2. package/.claude-plugin/plugin.json +27 -0
  3. package/.mcp.json +13 -0
  4. package/CLAUDE.md +226 -0
  5. package/LICENSE +202 -0
  6. package/PLUGIN_README.md +253 -0
  7. package/README.md +1013 -0
  8. package/agents/sia-changelog-writer.md +89 -0
  9. package/agents/sia-code-reviewer.md +86 -0
  10. package/agents/sia-conflict-resolver.md +100 -0
  11. package/agents/sia-convention-enforcer.md +69 -0
  12. package/agents/sia-debug.md +106 -0
  13. package/agents/sia-decision-reviewer.md +101 -0
  14. package/agents/sia-dependency-tracker.md +80 -0
  15. package/agents/sia-explain.md +126 -0
  16. package/agents/sia-feature.md +116 -0
  17. package/agents/sia-knowledge-capture.md +117 -0
  18. package/agents/sia-lead-architecture-advisor.md +93 -0
  19. package/agents/sia-lead-team-health.md +107 -0
  20. package/agents/sia-migration.md +100 -0
  21. package/agents/sia-onboarding.md +115 -0
  22. package/agents/sia-orientation.md +99 -0
  23. package/agents/sia-pm-briefing.md +106 -0
  24. package/agents/sia-pm-risk-advisor.md +82 -0
  25. package/agents/sia-qa-analyst.md +116 -0
  26. package/agents/sia-qa-regression-map.md +94 -0
  27. package/agents/sia-refactor.md +115 -0
  28. package/agents/sia-regression.md +112 -0
  29. package/agents/sia-security-audit.md +125 -0
  30. package/agents/sia-test-advisor.md +91 -0
  31. package/hooks/hooks.json +98 -0
  32. package/migrations/bridge/001_initial.sql +34 -0
  33. package/migrations/episodic/001_initial.sql +35 -0
  34. package/migrations/meta/001_initial.sql +68 -0
  35. package/migrations/semantic/001_initial.sql +292 -0
  36. package/migrations/semantic/002_ontology.sql +89 -0
  37. package/migrations/semantic/003_freshness.sql +63 -0
  38. package/migrations/semantic/004_v5_unified_schema.sql +194 -0
  39. package/migrations/semantic/005_backfill_event_kinds.sql +8 -0
  40. package/migrations/semantic/006_tree_sitter.sql +6 -0
  41. package/migrations/semantic/007_branch_snapshots.sql +22 -0
  42. package/package.json +110 -0
  43. package/scripts/branch-switch.sh +13 -0
  44. package/scripts/build-wasm-grammars.sh +81 -0
  45. package/scripts/post-compact.sh +8 -0
  46. package/scripts/post-tool-use.sh +10 -0
  47. package/scripts/pre-compact.sh +8 -0
  48. package/scripts/session-end.sh +8 -0
  49. package/scripts/session-start.sh +8 -0
  50. package/scripts/start-mcp.ts +45 -0
  51. package/scripts/stop-hook.sh +8 -0
  52. package/scripts/user-prompt-submit.sh +8 -0
  53. package/scripts/viz-server.ts +152 -0
  54. package/skills/sia-brainstorm/SKILL.md +156 -0
  55. package/skills/sia-brainstorm/scripts/frame-template.html +214 -0
  56. package/skills/sia-brainstorm/scripts/helper.js +95 -0
  57. package/skills/sia-brainstorm/scripts/server.cjs +338 -0
  58. package/skills/sia-brainstorm/scripts/start-server.sh +153 -0
  59. package/skills/sia-brainstorm/scripts/stop-server.sh +55 -0
  60. package/skills/sia-brainstorm/spec-document-reviewer-prompt.md +49 -0
  61. package/skills/sia-brainstorm/visual-companion.md +286 -0
  62. package/skills/sia-capture/SKILL.md +64 -0
  63. package/skills/sia-compare/SKILL.md +33 -0
  64. package/skills/sia-conflicts/SKILL.md +38 -0
  65. package/skills/sia-debug-workflow/SKILL.md +120 -0
  66. package/skills/sia-debug-workflow/root-cause-tracing.md +70 -0
  67. package/skills/sia-debug-workflow/scripts/find-polluter.sh +64 -0
  68. package/skills/sia-debug-workflow/temporal-investigation.md +72 -0
  69. package/skills/sia-digest/SKILL.md +23 -0
  70. package/skills/sia-dispatch/SKILL.md +69 -0
  71. package/skills/sia-dispatch/agent-task-template.md +99 -0
  72. package/skills/sia-doctor/SKILL.md +39 -0
  73. package/skills/sia-execute/SKILL.md +70 -0
  74. package/skills/sia-execute-plan/SKILL.md +85 -0
  75. package/skills/sia-export-import/SKILL.md +49 -0
  76. package/skills/sia-export-knowledge/SKILL.md +46 -0
  77. package/skills/sia-finish/SKILL.md +100 -0
  78. package/skills/sia-finish/pr-summary-template.md +54 -0
  79. package/skills/sia-freshness/SKILL.md +38 -0
  80. package/skills/sia-history/SKILL.md +42 -0
  81. package/skills/sia-impact/SKILL.md +70 -0
  82. package/skills/sia-index/SKILL.md +54 -0
  83. package/skills/sia-install/SKILL.md +39 -0
  84. package/skills/sia-lead-compliance/SKILL.md +16 -0
  85. package/skills/sia-lead-drift-report/SKILL.md +16 -0
  86. package/skills/sia-lead-knowledge-map/SKILL.md +16 -0
  87. package/skills/sia-learn/SKILL.md +58 -0
  88. package/skills/sia-plan/SKILL.md +68 -0
  89. package/skills/sia-plan/plan-reviewer-prompt.md +63 -0
  90. package/skills/sia-playbooks/SKILL.md +29 -0
  91. package/skills/sia-playbooks/reference-feature.md +100 -0
  92. package/skills/sia-playbooks/reference-flagging.md +50 -0
  93. package/skills/sia-playbooks/reference-orientation.md +92 -0
  94. package/skills/sia-playbooks/reference-regression.md +115 -0
  95. package/skills/sia-playbooks/reference-review.md +64 -0
  96. package/skills/sia-playbooks/reference-tools.md +239 -0
  97. package/skills/sia-pm-decision-log/SKILL.md +28 -0
  98. package/skills/sia-pm-risk-dashboard/SKILL.md +24 -0
  99. package/skills/sia-pm-sprint-summary/SKILL.md +27 -0
  100. package/skills/sia-prune/SKILL.md +45 -0
  101. package/skills/sia-qa-coverage/SKILL.md +28 -0
  102. package/skills/sia-qa-flaky/SKILL.md +20 -0
  103. package/skills/sia-qa-report/SKILL.md +26 -0
  104. package/skills/sia-reindex/SKILL.md +30 -0
  105. package/skills/sia-review-respond/SKILL.md +88 -0
  106. package/skills/sia-review-respond/pushback-patterns.md +90 -0
  107. package/skills/sia-search/SKILL.md +47 -0
  108. package/skills/sia-setup/SKILL.md +82 -0
  109. package/skills/sia-setup/setup-checklist.md +97 -0
  110. package/skills/sia-stats/SKILL.md +36 -0
  111. package/skills/sia-status/SKILL.md +44 -0
  112. package/skills/sia-sync/SKILL.md +46 -0
  113. package/skills/sia-team/SKILL.md +64 -0
  114. package/skills/sia-test/SKILL.md +92 -0
  115. package/skills/sia-test/testing-anti-patterns.md +104 -0
  116. package/skills/sia-tour/SKILL.md +29 -0
  117. package/skills/sia-upgrade/SKILL.md +43 -0
  118. package/skills/sia-verify/SKILL.md +81 -0
  119. package/skills/sia-visualize/SKILL.md +28 -0
  120. package/skills/sia-visualize-live/SKILL.md +55 -0
  121. package/skills/sia-visualize-live/scripts/graph-template.html +389 -0
  122. package/skills/sia-visualize-live/scripts/start-visualizer.sh +161 -0
  123. package/skills/sia-visualize-live/scripts/stop-visualizer.sh +55 -0
  124. package/skills/sia-visualize-live/scripts/visualizer-server.cjs +264 -0
  125. package/skills/sia-workspace/SKILL.md +57 -0
  126. package/src/agent/claude-md-template-flagging.md +219 -0
  127. package/src/agent/claude-md-template.md +213 -0
  128. package/src/agent/modules/sia-feature.md +100 -0
  129. package/src/agent/modules/sia-flagging.md +50 -0
  130. package/src/agent/modules/sia-orientation.md +92 -0
  131. package/src/agent/modules/sia-regression.md +115 -0
  132. package/src/agent/modules/sia-review.md +64 -0
  133. package/src/agent/modules/sia-tools.md +239 -0
  134. package/src/ast/extractors/c-include.ts +189 -0
  135. package/src/ast/extractors/csharp-project.ts +260 -0
  136. package/src/ast/extractors/prisma-schema.ts +44 -0
  137. package/src/ast/extractors/project-manifest.ts +111 -0
  138. package/src/ast/extractors/sql-schema.ts +67 -0
  139. package/src/ast/extractors/tier-a.ts +423 -0
  140. package/src/ast/extractors/tier-b.ts +289 -0
  141. package/src/ast/extractors/tier-dispatch.ts +247 -0
  142. package/src/ast/index-worker.ts +108 -0
  143. package/src/ast/indexer.ts +484 -0
  144. package/src/ast/languages.ts +408 -0
  145. package/src/ast/pagerank-builder.ts +125 -0
  146. package/src/ast/path-utils.ts +137 -0
  147. package/src/ast/tree-sitter/backends/native.ts +57 -0
  148. package/src/ast/tree-sitter/backends/wasm.ts +39 -0
  149. package/src/ast/tree-sitter/call-walker.ts +44 -0
  150. package/src/ast/tree-sitter/edit-computer.ts +55 -0
  151. package/src/ast/tree-sitter/query-runner.ts +46 -0
  152. package/src/ast/tree-sitter/service.ts +174 -0
  153. package/src/ast/tree-sitter/tree-cache.ts +39 -0
  154. package/src/ast/tree-sitter/types.ts +79 -0
  155. package/src/ast/watcher.ts +322 -0
  156. package/src/capture/chunker.ts +169 -0
  157. package/src/capture/consolidate.ts +127 -0
  158. package/src/capture/edge-inferrer.ts +161 -0
  159. package/src/capture/embedder.ts +166 -0
  160. package/src/capture/embedding-cache.ts +73 -0
  161. package/src/capture/flag-processor.ts +64 -0
  162. package/src/capture/hook.ts +67 -0
  163. package/src/capture/pipeline.ts +450 -0
  164. package/src/capture/prompts/consolidate.ts +25 -0
  165. package/src/capture/prompts/edge-infer.ts +29 -0
  166. package/src/capture/prompts/extract-flagged.ts +36 -0
  167. package/src/capture/prompts/extract.ts +42 -0
  168. package/src/capture/tokenizer.ts +147 -0
  169. package/src/capture/track-a-ast.ts +93 -0
  170. package/src/capture/track-b-llm.ts +149 -0
  171. package/src/capture/types.ts +64 -0
  172. package/src/cli/commands/community.ts +137 -0
  173. package/src/cli/commands/compare.ts +123 -0
  174. package/src/cli/commands/conflicts.ts +41 -0
  175. package/src/cli/commands/digest.ts +197 -0
  176. package/src/cli/commands/disable-flagging.ts +34 -0
  177. package/src/cli/commands/doctor.ts +240 -0
  178. package/src/cli/commands/download-model.ts +161 -0
  179. package/src/cli/commands/enable-flagging.ts +34 -0
  180. package/src/cli/commands/export-knowledge.ts +208 -0
  181. package/src/cli/commands/export.ts +85 -0
  182. package/src/cli/commands/freshness.ts +164 -0
  183. package/src/cli/commands/graph.ts +51 -0
  184. package/src/cli/commands/history.ts +139 -0
  185. package/src/cli/commands/import.ts +335 -0
  186. package/src/cli/commands/install.ts +156 -0
  187. package/src/cli/commands/lead-report.ts +241 -0
  188. package/src/cli/commands/learn.ts +321 -0
  189. package/src/cli/commands/pm-report.ts +413 -0
  190. package/src/cli/commands/prune.ts +75 -0
  191. package/src/cli/commands/qa-report.ts +278 -0
  192. package/src/cli/commands/reindex.ts +104 -0
  193. package/src/cli/commands/rollback.ts +70 -0
  194. package/src/cli/commands/search.ts +103 -0
  195. package/src/cli/commands/server.ts +91 -0
  196. package/src/cli/commands/share.ts +33 -0
  197. package/src/cli/commands/stats.ts +79 -0
  198. package/src/cli/commands/status.ts +176 -0
  199. package/src/cli/commands/sync.ts +96 -0
  200. package/src/cli/commands/team.ts +118 -0
  201. package/src/cli/commands/tour.ts +157 -0
  202. package/src/cli/commands/visualize-live.ts +162 -0
  203. package/src/cli/commands/workspace.ts +117 -0
  204. package/src/cli/index.ts +424 -0
  205. package/src/cli/learn-progress.ts +87 -0
  206. package/src/community/detection-bridge.ts +344 -0
  207. package/src/community/leiden.ts +462 -0
  208. package/src/community/raptor.ts +210 -0
  209. package/src/community/scheduler.ts +74 -0
  210. package/src/community/summarize.ts +115 -0
  211. package/src/decay/archiver.ts +73 -0
  212. package/src/decay/bridge-orphan-cleanup.ts +212 -0
  213. package/src/decay/consolidation-sweep.ts +112 -0
  214. package/src/decay/decay.ts +116 -0
  215. package/src/decay/deep-validator.ts +62 -0
  216. package/src/decay/episodic-promoter.ts +132 -0
  217. package/src/decay/maintenance-scheduler.ts +326 -0
  218. package/src/decay/scheduler.ts +6 -0
  219. package/src/decay/session-sweeper.ts +79 -0
  220. package/src/decay/types.ts +17 -0
  221. package/src/freshness/confidence-decay.ts +122 -0
  222. package/src/freshness/cuckoo-filter.ts +176 -0
  223. package/src/freshness/deep-validation.ts +345 -0
  224. package/src/freshness/dirty-tracker.ts +237 -0
  225. package/src/freshness/file-watcher-layer.ts +119 -0
  226. package/src/freshness/firewall.ts +64 -0
  227. package/src/freshness/git-reconcile-layer.ts +161 -0
  228. package/src/freshness/inverted-index.ts +158 -0
  229. package/src/freshness/stale-read-layer.ts +222 -0
  230. package/src/graph/audit.ts +69 -0
  231. package/src/graph/bridge-db.ts +141 -0
  232. package/src/graph/communities.ts +195 -0
  233. package/src/graph/db-interface.ts +259 -0
  234. package/src/graph/edges.ts +163 -0
  235. package/src/graph/entities.ts +327 -0
  236. package/src/graph/episodic-db.ts +113 -0
  237. package/src/graph/flags.ts +31 -0
  238. package/src/graph/meta-db.ts +200 -0
  239. package/src/graph/semantic-db.ts +101 -0
  240. package/src/graph/session-resume.ts +56 -0
  241. package/src/graph/snapshots.ts +342 -0
  242. package/src/graph/staging.ts +151 -0
  243. package/src/graph/types.ts +128 -0
  244. package/src/hooks/adapters/claude-code.ts +21 -0
  245. package/src/hooks/adapters/cline.ts +43 -0
  246. package/src/hooks/adapters/cursor.ts +65 -0
  247. package/src/hooks/adapters/generic.ts +12 -0
  248. package/src/hooks/agent-detect.ts +34 -0
  249. package/src/hooks/claude-md-directives.ts +32 -0
  250. package/src/hooks/event-router.ts +182 -0
  251. package/src/hooks/extractors/pattern-detector.ts +111 -0
  252. package/src/hooks/handlers/post-compact.ts +30 -0
  253. package/src/hooks/handlers/post-tool-use.ts +403 -0
  254. package/src/hooks/handlers/pre-compact.ts +100 -0
  255. package/src/hooks/handlers/session-end.ts +47 -0
  256. package/src/hooks/handlers/session-start.ts +154 -0
  257. package/src/hooks/handlers/stop.ts +128 -0
  258. package/src/hooks/handlers/user-prompt-submit.ts +68 -0
  259. package/src/hooks/plugin-branch-switch.ts +68 -0
  260. package/src/hooks/plugin-common.ts +47 -0
  261. package/src/hooks/plugin-post-compact.ts +28 -0
  262. package/src/hooks/plugin-post-tool-use.ts +38 -0
  263. package/src/hooks/plugin-pre-compact.ts +37 -0
  264. package/src/hooks/plugin-session-end.ts +37 -0
  265. package/src/hooks/plugin-session-start.ts +75 -0
  266. package/src/hooks/plugin-stop.ts +61 -0
  267. package/src/hooks/plugin-user-prompt-submit.ts +47 -0
  268. package/src/hooks/types.ts +43 -0
  269. package/src/knowledge/discovery.ts +238 -0
  270. package/src/knowledge/external-refs.ts +98 -0
  271. package/src/knowledge/freshness.ts +221 -0
  272. package/src/knowledge/ingest.ts +330 -0
  273. package/src/knowledge/markdown-export.ts +229 -0
  274. package/src/knowledge/markdown-import.ts +359 -0
  275. package/src/knowledge/patterns.ts +74 -0
  276. package/src/knowledge/templates.ts +307 -0
  277. package/src/llm/ai-sdk-adapter.ts +46 -0
  278. package/src/llm/config.ts +88 -0
  279. package/src/llm/cost-tracker.ts +110 -0
  280. package/src/llm/prompts/extraction.ts +55 -0
  281. package/src/llm/prompts/summarization.ts +36 -0
  282. package/src/llm/prompts/validation.ts +37 -0
  283. package/src/llm/provider-registry.ts +68 -0
  284. package/src/llm/reliability.ts +179 -0
  285. package/src/llm/schemas.ts +52 -0
  286. package/src/mcp/freshness-annotator.ts +69 -0
  287. package/src/mcp/server.ts +949 -0
  288. package/src/mcp/tools/sia-ast-query.ts +225 -0
  289. package/src/mcp/tools/sia-at-time.ts +151 -0
  290. package/src/mcp/tools/sia-backlinks.ts +87 -0
  291. package/src/mcp/tools/sia-batch-execute.ts +169 -0
  292. package/src/mcp/tools/sia-by-file.ts +89 -0
  293. package/src/mcp/tools/sia-community.ts +113 -0
  294. package/src/mcp/tools/sia-doctor.ts +73 -0
  295. package/src/mcp/tools/sia-execute-file.ts +122 -0
  296. package/src/mcp/tools/sia-execute.ts +104 -0
  297. package/src/mcp/tools/sia-expand.ts +158 -0
  298. package/src/mcp/tools/sia-fetch-and-index.ts +241 -0
  299. package/src/mcp/tools/sia-flag.ts +65 -0
  300. package/src/mcp/tools/sia-index.ts +111 -0
  301. package/src/mcp/tools/sia-note.ts +134 -0
  302. package/src/mcp/tools/sia-search.ts +105 -0
  303. package/src/mcp/tools/sia-stats.ts +63 -0
  304. package/src/mcp/tools/sia-sync-status.ts +44 -0
  305. package/src/mcp/tools/sia-upgrade.ts +247 -0
  306. package/src/mcp/truncate.ts +231 -0
  307. package/src/native/bridge.ts +167 -0
  308. package/src/native/fallback-ast-diff.ts +144 -0
  309. package/src/native/fallback-graph.ts +325 -0
  310. package/src/ontology/constraints.ts +56 -0
  311. package/src/ontology/errors.ts +8 -0
  312. package/src/ontology/middleware.ts +266 -0
  313. package/src/retrieval/bm25-search.ts +151 -0
  314. package/src/retrieval/context-assembly.ts +76 -0
  315. package/src/retrieval/graph-traversal.ts +168 -0
  316. package/src/retrieval/pagerank.ts +40 -0
  317. package/src/retrieval/query-classifier.ts +106 -0
  318. package/src/retrieval/reranker.ts +156 -0
  319. package/src/retrieval/search.ts +236 -0
  320. package/src/retrieval/throttle.ts +102 -0
  321. package/src/retrieval/vector-search.ts +203 -0
  322. package/src/retrieval/workspace-search.ts +130 -0
  323. package/src/sandbox/context-mode.ts +285 -0
  324. package/src/sandbox/credential-pass.ts +55 -0
  325. package/src/sandbox/executor.ts +235 -0
  326. package/src/security/pattern-detector.ts +127 -0
  327. package/src/security/rule-of-two.ts +50 -0
  328. package/src/security/sanitize.ts +46 -0
  329. package/src/security/semantic-consistency.ts +93 -0
  330. package/src/security/staging-promoter.ts +154 -0
  331. package/src/shared/config.ts +302 -0
  332. package/src/shared/diagnostics.ts +210 -0
  333. package/src/shared/errors.ts +48 -0
  334. package/src/shared/git-utils.ts +143 -0
  335. package/src/shared/llm-client.ts +120 -0
  336. package/src/shared/logger.ts +99 -0
  337. package/src/shared/types.ts +79 -0
  338. package/src/sync/client.ts +43 -0
  339. package/src/sync/conflict.ts +106 -0
  340. package/src/sync/dedup.ts +183 -0
  341. package/src/sync/hlc.ts +117 -0
  342. package/src/sync/keychain.ts +144 -0
  343. package/src/sync/pull.ts +232 -0
  344. package/src/sync/push.ts +131 -0
  345. package/src/types/chokidar.d.ts +23 -0
  346. package/src/visualization/graph-renderer.ts +312 -0
  347. package/src/visualization/subgraph-extract.ts +208 -0
  348. package/src/visualization/views/community-clusters.ts +246 -0
  349. package/src/visualization/views/dependency-map.ts +189 -0
  350. package/src/visualization/views/graph-explorer.ts +364 -0
  351. package/src/visualization/views/timeline.ts +247 -0
  352. package/src/workspace/api-contracts.ts +226 -0
  353. package/src/workspace/cross-repo.ts +61 -0
  354. package/src/workspace/detector.ts +190 -0
  355. package/src/workspace/manifest.ts +141 -0
@@ -0,0 +1,63 @@
1
+ // Module: sia-stats — Graph metrics: node/edge counts by type, optional session stats
2
+
3
+ import type { z } from "zod";
4
+ import type { SiaDb } from "@/graph/db-interface";
5
+ import type { SiaStatsInput } from "@/mcp/server";
6
+
7
+ export interface SiaStatsResult {
8
+ nodes: Record<string, number>;
9
+ edges: Record<string, number>;
10
+ session?: { callCounts: Record<string, number> };
11
+ error?: string;
12
+ }
13
+
14
+ /**
15
+ * Return graph metrics: active node counts by type, active edge counts by type,
16
+ * and optionally the per-tool call counts for the current session from search_throttle.
17
+ */
18
+ export async function handleSiaStats(
19
+ db: SiaDb,
20
+ input: z.infer<typeof SiaStatsInput>,
21
+ sessionId?: string,
22
+ ): Promise<SiaStatsResult> {
23
+ try {
24
+ // Node counts by type (active only)
25
+ const { rows: nodeRows } = await db.execute(
26
+ "SELECT type, COUNT(*) as count FROM graph_nodes WHERE t_expired IS NULL GROUP BY type",
27
+ );
28
+
29
+ const nodes: Record<string, number> = {};
30
+ for (const row of nodeRows) {
31
+ nodes[row.type as string] = row.count as number;
32
+ }
33
+
34
+ // Edge counts by type (active only)
35
+ const { rows: edgeRows } = await db.execute(
36
+ "SELECT type, COUNT(*) as count FROM graph_edges WHERE t_expired IS NULL GROUP BY type",
37
+ );
38
+
39
+ const edges: Record<string, number> = {};
40
+ for (const row of edgeRows) {
41
+ edges[row.type as string] = row.count as number;
42
+ }
43
+
44
+ // Optional session stats from search_throttle
45
+ if (input.include_session && sessionId) {
46
+ const { rows: throttleRows } = await db.execute(
47
+ "SELECT tool_name, call_count FROM search_throttle WHERE session_id = ?",
48
+ [sessionId],
49
+ );
50
+
51
+ const callCounts: Record<string, number> = {};
52
+ for (const row of throttleRows) {
53
+ callCounts[row.tool_name as string] = row.call_count as number;
54
+ }
55
+
56
+ return { nodes, edges, session: { callCounts } };
57
+ }
58
+
59
+ return { nodes, edges };
60
+ } catch (err) {
61
+ return { nodes: {}, edges: {}, error: `Stats query failed: ${(err as Error).message}` };
62
+ }
63
+ }
@@ -0,0 +1,44 @@
1
+ // Module: sia-sync-status — MCP tool handler for team sync status.
2
+ //
3
+ // Returns the current sync configuration and status.
4
+ // Read-only — does not require a database connection.
5
+
6
+ import { getConfig, resolveSiaHome } from "@/shared/config";
7
+
8
+ export interface SiaSyncStatusResult {
9
+ enabled: boolean;
10
+ status: "not_configured" | "active" | "error";
11
+ server_url?: string;
12
+ developer_id?: string;
13
+ sync_interval_seconds?: number;
14
+ error?: string;
15
+ }
16
+
17
+ /**
18
+ * Check the current team sync configuration and status.
19
+ */
20
+ export async function handleSiaSyncStatus(): Promise<SiaSyncStatusResult> {
21
+ try {
22
+ const config = getConfig(resolveSiaHome());
23
+ const sync = config.sync;
24
+
25
+ if (!sync.enabled || !sync.serverUrl) {
26
+ return { enabled: false, status: "not_configured" };
27
+ }
28
+
29
+ return {
30
+ enabled: true,
31
+ status: "active",
32
+ server_url: sync.serverUrl,
33
+ developer_id: sync.developerId ?? undefined,
34
+ sync_interval_seconds: sync.syncInterval,
35
+ };
36
+ } catch (err) {
37
+ console.error("[sia] sia_sync_status error:", err);
38
+ return {
39
+ enabled: false,
40
+ status: "error",
41
+ error: err instanceof Error ? err.message : String(err),
42
+ };
43
+ }
44
+ }
@@ -0,0 +1,247 @@
1
+ // Module: sia-upgrade — Self-update handler with npm/git/binary strategies
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import type { z } from "zod";
7
+ import type { SiaDb } from "@/graph/db-interface";
8
+ import type { SiaUpgradeInput } from "@/mcp/server";
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Types
12
+ // ---------------------------------------------------------------------------
13
+
14
+ export type InstallationType = "npm" | "git" | "binary";
15
+
16
+ export interface UpdateStrategy {
17
+ name: InstallationType;
18
+ detect(): boolean;
19
+ currentVersion(): string;
20
+ update(target: string): void;
21
+ rollback(snapshot: string): void;
22
+ }
23
+
24
+ export interface SiaUpgradeResult {
25
+ previousVersion?: string;
26
+ newVersion?: string;
27
+ strategy?: string;
28
+ migrationsRun?: number;
29
+ hooksReconfigured?: boolean;
30
+ vssRebuilt?: boolean;
31
+ dryRun?: boolean;
32
+ error?: string;
33
+ }
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // NpmUpdateStrategy
37
+ // ---------------------------------------------------------------------------
38
+
39
+ export class NpmUpdateStrategy implements UpdateStrategy {
40
+ name: InstallationType = "npm";
41
+
42
+ constructor(private readonly siaRoot: string) {}
43
+
44
+ detect(): boolean {
45
+ return existsSync(join(this.siaRoot, "node_modules", "sia"));
46
+ }
47
+
48
+ currentVersion(): string {
49
+ const pkgPath = join(this.siaRoot, "node_modules", "sia", "package.json");
50
+ if (existsSync(pkgPath)) {
51
+ try {
52
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as { version?: string };
53
+ return pkg.version ?? "unknown";
54
+ } catch (err) {
55
+ console.error(`[sia-upgrade] Failed to read version: ${(err as Error).message}`);
56
+ return "unknown";
57
+ }
58
+ }
59
+ return "unknown";
60
+ }
61
+
62
+ update(target: string): void {
63
+ const pkg = target ? `sia@${target}` : "sia";
64
+ const result = spawnSync("bun", ["update", pkg], { encoding: "utf-8" });
65
+ if (result.status !== 0) {
66
+ throw new Error(result.stderr ?? "bun update failed");
67
+ }
68
+ }
69
+
70
+ rollback(snapshot: string): void {
71
+ const result = spawnSync("bun", ["add", `sia@${snapshot}`], { encoding: "utf-8" });
72
+ if (result.status !== 0) {
73
+ throw new Error(result.stderr ?? "bun add rollback failed");
74
+ }
75
+ }
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // GitUpdateStrategy
80
+ // ---------------------------------------------------------------------------
81
+
82
+ export class GitUpdateStrategy implements UpdateStrategy {
83
+ name: InstallationType = "git";
84
+
85
+ constructor(private readonly siaRoot: string) {}
86
+
87
+ detect(): boolean {
88
+ return existsSync(join(this.siaRoot, ".git"));
89
+ }
90
+
91
+ currentVersion(): string {
92
+ const result = spawnSync("git", ["rev-parse", "--short", "HEAD"], {
93
+ cwd: this.siaRoot,
94
+ encoding: "utf-8",
95
+ });
96
+ if (result.status === 0 && result.stdout) {
97
+ return result.stdout.trim();
98
+ }
99
+ console.error(
100
+ `[sia-upgrade] Failed to read version: ${result.stderr?.trim() ?? "git rev-parse failed"}`,
101
+ );
102
+ return "unknown";
103
+ }
104
+
105
+ update(_target: string): void {
106
+ const pull = spawnSync("git", ["pull", "origin", "main"], {
107
+ cwd: this.siaRoot,
108
+ encoding: "utf-8",
109
+ });
110
+ if (pull.status !== 0) {
111
+ throw new Error(pull.stderr ?? "git pull failed");
112
+ }
113
+
114
+ const install = spawnSync("bun", ["install"], {
115
+ cwd: this.siaRoot,
116
+ encoding: "utf-8",
117
+ });
118
+ if (install.status !== 0) {
119
+ throw new Error(install.stderr ?? "bun install failed");
120
+ }
121
+ }
122
+
123
+ rollback(snapshot: string): void {
124
+ const result = spawnSync("git", ["reset", "--hard", snapshot], {
125
+ cwd: this.siaRoot,
126
+ encoding: "utf-8",
127
+ });
128
+ if (result.status !== 0) {
129
+ throw new Error(result.stderr ?? "git reset failed");
130
+ }
131
+ }
132
+ }
133
+
134
+ // ---------------------------------------------------------------------------
135
+ // BinaryUpdateStrategy
136
+ // ---------------------------------------------------------------------------
137
+
138
+ export class BinaryUpdateStrategy implements UpdateStrategy {
139
+ name: InstallationType = "binary";
140
+
141
+ detect(): boolean {
142
+ // fallback — always detects
143
+ return true;
144
+ }
145
+
146
+ currentVersion(): string {
147
+ return "unknown";
148
+ }
149
+
150
+ update(_target: string): void {
151
+ throw new Error("not yet implemented");
152
+ }
153
+
154
+ rollback(_snapshot: string): void {
155
+ throw new Error("not yet implemented");
156
+ }
157
+ }
158
+
159
+ // ---------------------------------------------------------------------------
160
+ // detectInstallationType
161
+ // ---------------------------------------------------------------------------
162
+
163
+ export function detectInstallationType(siaRoot: string): InstallationType {
164
+ // npm preferred over git when both exist
165
+ const npm = new NpmUpdateStrategy(siaRoot);
166
+ if (npm.detect()) return "npm";
167
+
168
+ const git = new GitUpdateStrategy(siaRoot);
169
+ if (git.detect()) return "git";
170
+
171
+ return "binary";
172
+ }
173
+
174
+ // ---------------------------------------------------------------------------
175
+ // handleSiaUpgrade
176
+ // ---------------------------------------------------------------------------
177
+
178
+ export async function handleSiaUpgrade(
179
+ _db: SiaDb,
180
+ input: z.infer<typeof SiaUpgradeInput>,
181
+ config?: { siaRoot?: string; upgradeReleaseUrl?: string },
182
+ ): Promise<SiaUpgradeResult> {
183
+ const siaRoot = config?.siaRoot ?? process.cwd();
184
+ const targetVersion = input.target_version ?? "";
185
+ const dryRun = input.dry_run ?? false;
186
+
187
+ // 1. Auto-detect installation type
188
+ const type = detectInstallationType(siaRoot);
189
+
190
+ // Build the strategy instance
191
+ let strategy: UpdateStrategy;
192
+ if (type === "npm") {
193
+ strategy = new NpmUpdateStrategy(siaRoot);
194
+ } else if (type === "git") {
195
+ strategy = new GitUpdateStrategy(siaRoot);
196
+ } else {
197
+ strategy = new BinaryUpdateStrategy();
198
+ }
199
+
200
+ // 2. Snapshot current version
201
+ const previousVersion = strategy.currentVersion();
202
+
203
+ // 3. If dry_run → return early
204
+ if (dryRun) {
205
+ return { previousVersion, strategy: type, dryRun: true };
206
+ }
207
+
208
+ // Guard: if version is unknown, rollback would be impossible
209
+ if (previousVersion === "unknown") {
210
+ return {
211
+ previousVersion,
212
+ strategy: type,
213
+ error: "Cannot determine current version — rollback would be impossible. Aborting upgrade.",
214
+ };
215
+ }
216
+
217
+ // 4. Try update → on failure, try rollback
218
+ try {
219
+ strategy.update(targetVersion);
220
+ } catch (updateErr) {
221
+ // Attempt rollback
222
+ let rollbackMsg = "";
223
+ try {
224
+ strategy.rollback(previousVersion);
225
+ rollbackMsg = " (rolled back to previous version)";
226
+ } catch (rollbackErr) {
227
+ rollbackMsg = ` (rollback also failed: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)})`;
228
+ }
229
+
230
+ return {
231
+ previousVersion,
232
+ strategy: type,
233
+ error: `${updateErr instanceof Error ? updateErr.message : String(updateErr)}${rollbackMsg}`,
234
+ };
235
+ }
236
+
237
+ // 5. Return success result
238
+ const newVersion = strategy.currentVersion();
239
+ return {
240
+ previousVersion,
241
+ newVersion,
242
+ strategy: type,
243
+ migrationsRun: 0,
244
+ hooksReconfigured: false,
245
+ vssRebuilt: false,
246
+ };
247
+ }
@@ -0,0 +1,231 @@
1
+ // Module: truncate — Bounds MCP tool response payloads to a character budget.
2
+ //
3
+ // MCP tool responses are injected into the Claude context window. Unbounded
4
+ // responses from large graph queries can consume thousands of tokens.
5
+ // This utility truncates responses to a configurable character limit.
6
+
7
+ const DEFAULT_MAX_CHARS = 8000;
8
+ const MIN_MAX_CHARS = 100;
9
+
10
+ /** Describes a truncatable collection found on the response object. */
11
+ interface TruncatableField {
12
+ key: string;
13
+ items: unknown[];
14
+ /** True when the original property was a Record<string, T[]> that we flattened. */
15
+ flattened: boolean;
16
+ /** When flattened, records original group keys and their item counts. */
17
+ originalGroups?: Record<string, number>;
18
+ }
19
+
20
+ /**
21
+ * Find the largest truncatable collection on an object.
22
+ *
23
+ * First pass: direct array properties (e.g. `entities`, `communities`, `results`).
24
+ * Picks the **largest** non-empty array by item count, so multi-array responses
25
+ * like sia_expand (neighbors + edges) truncate the dominant collection.
26
+ *
27
+ * Second pass (if no arrays found): grouped collections — plain objects whose
28
+ * values are all arrays and that have >= 2 groups (e.g. `backlinks:
29
+ * Record<string, BacklinkEntry[]>`). The >= 2 group threshold avoids
30
+ * false-positives on objects that happen to have a single array-valued property.
31
+ * These are flattened into a single array so the binary search can trim them
32
+ * uniformly, with `_original_groups` metadata for consumer reconstruction.
33
+ */
34
+ function findTruncatableField(obj: Record<string, unknown>): TruncatableField | null {
35
+ // First pass: find the largest direct array property
36
+ let best: TruncatableField | null = null;
37
+ for (const [key, val] of Object.entries(obj)) {
38
+ if (Array.isArray(val) && val.length > 0) {
39
+ if (!best || val.length > best.items.length) {
40
+ best = { key, items: val, flattened: false };
41
+ }
42
+ }
43
+ }
44
+ if (best) return best;
45
+
46
+ // Second pass: Record<string, T[]> grouped collections (>= 2 groups to avoid false positives)
47
+ for (const [key, val] of Object.entries(obj)) {
48
+ if (val !== null && typeof val === "object" && !Array.isArray(val)) {
49
+ const nested = val as Record<string, unknown>;
50
+ const nestedEntries = Object.entries(nested);
51
+ if (nestedEntries.length >= 2 && nestedEntries.every(([, v]) => Array.isArray(v))) {
52
+ const originalGroups: Record<string, number> = {};
53
+ const flatItems: unknown[] = [];
54
+ for (const [groupKey, groupVal] of nestedEntries) {
55
+ const arr = groupVal as unknown[];
56
+ originalGroups[groupKey] = arr.length;
57
+ flatItems.push(...arr);
58
+ }
59
+ if (flatItems.length > 0) {
60
+ return { key, items: flatItems, flattened: true, originalGroups };
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ return null;
67
+ }
68
+
69
+ /**
70
+ * Binary-search for the max items from a bare array that fit within budget.
71
+ * Wraps the result in an envelope with truncation metadata.
72
+ */
73
+ function truncateArray(items: unknown[], serialized: string, maxChars: number): unknown {
74
+ const originalCount = items.length;
75
+
76
+ let lo = 0;
77
+ let hi = items.length;
78
+ while (lo < hi) {
79
+ const mid = Math.ceil((lo + hi) / 2);
80
+ const candidate = {
81
+ items: items.slice(0, mid),
82
+ _truncated: true,
83
+ _original_count: originalCount,
84
+ _showing: mid,
85
+ };
86
+ if (JSON.stringify(candidate).length <= maxChars) {
87
+ lo = mid;
88
+ } else {
89
+ hi = mid - 1;
90
+ }
91
+ }
92
+
93
+ const showing = Math.max(lo, 1);
94
+ const result = {
95
+ items: items.slice(0, showing),
96
+ _truncated: true,
97
+ _original_count: originalCount,
98
+ _showing: showing,
99
+ };
100
+
101
+ if (lo === 0 && JSON.stringify(result).length > maxChars) {
102
+ return {
103
+ _truncated: true,
104
+ _original_count: originalCount,
105
+ _original_size: serialized.length,
106
+ _max_chars: maxChars,
107
+ _message: `Response too large: array has ${originalCount} items but even 1 exceeds the ${maxChars}-char budget. Use more specific query parameters.`,
108
+ };
109
+ }
110
+
111
+ return result;
112
+ }
113
+
114
+ /**
115
+ * Truncate an MCP tool response to fit within a character budget.
116
+ *
117
+ * Strategy (in execution order):
118
+ * 1. null/undefined — pass through unchanged.
119
+ * 2. maxChars below MIN_MAX_CHARS (100) — pass through unchanged to avoid
120
+ * degenerate truncation.
121
+ * 3. Serialization failure (e.g. circular references) — return a
122
+ * `_serialization_error` envelope.
123
+ * 4. Serialized response fits within budget — return unchanged.
124
+ * 5. String response — return a truncation envelope object (not a raw
125
+ * string, to avoid double-encoding when the caller JSON.stringify's).
126
+ * 6. Bare array (e.g. sia_search returns T[]) — binary-search for max
127
+ * items that fit, wrapped in an `{items, _truncated}` envelope.
128
+ * 7. Object with an array property (or grouped Record<string, T[]>) —
129
+ * binary-search for the max items that fit, add truncation metadata.
130
+ * If even 1 item exceeds the budget, an overflow envelope (with no
131
+ * items) is returned instead.
132
+ * 8. Object with no truncatable collection — return a structured overflow envelope.
133
+ */
134
+ export function truncateResponse(response: unknown, maxChars: number = DEFAULT_MAX_CHARS): unknown {
135
+ if (response === null || response === undefined) return response;
136
+ if (maxChars < MIN_MAX_CHARS) return response;
137
+
138
+ let serialized: string;
139
+ try {
140
+ serialized = JSON.stringify(response);
141
+ } catch (err) {
142
+ return {
143
+ _truncated: true,
144
+ _serialization_error: true,
145
+ _message: `Response could not be serialized: ${err instanceof Error ? err.message : String(err)}`,
146
+ };
147
+ }
148
+ if (serialized.length <= maxChars) return response;
149
+
150
+ // String: return structured envelope to avoid double-encoding
151
+ // Reserve ~200 chars for the JSON envelope keys (_truncated, _original_size, text, quotes, braces)
152
+ if (typeof response === "string") {
153
+ return {
154
+ _truncated: true,
155
+ _original_size: response.length,
156
+ text: response.slice(0, Math.max(maxChars - 200, MIN_MAX_CHARS)),
157
+ };
158
+ }
159
+
160
+ // Bare array (e.g. sia_search returns SiaSearchResult[]): truncate directly
161
+ if (Array.isArray(response)) {
162
+ return truncateArray(response, serialized, maxChars);
163
+ }
164
+
165
+ // Object: find the dominant truncatable collection and binary-search for max items that fit
166
+ if (typeof response === "object") {
167
+ const obj = response as Record<string, unknown>;
168
+ const field = findTruncatableField(obj);
169
+
170
+ if (field) {
171
+ const { key: arrayKey, items, flattened, originalGroups } = field;
172
+ const originalCount = items.length;
173
+ const flatMeta = flattened
174
+ ? { _flattened: true, ...(originalGroups ? { _original_groups: originalGroups } : {}) }
175
+ : {};
176
+
177
+ let lo = 0;
178
+ let hi = items.length;
179
+ while (lo < hi) {
180
+ const mid = Math.ceil((lo + hi) / 2);
181
+ const candidate = {
182
+ ...obj,
183
+ [arrayKey]: items.slice(0, mid),
184
+ _truncated: true,
185
+ _original_count: originalCount,
186
+ _showing: mid,
187
+ ...flatMeta,
188
+ };
189
+ if (JSON.stringify(candidate).length <= maxChars) {
190
+ lo = mid;
191
+ } else {
192
+ hi = mid - 1;
193
+ }
194
+ }
195
+
196
+ const showing = Math.max(lo, 1);
197
+ const result = {
198
+ ...obj,
199
+ [arrayKey]: items.slice(0, showing),
200
+ _truncated: true,
201
+ _original_count: originalCount,
202
+ _showing: showing,
203
+ ...flatMeta,
204
+ };
205
+
206
+ // If even 1 item exceeds budget, return an overflow envelope
207
+ if (lo === 0 && JSON.stringify(result).length > maxChars) {
208
+ return {
209
+ _truncated: true,
210
+ _original_count: originalCount,
211
+ _original_size: serialized.length,
212
+ _max_chars: maxChars,
213
+ _message: `Response too large: '${arrayKey}' has ${originalCount} items but even 1 exceeds the ${maxChars}-char budget. Use more specific query parameters.`,
214
+ };
215
+ }
216
+
217
+ return result;
218
+ }
219
+
220
+ // No truncatable collection — overflow envelope
221
+ return {
222
+ _truncated: true,
223
+ _original_size: serialized.length,
224
+ _max_chars: maxChars,
225
+ _message:
226
+ "Response exceeded size limit. Use more specific query parameters to narrow results.",
227
+ };
228
+ }
229
+
230
+ return response;
231
+ }