@grafema/util 0.3.0-beta

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 (324) hide show
  1. package/LICENSE +190 -0
  2. package/dist/api/GraphAPI.d.ts +87 -0
  3. package/dist/api/GraphAPI.d.ts.map +1 -0
  4. package/dist/api/GraphAPI.js +212 -0
  5. package/dist/api/GraphAPI.js.map +1 -0
  6. package/dist/api/GuaranteeAPI.d.ts +147 -0
  7. package/dist/api/GuaranteeAPI.d.ts.map +1 -0
  8. package/dist/api/GuaranteeAPI.js +290 -0
  9. package/dist/api/GuaranteeAPI.js.map +1 -0
  10. package/dist/config/ConfigLoader.d.ts +214 -0
  11. package/dist/config/ConfigLoader.d.ts.map +1 -0
  12. package/dist/config/ConfigLoader.js +441 -0
  13. package/dist/config/ConfigLoader.js.map +1 -0
  14. package/dist/config/index.d.ts +6 -0
  15. package/dist/config/index.d.ts.map +1 -0
  16. package/dist/config/index.js +5 -0
  17. package/dist/config/index.js.map +1 -0
  18. package/dist/core/CoverageAnalyzer.d.ts +65 -0
  19. package/dist/core/CoverageAnalyzer.d.ts.map +1 -0
  20. package/dist/core/CoverageAnalyzer.js +199 -0
  21. package/dist/core/CoverageAnalyzer.js.map +1 -0
  22. package/dist/core/FileExplainer.d.ts +101 -0
  23. package/dist/core/FileExplainer.d.ts.map +1 -0
  24. package/dist/core/FileExplainer.js +140 -0
  25. package/dist/core/FileExplainer.js.map +1 -0
  26. package/dist/core/FileOverview.d.ts +124 -0
  27. package/dist/core/FileOverview.d.ts.map +1 -0
  28. package/dist/core/FileOverview.js +279 -0
  29. package/dist/core/FileOverview.js.map +1 -0
  30. package/dist/core/GrafemaUri.d.ts +66 -0
  31. package/dist/core/GrafemaUri.d.ts.map +1 -0
  32. package/dist/core/GrafemaUri.js +191 -0
  33. package/dist/core/GrafemaUri.js.map +1 -0
  34. package/dist/core/GraphBackend.d.ts +158 -0
  35. package/dist/core/GraphBackend.d.ts.map +1 -0
  36. package/dist/core/GraphBackend.js +85 -0
  37. package/dist/core/GraphBackend.js.map +1 -0
  38. package/dist/core/GraphFreshnessChecker.d.ts +33 -0
  39. package/dist/core/GraphFreshnessChecker.d.ts.map +1 -0
  40. package/dist/core/GraphFreshnessChecker.js +104 -0
  41. package/dist/core/GraphFreshnessChecker.js.map +1 -0
  42. package/dist/core/GuaranteeManager.d.ts +254 -0
  43. package/dist/core/GuaranteeManager.d.ts.map +1 -0
  44. package/dist/core/GuaranteeManager.js +447 -0
  45. package/dist/core/GuaranteeManager.js.map +1 -0
  46. package/dist/core/HashUtils.d.ts +24 -0
  47. package/dist/core/HashUtils.d.ts.map +1 -0
  48. package/dist/core/HashUtils.js +46 -0
  49. package/dist/core/HashUtils.js.map +1 -0
  50. package/dist/core/IncrementalReanalyzer.d.ts +33 -0
  51. package/dist/core/IncrementalReanalyzer.d.ts.map +1 -0
  52. package/dist/core/IncrementalReanalyzer.js +67 -0
  53. package/dist/core/IncrementalReanalyzer.js.map +1 -0
  54. package/dist/core/ResourceRegistry.d.ts +17 -0
  55. package/dist/core/ResourceRegistry.d.ts.map +1 -0
  56. package/dist/core/ResourceRegistry.js +32 -0
  57. package/dist/core/ResourceRegistry.js.map +1 -0
  58. package/dist/core/SemanticId.d.ts +159 -0
  59. package/dist/core/SemanticId.d.ts.map +1 -0
  60. package/dist/core/SemanticId.js +291 -0
  61. package/dist/core/SemanticId.js.map +1 -0
  62. package/dist/core/VersionManager.d.ts +166 -0
  63. package/dist/core/VersionManager.d.ts.map +1 -0
  64. package/dist/core/VersionManager.js +239 -0
  65. package/dist/core/VersionManager.js.map +1 -0
  66. package/dist/core/brandNodeInternal.d.ts +14 -0
  67. package/dist/core/brandNodeInternal.d.ts.map +1 -0
  68. package/dist/core/brandNodeInternal.js +4 -0
  69. package/dist/core/brandNodeInternal.js.map +1 -0
  70. package/dist/core/nodes/GuaranteeNode.d.ts +76 -0
  71. package/dist/core/nodes/GuaranteeNode.d.ts.map +1 -0
  72. package/dist/core/nodes/GuaranteeNode.js +118 -0
  73. package/dist/core/nodes/GuaranteeNode.js.map +1 -0
  74. package/dist/core/nodes/IssueNode.d.ts +73 -0
  75. package/dist/core/nodes/IssueNode.d.ts.map +1 -0
  76. package/dist/core/nodes/IssueNode.js +130 -0
  77. package/dist/core/nodes/IssueNode.js.map +1 -0
  78. package/dist/core/nodes/NodeKind.d.ts +104 -0
  79. package/dist/core/nodes/NodeKind.d.ts.map +1 -0
  80. package/dist/core/nodes/NodeKind.js +166 -0
  81. package/dist/core/nodes/NodeKind.js.map +1 -0
  82. package/dist/diagnostics/DiagnosticCollector.d.ts +103 -0
  83. package/dist/diagnostics/DiagnosticCollector.d.ts.map +1 -0
  84. package/dist/diagnostics/DiagnosticCollector.js +133 -0
  85. package/dist/diagnostics/DiagnosticCollector.js.map +1 -0
  86. package/dist/diagnostics/DiagnosticReporter.d.ts +122 -0
  87. package/dist/diagnostics/DiagnosticReporter.d.ts.map +1 -0
  88. package/dist/diagnostics/DiagnosticReporter.js +300 -0
  89. package/dist/diagnostics/DiagnosticReporter.js.map +1 -0
  90. package/dist/diagnostics/DiagnosticWriter.d.ts +31 -0
  91. package/dist/diagnostics/DiagnosticWriter.d.ts.map +1 -0
  92. package/dist/diagnostics/DiagnosticWriter.js +44 -0
  93. package/dist/diagnostics/DiagnosticWriter.js.map +1 -0
  94. package/dist/diagnostics/categories.d.ts +57 -0
  95. package/dist/diagnostics/categories.d.ts.map +1 -0
  96. package/dist/diagnostics/categories.js +71 -0
  97. package/dist/diagnostics/categories.js.map +1 -0
  98. package/dist/diagnostics/index.d.ts +17 -0
  99. package/dist/diagnostics/index.d.ts.map +1 -0
  100. package/dist/diagnostics/index.js +15 -0
  101. package/dist/diagnostics/index.js.map +1 -0
  102. package/dist/errors/GrafemaError.d.ts +200 -0
  103. package/dist/errors/GrafemaError.d.ts.map +1 -0
  104. package/dist/errors/GrafemaError.js +209 -0
  105. package/dist/errors/GrafemaError.js.map +1 -0
  106. package/dist/index.d.ts +75 -0
  107. package/dist/index.d.ts.map +1 -0
  108. package/dist/index.js +76 -0
  109. package/dist/index.js.map +1 -0
  110. package/dist/instructions/index.d.ts +8 -0
  111. package/dist/instructions/index.d.ts.map +1 -0
  112. package/dist/instructions/index.js +20 -0
  113. package/dist/instructions/index.js.map +1 -0
  114. package/dist/instructions/onboarding.md +133 -0
  115. package/dist/knowledge/KnowledgeBase.d.ts +113 -0
  116. package/dist/knowledge/KnowledgeBase.d.ts.map +1 -0
  117. package/dist/knowledge/KnowledgeBase.js +420 -0
  118. package/dist/knowledge/KnowledgeBase.js.map +1 -0
  119. package/dist/knowledge/SemanticAddressResolver.d.ts +59 -0
  120. package/dist/knowledge/SemanticAddressResolver.d.ts.map +1 -0
  121. package/dist/knowledge/SemanticAddressResolver.js +160 -0
  122. package/dist/knowledge/SemanticAddressResolver.js.map +1 -0
  123. package/dist/knowledge/git-ingest.d.ts +58 -0
  124. package/dist/knowledge/git-ingest.d.ts.map +1 -0
  125. package/dist/knowledge/git-ingest.js +301 -0
  126. package/dist/knowledge/git-ingest.js.map +1 -0
  127. package/dist/knowledge/git-queries.d.ts +86 -0
  128. package/dist/knowledge/git-queries.d.ts.map +1 -0
  129. package/dist/knowledge/git-queries.js +177 -0
  130. package/dist/knowledge/git-queries.js.map +1 -0
  131. package/dist/knowledge/index.d.ts +14 -0
  132. package/dist/knowledge/index.d.ts.map +1 -0
  133. package/dist/knowledge/index.js +10 -0
  134. package/dist/knowledge/index.js.map +1 -0
  135. package/dist/knowledge/parser.d.ts +39 -0
  136. package/dist/knowledge/parser.d.ts.map +1 -0
  137. package/dist/knowledge/parser.js +292 -0
  138. package/dist/knowledge/parser.js.map +1 -0
  139. package/dist/knowledge/types.d.ts +133 -0
  140. package/dist/knowledge/types.d.ts.map +1 -0
  141. package/dist/knowledge/types.js +8 -0
  142. package/dist/knowledge/types.js.map +1 -0
  143. package/dist/logging/Logger.d.ts +98 -0
  144. package/dist/logging/Logger.d.ts.map +1 -0
  145. package/dist/logging/Logger.js +274 -0
  146. package/dist/logging/Logger.js.map +1 -0
  147. package/dist/notation/archetypes.d.ts +36 -0
  148. package/dist/notation/archetypes.d.ts.map +1 -0
  149. package/dist/notation/archetypes.js +173 -0
  150. package/dist/notation/archetypes.js.map +1 -0
  151. package/dist/notation/fold.d.ts +25 -0
  152. package/dist/notation/fold.d.ts.map +1 -0
  153. package/dist/notation/fold.js +598 -0
  154. package/dist/notation/fold.js.map +1 -0
  155. package/dist/notation/index.d.ts +18 -0
  156. package/dist/notation/index.d.ts.map +1 -0
  157. package/dist/notation/index.js +16 -0
  158. package/dist/notation/index.js.map +1 -0
  159. package/dist/notation/lodExtractor.d.ts +32 -0
  160. package/dist/notation/lodExtractor.d.ts.map +1 -0
  161. package/dist/notation/lodExtractor.js +149 -0
  162. package/dist/notation/lodExtractor.js.map +1 -0
  163. package/dist/notation/nameShortener.d.ts +22 -0
  164. package/dist/notation/nameShortener.d.ts.map +1 -0
  165. package/dist/notation/nameShortener.js +24 -0
  166. package/dist/notation/nameShortener.js.map +1 -0
  167. package/dist/notation/perspectives.d.ts +11 -0
  168. package/dist/notation/perspectives.d.ts.map +1 -0
  169. package/dist/notation/perspectives.js +16 -0
  170. package/dist/notation/perspectives.js.map +1 -0
  171. package/dist/notation/renderer.d.ts +31 -0
  172. package/dist/notation/renderer.d.ts.map +1 -0
  173. package/dist/notation/renderer.js +315 -0
  174. package/dist/notation/renderer.js.map +1 -0
  175. package/dist/notation/traceRenderer.d.ts +39 -0
  176. package/dist/notation/traceRenderer.d.ts.map +1 -0
  177. package/dist/notation/traceRenderer.js +358 -0
  178. package/dist/notation/traceRenderer.js.map +1 -0
  179. package/dist/notation/types.d.ts +66 -0
  180. package/dist/notation/types.d.ts.map +1 -0
  181. package/dist/notation/types.js +10 -0
  182. package/dist/notation/types.js.map +1 -0
  183. package/dist/queries/NodeContext.d.ts +81 -0
  184. package/dist/queries/NodeContext.d.ts.map +1 -0
  185. package/dist/queries/NodeContext.js +196 -0
  186. package/dist/queries/NodeContext.js.map +1 -0
  187. package/dist/queries/findCallsInFunction.d.ts +62 -0
  188. package/dist/queries/findCallsInFunction.d.ts.map +1 -0
  189. package/dist/queries/findCallsInFunction.js +169 -0
  190. package/dist/queries/findCallsInFunction.js.map +1 -0
  191. package/dist/queries/findContainingFunction.d.ts +57 -0
  192. package/dist/queries/findContainingFunction.d.ts.map +1 -0
  193. package/dist/queries/findContainingFunction.js +91 -0
  194. package/dist/queries/findContainingFunction.js.map +1 -0
  195. package/dist/queries/index.d.ts +18 -0
  196. package/dist/queries/index.d.ts.map +1 -0
  197. package/dist/queries/index.js +14 -0
  198. package/dist/queries/index.js.map +1 -0
  199. package/dist/queries/traceDataflow.d.ts +65 -0
  200. package/dist/queries/traceDataflow.d.ts.map +1 -0
  201. package/dist/queries/traceDataflow.js +754 -0
  202. package/dist/queries/traceDataflow.js.map +1 -0
  203. package/dist/queries/traceValues.d.ts +70 -0
  204. package/dist/queries/traceValues.d.ts.map +1 -0
  205. package/dist/queries/traceValues.js +373 -0
  206. package/dist/queries/traceValues.js.map +1 -0
  207. package/dist/queries/types.d.ts +166 -0
  208. package/dist/queries/types.d.ts.map +1 -0
  209. package/dist/queries/types.js +10 -0
  210. package/dist/queries/types.js.map +1 -0
  211. package/dist/schema/GraphSchemaExtractor.d.ts +53 -0
  212. package/dist/schema/GraphSchemaExtractor.d.ts.map +1 -0
  213. package/dist/schema/GraphSchemaExtractor.js +125 -0
  214. package/dist/schema/GraphSchemaExtractor.js.map +1 -0
  215. package/dist/schema/InterfaceSchemaExtractor.d.ts +73 -0
  216. package/dist/schema/InterfaceSchemaExtractor.d.ts.map +1 -0
  217. package/dist/schema/InterfaceSchemaExtractor.js +113 -0
  218. package/dist/schema/InterfaceSchemaExtractor.js.map +1 -0
  219. package/dist/schema/index.d.ts +5 -0
  220. package/dist/schema/index.d.ts.map +1 -0
  221. package/dist/schema/index.js +3 -0
  222. package/dist/schema/index.js.map +1 -0
  223. package/dist/storage/backends/RFDBServerBackend.d.ts +356 -0
  224. package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -0
  225. package/dist/storage/backends/RFDBServerBackend.js +748 -0
  226. package/dist/storage/backends/RFDBServerBackend.js.map +1 -0
  227. package/dist/storage/backends/typeValidation.d.ts +47 -0
  228. package/dist/storage/backends/typeValidation.d.ts.map +1 -0
  229. package/dist/storage/backends/typeValidation.js +141 -0
  230. package/dist/storage/backends/typeValidation.js.map +1 -0
  231. package/dist/utils/findRfdbBinary.d.ts +67 -0
  232. package/dist/utils/findRfdbBinary.d.ts.map +1 -0
  233. package/dist/utils/findRfdbBinary.js +261 -0
  234. package/dist/utils/findRfdbBinary.js.map +1 -0
  235. package/dist/utils/lazyDownload.d.ts +43 -0
  236. package/dist/utils/lazyDownload.d.ts.map +1 -0
  237. package/dist/utils/lazyDownload.js +175 -0
  238. package/dist/utils/lazyDownload.js.map +1 -0
  239. package/dist/utils/moduleResolution.d.ts +134 -0
  240. package/dist/utils/moduleResolution.d.ts.map +1 -0
  241. package/dist/utils/moduleResolution.js +189 -0
  242. package/dist/utils/moduleResolution.js.map +1 -0
  243. package/dist/utils/resolveNodeFile.d.ts +13 -0
  244. package/dist/utils/resolveNodeFile.d.ts.map +1 -0
  245. package/dist/utils/resolveNodeFile.js +18 -0
  246. package/dist/utils/resolveNodeFile.js.map +1 -0
  247. package/dist/utils/startRfdbServer.d.ts +63 -0
  248. package/dist/utils/startRfdbServer.d.ts.map +1 -0
  249. package/dist/utils/startRfdbServer.js +142 -0
  250. package/dist/utils/startRfdbServer.js.map +1 -0
  251. package/dist/validation/PathValidator.d.ts +80 -0
  252. package/dist/validation/PathValidator.d.ts.map +1 -0
  253. package/dist/validation/PathValidator.js +252 -0
  254. package/dist/validation/PathValidator.js.map +1 -0
  255. package/dist/version.d.ts +11 -0
  256. package/dist/version.d.ts.map +1 -0
  257. package/dist/version.js +26 -0
  258. package/dist/version.js.map +1 -0
  259. package/package.json +50 -0
  260. package/src/api/GraphAPI.ts +307 -0
  261. package/src/api/GuaranteeAPI.ts +402 -0
  262. package/src/config/ConfigLoader.ts +653 -0
  263. package/src/config/index.ts +13 -0
  264. package/src/core/CoverageAnalyzer.ts +243 -0
  265. package/src/core/FileExplainer.ts +179 -0
  266. package/src/core/FileOverview.ts +397 -0
  267. package/src/core/GrafemaUri.ts +216 -0
  268. package/src/core/GraphBackend.ts +266 -0
  269. package/src/core/GraphFreshnessChecker.ts +145 -0
  270. package/src/core/GuaranteeManager.ts +684 -0
  271. package/src/core/HashUtils.ts +48 -0
  272. package/src/core/IncrementalReanalyzer.ts +106 -0
  273. package/src/core/ResourceRegistry.ts +39 -0
  274. package/src/core/SemanticId.ts +423 -0
  275. package/src/core/VersionManager.ts +405 -0
  276. package/src/core/brandNodeInternal.ts +16 -0
  277. package/src/core/nodes/GuaranteeNode.ts +162 -0
  278. package/src/core/nodes/IssueNode.ts +177 -0
  279. package/src/core/nodes/NodeKind.ts +192 -0
  280. package/src/diagnostics/DiagnosticCollector.ts +170 -0
  281. package/src/diagnostics/DiagnosticReporter.ts +395 -0
  282. package/src/diagnostics/DiagnosticWriter.ts +50 -0
  283. package/src/diagnostics/categories.ts +104 -0
  284. package/src/diagnostics/index.ts +30 -0
  285. package/src/errors/GrafemaError.ts +297 -0
  286. package/src/index.ts +261 -0
  287. package/src/instructions/index.ts +21 -0
  288. package/src/instructions/onboarding.md +133 -0
  289. package/src/knowledge/KnowledgeBase.ts +486 -0
  290. package/src/knowledge/SemanticAddressResolver.ts +191 -0
  291. package/src/knowledge/git-ingest.ts +402 -0
  292. package/src/knowledge/git-queries.ts +269 -0
  293. package/src/knowledge/index.ts +29 -0
  294. package/src/knowledge/parser.ts +294 -0
  295. package/src/knowledge/types.ts +146 -0
  296. package/src/logging/Logger.ts +303 -0
  297. package/src/notation/archetypes.ts +189 -0
  298. package/src/notation/fold.ts +696 -0
  299. package/src/notation/index.ts +27 -0
  300. package/src/notation/lodExtractor.ts +177 -0
  301. package/src/notation/nameShortener.ts +24 -0
  302. package/src/notation/perspectives.ts +18 -0
  303. package/src/notation/renderer.ts +394 -0
  304. package/src/notation/traceRenderer.ts +458 -0
  305. package/src/notation/types.ts +79 -0
  306. package/src/queries/NodeContext.ts +280 -0
  307. package/src/queries/findCallsInFunction.ts +249 -0
  308. package/src/queries/findContainingFunction.ts +124 -0
  309. package/src/queries/index.ts +44 -0
  310. package/src/queries/traceDataflow.ts +838 -0
  311. package/src/queries/traceValues.ts +531 -0
  312. package/src/queries/types.ts +191 -0
  313. package/src/schema/GraphSchemaExtractor.ts +177 -0
  314. package/src/schema/InterfaceSchemaExtractor.ts +173 -0
  315. package/src/schema/index.ts +5 -0
  316. package/src/storage/backends/RFDBServerBackend.ts +895 -0
  317. package/src/storage/backends/typeValidation.ts +154 -0
  318. package/src/utils/findRfdbBinary.ts +288 -0
  319. package/src/utils/lazyDownload.ts +206 -0
  320. package/src/utils/moduleResolution.ts +271 -0
  321. package/src/utils/resolveNodeFile.ts +18 -0
  322. package/src/utils/startRfdbServer.ts +197 -0
  323. package/src/validation/PathValidator.ts +334 -0
  324. package/src/version.ts +28 -0
