@rahmantech/knl-view 0.0.0-stage → 0.2.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 (115) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +483 -2
  3. package/bin/knl-view.js +1320 -0
  4. package/dist/assets/abnfDiagram-WEYOIA3C-hoSbsMOz.js +1 -0
  5. package/dist/assets/arc-g87Np6E5.js +1 -0
  6. package/dist/assets/architecture-WOLXFQ4H-DOIq-0IA.js +1 -0
  7. package/dist/assets/architectureDiagram-ZHVYKT53-Dxwe8n7m.js +36 -0
  8. package/dist/assets/array-BifhSqXX.js +1 -0
  9. package/dist/assets/blockDiagram-RJAE7QA3-BnGng59Z.js +140 -0
  10. package/dist/assets/c4Diagram-XDPPPE54-C5LjqIIo.js +38 -0
  11. package/dist/assets/channel-BVWkRFBw.js +1 -0
  12. package/dist/assets/chunk-2E7WHVMD-BJ1dyGLq.js +88 -0
  13. package/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  14. package/dist/assets/chunk-4EMNKR2T-DXF02Sgw.js +1 -0
  15. package/dist/assets/chunk-55XCU76K-CM1akAbu.js +1 -0
  16. package/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  17. package/dist/assets/chunk-7S6XSTFO-BOUjqldL.js +2 -0
  18. package/dist/assets/chunk-AQDTSKZB-C4isdNLA.js +72 -0
  19. package/dist/assets/chunk-DSNLSHJZ-CbOTkSVM.js +129 -0
  20. package/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
  21. package/dist/assets/chunk-HAGFOTED-CRfnketR.js +62 -0
  22. package/dist/assets/chunk-HNE4Q36P-rxlpGUU8.js +1 -0
  23. package/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
  24. package/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  25. package/dist/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
  26. package/dist/assets/chunk-NQOKRVIF-BOYCgTmV.js +213 -0
  27. package/dist/assets/chunk-UHW7PFBP-Cnogp1Fb.js +1 -0
  28. package/dist/assets/chunk-VQ5W62QI-Cu1WY5Uo.js +1 -0
  29. package/dist/assets/chunk-WQQL4CDK-BJc3FPE2.js +10 -0
  30. package/dist/assets/chunk-XXDRQBXY-BlPj4H7s.js +1 -0
  31. package/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  32. package/dist/assets/chunk-YZO67HFG-V85H31x4.js +1 -0
  33. package/dist/assets/chunk-ZAUX2LTX-C3XKXW09.js +1 -0
  34. package/dist/assets/chunk-ZCHMQSOR-B0mmpEgW.js +2 -0
  35. package/dist/assets/classDiagram-v2-4MYWR3UA-oMd7YaHP.js +217 -0
  36. package/dist/assets/cose-bilkent-JH36ORCC-J0kSVsDf.js +1 -0
  37. package/dist/assets/cynefin-EF2NZ3EQ-DK2TNhel.js +1 -0
  38. package/dist/assets/cynefinDiagram-WF24UDRP-DDvKgieA.js +62 -0
  39. package/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
  40. package/dist/assets/dagre-NK4GIZVX-Ca2Fe-i-.js +4 -0
  41. package/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
  42. package/dist/assets/diagram-CBK3MWTC-L6dmeQ2V.js +24 -0
  43. package/dist/assets/diagram-I5XIIV2D-DhG8R-v5.js +24 -0
  44. package/dist/assets/diagram-ODIL2UCW-BpYYbYBT.js +30 -0
  45. package/dist/assets/diagram-PFBEJUXE-DKQpHSsC.js +3 -0
  46. package/dist/assets/diagram-RJA3I4VV-DP7Dsnya.js +41 -0
  47. package/dist/assets/diagram-TYUNFSNR-CfSFewUS.js +200 -0
  48. package/dist/assets/dist-CQNjnrld.js +1 -0
  49. package/dist/assets/ebnfDiagram-V4P3TTPT-DE3MZoJO.js +1 -0
  50. package/dist/assets/elk-QONTBCTQ-p_aAQPHU.js +27 -0
  51. package/dist/assets/erDiagram-KYQIGQAA-DZO3vQrk.js +99 -0
  52. package/dist/assets/eventmodeling-K75KTNOO-BII9EPl5.js +1 -0
  53. package/dist/assets/flowDiagram-IUGLLWBU-D9uPno39.js +1 -0
  54. package/dist/assets/ganttDiagram-R7GPNTJY-BKiH3v2r.js +292 -0
  55. package/dist/assets/gitGraph-VSP46ZUC-oHL6cr73.js +1 -0
  56. package/dist/assets/gitGraphDiagram-PDYL7XHY-CqPVIEeo.js +106 -0
  57. package/dist/assets/graphlib-DS17s2tU.js +1 -0
  58. package/dist/assets/index-CSpirV16.js +76 -0
  59. package/dist/assets/index-DLgMsUs2.css +1 -0
  60. package/dist/assets/info-OHQRW6UA-Drrbnhpd.js +1 -0
  61. package/dist/assets/infoDiagram-MVDRKAX5-DnQQFMAd.js +2 -0
  62. package/dist/assets/init-C-OQMol4.js +1 -0
  63. package/dist/assets/ishikawaDiagram-HPEHV2GE-DIpQlxRh.js +70 -0
  64. package/dist/assets/journeyDiagram-BOSHW7O7-EBR9I5h1.js +139 -0
  65. package/dist/assets/kanban-definition-FNSA4HC5-CdvNsjik.js +89 -0
  66. package/dist/assets/katex-B_JcIwqj.js +257 -0
  67. package/dist/assets/line-CDLzgxDc.js +1 -0
  68. package/dist/assets/linear-DfXKql9u.js +1 -0
  69. package/dist/assets/mermaid-parser.core-DnXGphT2.js +7 -0
  70. package/dist/assets/mermaid.core-h6fiCaSZ.js +44 -0
  71. package/dist/assets/mindmap-definition-6OY57UG3-BiNfio-j.js +98 -0
  72. package/dist/assets/ordinal-BDEzSJ7C.js +1 -0
  73. package/dist/assets/packet-JDAUHWVQ-D76byv1S.js +1 -0
  74. package/dist/assets/path-fybaL0A-.js +1 -0
  75. package/dist/assets/pegDiagram-ECIUJNBX-Drs8Pxdt.js +1 -0
  76. package/dist/assets/pie-XZMESJXO-D_83z1lM.js +1 -0
  77. package/dist/assets/pieDiagram-GNMPY74T-wvzIphSf.js +39 -0
  78. package/dist/assets/quadrantDiagram-D57K4MC3-B7EfMRWb.js +7 -0
  79. package/dist/assets/radar-ABXABTNO-Bs6dVdAR.js +1 -0
  80. package/dist/assets/railroad-I3PHUGI6-lKaN4miw.js +1 -0
  81. package/dist/assets/railroad-abnf-LNEFI6M7-Bhh16b7Z.js +1 -0
  82. package/dist/assets/railroad-ebnf-DWYD2UWJ-m6ybW-Y_.js +1 -0
  83. package/dist/assets/railroad-peg-JR7BVNR7-DNn72e7r.js +1 -0
  84. package/dist/assets/railroadDiagram-Y72NRBL7-DNeop1iZ.js +1 -0
  85. package/dist/assets/requirementDiagram-EQYSW5RA-BVYZ2XYJ.js +84 -0
  86. package/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
  87. package/dist/assets/sankeyDiagram-SY24JHO5-B2MP51on.js +40 -0
  88. package/dist/assets/sequenceDiagram-BLEHNHQU-BUvxstXG.js +169 -0
  89. package/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
  90. package/dist/assets/src-BrR0lqlG.js +1 -0
  91. package/dist/assets/stateDiagram-v2-A6IESKWZ-Ba84OGKE.js +291 -0
  92. package/dist/assets/swimlanes-B3ENRKFQ-BMSl2PNA.js +1 -0
  93. package/dist/assets/swimlanesDiagram-AYQRV4AK-M1dmvq7m.js +8 -0
  94. package/dist/assets/timeline-definition-JXYKGLGB-DOdW-dxq.js +120 -0
  95. package/dist/assets/treeView-D4JQ5SDB-DZ9KQyn5.js +1 -0
  96. package/dist/assets/treemap-SAJKECNS-WEC5wtDV.js +1 -0
  97. package/dist/assets/usecaseDiagram-WPOLBNNX-cldTztFL.js +328 -0
  98. package/dist/assets/vennDiagram-Q3Y3IDCW-c41KySoY.js +34 -0
  99. package/dist/assets/wardley-7MLQ67FV-Bf9_aSSZ.js +1 -0
  100. package/dist/assets/wardleyDiagram-IVAIDKWA-BeKEvEBG.js +78 -0
  101. package/dist/assets/xychartDiagram-4URNHDU4-tNkAxTxG.js +7 -0
  102. package/dist/index.html +22 -0
  103. package/package.json +50 -4
  104. package/server/config.js +389 -0
  105. package/server/consensus.js +180 -0
  106. package/server/graph/anchordb_client.js +253 -0
  107. package/server/graph/model.js +157 -0
  108. package/server/graph/parser.js +327 -0
  109. package/server/graph/sources.js +801 -0
  110. package/server/graph/syntax.js +437 -0
  111. package/server/index.js +742 -0
  112. package/server/install.js +437 -0
  113. package/server/map.js +126 -0
  114. package/server/models_registry.js +109 -0
  115. package/server/scaffold.js +466 -0
