@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,120 @@
1
+ // Module: llm-client — Shared LLM client with Anthropic SDK + air-gapped fallback
2
+ import type { SiaConfig } from "@/shared/config";
3
+
4
+ /** LLM client interface for summarization and classification tasks. */
5
+ export interface LlmClient {
6
+ summarize(prompt: string): Promise<string>;
7
+ classify(prompt: string, options: string[]): Promise<string>;
8
+ }
9
+
10
+ /** Rate limiter state */
11
+ interface RateLimiter {
12
+ tokens: number;
13
+ maxTokens: number;
14
+ refillRate: number; // tokens per ms
15
+ lastRefill: number;
16
+ }
17
+
18
+ function createRateLimiter(maxPerMinute: number): RateLimiter {
19
+ return {
20
+ tokens: maxPerMinute,
21
+ maxTokens: maxPerMinute,
22
+ refillRate: maxPerMinute / 60000,
23
+ lastRefill: Date.now(),
24
+ };
25
+ }
26
+
27
+ async function acquireToken(limiter: RateLimiter): Promise<void> {
28
+ const now = Date.now();
29
+ const elapsed = now - limiter.lastRefill;
30
+ limiter.tokens = Math.min(limiter.maxTokens, limiter.tokens + elapsed * limiter.refillRate);
31
+ limiter.lastRefill = now;
32
+
33
+ if (limiter.tokens < 1) {
34
+ const waitMs = (1 - limiter.tokens) / limiter.refillRate;
35
+ await new Promise((r) => setTimeout(r, waitMs));
36
+ limiter.tokens = 0;
37
+ } else {
38
+ limiter.tokens -= 1;
39
+ }
40
+ }
41
+
42
+ /** Air-gapped/fallback client that uses heuristic string concatenation. */
43
+ export function createFallbackClient(): LlmClient {
44
+ return {
45
+ async summarize(prompt: string): Promise<string> {
46
+ const lines = prompt.split("\n").filter((l) => l.trim().length > 0);
47
+ return lines.slice(0, 5).join("; ").slice(0, 500);
48
+ },
49
+ async classify(_prompt: string, options: string[]): Promise<string> {
50
+ return options[0] ?? "unknown";
51
+ },
52
+ };
53
+ }
54
+
55
+ /** Create an LLM client backed by the Anthropic SDK with rate limiting. */
56
+ export function createLlmClient(config: SiaConfig): LlmClient {
57
+ // Air-gapped mode or no API key → fallback
58
+ const apiKey = process.env.ANTHROPIC_API_KEY;
59
+ if (config.airGapped || !apiKey) {
60
+ return createFallbackClient();
61
+ }
62
+
63
+ const model = config.captureModel ?? "claude-haiku-4-5-20251001";
64
+ const limiter = createRateLimiter(10);
65
+ let anthropicClient: {
66
+ messages: {
67
+ create: (opts: Record<string, unknown>) => Promise<{ content: Array<{ text?: string }> }>;
68
+ };
69
+ } | null = null;
70
+
71
+ async function getClient() {
72
+ if (!anthropicClient) {
73
+ const { default: Anthropic } = await import("@anthropic-ai/sdk");
74
+ anthropicClient = new Anthropic({ apiKey }) as unknown as typeof anthropicClient;
75
+ }
76
+ // biome-ignore lint/style/noNonNullAssertion: assigned above
77
+ return anthropicClient!;
78
+ }
79
+
80
+ return {
81
+ async summarize(prompt: string): Promise<string> {
82
+ try {
83
+ await acquireToken(limiter);
84
+ const client = await getClient();
85
+ const response = await client.messages.create({
86
+ model,
87
+ max_tokens: 300,
88
+ messages: [{ role: "user", content: prompt }],
89
+ });
90
+ const text = response.content[0]?.text;
91
+ return text ?? createFallbackClient().summarize(prompt);
92
+ } catch (err) {
93
+ console.warn("LLM summarize failed, using fallback:", err);
94
+ return createFallbackClient().summarize(prompt);
95
+ }
96
+ },
97
+ async classify(prompt: string, options: string[]): Promise<string> {
98
+ try {
99
+ await acquireToken(limiter);
100
+ const client = await getClient();
101
+ const response = await client.messages.create({
102
+ model,
103
+ max_tokens: 50,
104
+ messages: [
105
+ {
106
+ role: "user",
107
+ content: `${prompt}\n\nRespond with exactly one of: ${options.join(", ")}`,
108
+ },
109
+ ],
110
+ });
111
+ const text = (response.content[0]?.text ?? "").trim().toLowerCase();
112
+ const match = options.find((o) => text.includes(o.toLowerCase()));
113
+ return match ?? options[0] ?? "unknown";
114
+ } catch (err) {
115
+ console.warn("LLM classify failed, using fallback:", err);
116
+ return options[0] ?? "unknown";
117
+ }
118
+ },
119
+ };
120
+ }
@@ -0,0 +1,99 @@
1
+ // Module: logger — structured JSON logging to sia.log
2
+
3
+ import { appendFileSync, existsSync, mkdirSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { SIA_HOME } from "@/shared/config";
6
+
7
+ export type LogLevel = "debug" | "info" | "warn" | "error";
8
+
9
+ export interface LogEntry {
10
+ ts: number;
11
+ level: LogLevel;
12
+ module: string;
13
+ op: string;
14
+ message: string;
15
+ error?: string;
16
+ [key: string]: unknown;
17
+ }
18
+
19
+ export interface Logger {
20
+ debug(module: string, op: string, message: string, extra?: Record<string, unknown>): void;
21
+ info(module: string, op: string, message: string, extra?: Record<string, unknown>): void;
22
+ warn(module: string, op: string, message: string, extra?: Record<string, unknown>): void;
23
+ error(
24
+ module: string,
25
+ op: string,
26
+ message: string,
27
+ err?: unknown,
28
+ extra?: Record<string, unknown>,
29
+ ): void;
30
+ }
31
+
32
+ export function createLogger(siaHome?: string): Logger {
33
+ const home = siaHome ?? SIA_HOME;
34
+ const logDir = join(home, "logs");
35
+ const logFile = join(logDir, "sia.log");
36
+
37
+ if (!existsSync(logDir)) {
38
+ mkdirSync(logDir, { recursive: true });
39
+ }
40
+
41
+ function write(entry: LogEntry): void {
42
+ appendFileSync(logFile, `${JSON.stringify(entry)}\n`);
43
+ }
44
+
45
+ function log(
46
+ level: LogLevel,
47
+ module: string,
48
+ op: string,
49
+ message: string,
50
+ extra?: Record<string, unknown>,
51
+ ): void {
52
+ const entry: LogEntry = {
53
+ ts: Date.now(),
54
+ level,
55
+ module,
56
+ op,
57
+ message,
58
+ ...extra,
59
+ };
60
+ write(entry);
61
+ }
62
+
63
+ return {
64
+ debug(module: string, op: string, message: string, extra?: Record<string, unknown>): void {
65
+ log("debug", module, op, message, extra);
66
+ },
67
+
68
+ info(module: string, op: string, message: string, extra?: Record<string, unknown>): void {
69
+ log("info", module, op, message, extra);
70
+ },
71
+
72
+ warn(module: string, op: string, message: string, extra?: Record<string, unknown>): void {
73
+ log("warn", module, op, message, extra);
74
+ },
75
+
76
+ error(
77
+ module: string,
78
+ op: string,
79
+ message: string,
80
+ err?: unknown,
81
+ extra?: Record<string, unknown>,
82
+ ): void {
83
+ const errorMessage =
84
+ err instanceof Error ? err.message : err != null ? String(err) : undefined;
85
+ const entry: LogEntry = {
86
+ ts: Date.now(),
87
+ level: "error",
88
+ module,
89
+ op,
90
+ message,
91
+ ...(errorMessage != null ? { error: errorMessage } : {}),
92
+ ...extra,
93
+ };
94
+ write(entry);
95
+ },
96
+ };
97
+ }
98
+
99
+ export const defaultLogger: Logger = createLogger();
@@ -0,0 +1,79 @@
1
+ // src/shared/types.ts — Cross-module type aliases
2
+ // No runtime code. No imports from other Sia modules (prevents circular deps).
3
+
4
+ // ── Semantic aliases ──────────────────────────────────────────
5
+ export type UnixMs = number;
6
+ export type RepoHash = string;
7
+ export type PackagePath = string | null;
8
+ export type NodeId = string;
9
+ export type EdgeId = string;
10
+ export type SessionId = string;
11
+
12
+ // ── Trust ─────────────────────────────────────────────────────
13
+ export type TrustTier = 1 | 2 | 3 | 4;
14
+
15
+ // ── Operations ────────────────────────────────────────────────
16
+ // Re-exported from capture/types.ts to provide a single shared import path.
17
+ export type { ConsolidationOp } from "@/capture/types";
18
+ export type ValidationStatus = "pending" | "passed" | "rejected" | "quarantined";
19
+ export type ProcessingStatus = "complete" | "partial" | "failed";
20
+ export type Visibility = "private" | "team" | "project";
21
+
22
+ // ── Current entity types ──────────────────────────────────────
23
+ // Re-exported from capture/types.ts to avoid duplication.
24
+ export type { EntityType } from "@/capture/types";
25
+
26
+ // ── Forward-looking node kinds (Phase 14+ unified graph_nodes schema) ──
27
+ // Current code should use EntityType above. These are for future phases.
28
+ export type StructuralKind = "CodeSymbol" | "FileNode" | "PackageNode";
29
+ export type SemanticKind =
30
+ | "Concept"
31
+ | "Decision"
32
+ | "Bug"
33
+ | "Solution"
34
+ | "Convention"
35
+ | "Community"
36
+ | "ContentChunk";
37
+ export type EventKind =
38
+ | "SessionNode"
39
+ | "EditEvent"
40
+ | "ExecutionEvent"
41
+ | "SearchEvent"
42
+ | "GitEvent"
43
+ | "ErrorEvent"
44
+ | "UserDecision"
45
+ | "UserPrompt"
46
+ | "TaskNode";
47
+ export type NodeKind = StructuralKind | SemanticKind | EventKind | "ExternalRef";
48
+
49
+ // ── Edge types (current schema) ───────────────────────────────
50
+ export type StructuralEdgeType =
51
+ | "defines"
52
+ | "imports"
53
+ | "calls"
54
+ | "inherits_from"
55
+ | "contains"
56
+ | "depends_on";
57
+ export type SemanticEdgeType =
58
+ | "pertains_to"
59
+ | "solves"
60
+ | "caused_by"
61
+ | "supersedes"
62
+ | "elaborates"
63
+ | "contradicts"
64
+ | "relates_to"
65
+ | "references";
66
+ export type CommunityEdgeType = "member_of" | "summarized_by";
67
+ export type EdgeType = StructuralEdgeType | SemanticEdgeType | CommunityEdgeType;
68
+
69
+ // ── Forward-looking edge types (Phase 14+) ────────────────────
70
+ export type EventEdgeType =
71
+ | "modifies"
72
+ | "triggered_by"
73
+ | "produced_by"
74
+ | "resolves"
75
+ | "during_task"
76
+ | "precedes";
77
+ export type SessionEdgeType = "part_of" | "continued_from";
78
+ export type DocEdgeType = "child_of";
79
+ export type AllEdgeType = EdgeType | EventEdgeType | SessionEdgeType | DocEdgeType;
@@ -0,0 +1,43 @@
1
+ // Module: client — LibSQL-backed SiaDb factory for team sync
2
+
3
+ import { mkdirSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import type { OpenDbOpts, SiaDb } from "@/graph/db-interface";
6
+ import { LibSqlDb } from "@/graph/db-interface";
7
+ import type { SyncConfig } from "@/shared/config";
8
+ import { SIA_HOME } from "@/shared/config";
9
+ import { getToken } from "@/sync/keychain";
10
+
11
+ /**
12
+ * Open a LibSqlDb (embedded replica) when sync is enabled; otherwise fall back
13
+ * to the local BunSqliteDb.
14
+ */
15
+ export async function createSiaDb(
16
+ repoHash: string,
17
+ config: SyncConfig,
18
+ opts: OpenDbOpts = {},
19
+ ): Promise<SiaDb> {
20
+ if (!config.enabled || !config.serverUrl) {
21
+ throw new Error("createSiaDb() called without sync enabled. Use openSiaDb() instead.");
22
+ }
23
+
24
+ const token = await getToken(config.serverUrl);
25
+ if (!token) {
26
+ throw new Error("Run 'npx sia team join' to authenticate");
27
+ }
28
+
29
+ const home = opts.siaHome ?? SIA_HOME;
30
+ const repoDir = join(home, "repos", repoHash);
31
+ mkdirSync(repoDir, { recursive: true });
32
+ const dbPath = join(repoDir, "graph.db");
33
+
34
+ const { createClient } = await import("@libsql/client");
35
+ const client = createClient({
36
+ url: `file:${dbPath}`,
37
+ syncUrl: config.serverUrl,
38
+ authToken: token,
39
+ syncInterval: config.syncInterval,
40
+ });
41
+
42
+ return new LibSqlDb(client as unknown as ConstructorParameters<typeof LibSqlDb>[0]);
43
+ }
@@ -0,0 +1,106 @@
1
+ // Module: conflict — conflict detection for concurrent facts
2
+
3
+ import { v4 as uuid } from "uuid";
4
+ import { wordJaccard } from "@/capture/consolidate";
5
+ import type { SiaDb } from "@/graph/db-interface";
6
+ import type { Entity } from "@/graph/entities";
7
+ import type { LlmClient } from "@/shared/llm-client";
8
+ import { cosineSimilarity } from "@/sync/dedup";
9
+
10
+ /**
11
+ * Compute the magnitude (L2 norm) of an embedding vector.
12
+ */
13
+ function embeddingMagnitude(e: Uint8Array | null): number | null {
14
+ if (!e) return null;
15
+ const f = new Float32Array(e.buffer, e.byteOffset, Math.floor(e.byteLength / 4));
16
+ if (f.length === 0) return null;
17
+ let sum = 0;
18
+ for (let i = 0; i < f.length; i++) {
19
+ sum += f[i] * f[i];
20
+ }
21
+ return Math.sqrt(sum);
22
+ }
23
+
24
+ function rangesOverlap(
25
+ aStart: number | null,
26
+ aEnd: number | null,
27
+ bStart: number | null,
28
+ bEnd: number | null,
29
+ ): boolean {
30
+ const a0 = aStart ?? Number.MIN_SAFE_INTEGER;
31
+ const a1 = aEnd ?? Number.MAX_SAFE_INTEGER;
32
+ const b0 = bStart ?? Number.MIN_SAFE_INTEGER;
33
+ const b1 = bEnd ?? Number.MAX_SAFE_INTEGER;
34
+ return a0 <= b1 && b0 <= a1;
35
+ }
36
+
37
+ export async function detectConflicts(db: SiaDb, llmClient?: LlmClient): Promise<number> {
38
+ const result = await db.execute(
39
+ "SELECT * FROM graph_nodes WHERE archived_at IS NULL AND t_valid_until IS NULL",
40
+ );
41
+ const entities = result.rows as unknown as Entity[];
42
+
43
+ let conflicts = 0;
44
+
45
+ for (let i = 0; i < entities.length; i++) {
46
+ for (let j = i + 1; j < entities.length; j++) {
47
+ const a = entities[i];
48
+ const b = entities[j];
49
+
50
+ if (a.type !== b.type) continue;
51
+ if (!rangesOverlap(a.t_valid_from, a.t_valid_until, b.t_valid_from, b.t_valid_until))
52
+ continue;
53
+
54
+ // Pre-filter: skip pairs where embedding magnitude difference > 0.3
55
+ const magA = embeddingMagnitude(a.embedding);
56
+ const magB = embeddingMagnitude(b.embedding);
57
+ if (magA !== null && magB !== null && Math.abs(magA - magB) > 0.3) continue;
58
+
59
+ // Similarity check: cosine for entities with embeddings, wordJaccard fallback
60
+ let similar = false;
61
+ const bothHaveEmbeddings = a.embedding !== null && b.embedding !== null;
62
+ if (bothHaveEmbeddings) {
63
+ const cosine = cosineSimilarity(a.embedding, b.embedding);
64
+ similar = cosine !== null && cosine > 0.85;
65
+ } else {
66
+ const jaccard = wordJaccard(a.content, b.content);
67
+ similar = jaccard > 0.95;
68
+ }
69
+
70
+ if (!similar) continue;
71
+
72
+ // Contradiction check: LLM classification or content comparison fallback
73
+ let contradictory: boolean;
74
+ if (llmClient) {
75
+ const prompt = `Fact A: "${a.name}" — ${a.content}\nFact B: "${b.name}" — ${b.content}\n\nAre these two facts contradictory, complementary, or duplicate?`;
76
+ const classification = await llmClient.classify(prompt, [
77
+ "contradictory",
78
+ "complementary",
79
+ "duplicate",
80
+ ]);
81
+ contradictory = classification === "contradictory";
82
+ } else {
83
+ contradictory = a.content !== b.content;
84
+ }
85
+
86
+ if (contradictory) {
87
+ const groupId = a.conflict_group_id ?? b.conflict_group_id ?? uuid();
88
+ if (a.conflict_group_id !== groupId) {
89
+ await db.execute("UPDATE graph_nodes SET conflict_group_id = ? WHERE id = ?", [
90
+ groupId,
91
+ a.id,
92
+ ]);
93
+ }
94
+ if (b.conflict_group_id !== groupId) {
95
+ await db.execute("UPDATE graph_nodes SET conflict_group_id = ? WHERE id = ?", [
96
+ groupId,
97
+ b.id,
98
+ ]);
99
+ }
100
+ conflicts++;
101
+ }
102
+ }
103
+ }
104
+
105
+ return conflicts;
106
+ }
@@ -0,0 +1,183 @@
1
+ // Module: dedup — three-layer deduplication for team sync
2
+
3
+ import { wordJaccard } from "@/capture/consolidate";
4
+ import type { SiaDb } from "@/graph/db-interface";
5
+ import { insertEdge } from "@/graph/edges";
6
+ import type { Entity } from "@/graph/entities";
7
+ import { invalidateEntity, updateEntity } from "@/graph/entities";
8
+ import type { LlmClient } from "@/shared/llm-client";
9
+
10
+ export interface DedupeResult {
11
+ merged: number;
12
+ flagged: number;
13
+ different: number;
14
+ }
15
+
16
+ function normalizeName(name: string): string {
17
+ return name
18
+ .trim()
19
+ .toLowerCase()
20
+ .replace(/[^a-z0-9\-_]+/g, " ");
21
+ }
22
+
23
+ export function cosineSimilarity(a: Uint8Array | null, b: Uint8Array | null): number | null {
24
+ if (!a || !b) return null;
25
+ const fa = new Float32Array(a.buffer, a.byteOffset, Math.floor(a.byteLength / 4));
26
+ const fb = new Float32Array(b.buffer, b.byteOffset, Math.floor(b.byteLength / 4));
27
+ if (fa.length !== fb.length || fa.length === 0) return null;
28
+
29
+ let dot = 0;
30
+ let normA = 0;
31
+ let normB = 0;
32
+ for (let i = 0; i < fa.length; i++) {
33
+ dot += fa[i] * fb[i];
34
+ normA += fa[i] * fa[i];
35
+ normB += fb[i] * fb[i];
36
+ }
37
+ if (normA === 0 || normB === 0) return null;
38
+ return dot / Math.sqrt(normA * normB);
39
+ }
40
+
41
+ /**
42
+ * Compute the magnitude (L2 norm) of an embedding vector.
43
+ */
44
+ function _embeddingMagnitude(e: Uint8Array | null): number | null {
45
+ if (!e) return null;
46
+ const f = new Float32Array(e.buffer, e.byteOffset, Math.floor(e.byteLength / 4));
47
+ if (f.length === 0) return null;
48
+ let sum = 0;
49
+ for (let i = 0; i < f.length; i++) {
50
+ sum += f[i] * f[i];
51
+ }
52
+ return Math.sqrt(sum);
53
+ }
54
+
55
+ /**
56
+ * Union two JSON-encoded string arrays.
57
+ */
58
+ function unionJsonArrays(jsonA: string, jsonB: string): string {
59
+ let arrA: string[] = [];
60
+ let arrB: string[] = [];
61
+ try {
62
+ arrA = JSON.parse(jsonA);
63
+ } catch {
64
+ /* empty */
65
+ }
66
+ try {
67
+ arrB = JSON.parse(jsonB);
68
+ } catch {
69
+ /* empty */
70
+ }
71
+ const merged = [...new Set([...arrA, ...arrB])];
72
+ return JSON.stringify(merged);
73
+ }
74
+
75
+ /**
76
+ * Compute importance for a merged entity using time-decay weighted average.
77
+ */
78
+ function mergedImportance(a: Entity, b: Entity, now: number): number {
79
+ const ageDaysA = (now - a.created_at) / 86400000;
80
+ const ageDaysB = (now - b.created_at) / 86400000;
81
+ const wA = Math.exp(-0.01 * ageDaysA);
82
+ const wB = Math.exp(-0.01 * ageDaysB);
83
+ return (a.importance * wA + b.importance * wB) / (wA + wB);
84
+ }
85
+
86
+ export async function deduplicateEntities(
87
+ db: SiaDb,
88
+ peerEntities: Entity[],
89
+ llmClient?: LlmClient,
90
+ ): Promise<DedupeResult> {
91
+ const localRows = await db.execute(
92
+ "SELECT * FROM graph_nodes WHERE archived_at IS NULL AND t_valid_until IS NULL",
93
+ );
94
+ const locals = localRows.rows as unknown as Entity[];
95
+ const now = Date.now();
96
+
97
+ const result: DedupeResult = { merged: 0, flagged: 0, different: 0 };
98
+
99
+ for (const peer of peerEntities) {
100
+ const peerNorm = normalizeName(peer.name);
101
+ const peerId = peer.created_by ?? "peer";
102
+ for (const local of locals) {
103
+ if (local.type !== peer.type) continue;
104
+
105
+ const nameJaccard = wordJaccard(peerNorm, normalizeName(local.name));
106
+ let decision: "merged" | "pending" | "different";
107
+
108
+ if (nameJaccard > 0.95) {
109
+ decision = "merged";
110
+ } else {
111
+ const cosine = cosineSimilarity(local.embedding, peer.embedding);
112
+ if (cosine !== null && cosine > 0.92) {
113
+ decision = "merged";
114
+ } else if (cosine !== null && cosine >= 0.8) {
115
+ decision = "pending";
116
+ } else {
117
+ decision = "different";
118
+ }
119
+ }
120
+
121
+ // Layer 3: LLM classification for pending pairs (0.80-0.92 range)
122
+ if (decision === "pending" && llmClient) {
123
+ const prompt = `Entity A: "${local.name}" — ${local.content}\nEntity B: "${peer.name}" — ${peer.content}\n\nAre these the same entity, different entities, or related entities?`;
124
+ const classification = await llmClient.classify(prompt, ["SAME", "DIFFERENT", "RELATED"]);
125
+
126
+ if (classification === "SAME") {
127
+ decision = "merged";
128
+ } else if (classification === "RELATED") {
129
+ // Create a relates_to edge
130
+ await insertEdge(db, {
131
+ from_id: local.id,
132
+ to_id: peer.id,
133
+ type: "relates_to",
134
+ weight: 0.6,
135
+ });
136
+ decision = "different";
137
+ } else {
138
+ decision = "different";
139
+ }
140
+ }
141
+
142
+ // Merge implementation for SAME decisions
143
+ if (decision === "merged") {
144
+ const mergedTags = unionJsonArrays(local.tags, peer.tags);
145
+ const mergedFilePaths = unionJsonArrays(local.file_paths, peer.file_paths);
146
+ const newTrustTier = Math.max(local.trust_tier, peer.trust_tier);
147
+ const newImportance = mergedImportance(local, peer, now);
148
+
149
+ let mergedContent = local.content;
150
+ if (llmClient) {
151
+ mergedContent = await llmClient.summarize(
152
+ `Synthesize a merged description from these two entity descriptions:\n1. ${local.content}\n2. ${peer.content}`,
153
+ );
154
+ }
155
+
156
+ await updateEntity(db, local.id, {
157
+ tags: mergedTags,
158
+ file_paths: mergedFilePaths,
159
+ trust_tier: newTrustTier,
160
+ importance: newImportance,
161
+ content: mergedContent,
162
+ });
163
+
164
+ // Invalidate the peer (losing entity)
165
+ await invalidateEntity(db, peer.id);
166
+
167
+ result.merged++;
168
+ } else if (decision === "pending") {
169
+ result.flagged++;
170
+ } else {
171
+ result.different++;
172
+ }
173
+
174
+ await db.execute(
175
+ `INSERT OR REPLACE INTO sync_dedup_log (entity_a_id, entity_b_id, peer_id, decision, checked_at)
176
+ VALUES (?, ?, ?, ?, ?)`,
177
+ [local.id, peer.id, peerId, decision, now],
178
+ );
179
+ }
180
+ }
181
+
182
+ return result;
183
+ }