@@ -0,0 +1,133 @@
1
+ # Grafema Project Onboarding
2
+
3
+ ## Goal
4
+ Study the target project and create a `.grafema/config.yaml` that correctly
5
+ describes its services, entry points, and analysis configuration.
6
+
7
+ ## Prerequisites
8
+ - The project directory exists and contains source code
9
+ - Node.js 18+ installed
10
+ - Run `grafema init` to create `.grafema/` directory if it doesn't exist
11
+
12
+ ## Step 1: Initial Reconnaissance
13
+
14
+ Use `read_project_structure` to get the directory tree.
15
+
16
+ Look for:
17
+ - `package.json` in root and subdirectories (indicates JS/TS packages)
18
+ - `pnpm-workspace.yaml`, `lerna.json`, root `package.json` with `workspaces`
19
+ field (workspace/monorepo indicators)
20
+ - `tsconfig.json` files (TypeScript project)
21
+ - `Dockerfile`, `docker-compose.yml`, `k8s/`, `apps.json` (deployment configs
22
+ that may reveal service boundaries)
23
+ - Directories named `apps/`, `packages/`, `services/`, `pkg/`, `modules/`
24
+ (common monorepo structures)
25
+
26
+ ## Step 2: Identify Services
27
+
28
+ A "service" in Grafema is an independently analyzable unit of code with its
29
+ own entry point. Typically:
30
+ - A standalone application (API server, web app, CLI tool)
31
+ - A package in a monorepo that other packages depend on
32
+ - A microservice in a deployment configuration
33
+
34
+ For each potential service, determine:
35
+ 1. **Name** — human-readable identifier (e.g., "backend", "dashboard")
36
+ 2. **Path** — directory path relative to project root
37
+ 3. **Entry point** — the main source file (prefer TypeScript source over
38
+ compiled output)
39
+
40
+ ### How to find entry points
41
+ Check in order:
42
+ 1. `package.json` "source" field (TypeScript source entry)
43
+ 2. `package.json` "main" field, but look for `.ts` equivalent in `src/`
44
+ 3. Common patterns: `src/index.ts`, `src/main.ts`, `src/app.ts`,
45
+ `src/server.ts`, `index.ts`
46
+ 4. For React apps: `src/App.tsx`, `src/index.tsx`
47
+ 5. Check `bin` field in package.json for CLI tools
48
+
49
+ ### When to ask the user
50
+ - "I found [X] that looks like [description]. Should I include it as a
51
+ service?"
52
+ - "This directory has multiple potential entry points: [list].
53
+ Which should I use?"
54
+ - "I found deployment configuration mentioning services not visible in
55
+ the code structure. Should I investigate?"
56
+
57
+ ## Step 3: Run Auto-Discovery (Optional)
58
+
59
+ Use `discover_services` to see what Grafema's built-in detection finds.
60
+ Compare with your own findings from Steps 1-2. Note discrepancies —
61
+ auto-discovery may miss services or misidentify entry points.
62
+
63
+ ## Step 4: Configure File Patterns
64
+
65
+ The Rust orchestrator has a built-in analysis pipeline — no plugin
66
+ configuration needed. Focus on:
67
+
68
+ - **`include`** — glob patterns for source files to analyze
69
+ (e.g., `src/**/*.{ts,tsx,js,jsx}`)
70
+ - **`exclude`** — patterns to skip (tests, generated code, dist)
71
+ - **`services`** — for monorepos, explicit service definitions
72
+
73
+ The minimal config generated by `grafema init` works for most projects.
74
+
75
+ ## Step 5: Write Configuration
76
+
77
+ Edit `.grafema/config.yaml` directly. Minimal format:
78
+
79
+ ```yaml
80
+ version: "0.3"
81
+ root: ".."
82
+ include:
83
+ - "src/**/*.{ts,tsx,js,jsx}"
84
+ exclude:
85
+ - "**/*.test.*"
86
+ - "**/node_modules/**"
87
+ - "**/dist/**"
88
+ ```
89
+
90
+ For monorepos, add services:
91
+
92
+ ```yaml
93
+ services:
94
+ - name: "api"
95
+ path: "packages/api"
96
+ entryPoint: "src/index.ts"
97
+ - name: "web"
98
+ path: "packages/web"
99
+ ```
100
+
101
+ Note: `root` is relative to `.grafema/` directory, so `".."` points to
102
+ the project root.
103
+
104
+ ## Step 6: Verify
105
+
106
+ Run `analyze_project` to build the graph. Then check:
107
+ - `get_stats` — are node/edge counts reasonable for the project size?
108
+ - `get_coverage` — are the expected files analyzed?
109
+
110
+ If coverage is low or results are unexpected, iterate:
111
+ revisit services, entry points, or include/exclude patterns.
112
+
113
+ ## Common Patterns
114
+
115
+ ### Monorepo with workspaces
116
+ Look for `pnpm-workspace.yaml` or `workspaces` in root `package.json`.
117
+ Each workspace package is typically a service. Use `read_project_structure`
118
+ with depth=2 to see the package layout.
119
+
120
+ ### Legacy project with multiple entry points
121
+ Look for `scripts` in `package.json`, `bin` field, or multiple files
122
+ in `src/` that look like entry points (contain `app.listen()`,
123
+ `createServer()`, `express()`).
124
+
125
+ ### Microservices with shared deployment
126
+ Look for `docker-compose.yml`, Kubernetes configs, or similar
127
+ deployment manifests that list services. Cross-reference with
128
+ code directories.
129
+
130
+ ### Single-package project
131
+ If there is only one `package.json` at the root and no monorepo
132
+ structure, the project is likely a single service. The service path
133
+ is `.` (root), and the entry point is determined from `package.json`.
@@ -0,0 +1,486 @@
1
+ /**
2
+ * KnowledgeBase — in-memory index over git-tracked knowledge files.
3
+ *
4
+ * Scans knowledge/ directory, parses markdown files with YAML frontmatter,
5
+ * builds an in-memory index for fast lookups. All mutations also write to disk.
6
+ */
7
+
8
+ import { join } from 'path';
9
+ import { readdirSync, statSync, readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
10
+ import { parseFrontmatter, parseKBNode, serializeKBNode, parseEdgesFile, appendEdge as appendEdgeToFile, parseYamlArrayFile } from './parser.js';
11
+ import type { KBNode, KBNodeType, KBDecision, KBFact, KBEdge, KBStats, KBQueryFilter, KBLifecycle, ResolvedAddress, DanglingCodeRef } from './types.js';
12
+ import { SemanticAddressResolver } from './SemanticAddressResolver.js';
13
+ import type { ResolverBackend } from './SemanticAddressResolver.js';
14
+
15
+ /** Pluralized directory names for each node type */
16
+ const TYPE_DIR: Record<string, string> = {
17
+ DECISION: 'decisions',
18
+ FACT: 'facts',
19
+ SESSION: 'sessions',
20
+ COMMIT: 'commits',
21
+ FILE_CHANGE: 'file-changes',
22
+ AUTHOR: 'authors',
23
+ TICKET: 'tickets',
24
+ INCIDENT: 'incidents',
25
+ };
26
+
27
+ export class KnowledgeBase {
28
+ private knowledgeDir: string;
29
+ private nodes: Map<string, KBNode> = new Map();
30
+ private edges: KBEdge[] = [];
31
+ private loaded = false;
32
+ private resolver: SemanticAddressResolver | null = null;
33
+
34
+ constructor(knowledgeDir: string) {
35
+ this.knowledgeDir = knowledgeDir;
36
+ }
37
+
38
+ /**
39
+ * Wire up a code graph backend for resolving semantic addresses.
40
+ * Creates a SemanticAddressResolver that lazily resolves `relates_to`
41
+ * and `applies_to` addresses to current code node IDs.
42
+ */
43
+ setBackend(backend: ResolverBackend): void {
44
+ this.resolver = new SemanticAddressResolver(backend);
45
+ }
46
+
47
+ /**
48
+ * Bump the resolver's generation counter, marking all cached resolutions stale.
49
+ * Call after re-analysis so next resolve() re-queries the code graph.
50
+ */
51
+ invalidateResolutionCache(): void {
52
+ this.resolver?.bumpGeneration();
53
+ }
54
+
55
+ /**
56
+ * Resolve all code addresses in a node's relates_to (and applies_to for decisions).
57
+ * KB-internal addresses (kb:...) pass through without backend query.
58
+ * Returns empty array if no resolver is set.
59
+ */
60
+ async resolveReferences(node: KBNode): Promise<ResolvedAddress[]> {
61
+ if (!this.resolver) return [];
62
+
63
+ const addresses: string[] = [];
64
+ if (node.relates_to) addresses.push(...node.relates_to);
65
+ if (node.type === 'DECISION') {
66
+ const d = node as KBDecision;
67
+ if (d.applies_to) addresses.push(...d.applies_to);
68
+ }
69
+
70
+ // Filter to code addresses only (not kb: internal refs)
71
+ const codeAddresses = addresses.filter(a => !a.startsWith('kb:'));
72
+ if (codeAddresses.length === 0) return [];
73
+
74
+ return this.resolver.resolveAll(codeAddresses);
75
+ }
76
+
77
+ /**
78
+ * Find all KB nodes with code addresses that don't resolve to graph nodes.
79
+ * Returns pairs of (KB node ID, dangling address).
80
+ */
81
+ async getDanglingCodeRefs(): Promise<DanglingCodeRef[]> {
82
+ if (!this.resolver) return [];
83
+
84
+ const results: DanglingCodeRef[] = [];
85
+
86
+ for (const node of this.nodes.values()) {
87
+ const resolved = await this.resolveReferences(node);
88
+ for (const r of resolved) {
89
+ if (r.status === 'dangling') {
90
+ results.push({ nodeId: node.id, address: r.address });
91
+ }
92
+ }
93
+ }
94
+
95
+ return results;
96
+ }
97
+
98
+ /**
99
+ * Scan knowledge directory recursively, parse all .md files, build index.
100
+ * Malformed files are skipped with console.warn, don't crash.
101
+ * Missing directory succeeds with empty index.
102
+ */
103
+ async load(): Promise<void> {
104
+ this.nodes.clear();
105
+ this.edges = [];
106
+
107
+ if (!existsSync(this.knowledgeDir)) {
108
+ this.loaded = true;
109
+ return;
110
+ }
111
+
112
+ // Scan for .md files recursively
113
+ const mdFiles = this.scanFiles(this.knowledgeDir, '.md');
114
+
115
+ for (const filePath of mdFiles) {
116
+ try {
117
+ const content = readFileSync(filePath, 'utf-8');
118
+ const { frontmatter, body } = parseFrontmatter(content);
119
+ const node = parseKBNode(frontmatter, body, filePath);
120
+
121
+ if (this.nodes.has(node.id)) {
122
+ const existing = this.nodes.get(node.id)!;
123
+ throw new Error(
124
+ `ID collision: "${node.id}" exists in both ${existing.filePath} and ${filePath}`
125
+ );
126
+ }
127
+
128
+ this.nodes.set(node.id, node);
129
+ } catch (error) {
130
+ // ID collisions should propagate — they're data integrity errors
131
+ if (error instanceof Error && error.message.startsWith('ID collision:')) {
132
+ throw error;
133
+ }
134
+ console.warn(`[KnowledgeBase] Skipping malformed file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
135
+ }
136
+ }
137
+
138
+ // Scan for .yaml array files (derived data like commits, authors)
139
+ const yamlFiles = this.scanFiles(this.knowledgeDir, '.yaml');
140
+ for (const filePath of yamlFiles) {
141
+ // Skip edges.yaml and meta.yaml — they have special formats
142
+ const fileName = filePath.split('/').pop() || '';
143
+ if (fileName === 'edges.yaml' || fileName === 'meta.yaml') continue;
144
+
145
+ try {
146
+ const nodes = parseYamlArrayFile(filePath);
147
+ for (const node of nodes) {
148
+ if (this.nodes.has(node.id)) {
149
+ // For derived data, silently skip duplicates (can happen with incremental ingest)
150
+ continue;
151
+ }
152
+ this.nodes.set(node.id, node);
153
+ }
154
+ } catch (error) {
155
+ console.warn(`[KnowledgeBase] Skipping malformed YAML file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
156
+ }
157
+ }
158
+
159
+ // Load edges
160
+ const edgesPath = join(this.knowledgeDir, 'edges.yaml');
161
+ try {
162
+ this.edges = parseEdgesFile(edgesPath);
163
+ } catch (error) {
164
+ console.warn(`[KnowledgeBase] Failed to parse edges.yaml: ${error instanceof Error ? error.message : String(error)}`);
165
+ }
166
+
167
+ this.loaded = true;
168
+ }
169
+
170
+ /**
171
+ * Get a node by its semantic ID.
172
+ */
173
+ getNode(id: string): KBNode | undefined {
174
+ return this.nodes.get(id);
175
+ }
176
+
177
+ /**
178
+ * Query nodes with filters. All filters are AND-combined.
179
+ * When include_dangling_only is true, only returns nodes with dangling code refs.
180
+ * Note: include_dangling_only requires a resolver backend; without one it returns empty.
181
+ */
182
+ async queryNodes(filter: KBQueryFilter): Promise<KBNode[]> {
183
+ let results = Array.from(this.nodes.values());
184
+
185
+ if (filter.type) {
186
+ results = results.filter(n => n.type === filter.type);
187
+ }
188
+ if (filter.projection) {
189
+ results = results.filter(n => n.projections.includes(filter.projection!));
190
+ }
191
+ if (filter.text) {
192
+ const lower = filter.text.toLowerCase();
193
+ results = results.filter(n => n.content.toLowerCase().includes(lower));
194
+ }
195
+ if (filter.status) {
196
+ results = results.filter(n => {
197
+ if (n.type === 'DECISION') return (n as KBDecision).status === filter.status;
198
+ return false;
199
+ });
200
+ }
201
+ if (filter.relates_to) {
202
+ results = results.filter(n => n.relates_to?.includes(filter.relates_to!));
203
+ }
204
+ if (filter.include_dangling_only) {
205
+ if (!this.resolver) return [];
206
+ const danglingNodeIds = new Set<string>();
207
+ for (const node of results) {
208
+ const resolved = await this.resolveReferences(node);
209
+ if (resolved.some(r => r.status === 'dangling')) {
210
+ danglingNodeIds.add(node.id);
211
+ }
212
+ }
213
+ results = results.filter(n => danglingNodeIds.has(n.id));
214
+ }
215
+
216
+ return results;
217
+ }
218
+
219
+ /**
220
+ * Find active decisions that apply to a given module/semantic address.
221
+ * Uses string includes matching on applies_to entries.
222
+ */
223
+ async activeDecisionsFor(module: string): Promise<KBDecision[]> {
224
+ const decisions = await this.queryNodes({ type: 'DECISION', status: 'active' }) as KBDecision[];
225
+ return decisions.filter(d =>
226
+ d.applies_to?.some(addr => addr.includes(module) || module.includes(addr))
227
+ );
228
+ }
229
+
230
+ /**
231
+ * Add a new node to the knowledge base.
232
+ * Creates the .md file on disk and updates the in-memory index.
233
+ *
234
+ * @param params - Node properties. `id` and `filePath` are auto-generated.
235
+ * @returns The created node.
236
+ */
237
+ async addNode(params: {
238
+ type: KBNodeType;
239
+ content: string;
240
+ slug?: string;
241
+ subtype?: string;
242
+ scope?: 'global' | 'project' | 'module';
243
+ projections?: string[];
244
+ source?: string;
245
+ relates_to?: string[];
246
+ // Decision-specific
247
+ status?: KBDecision['status'];
248
+ effective_from?: string;
249
+ applies_to?: string[];
250
+ // Fact-specific
251
+ confidence?: KBFact['confidence'];
252
+ // Session-specific
253
+ task_id?: string;
254
+ session_path?: string;
255
+ produced?: string[];
256
+ }): Promise<KBNode> {
257
+ const slug = params.slug || this.generateSlug(params.content);
258
+ const typeLower = params.type.toLowerCase();
259
+ const id = `kb:${typeLower}:${slug}`;
260
+
261
+ // Collision check
262
+ if (this.nodes.has(id)) {
263
+ throw new Error(
264
+ `Slug collision: "${id}" already exists. Did you mean to supersede? Use supersedeFact() for facts.`
265
+ );
266
+ }
267
+
268
+ const typeDir = TYPE_DIR[params.type] || typeLower + 's';
269
+ const dir = join(this.knowledgeDir, 'declared', typeDir);
270
+ if (!existsSync(dir)) {
271
+ mkdirSync(dir, { recursive: true });
272
+ }
273
+
274
+ const fileName = `${slug}.md`;
275
+ const filePath = join(dir, fileName);
276
+
277
+ // Secondary collision guard — file on disk
278
+ if (existsSync(filePath)) {
279
+ throw new Error(`File already exists: ${filePath}`);
280
+ }
281
+
282
+ const created = new Date().toISOString().split('T')[0];
283
+
284
+ // Build frontmatter
285
+ const frontmatter: Record<string, unknown> = {
286
+ id,
287
+ type: params.type,
288
+ created,
289
+ };
290
+ if (params.subtype) frontmatter.subtype = params.subtype;
291
+ if (params.scope) frontmatter.scope = params.scope;
292
+ if (params.projections?.length) frontmatter.projections = params.projections;
293
+ if (params.source) frontmatter.source = params.source;
294
+ if (params.relates_to?.length) frontmatter.relates_to = params.relates_to;
295
+
296
+ // Decision fields
297
+ if (params.type === 'DECISION') {
298
+ frontmatter.status = params.status || 'proposed';
299
+ if (params.applies_to?.length) frontmatter.applies_to = params.applies_to;
300
+ if (params.effective_from) frontmatter.effective_from = params.effective_from;
301
+ }
302
+
303
+ // Fact fields
304
+ if (params.type === 'FACT') {
305
+ if (params.confidence) frontmatter.confidence = params.confidence;
306
+ }
307
+
308
+ // Session fields
309
+ if (params.type === 'SESSION') {
310
+ if (params.task_id) frontmatter.task_id = params.task_id;
311
+ if (params.session_path) frontmatter.session_path = params.session_path;
312
+ if (params.produced?.length) frontmatter.produced = params.produced;
313
+ }
314
+
315
+ // Parse to get a proper node, then serialize
316
+ const node = parseKBNode(frontmatter, params.content, filePath);
317
+ const markdown = serializeKBNode(node);
318
+ writeFileSync(filePath, markdown, 'utf-8');
319
+
320
+ // Update index
321
+ this.nodes.set(id, node);
322
+
323
+ // Create edges for relates_to
324
+ if (params.relates_to?.length) {
325
+ const edgesPath = join(this.knowledgeDir, 'edges.yaml');
326
+ for (const target of params.relates_to) {
327
+ const edge: KBEdge = { type: 'RELATES_TO', from: id, to: target };
328
+ appendEdgeToFile(edgesPath, edge);
329
+ this.edges.push(edge);
330
+ }
331
+ }
332
+
333
+ return node;
334
+ }
335
+
336
+ /**
337
+ * Supersede an existing fact with a new version.
338
+ * Creates new fact, updates old fact with superseded_by.
339
+ */
340
+ async supersedeFact(
341
+ oldId: string,
342
+ newContent: string,
343
+ newSlug?: string,
344
+ ): Promise<{ old: KBFact; new: KBFact }> {
345
+ const oldNode = this.nodes.get(oldId);
346
+ if (!oldNode) {
347
+ throw new Error(`Fact not found: ${oldId}`);
348
+ }
349
+ if (oldNode.type !== 'FACT') {
350
+ throw new Error(`Node "${oldId}" is type ${oldNode.type}, not FACT. Only facts can be superseded.`);
351
+ }
352
+
353
+ const oldFact = oldNode as KBFact;
354
+ const slug = newSlug || this.generateSlug(newContent);
355
+ const newId = `kb:fact:${slug}`;
356
+
357
+ // Create new fact
358
+ const newFact = await this.addNode({
359
+ type: 'FACT',
360
+ content: newContent,
361
+ slug,
362
+ projections: oldFact.projections,
363
+ source: oldFact.source,
364
+ confidence: oldFact.confidence,
365
+ }) as KBFact;
366
+
367
+ // Update old fact: set superseded_by and rewrite file
368
+ oldFact.superseded_by = newId;
369
+ const markdown = serializeKBNode(oldFact);
370
+ writeFileSync(oldFact.filePath, markdown, 'utf-8');
371
+
372
+ // Add supersedes edge
373
+ const edgesPath = join(this.knowledgeDir, 'edges.yaml');
374
+ const edge: KBEdge = { type: 'SUPERSEDES', from: newId, to: oldId };
375
+ appendEdgeToFile(edgesPath, edge);
376
+ this.edges.push(edge);
377
+
378
+ return { old: oldFact, new: newFact };
379
+ }
380
+
381
+ /**
382
+ * Add an edge to the knowledge graph.
383
+ */
384
+ addEdge(edge: KBEdge): void {
385
+ const edgesPath = join(this.knowledgeDir, 'edges.yaml');
386
+ appendEdgeToFile(edgesPath, edge);
387
+ this.edges.push(edge);
388
+ }
389
+
390
+ /**
391
+ * Get edges, optionally filtered by node ID (from or to).
392
+ */
393
+ getEdges(nodeId?: string): KBEdge[] {
394
+ if (!nodeId) return [...this.edges];
395
+ return this.edges.filter(e => e.from === nodeId || e.to === nodeId);
396
+ }
397
+
398
+ /**
399
+ * Get statistics about the knowledge base.
400
+ * Includes dangling code references if a resolver backend is available.
401
+ */
402
+ async getStats(): Promise<KBStats> {
403
+ const byType: Partial<Record<KBNodeType, number>> = {};
404
+ const byLifecycle: Partial<Record<KBLifecycle, number>> = {};
405
+
406
+ for (const node of this.nodes.values()) {
407
+ byType[node.type] = (byType[node.type] || 0) + 1;
408
+ byLifecycle[node.lifecycle] = (byLifecycle[node.lifecycle] || 0) + 1;
409
+ }
410
+
411
+ const edgesByType: Record<string, number> = {};
412
+ for (const edge of this.edges) {
413
+ edgesByType[edge.type] = (edgesByType[edge.type] || 0) + 1;
414
+ }
415
+
416
+ // Find dangling KB-internal refs
417
+ const nodeIds = new Set(this.nodes.keys());
418
+ const danglingRefs: string[] = [];
419
+ for (const edge of this.edges) {
420
+ if (!nodeIds.has(edge.from) && !danglingRefs.includes(edge.from)) {
421
+ danglingRefs.push(edge.from);
422
+ }
423
+ if (!nodeIds.has(edge.to) && !danglingRefs.includes(edge.to)) {
424
+ danglingRefs.push(edge.to);
425
+ }
426
+ }
427
+
428
+ // Find dangling code references
429
+ const danglingCodeRefs = await this.getDanglingCodeRefs();
430
+
431
+ return {
432
+ totalNodes: this.nodes.size,
433
+ byType,
434
+ byLifecycle,
435
+ totalEdges: this.edges.length,
436
+ edgesByType,
437
+ danglingRefs,
438
+ danglingCodeRefs,
439
+ };
440
+ }
441
+
442
+ // --- Private helpers ---
443
+
444
+ /**
445
+ * Recursively scan directory for files with given extension.
446
+ */
447
+ private scanFiles(dir: string, ext: string): string[] {
448
+ const results: string[] = [];
449
+
450
+ let entries: string[];
451
+ try {
452
+ entries = readdirSync(dir);
453
+ } catch {
454
+ return results;
455
+ }
456
+
457
+ for (const entry of entries) {
458
+ const fullPath = join(dir, entry);
459
+ try {
460
+ const stat = statSync(fullPath);
461
+ if (stat.isDirectory()) {
462
+ results.push(...this.scanFiles(fullPath, ext));
463
+ } else if (entry.endsWith(ext)) {
464
+ results.push(fullPath);
465
+ }
466
+ } catch {
467
+ // Skip unreadable entries
468
+ }
469
+ }
470
+ return results;
471
+ }
472
+
473
+ /**
474
+ * Generate a slug from content (first line, simplified).
475
+ */
476
+ private generateSlug(content: string): string {
477
+ const firstLine = content.split('\n')[0].trim();
478
+ return firstLine
479
+ .toLowerCase()
480
+ .replace(/[^a-z0-9\s-]/g, '')
481
+ .replace(/\s+/g, '-')
482
+ .replace(/-+/g, '-')
483
+ .replace(/^-|-$/g, '')
484
+ .slice(0, 60) || 'untitled';
485
+ }
486
+ }