@@ -0,0 +1,466 @@
1
+ import { promises as fs } from 'node:fs'
2
+ import path from 'node:path'
3
+
4
+ // Knowledge base builder for --init and --seed-skill.
5
+ // Same principle as the map: NEVER destroy anything that already exists.
6
+ // Existing files are skipped, and the .gitignore line is only added when
7
+ // not already present.
8
+
9
+ export const STANDARD_DIRS = ['roadmap', 'features', 'docs']
10
+ export const SKILL_ID = 'knl-view'
11
+
12
+ const ROOT_README = `# Knowledge Base
13
+
14
+ Lede: write a one-paragraph summary of this base here.
15
+
16
+ ## How to read
17
+
18
+ - \`roadmap/\`: direction and plans.
19
+ - \`features/\`: feature notes.
20
+ - \`docs/\`: general documents.
21
+
22
+ Add \`map.json\` to this folder if you want to control menu labels and
23
+ display order without moving files. See the \`knl-view\` skill for the
24
+ full format.
25
+ `
26
+
27
+ const SECTION_README = (name) => `# ${name}
28
+
29
+ Nothing here yet. Put markdown files in this folder.
30
+ `
31
+
32
+ export async function scaffoldBase({ root, structure, folders = [] }) {
33
+ const actions = []
34
+ const rootAbs = path.resolve(root)
35
+ await fs.mkdir(rootAbs, { recursive: true })
36
+ actions.push({ kind: 'dir', path: rootAbs, status: 'created' })
37
+
38
+ if (structure === 'empty') return actions
39
+
40
+ const dirs = structure === 'custom' ? folders.map(cleanRel).filter(Boolean) : STANDARD_DIRS
41
+
42
+ await writeFileSafe(path.join(rootAbs, 'README.md'), ROOT_README, actions)
43
+ for (const dir of dirs) {
44
+ const abs = path.resolve(rootAbs, dir)
45
+ if (!abs.startsWith(rootAbs + path.sep)) continue
46
+ await fs.mkdir(abs, { recursive: true })
47
+ actions.push({ kind: 'dir', path: abs, status: 'created' })
48
+ await writeFileSafe(path.join(abs, 'README.md'), SECTION_README(dir), actions)
49
+ }
50
+ return actions
51
+ }
52
+
53
+ export async function seedSkill({ scope, cwd, home = process.env.HOME, xdgConfig = process.env.XDG_CONFIG_HOME }) {
54
+ const base =
55
+ scope === 'global'
56
+ ? path.join(xdgConfig || path.join(home, '.config'), 'opencode', 'skills')
57
+ : path.join(cwd, '.agents', 'skills')
58
+ const target = path.join(base, SKILL_ID, 'SKILL.md')
59
+
60
+ const exists = await statOrNull(target)
61
+ if (exists) return { path: target, status: 'skipped' }
62
+
63
+ await fs.mkdir(path.dirname(target), { recursive: true })
64
+ await fs.writeFile(target, SKILL_MD, 'utf8')
65
+ return { path: target, status: 'created' }
66
+ }
67
+
68
+ export async function ensureGitignore({ cwd }) {
69
+ const abs = path.join(cwd, '.gitignore')
70
+ const line = '.knowledges/'
71
+ let content = ''
72
+ const stat = await statOrNull(abs)
73
+ if (stat && stat.isFile()) content = await fs.readFile(abs, 'utf8')
74
+
75
+ const present = content.split(/\r?\n/).some((row) => row.trim() === line || row.trim() === '.knowledges')
76
+ if (present) return { path: abs, status: 'skipped' }
77
+
78
+ const next = content.length && !content.endsWith('\n') ? `${content}\n${line}\n` : `${content}${line}\n`
79
+ await fs.writeFile(abs, next, 'utf8')
80
+ return { path: abs, status: 'created' }
81
+ }
82
+
83
+ export function buildAgentInstructionBlock(hasGraphify = false) {
84
+ const fallback = hasGraphify
85
+ ? `- Graphify is the fallback. Use it only when a specific need remains unresolved by KNL-View or the user explicitly requests Graphify. If KNL-View answers the task, stop; do not run Graphify for duplicate analysis.
86
+ - When the user explicitly invokes \`/graphify\`, honor that request without automatically running both tools.
87
+
88
+ `
89
+ : ''
90
+
91
+ return `<!-- knl-view:start -->
92
+ # Knowledge graph tools
93
+ - KNL-View is the primary graph tool. For \`.knowledges\`, source graphs, and project graph questions, read the project's \`knl-view\` skill and use the relevant KNL-View feature first.
94
+ ${fallback}## KNL-View graph access
95
+ - Use the KNL-View feature that fits the task (\`graph trace\`, \`sources trace/sync/collect\`, \`model check/new\`, consensus commands, or the viewer). Do not approximate a supported KNL-View operation with ad-hoc graph queries.
96
+ - Route all graph reads and writes through KNL-View's documented CLI, viewer, or application endpoints. Never connect to AnchorDB directly through AnchorWire, its CLI, direct database endpoints, files, SQL, or custom clients/scripts. If KNL-View cannot perform a requested operation, explain the limitation; do not bypass it.
97
+ - Keep graph work targeted: specify the relevant node/path, direction, and smallest useful hop count; summarize only relevant nodes and edges; avoid dumping whole graphs or large raw JSON.
98
+ - Follow the KNL-View skill's trace-before-edit and \`sources sync\`-after-change rules. Treat edge direction as lineage; do not claim causality without separate evidence.
99
+ <!-- knl-view:end -->`
100
+ }
101
+
102
+ export function stripLegacyKnlInstructions(text) {
103
+ let cleaned = text
104
+ cleaned = cleaned.replace(/<!-- knl-view:start -->[\s\S]*?<!-- knl-view:end -->/g, '\n')
105
+ cleaned = cleaned.replace(/<!--\s*knl-view:(?:start|end)\s*-->/g, '\n')
106
+ cleaned = cleaned.replace(
107
+ /(?:^|\n)# Knowledge graph tools[\s\S]*?(?=(?:\n#+\s)|$)/g,
108
+ '\n'
109
+ )
110
+ cleaned = cleaned.replace(
111
+ /(?:^|\n)## KNL-View graph access[\s\S]*?(?=(?:\n#+\s)|$)/g,
112
+ '\n'
113
+ )
114
+ return cleaned.trim()
115
+ }
116
+
117
+ export async function seedAgentInstructions({ cwd = process.cwd(), home = process.env.HOME, hasGraphify = false }) {
118
+ const block = buildAgentInstructionBlock(hasGraphify)
119
+ const candidatePaths = [
120
+ // Project files:
121
+ path.join(cwd, 'AGENTS.md'),
122
+ path.join(cwd, 'GEMINI.md'),
123
+ path.join(cwd, 'CLAUDE.md'),
124
+ // Global files:
125
+ path.join(home || '', '.codex', 'AGENTS.md'),
126
+ path.join(home || '', '.gemini', 'GEMINI.md'),
127
+ path.join(home || '', '.claude', 'CLAUDE.md'),
128
+ path.join(home || '', '.agents', 'AGENTS.md')
129
+ ]
130
+
131
+ const actions = []
132
+
133
+ for (const abs of candidatePaths) {
134
+ const parentDir = path.dirname(abs)
135
+ const parentExists = await statOrNull(parentDir)
136
+ if (!parentExists) continue
137
+
138
+ const stat = await statOrNull(abs)
139
+ if (stat && stat.isFile()) {
140
+ const content = await fs.readFile(abs, 'utf8')
141
+ const cleaned = stripLegacyKnlInstructions(content)
142
+ const next = cleaned.length ? `${cleaned}\n\n${block}\n` : `${block}\n`
143
+
144
+ if (next !== content) {
145
+ await fs.writeFile(abs, next, 'utf8')
146
+ actions.push({ path: abs, status: 'updated' })
147
+ } else {
148
+ actions.push({ path: abs, status: 'skipped' })
149
+ }
150
+ }
151
+ }
152
+
153
+ // If no project file was found/updated, create ./AGENTS.md
154
+ const projectAgent = path.join(cwd, 'AGENTS.md')
155
+ if (!actions.some((a) => a.path.startsWith(cwd))) {
156
+ const stat = await statOrNull(projectAgent)
157
+ if (!stat) {
158
+ await fs.writeFile(projectAgent, `${block}\n`, 'utf8')
159
+ actions.push({ path: projectAgent, status: 'created' })
160
+ }
161
+ }
162
+
163
+ return actions
164
+ }
165
+
166
+ async function writeFileSafe(abs, content, actions) {
167
+ const exists = await statOrNull(abs)
168
+ if (exists) {
169
+ actions.push({ kind: 'file', path: abs, status: 'skipped' })
170
+ return
171
+ }
172
+ await fs.writeFile(abs, content, 'utf8')
173
+ actions.push({ kind: 'file', path: abs, status: 'created' })
174
+ }
175
+
176
+ function cleanRel(value) {
177
+ const cleaned = String(value || '').trim()
178
+ const normalized = path.normalize(cleaned).replace(/^(\.\.[\/\\])+/, '').replace(/^[\/\\]+/, '').replace(/[\/\\]+$/, '')
179
+ if (!normalized || normalized === '.' || normalized === '..' || normalized.startsWith('..' + path.sep)) {
180
+ return ''
181
+ }
182
+ return normalized
183
+ }
184
+
185
+ async function statOrNull(abs) {
186
+ try {
187
+ return await fs.stat(abs)
188
+ } catch {
189
+ return null
190
+ }
191
+ }
192
+
193
+ // The skill is in English, following skill ecosystem conventions. It teaches
194
+ // the agent how to read the .knowledges base: README first, then map.json as
195
+ // the source of labels and menu order, then the anchor-connector-like
196
+ // structure.
197
+ const SKILL_MD = `---
198
+ name: knl-view
199
+ description: Read and maintain .knowledges knowledge bases. Use whenever a task involves a .knowledges folder, its map.json, or the knl-view viewer.
200
+ ---
201
+
202
+ # Knowledges base
203
+
204
+ A \`.knowledges\` folder is a plain-directory knowledge base, viewed with
205
+ knl-view. The disk is the source of truth: files and folders exist because
206
+ they exist on disk, never because a map says so.
207
+
208
+ ## Read in this order
209
+
210
+ 1. \`README.md\` at the base root: the lede and the overview.
211
+ 2. \`map.json\` at the base root, when present: the authoritative menu
212
+ labels, grouping, and order for everything below.
213
+ 3. Then the structure the map (or the disk) describes. A typical base
214
+ mirrors anchor-connector: \`roadmap/\`, \`features/\`, \`futures/\`,
215
+ \`audit/\`, \`roasted/\`, plus root files \`coding-rules.md\` and
216
+ \`documentation-rules.md\`. Anything not on disk does not exist; do not
217
+ invent it from map entries.
218
+
219
+ ## map.json
220
+
221
+ Optional. Every entry is decoration only. It sets the label shown in the
222
+ menu, the order in menus, and a short note. It never creates, moves, or
223
+ deletes files. Entries pointing at paths that are not on disk are ignored
224
+ silently. Without \`map.json\`, the viewer falls back to the first
225
+ \`# heading\` of each markdown file and plain name order: files first,
226
+ then folders.
227
+
228
+ \`\`\`json
229
+ {
230
+ "title": "Base title shown in the masthead",
231
+ "sections": {
232
+ "roadmap": { "title": "Roadmap", "order": 1, "meta": "Where this is going" },
233
+ "features": {
234
+ "title": "Features",
235
+ "order": 2,
236
+ "docs": { "auth.md": { "title": "Auth", "order": 1 } }
237
+ }
238
+ },
239
+ "docs": {
240
+ "coding-rules.md": { "title": "Coding rules", "order": 1 },
241
+ "documentation-rules.md": { "title": "Documentation rules", "order": 2 }
242
+ },
243
+ "files": {
244
+ "personal-assistant.md": { "title": "Personal Assistant" },
245
+ "secret.md": { "ignore": true }
246
+ },
247
+ "dirs": {
248
+ "old-drafts": { "ignore": true }
249
+ }
250
+ }
251
+ \`\`\`
252
+
253
+ | Field | Applies to | Effect |
254
+ | --- | --- | --- |
255
+ | \`title\` | the whole file | Masthead title; wins over the README heading |
256
+ | \`sections.<dir>\` / \`dirs.<path>\` | a directory | \`title\` is the menu label, \`meta\` is a short note |
257
+ | \`sections.<dir>.docs.<file>\` | a file inside that directory | same shape as \`docs\` entries |
258
+ | \`docs.<file>\` / \`files.<path>\` | a file | \`title\` is the menu label |
259
+ | \`order\` | any entry | Ascending menu order. Entries without \`order\` follow, still files before folders |
260
+ | \`ignore\` (or \`hidden\`) | any entry | \`true\` hides the file or folder from the menu, lists, search, home, and contents. Hiding a folder hides everything inside it |
261
+
262
+ \`ignore\` is view-only: the file still exists on disk. Remove the flag to
263
+ bring it back:
264
+
265
+ \`\`\`json
266
+ { "files": { "secret.md": { "ignore": true } }, "dirs": { "old-drafts": { "ignore": true } } }
267
+ \`\`\`
268
+
269
+ Paths are relative to the \`.knowledges\` root, use \`/\` separators, and
270
+ must match the on-disk path exactly as the viewer shows it, for example
271
+ \`features/auth.md\`. Nested folders use the full relative path as the key.
272
+
273
+ The sidebar always opens with two fixed entries above the tree: \`index\`
274
+ (the root \`README.md\`) and \`contents\` (\`#/a/contents\`, a system route with
275
+ no file on disk that lists the whole base). The root README is therefore
276
+ not repeated inside the tree.
277
+
278
+ ## Config (two layers)
279
+
280
+ knl-view reads two optional config files. A missing file is never an error;
281
+ a broken file is ignored with one warning.
282
+
283
+ **Global (\`~/.knlview/knl-config.json\`)**: shared by every project on this
284
+ machine. Holds the \`anchordb\` block (connection). Without it, AnchorDB
285
+ stays completely off:
286
+
287
+ \`\`\`json
288
+ {
289
+ "anchordb": {
290
+ "url": "wire://127.0.0.1:7448",
291
+ "token": "your-token",
292
+ "sync": true
293
+ }
294
+ }
295
+ \`\`\`
296
+
297
+ **Per project: \`knl-config.json\` at the project root**, machine-local,
298
+ gitignored. Holds only \`path\`, \`port\`, \`host\`, and the \`models\` map:
299
+
300
+ \`\`\`json
301
+ {
302
+ "path": ".knowledges",
303
+ "port": 8085,
304
+ "models": { "knowledges": "vez0vqrp" }
305
+ }
306
+ \`\`\`
307
+
308
+ The map owns model naming: role → AnchorDB model name, names may be
309
+ random; the map records what each name is for, and names stay stable
310
+ across project renames. Create names with \`knl-view model new <role>\`
311
+ and inspect ownership with \`knl-view model check\`. Without a map entry
312
+ for a role, that role runs fully local. CLI flags always win over
313
+ config values.
314
+
315
+ Installing AnchorDB (optional; the viewer runs fully local without it):
316
+ \`knl-view install anchordb\`: native binary from GitHub Releases on
317
+ Linux and macOS (SHA256-verified), or a container from
318
+ ghcr.io/rachmanzz/anchordb via docker/podman on other platforms. Flags:
319
+ \`--yes\`, \`--method=binary|container\`, \`--token=...\`.
320
+
321
+ Two-way lineage: graph traces take \`--direction\` (CLI) or
322
+ \`?direction=\` (HTTP): \`out\` follows edges leaving the target, \`in\`
323
+ follows edges entering it, \`both\` is the local default. Direction is
324
+ mechanical (edge direction), never cause-and-effect interpretation.
325
+ Each node in the result carries \`via\` (\`center\`, \`in\`, \`out\`,
326
+ \`in+out\`). On /api/anchordb/trace the default is \`out\`; \`in\` is
327
+ walked backwards from the local sync mirror and reported with
328
+ \`mirrored: true\`.
329
+
330
+ Consensus (claims): a second AnchorDB model (the \`consensus\` role in
331
+ the models map) holds agreement and conflict between claims only (never
332
+ decisions or files). Claims are content-addressed:
333
+ \`knl-view consensus claim "<statement>"\` prints the id (\`c-\` + 16 hex
334
+ chars of the statement hash); the same text is the same claim everywhere.
335
+ Relations: \`agree\`, \`contradict\`, \`retract\` take two claim
336
+ ids. \`status <id>\` derives: any incoming \`contradicts\` → \`disputed\`,
337
+ else incoming \`agrees_with\` → \`consensus\`, else \`uncontested\`.
338
+ \`list\` shows every locally known claim. Mutations are CLI-only;
339
+ \`GET /api/consensus?target=<claimId>\` is read-only. Requires AnchorDB
340
+ plus a mapped \`consensus\` role.
341
+
342
+ Sources graph (whole project): a third AnchorDB model, the \`sources\`
343
+ role, holds the dependency graph of the whole project: files and their
344
+ named symbols. Every file is a node (id = repo-relative path); import
345
+ edges are extracted with tree-sitter syntax parsing (JS family and Go
346
+ today) and stored in both directions (\`imports\`, \`imported_by\`).
347
+ Each file also defines symbol nodes: ids are \`<path>#<name>\` (class
348
+ methods \`Class.method\`; a name defined more than once in one file gets
349
+ a line suffix \`<path>#<name>@<line>\`), kinds are \`function\`,
350
+ \`class\`, \`method\`, \`type\`. Edges: \`defines\`/\`defined_in\`
351
+ (file → symbol) and \`calls\`/\`called_by\` (symbol → symbol), both
352
+ directions stored. Call resolution is conservative: the call belongs to
353
+ its innermost enclosing symbol and the callee must match exactly one
354
+ definition project-wide; ambiguous names are skipped, never guessed.
355
+ Label text (symbol names and kinds; AnchorDB attributes are
356
+ numeric-only) lives in \`.knowledges/.knl-data/<path>.md\`, one file per
357
+ source file that has symbols; override with \`labelDir\` in the project
358
+ config. Hidden folders are skipped except .github; node_modules, dist,
359
+ and .gitignore patterns are ignored. \`knl-view sources sync\` is
360
+ idempotent and server-authoritative (no local mirror);
361
+ \`knl-view sources trace --target=<id>\` and
362
+ \`GET /api/sources/trace?target=<id>\` answer lineage: a target
363
+ containing \`#\` walks symbol lineage (\`calls\`/\`called_by\`), a
364
+ plain path walks file lineage (\`imports\`/\`imported_by\`). A removed
365
+ file or symbol is tombstoned (attribute \`tombstone = 2\`) after its
366
+ edges are unlinked; if it comes back, the tombstone lifts and the edges
367
+ are relinked.
368
+
369
+ Decision history skill: recording architectural decisions is delegated
370
+ to the separate decision-history skill (its decision block format is its
371
+ own domain; this skill never writes decision blocks). If it is not
372
+ installed in the project yet, install it first:
373
+ \`npx skills add rachmanzz/decisionskills --skill decision-history\`
374
+ or, with agy: \`agy plugin install https://github.com/rachmanzz/decisionskills\`.
375
+
376
+ ## Rules
377
+
378
+ - Never move or delete files to satisfy the map. Either add the file or
379
+ remove the stale map entry.
380
+ - Give every markdown file a top-level \`# heading\` so it still reads well
381
+ when no map entry covers it.
382
+ - Keep \`order\` values small and dense per menu level (1, 2, 3).
383
+ - When the map and disk disagree about existence, the disk wins.
384
+
385
+ ## Features writing rules
386
+
387
+ Under \`features/{feature_name}/\`, prefer to keep at least:
388
+ - \`prd.md\`: Product Requirements Document for this feature
389
+ - \`detail-plan.md\`: concrete implementation plan, decisions, trade-offs
390
+
391
+ Names are kebab-case for the folder. Keep other artifacts minimal and cover
392
+ them via \`map.json\` if they need custom menu labels/order.
393
+
394
+ ## Futures writing rules
395
+
396
+ Under \`futures/\`, the base typically has:
397
+ - \`README.md\`: vision/overview
398
+ - \`prd.md\`: global PRD covering proposed future work
399
+
400
+ Use kebab-case for any additional future documents. Defer exotic filenames
401
+ to \`map.json\`.
402
+
403
+ ## Roadmap writing rules
404
+
405
+ Under \`roadmap/\`, maintain milestone-oriented notes. Keep \`README.md\` as
406
+ the index and split by milestones only when they grow large. Use
407
+ \`map.json\` to reorder milestones/files without renaming.
408
+
409
+ ## Roasted writing rules
410
+
411
+ All artifacts from roasting/audits go under \`roasted/\` (relative to the
412
+ knowledge base root in use; i.e. the directory passed via \`--path\`, which
413
+ may be \`.knowledges\` or a custom one). Do **not** place them in the root
414
+ of that directory.
415
+
416
+ ### Naming
417
+
418
+ \`roasted/{roasted_name}_v{number}.md\`: kebab-case name, integer
419
+ \`number\` starting at \`1\`.
420
+
421
+ ### Stack order
422
+
423
+ - \`_v1\` is the first roast → placed at the bottom of the stack
424
+ - each new roast increments number and goes at the top (highest number =
425
+ newest)
426
+ - files are immutable by number; updates belong to the same file, not a
427
+ new one
428
+
429
+ ### Classification
430
+
431
+ Every roast must state:
432
+ - \`Severity\`: \`P0|P1|P2|P3\`
433
+ - \`Class\`: \`BUG|PERF|VULN|DEBT|CEILING|PROCESS\`
434
+
435
+ ## When the agent triggers the graph
436
+
437
+ The graph stays true only if the agent feeds it. Every trigger goes
438
+ through the single \`knl-view\` entrypoint with the existing subcommands
439
+ below; never invent another binary, script, or wrapper to fire any of
440
+ this.
441
+
442
+ - After creating, editing, renaming, or deleting any project file:
443
+ run \`knl-view sources sync\`. It is server-authoritative and
444
+ idempotent: run it again and only the difference is applied.
445
+ - Before changing a file that other files may depend on:
446
+ \`knl-view sources trace --target=<path> --direction=in\` shows who
447
+ depends on it first (the blast radius).
448
+ - Before adding an import to, or moving logic out of, another file:
449
+ \`knl-view sources trace --target=<path> --direction=out\` shows what
450
+ it already touches.
451
+ - Before changing or removing a function, method, or type:
452
+ \`knl-view sources trace --target=<path>#<name> --direction=in\` shows
453
+ every symbol that calls it first.
454
+ - Before renaming a symbol or moving it to another file: remember its
455
+ record id contains the old name/path. Sync tombstones the stale record
456
+ and creates the new one; trace by the new id afterwards.
457
+ - A new architectural decision: trigger the decision-history skill
458
+ (install it first if missing; see "Decision history skill" above).
459
+ The decision block format is that skill's domain; this skill never
460
+ writes decision blocks.
461
+ - A technical disagreement worth recording as claims:
462
+ \`knl-view consensus claim "<statement>"\`, then
463
+ \`knl-view consensus agree <id> <id>\` or
464
+ \`knl-view consensus contradict <id> <id>\`. A claim that lost its
465
+ ground: \`knl-view consensus retract <id> <id>\`.
466
+ `