@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,413 @@
1
+ // Module: pm-report — Project management intelligence reports
2
+ //
3
+ // Generates sprint summaries, decision logs, and risk dashboards from the
4
+ // knowledge graph in PM-friendly language.
5
+
6
+ import { writeFileSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import type { SiaDb } from "@/graph/db-interface";
9
+
10
+ export type PmReportType = "sprint" | "decisions" | "risks";
11
+
12
+ export interface PmReportOptions {
13
+ type: PmReportType;
14
+ since?: number;
15
+ until?: number;
16
+ outputPath?: string;
17
+ }
18
+
19
+ interface EntityRow {
20
+ id: string;
21
+ type: string;
22
+ name: string;
23
+ content: string;
24
+ summary: string | null;
25
+ trust_tier: number;
26
+ importance: number;
27
+ created_at: number;
28
+ file_paths: string | null;
29
+ kind: string | null;
30
+ conflict_group_id: string | null;
31
+ }
32
+
33
+ function formatDate(ms: number): string {
34
+ return new Date(ms).toISOString().split("T")[0];
35
+ }
36
+
37
+ async function queryEntities(
38
+ db: SiaDb,
39
+ types: string[],
40
+ since?: number,
41
+ until?: number,
42
+ ): Promise<EntityRow[]> {
43
+ const placeholders = types.map(() => "?").join(", ");
44
+ let sql = `SELECT id, type, name, content, summary, trust_tier, importance, created_at, file_paths, kind, conflict_group_id
45
+ FROM graph_nodes
46
+ WHERE type IN (${placeholders}) AND t_valid_until IS NULL AND archived_at IS NULL`;
47
+ const params: unknown[] = [...types];
48
+
49
+ if (since != null) {
50
+ sql += " AND created_at >= ?";
51
+ params.push(since);
52
+ }
53
+ if (until != null) {
54
+ sql += " AND created_at <= ?";
55
+ params.push(until);
56
+ }
57
+ sql += " ORDER BY created_at ASC";
58
+
59
+ const result = await db.execute(sql, params);
60
+ return result.rows as unknown as EntityRow[];
61
+ }
62
+
63
+ function groupBy<T>(items: T[], key: (item: T) => string): Record<string, T[]> {
64
+ const groups: Record<string, T[]> = {};
65
+ for (const item of items) {
66
+ const k = key(item);
67
+ if (!groups[k]) groups[k] = [];
68
+ groups[k].push(item);
69
+ }
70
+ return groups;
71
+ }
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // Sprint Summary
75
+ // ---------------------------------------------------------------------------
76
+
77
+ async function generateSprintSummary(db: SiaDb, since: number, until: number): Promise<string> {
78
+ const entities = await queryEntities(
79
+ db,
80
+ ["Decision", "Bug", "Solution", "Convention", "Concept"],
81
+ since,
82
+ until,
83
+ );
84
+
85
+ if (entities.length === 0) {
86
+ return `# Sprint Summary (${formatDate(since)} — ${formatDate(until)})\n\nNo activity captured in this time range.\n`;
87
+ }
88
+
89
+ const byType = groupBy(entities, (e) => e.type);
90
+ const decisions = byType.Decision ?? [];
91
+ const bugs = byType.Bug ?? [];
92
+ const solutions = byType.Solution ?? [];
93
+ const conventions = byType.Convention ?? [];
94
+ const concepts = byType.Concept ?? [];
95
+
96
+ const sections: string[] = [];
97
+
98
+ // Header
99
+ sections.push(`# Sprint Summary (${formatDate(since)} — ${formatDate(until)})`);
100
+ sections.push("");
101
+
102
+ // Executive summary
103
+ const parts: string[] = [];
104
+ if (decisions.length > 0)
105
+ parts.push(`${decisions.length} decision${decisions.length === 1 ? "" : "s"} made`);
106
+ if (bugs.length > 0) parts.push(`${bugs.length} bug${bugs.length === 1 ? "" : "s"} found`);
107
+ if (solutions.length > 0)
108
+ parts.push(`${solutions.length} fix${solutions.length === 1 ? "" : "es"} applied`);
109
+ if (conventions.length > 0)
110
+ parts.push(
111
+ `${conventions.length} convention${conventions.length === 1 ? "" : "s"} established`,
112
+ );
113
+ sections.push(`**Overview:** ${parts.join(", ")}.`);
114
+ sections.push("");
115
+
116
+ // Key Decisions
117
+ if (decisions.length > 0) {
118
+ sections.push("## Key Decisions");
119
+ sections.push("");
120
+ for (const d of decisions) {
121
+ sections.push(
122
+ `- **${d.name}** (${formatDate(d.created_at)}) — ${d.summary ?? d.content.slice(0, 120)}`,
123
+ );
124
+ }
125
+ sections.push("");
126
+ }
127
+
128
+ // Bugs
129
+ if (bugs.length > 0) {
130
+ sections.push("## Bugs Found");
131
+ sections.push("");
132
+ for (const b of bugs) {
133
+ sections.push(
134
+ `- **${b.name}** (${formatDate(b.created_at)}) — ${b.summary ?? b.content.slice(0, 120)}`,
135
+ );
136
+ }
137
+ sections.push("");
138
+ }
139
+
140
+ // Solutions
141
+ if (solutions.length > 0) {
142
+ sections.push("## Solutions Applied");
143
+ sections.push("");
144
+ for (const s of solutions) {
145
+ sections.push(
146
+ `- **${s.name}** (${formatDate(s.created_at)}) — ${s.summary ?? s.content.slice(0, 120)}`,
147
+ );
148
+ }
149
+ sections.push("");
150
+ }
151
+
152
+ // Conventions
153
+ if (conventions.length > 0) {
154
+ sections.push("## Conventions Established");
155
+ sections.push("");
156
+ for (const c of conventions) {
157
+ sections.push(`- **${c.name}** — ${c.summary ?? c.content.slice(0, 120)}`);
158
+ }
159
+ sections.push("");
160
+ }
161
+
162
+ // Concepts
163
+ if (concepts.length > 0) {
164
+ sections.push("## Key Concepts");
165
+ sections.push("");
166
+ for (const c of concepts) {
167
+ sections.push(`- **${c.name}** — ${c.summary ?? c.content.slice(0, 120)}`);
168
+ }
169
+ sections.push("");
170
+ }
171
+
172
+ // Metrics
173
+ sections.push("## Metrics");
174
+ sections.push("");
175
+ sections.push(`| Category | Count |`);
176
+ sections.push(`|---|---|`);
177
+ sections.push(`| Decisions | ${decisions.length} |`);
178
+ sections.push(`| Bugs found | ${bugs.length} |`);
179
+ sections.push(`| Solutions applied | ${solutions.length} |`);
180
+ sections.push(`| Conventions | ${conventions.length} |`);
181
+ sections.push(`| Total entities | ${entities.length} |`);
182
+ sections.push("");
183
+
184
+ return sections.join("\n");
185
+ }
186
+
187
+ // ---------------------------------------------------------------------------
188
+ // Decision Log
189
+ // ---------------------------------------------------------------------------
190
+
191
+ async function generateDecisionLog(db: SiaDb, since?: number): Promise<string> {
192
+ const decisions = await queryEntities(db, ["Decision"], since);
193
+
194
+ if (decisions.length === 0) {
195
+ const sinceStr = since ? ` since ${formatDate(since)}` : "";
196
+ return `# Decision Log\n\nNo decisions captured${sinceStr}.\n`;
197
+ }
198
+
199
+ const sections: string[] = [];
200
+ sections.push("# Decision Log");
201
+ sections.push("");
202
+ sections.push(
203
+ `*${decisions.length} decision${decisions.length === 1 ? "" : "s"} captured${since ? ` since ${formatDate(since)}` : ""}*`,
204
+ );
205
+ sections.push("");
206
+
207
+ for (let i = 0; i < decisions.length; i++) {
208
+ const d = decisions[i];
209
+ sections.push(`## ${i + 1}. ${d.name}`);
210
+ sections.push("");
211
+ sections.push(`- **Date:** ${formatDate(d.created_at)}`);
212
+ sections.push(
213
+ `- **Trust:** ${d.trust_tier === 1 ? "Verified" : d.trust_tier === 2 ? "Code-derived" : "Inferred"}`,
214
+ );
215
+ if (d.file_paths && d.file_paths !== "[]") {
216
+ sections.push(`- **Files:** ${d.file_paths}`);
217
+ }
218
+ sections.push("");
219
+ sections.push(d.content.slice(0, 500));
220
+ sections.push("");
221
+ }
222
+
223
+ return sections.join("\n");
224
+ }
225
+
226
+ // ---------------------------------------------------------------------------
227
+ // Risk Dashboard
228
+ // ---------------------------------------------------------------------------
229
+
230
+ async function generateRiskDashboard(db: SiaDb): Promise<string> {
231
+ const bugs = await queryEntities(db, ["Bug"]);
232
+ const conflicts = await queryEntities(db, ["Decision", "Convention"]);
233
+ const conflictsWithGroup = conflicts.filter((e) => e.conflict_group_id != null);
234
+ const conventions = await queryEntities(db, ["Convention"]);
235
+
236
+ if (bugs.length === 0 && conflictsWithGroup.length === 0 && conventions.length === 0) {
237
+ return "# Risk Dashboard\n\nNo risks identified. The knowledge graph has no bugs, conflicts, or stale conventions.\n";
238
+ }
239
+
240
+ const sections: string[] = [];
241
+ sections.push("# Risk Dashboard");
242
+ sections.push("");
243
+ sections.push(`*Generated ${formatDate(Date.now())}*`);
244
+ sections.push("");
245
+
246
+ // Group bugs by file path to find recurring areas
247
+ const bugsByFile: Record<string, EntityRow[]> = {};
248
+ for (const bug of bugs) {
249
+ let filePaths: string[] = [];
250
+ try {
251
+ filePaths = JSON.parse(bug.file_paths ?? "[]");
252
+ } catch {
253
+ filePaths = [];
254
+ }
255
+ if (filePaths.length === 0) filePaths = ["unknown"];
256
+ for (const fp of filePaths) {
257
+ if (!bugsByFile[fp]) bugsByFile[fp] = [];
258
+ bugsByFile[fp].push(bug);
259
+ }
260
+ }
261
+
262
+ // Critical: areas with multiple bugs or unresolved conflicts
263
+ const criticalAreas = Object.entries(bugsByFile).filter(([, b]) => b.length >= 2);
264
+ const hasCritical = criticalAreas.length > 0 || conflictsWithGroup.length > 0;
265
+
266
+ if (hasCritical) {
267
+ sections.push("## Critical Risks");
268
+ sections.push("");
269
+ for (const [filePath, fileBugs] of criticalAreas) {
270
+ sections.push(`### Recurring bugs: ${filePath}`);
271
+ sections.push(`${fileBugs.length} bugs in the same area:`);
272
+ for (const b of fileBugs) {
273
+ sections.push(
274
+ `- **${b.name}** (${formatDate(b.created_at)}) — ${b.summary ?? b.content.slice(0, 100)}`,
275
+ );
276
+ }
277
+ sections.push("");
278
+ }
279
+ if (conflictsWithGroup.length > 0) {
280
+ sections.push("### Unresolved conflicts");
281
+ sections.push(`${conflictsWithGroup.length} entities have unresolved conflicts:`);
282
+ for (const c of conflictsWithGroup) {
283
+ sections.push(`- **${c.name}** (${c.type}) — conflict group: ${c.conflict_group_id}`);
284
+ }
285
+ sections.push("");
286
+ }
287
+ }
288
+
289
+ // Moderate: stale conventions (older than 30 days)
290
+ const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
291
+ const staleConventions = conventions.filter((c) => c.created_at < thirtyDaysAgo);
292
+
293
+ if (staleConventions.length > 0) {
294
+ sections.push("## Moderate Risks");
295
+ sections.push("");
296
+ sections.push(`### Stale conventions (${staleConventions.length})`);
297
+ sections.push("Conventions older than 30 days that may need review:");
298
+ for (const c of staleConventions) {
299
+ sections.push(
300
+ `- **${c.name}** (${formatDate(c.created_at)}) — ${c.summary ?? c.content.slice(0, 100)}`,
301
+ );
302
+ }
303
+ sections.push("");
304
+ }
305
+
306
+ // Individual bugs (not in recurring areas)
307
+ const nonRecurringBugs = bugs.filter((b) => {
308
+ let filePaths: string[] = [];
309
+ try {
310
+ filePaths = JSON.parse(b.file_paths ?? "[]");
311
+ } catch {
312
+ filePaths = [];
313
+ }
314
+ if (filePaths.length === 0) filePaths = ["unknown"];
315
+ return filePaths.every((fp) => (bugsByFile[fp]?.length ?? 0) < 2);
316
+ });
317
+
318
+ if (nonRecurringBugs.length > 0) {
319
+ sections.push("## All Bugs");
320
+ sections.push("");
321
+ for (const b of nonRecurringBugs) {
322
+ sections.push(
323
+ `- **${b.name}** (${formatDate(b.created_at)}) — ${b.summary ?? b.content.slice(0, 100)}`,
324
+ );
325
+ }
326
+ sections.push("");
327
+ }
328
+
329
+ // Summary metrics
330
+ sections.push("## Summary");
331
+ sections.push("");
332
+ sections.push(`| Metric | Count |`);
333
+ sections.push(`|---|---|`);
334
+ sections.push(`| Total bugs | ${bugs.length} |`);
335
+ sections.push(`| Recurring bug areas | ${criticalAreas.length} |`);
336
+ sections.push(`| Unresolved conflicts | ${conflictsWithGroup.length} |`);
337
+ sections.push(`| Stale conventions (>30d) | ${staleConventions.length} |`);
338
+ sections.push("");
339
+
340
+ return sections.join("\n");
341
+ }
342
+
343
+ // ---------------------------------------------------------------------------
344
+ // Public API
345
+ // ---------------------------------------------------------------------------
346
+
347
+ export async function generatePmReport(db: SiaDb, opts: PmReportOptions): Promise<string> {
348
+ switch (opts.type) {
349
+ case "sprint": {
350
+ const since = opts.since ?? Date.now() - 14 * 24 * 60 * 60 * 1000;
351
+ const until = opts.until ?? Date.now();
352
+ return generateSprintSummary(db, since, until);
353
+ }
354
+ case "decisions":
355
+ return generateDecisionLog(db, opts.since);
356
+ case "risks":
357
+ return generateRiskDashboard(db);
358
+ default:
359
+ throw new Error(`Unknown report type: ${opts.type}`);
360
+ }
361
+ }
362
+
363
+ const DEFAULT_OUTPUT: Record<PmReportType, string> = {
364
+ sprint: "SPRINT-SUMMARY.md",
365
+ decisions: "DECISION-LOG.md",
366
+ risks: "RISK-DASHBOARD.md",
367
+ };
368
+
369
+ export async function runPmReport(args: string[]): Promise<void> {
370
+ const { resolveRepoHash } = await import("@/capture/hook");
371
+ const { openGraphDb } = await import("@/graph/semantic-db");
372
+ const { resolveSiaHome } = await import("@/shared/config");
373
+
374
+ let type: PmReportType = "sprint";
375
+ let since: number | undefined;
376
+ let until: number | undefined;
377
+ let outputPath: string | undefined;
378
+
379
+ for (let i = 0; i < args.length; i++) {
380
+ if (args[i] === "--type" && args[i + 1]) {
381
+ const t = args[++i];
382
+ if (t === "sprint" || t === "decisions" || t === "risks") type = t;
383
+ else {
384
+ console.error(`Unknown report type: ${t}. Use sprint, decisions, or risks.`);
385
+ return;
386
+ }
387
+ }
388
+ if (args[i] === "--since" && args[i + 1]) {
389
+ since = new Date(args[++i]).getTime();
390
+ }
391
+ if (args[i] === "--until" && args[i + 1]) {
392
+ until = new Date(args[++i]).getTime();
393
+ }
394
+ if (args[i] === "--output" && args[i + 1]) {
395
+ outputPath = args[++i];
396
+ }
397
+ }
398
+
399
+ const cwd = process.cwd();
400
+ const repoHash = resolveRepoHash(cwd);
401
+ const siaHome = resolveSiaHome();
402
+ const db = openGraphDb(repoHash, siaHome);
403
+
404
+ try {
405
+ const markdown = await generatePmReport(db, { type, since, until });
406
+ const outFile = outputPath ?? DEFAULT_OUTPUT[type];
407
+ const fullPath = join(cwd, outFile);
408
+ writeFileSync(fullPath, markdown, "utf-8");
409
+ console.log(`PM report (${type}) written to ${outFile} (${markdown.length} chars)`);
410
+ } finally {
411
+ await db.close();
412
+ }
413
+ }
@@ -0,0 +1,75 @@
1
+ // Module: prune — hard-delete archived entities
2
+
3
+ import type { SiaDb } from "@/graph/db-interface";
4
+
5
+ export interface PruneCandidate {
6
+ id: string;
7
+ name: string;
8
+ type: string;
9
+ importance: number;
10
+ daysSinceAccess: number;
11
+ }
12
+
13
+ /**
14
+ * Return the list of archived-but-not-invalidated entities that would be
15
+ * removed by `pruneConfirm`, together with a computed `daysSinceAccess`.
16
+ */
17
+ export async function pruneDryRun(db: SiaDb): Promise<PruneCandidate[]> {
18
+ const { rows } = await db.execute(
19
+ "SELECT id, name, type, importance, last_accessed FROM graph_nodes WHERE archived_at IS NOT NULL AND t_valid_until IS NULL",
20
+ );
21
+
22
+ const now = Date.now();
23
+
24
+ return rows.map((row) => {
25
+ const lastAccessed = row.last_accessed as string | number | null;
26
+ let daysSinceAccess = 0;
27
+ if (lastAccessed != null) {
28
+ const ts = typeof lastAccessed === "number" ? lastAccessed : new Date(lastAccessed).getTime();
29
+ daysSinceAccess = Math.floor((now - ts) / (1000 * 60 * 60 * 24));
30
+ }
31
+
32
+ return {
33
+ id: row.id as string,
34
+ name: row.name as string,
35
+ type: row.type as string,
36
+ importance: row.importance as number,
37
+ daysSinceAccess,
38
+ };
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Hard-delete all archived (but not bi-temporally invalidated) entities,
44
+ * their community memberships, and related edges.
45
+ *
46
+ * Returns the number of entities deleted.
47
+ */
48
+ export async function pruneConfirm(db: SiaDb): Promise<number> {
49
+ let deletedCount = 0;
50
+
51
+ await db.transaction(async (tx) => {
52
+ // 1. Remove community memberships for archived entities
53
+ await tx.execute(
54
+ "DELETE FROM community_members WHERE entity_id IN (SELECT id FROM graph_nodes WHERE archived_at IS NOT NULL AND t_valid_until IS NULL)",
55
+ );
56
+
57
+ // 2. Remove edges referencing archived entities
58
+ await tx.execute(
59
+ "DELETE FROM graph_edges WHERE from_id IN (SELECT id FROM graph_nodes WHERE archived_at IS NOT NULL AND t_valid_until IS NULL) OR to_id IN (SELECT id FROM graph_nodes WHERE archived_at IS NOT NULL AND t_valid_until IS NULL)",
60
+ );
61
+
62
+ // 3. Count entities about to be deleted
63
+ const { rows } = await tx.execute(
64
+ "SELECT COUNT(*) AS cnt FROM graph_nodes WHERE archived_at IS NOT NULL AND t_valid_until IS NULL",
65
+ );
66
+ deletedCount = (rows[0]?.cnt as number) ?? 0;
67
+
68
+ // 4. Delete the archived entities themselves
69
+ await tx.execute(
70
+ "DELETE FROM graph_nodes WHERE archived_at IS NOT NULL AND t_valid_until IS NULL",
71
+ );
72
+ });
73
+
74
+ return deletedCount;
75
+ }