@kentwynn/kgraph 0.1.15 → 0.1.17

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.
package/README.md CHANGED
@@ -1,112 +1,71 @@
1
1
  # KGraph
2
2
 
3
- > **Persistent repository intelligence for AI coding tools.**
4
- > Stop paying the context tax on every session. KGraph gives your AI assistant a memory.
3
+ Persistent repository intelligence for AI coding tools.
5
4
 
6
- ---
5
+ KGraph gives Codex, GitHub Copilot, Cursor, and Claude Code a local knowledge layer for your repo: file maps, symbols, imports, relationships, and durable notes from previous AI sessions. The goal is simple: your assistant should not spend every session re-learning the same codebase.
7
6
 
8
- ## The Problem
7
+ ## The Workflow
9
8
 
10
- Every AI coding session starts with the same expensive ritual:
9
+ Use KGraph in two steps:
11
10
 
12
- ```
13
- "Let me read your package.json..."
14
- "Let me trace the imports in auth.ts..."
15
- "Let me find where sessions are created..."
16
- "Let me understand the database layer..."
17
- ```
18
-
19
- On a medium-sized codebase, this exploration burns **3,000–8,000 tokens** before the AI writes a single line of code. Multiply that across 10 sessions per day and you're spending the majority of your context budget re-learning things you already know.
20
-
21
- Worse: AI tools forget. The debugging insight from Tuesday, the architecture decision from last sprint, the "don't touch this or it breaks payments" gotcha — gone after every session.
22
-
23
- ---
24
-
25
- ## The Solution
26
-
27
- KGraph builds a local knowledge layer that grows with your project. It maps your codebase once, captures reasoning from your AI sessions, and serves compact, targeted context on demand.
11
+ ```bash
12
+ # Required once per repository
13
+ kgraph init --integrations codex,copilot,cursor,claude-code
28
14
 
15
+ # Normal daily command
16
+ kgraph "auth token refresh"
29
17
  ```
30
- Without KGraph With KGraph
31
- ───────────────────────────────── ──────────────────────────────────
32
- Session start: ~5,000 tokens Session start: ~300 tokens
33
- exploring files and structure kgraph context "auth token refresh"
34
18
 
35
- Re-learns same architecture Recalls prior decisions instantly
36
- every single session from cognition store
19
+ That second command runs the full practical workflow:
37
20
 
38
- Context limit hit mid-task Full context budget for actual work
39
-
40
- Debugging insight lost forever Captured in .kgraph/inbox/
41
- available in every future session
42
- ```
21
+ 1. Refreshes the repository scan.
22
+ 2. Updates file, symbol, import, and relationship maps.
23
+ 3. Processes any Markdown notes waiting in `.kgraph/inbox/`.
24
+ 4. Returns compact context for the topic you asked about.
43
25
 
44
- ---
26
+ You can also run just:
45
27
 
46
- ## Token Savings — What This Looks Like in Practice
28
+ ```bash
29
+ kgraph
30
+ ```
47
31
 
48
- A typical `kgraph context` response for a focused topic:
32
+ That refreshes maps and cognition without printing topic-specific context.
49
33
 
50
- ```
51
- topic: auth token refresh
34
+ The smaller commands, such as `kgraph scan`, `kgraph update`, and `kgraph context`, still exist. They are useful when you want one specific step, but they are not the main workflow.
52
35
 
53
- files:
54
- src/lib/auth.ts (createSession, validateToken, refreshToken)
55
- app/api/auth/route.ts (POST handler → createSession)
56
- middleware.ts (reads session cookie, calls validateToken)
36
+ ## Why It Exists
57
37
 
58
- key relationships:
59
- POST /api/auth → createSession → writes JWT to cookie
60
- middleware → validateToken → redirects on expiry
38
+ Most AI coding sessions start like this:
61
39
 
62
- cognition:
63
- refreshToken has a race condition under concurrent requests — see issue #47
64
- JWT secret must come from env, never hardcoded — broke staging in March
65
- token TTL is 15 min by design, not a bug
40
+ ```text
41
+ Let me inspect package.json.
42
+ Let me search for auth routes.
43
+ Let me trace imports.
44
+ Let me understand where sessions are stored.
66
45
  ```
67
46
 
68
- **~280 tokens.** The equivalent file-by-file exploration: **4,200+ tokens.**
69
- That's a **15x reduction** in context cost for navigation alone.
47
+ That exploration is useful once. It is wasteful the tenth time.
70
48
 
71
- The gap widens every week as cognition accumulates — past decisions, debugging discoveries, and architectural gotchas that would otherwise cost the AI thousands of tokens to re-derive.
49
+ KGraph stores the reusable parts locally:
72
50
 
73
- ---
51
+ - What files exist and what language they use.
52
+ - What symbols each source file defines.
53
+ - Which files import each other.
54
+ - Which notes, decisions, debugging findings, and gotchas were captured from prior sessions.
55
+ - Which cognition references are current, mixed, stale, or unresolved after code moves.
74
56
 
75
- ## How It Works
57
+ Then an AI assistant can ask for focused context before broad exploration:
76
58
 
77
- ```
78
- ┌─────────────────────────────────────────────────────────────┐
79
- │ Your Codebase │
80
- └──────────────────────────────┬──────────────────────────────┘
81
- │ kgraph scan
82
-
83
- ┌─────────────────────────────────────────────────────────────┐
84
- │ .kgraph/ │
85
- │ ├── maps/ file graph, symbol index, imports │
86
- │ ├── cognition.md decisions, gotchas, debugging history │
87
- │ └── config.yaml include/exclude rules │
88
- └──────────────────────────────┬──────────────────────────────┘
89
- │ kgraph context "topic"
90
-
91
- ┌─────────────────────────────────────────────────────────────┐
92
- │ AI Tool (Copilot / Codex / Cursor / Claude Code) │
93
- │ Reads compact context → navigates directly → works faster │
94
- └─────────────────────────────────────────────────────────────┘
95
- │ session ends
96
-
97
- ┌─────────────────────────────────────────────────────────────┐
98
- │ .kgraph/inbox/ AI drops a note: what it learned │
99
- │ kgraph update → distilled into │
100
- │ cognition.md for the next session │
101
- └─────────────────────────────────────────────────────────────┘
59
+ ```bash
60
+ kgraph "blog admin token usage"
102
61
  ```
103
62
 
104
- This creates a **compounding feedback loop**: the more you use KGraph, the richer the cognition store, the less exploration the AI needs to do.
105
-
106
- ---
63
+ Instead of reading the whole repo, it gets a compact starting point: relevant files, symbols, relationships, domains, prior notes, and stale references to watch.
107
64
 
108
65
  ## Install
109
66
 
67
+ Use the published CLI:
68
+
110
69
  ```bash
111
70
  npm install -g @kentwynn/kgraph@latest
112
71
  kgraph --version
@@ -116,107 +75,171 @@ Or run without installing:
116
75
 
117
76
  ```bash
118
77
  npx @kentwynn/kgraph@latest init
78
+ npx @kentwynn/kgraph@latest "auth token refresh"
119
79
  ```
120
80
 
121
- ---
81
+ KGraph requires Node.js 20 or newer.
122
82
 
123
83
  ## Quick Start
124
84
 
85
+ From the root of a repository:
86
+
87
+ ```bash
88
+ # 1. Create the local KGraph workspace
89
+ kgraph init
90
+
91
+ # 2. Optional: connect AI tools so they know the KGraph workflow
92
+ kgraph integrate add codex copilot cursor claude-code
93
+
94
+ # 3. Run the normal workflow for a topic
95
+ kgraph "auth token refresh"
96
+
97
+ # 4. Check health if something feels off
98
+ kgraph doctor
99
+ ```
100
+
101
+ After useful AI work, assistants can save durable notes into `.kgraph/inbox/`. The next `kgraph` run processes those notes automatically. You can also process them directly with `kgraph update`.
102
+
103
+ ## Main Commands
104
+
105
+ ```bash
106
+ kgraph init
107
+ ```
108
+
109
+ Required once per repo. Creates `.kgraph/` and the local config.
110
+
125
111
  ```bash
126
- # 1. Initialize and connect your AI tools
127
112
  kgraph init --integrations codex,copilot,cursor,claude-code
113
+ ```
128
114
 
129
- # 2. Scan the codebase
130
- kgraph scan
115
+ Initializes KGraph and writes local instruction files for supported AI tools.
131
116
 
132
- # 3. Ask for context before exploring (your AI does this automatically)
133
- kgraph context "auth token refresh"
117
+ ```bash
118
+ kgraph "some topic"
119
+ ```
134
120
 
135
- # 4. After an AI session, save what was learned
136
- kgraph update
121
+ The normal command. Scans the repo, updates cognition, and returns focused context for the topic.
122
+
123
+ ```bash
124
+ kgraph
137
125
  ```
138
126
 
139
- That's the entire loop. From session 2 onward, your AI tool loads existing intelligence before touching a single file.
127
+ Refreshes maps and cognition without returning topic-specific context.
140
128
 
141
- ---
129
+ ```bash
130
+ kgraph doctor
131
+ ```
142
132
 
143
- ## AI Tool Integrations
133
+ Checks whether the workspace is initialized, maps exist, inbox notes are pending, and configured integrations point to real files.
134
+
135
+ ## Optional Step Commands
144
136
 
145
- `kgraph integrate` writes instruction files and command/skill packs directly into your repo so each tool knows how to use the knowledge layer — no manual setup.
137
+ These are useful for scripting, debugging, or when you want a single operation.
146
138
 
147
139
  ```bash
148
- kgraph integrate add codex copilot cursor claude-code
149
- kgraph integrate list
140
+ kgraph scan
141
+ ```
142
+
143
+ Refresh only the structural maps in `.kgraph/map/`.
144
+
145
+ ```bash
146
+ kgraph context "auth token refresh"
147
+ kgraph context "auth token refresh" --json
150
148
  ```
151
149
 
152
- | Tool | Always-on instruction | Skills / commands |
153
- | -------------- | --------------------------------- | ----------------------------------------------------------------------------------------------- |
154
- | GitHub Copilot | `.github/copilot-instructions.md` | `/kgraph-scan` · `/kgraph-update` · `/kgraph-visualize` · `/kgraph-history` · `/kgraph-capture` |
155
- | Codex | `AGENTS.md` | `.agents/skills/kgraph/SKILL.md` (VS Code Agent Skills standard) |
156
- | Cursor | `.cursor/rules/kgraph.mdc` | Built into the rule |
157
- | Claude Code | `CLAUDE.md` | `/kgraph` · `/kgraph-scan` · `/kgraph-update` · `/kgraph-visualize` · `/kgraph-history` |
150
+ Return context from existing maps and cognition without scanning or updating first.
158
151
 
159
- Each integration installs a `/kgraph` skill or command that handles the full workflow automatically: load context → work → capture findings → update cognition. `/kgraph-scan`, `/kgraph-update`, and `/kgraph-visualize` are available for manual maintenance.
152
+ ```bash
153
+ kgraph update
154
+ kgraph update --dry-run
155
+ ```
160
156
 
161
- Existing user content in `AGENTS.md`, `CLAUDE.md`, etc. is preserved — KGraph manages only its own clearly-marked blocks.
157
+ Process Markdown notes from `.kgraph/inbox/` into durable cognition records.
162
158
 
163
- ---
159
+ ```bash
160
+ kgraph visualize
161
+ kgraph visualize --port 3000
162
+ kgraph visualize --no-open
163
+ ```
164
164
 
165
- ## CLI Reference
165
+ Open the local interactive dependency graph at `http://localhost:4242`.
166
166
 
167
167
  ```bash
168
- kgraph init # initialize .kgraph/ workspace
169
- kgraph init --integrations codex,copilot # init + configure integrations
168
+ kgraph history
169
+ kgraph history --last 10
170
+ kgraph history --json
171
+ ```
170
172
 
171
- kgraph scan # scan codebase, update maps
172
- kgraph context "auth token refresh" # get compact context for a topic
173
- kgraph context "auth token refresh" --json # machine-readable output
174
- kgraph update # process inbox notes into cognition
173
+ Show processed cognition sessions.
175
174
 
176
- kgraph integrate list # show integration status
177
- kgraph integrate add codex copilot cursor # add integrations
178
- kgraph integrate remove cursor # remove an integration
175
+ ## AI Tool Integrations
179
176
 
180
- kgraph visualize # interactive graph at http://localhost:4242
181
- kgraph visualize --port 3000 # custom port
182
- kgraph visualize --no-open # print URL, don't open browser
177
+ KGraph integrations are local files. They do not start background agents, call AI providers, or send data anywhere.
183
178
 
184
- kgraph history # timeline of processed cognition sessions
185
- kgraph history --last 10 # show last 10 entries
186
- kgraph history --json # machine-readable output
179
+ ```bash
180
+ kgraph integrate add codex copilot cursor claude-code
181
+ kgraph integrate list
182
+ kgraph integrate remove cursor
187
183
  ```
188
184
 
189
- ---
185
+ | Tool | Files KGraph manages |
186
+ | --- | --- |
187
+ | Codex | `AGENTS.md`, `.agents/skills/kgraph/SKILL.md` |
188
+ | GitHub Copilot | `.github/copilot-instructions.md`, `.github/prompts/*` |
189
+ | Cursor | `.cursor/rules/kgraph.mdc` |
190
+ | Claude Code | `CLAUDE.md`, `.claude/commands/*` |
191
+
192
+ KGraph preserves existing user-authored content and updates only its marked instruction blocks or generated command files.
193
+
194
+ ## What Gets Stored
195
+
196
+ All runtime data lives under `.kgraph/`:
197
+
198
+ ```text
199
+ .kgraph/
200
+ ├── config.yaml
201
+ ├── map/
202
+ │ ├── files.json
203
+ │ ├── symbols.json
204
+ │ ├── dependencies.json
205
+ │ └── relationships.json
206
+ ├── inbox/
207
+ ├── cognition/
208
+ ├── domains/
209
+ ├── interactions/processed/
210
+ └── context/
211
+ ```
190
212
 
191
- ## What KGraph Tracks
213
+ The files are local, inspectable, and human-readable. There is no database, telemetry, cloud service, account, API key, embedding service, or model provider.
192
214
 
193
- | Category | Examples |
194
- | ----------------- | ---------------------------------------------------------------------- |
195
- | **File map** | every source file, language, size |
196
- | **Symbol index** | functions, classes, methods, exports per file |
197
- | **Import graph** | which files import which, dependency chains |
198
- | **Relationships** | call sites, re-exports, shared types |
199
- | **Cognition** | past decisions, architectural constraints, debugging insights, gotchas |
215
+ ## Language Support
200
216
 
201
- **Deep scan** (symbols, functions, classes, methods, imports): TypeScript, JavaScript, Python, Go, Rust, Java, Kotlin, C, C++, C#
217
+ KGraph deeply scans:
202
218
 
203
- **Generic scan** (file path, language, size — contributes to context): Ruby, PHP, Swift, Shell, HTML, CSS, SQL, YAML, TOML, and 20+ more — detected by file extension, no configuration needed.
219
+ - TypeScript and JavaScript
220
+ - Python
221
+ - Go
222
+ - Rust
223
+ - Java and Kotlin
224
+ - C and C++
225
+ - C#
204
226
 
205
- ---
227
+ Other common file types still appear in the file map with generic metadata, so context queries can still point to docs, config, SQL, CSS, HTML, YAML, and similar files.
206
228
 
207
- ## Local-First, Zero Dependencies
229
+ ## Visualization
208
230
 
209
- KGraph requires nothing beyond Node.js ≥ 20:
231
+ ```bash
232
+ kgraph visualize
233
+ ```
210
234
 
211
- - No accounts or API keys
212
- - No embeddings or vector databases
213
- - No cloud services or telemetry
214
- - No background daemons
215
- - No model provider
235
+ The graph shows files, symbols, imports, cognition notes, and relationship edges. Cognition notes are colored by reference health:
216
236
 
217
- All data lives in `.kgraph/` as human-readable JSON, YAML, and Markdown. Commit it, diff it, inspect it anytime.
237
+ - current
238
+ - mixed
239
+ - stale
240
+ - unresolved
218
241
 
219
- ---
242
+ Use it when you want to inspect what KGraph currently knows, find stale notes after refactors, or export a graph image for a report.
220
243
 
221
244
  ## Development
222
245
 
@@ -226,60 +249,54 @@ npm run build
226
249
  npm test
227
250
  ```
228
251
 
229
- Test a command without installing:
252
+ Run the local TypeScript CLI without installing globally:
230
253
 
231
254
  ```bash
232
- npm run kgraph -- init --integrations codex,cursor
233
- npm run kgraph -- context "auth token refresh"
255
+ npm run kgraph -- init
256
+ npm run kgraph -- "auth token refresh"
257
+ npm run kgraph -- doctor
234
258
  ```
235
259
 
236
- Install the local build globally to test the `kgraph` binary end-to-end:
260
+ Test the built package as a global local install:
237
261
 
238
262
  ```bash
263
+ npm run build
239
264
  npm install -g .
240
265
  kgraph --version
241
- kgraph init --integrations codex,copilot
266
+ kgraph doctor
267
+ kgraph "auth token refresh"
242
268
  ```
243
269
 
244
- ---
245
-
246
- ## Release
247
-
248
- Releases are tag-driven. Bump the version, push the commit and tag:
270
+ Package checks:
249
271
 
250
272
  ```bash
251
- npm version patch
252
- git push origin main --follow-tags
273
+ npm run pack:dry
274
+ npm run release:pack
253
275
  ```
254
276
 
255
- CI verifies the tag matches `package.json`, checks the version is unpublished, publishes to npm, creates a GitHub Release, and attaches the tarball.
256
-
257
- ---
277
+ ## Release
258
278
 
259
- ## Visualization
279
+ Releases are tag-driven:
260
280
 
261
281
  ```bash
262
- kgraph visualize
282
+ npm version patch
283
+ git push origin main --follow-tags
263
284
  ```
264
285
 
265
- Starts a local server at `http://localhost:4242` and opens an interactive dependency graph in your browser. No install required two CDN scripts (Cytoscape.js + dagre layout) are loaded at view time.
266
-
267
- **What you see:**
286
+ The release workflow builds, tests, packs, publishes the npm package on version tags, creates a GitHub Release, and uploads the tarball artifact.
268
287
 
269
- - File nodes colored by language (TypeScript, JavaScript, Markdown, YAML, …)
270
- - Cognition notes as diamonds, colored by health (green = current, amber = mixed, red = stale)
271
- - Import edges showing real dependency flow
272
- - Dashed blue edges linking cognition notes to the files they describe
273
- - Click any node for a metadata panel (path, size, domain, related symbols)
274
- - Toggle cognition overlay on/off
275
- - Switch layout: Hierarchical (default), Force-directed, Grid, Concentric
276
- - **Export PNG** — 2× resolution, dark background, ready for reports or slides
288
+ ## Design Principles
277
289
 
278
- ---
290
+ - Local-first: the repo intelligence stays in your repo.
291
+ - Explicit: no daemon and no hidden background process.
292
+ - Inspectable: generated knowledge is JSON, YAML, and Markdown.
293
+ - Deterministic first: useful ranking without requiring embeddings or a model.
294
+ - Assistant-friendly: one normal command, with lower-level commands available when needed.
279
295
 
280
296
  ## Roadmap
281
297
 
282
- - Git-aware history and rename tracking
283
- - richer language scanners (deeper AST, cross-file type resolution)
284
- - MCP server for editor tool-call access
285
- - team-shared cognition via committed `.kgraph/`
298
+ - Smarter cross-file symbol and call relationship inference.
299
+ - Stronger TypeScript path alias and package export resolution.
300
+ - Richer graph filtering for large repositories.
301
+ - Optional MCP server for editor tool-call access.
302
+ - Team workflows for shared committed cognition.
@@ -1,2 +1,4 @@
1
1
  import type { Command } from "commander";
2
+ import type { ContextResponse } from "../../types/cognition.js";
2
3
  export declare function registerContextCommand(program: Command): void;
4
+ export declare function renderContextMarkdown(response: ContextResponse): string;
@@ -19,10 +19,10 @@ export function registerContextCommand(program) {
19
19
  const config = await loadConfig(workspace);
20
20
  const maps = await readMaps(workspace);
21
21
  const response = await queryContext(workspace, config, maps, query);
22
- console.log(options.json ? JSON.stringify(response, null, 2) : renderMarkdown(response));
22
+ console.log(options.json ? JSON.stringify(response, null, 2) : renderContextMarkdown(response));
23
23
  }));
24
24
  }
25
- function renderMarkdown(response) {
25
+ export function renderContextMarkdown(response) {
26
26
  const lines = [`# KGraph Context`, ``, `Query: ${response.query}`, ``];
27
27
  lines.push("## Matched Domains", "");
28
28
  lines.push(...formatList(response.matchedDomains.map((item) => `- ${item.item.name} (${item.reasons.join(", ")})`)));
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerDoctorCommand(program: Command): void;
@@ -0,0 +1,100 @@
1
+ import { readdir } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { loadConfig } from '../../config/config.js';
4
+ import { listIntegrations } from '../../integrations/integration-store.js';
5
+ import { assertWorkspace, pathExists, resolveWorkspace, } from '../../storage/kgraph-paths.js';
6
+ import { mapPaths, mapsExist, readMaps } from '../../storage/map-store.js';
7
+ import { runCommand } from '../errors.js';
8
+ export function registerDoctorCommand(program) {
9
+ program
10
+ .command('doctor')
11
+ .description('Check KGraph workspace health and next actions')
12
+ .action(() => runCommand(async () => {
13
+ const rootPath = process.cwd();
14
+ const workspace = resolveWorkspace(rootPath);
15
+ const checks = [];
16
+ const initialized = await pathExists(workspace.kgraphPath);
17
+ checks.push({
18
+ label: 'workspace',
19
+ ok: initialized,
20
+ detail: initialized ? '.kgraph exists' : 'run `kgraph init` first',
21
+ });
22
+ if (!initialized) {
23
+ printChecks(checks);
24
+ process.exitCode = 1;
25
+ return;
26
+ }
27
+ await assertWorkspace(rootPath);
28
+ const config = await loadConfig(workspace);
29
+ checks.push({
30
+ label: 'config',
31
+ ok: true,
32
+ detail: `${config.include.length} include pattern(s), ${config.exclude.length} exclude pattern(s)`,
33
+ });
34
+ const mapStatus = await mapsExist(workspace);
35
+ checks.push({
36
+ label: 'maps',
37
+ ok: mapStatus,
38
+ detail: mapStatus ? 'structural maps are present' : 'run `kgraph scan` or just `kgraph`',
39
+ });
40
+ if (mapStatus) {
41
+ const maps = await readMaps(workspace);
42
+ checks.push({
43
+ label: 'scan result',
44
+ ok: true,
45
+ detail: `${maps.fileMap.files.length} files, ${maps.symbolMap.symbols.length} symbols, ${maps.dependencyMap.dependencies.length} dependencies`,
46
+ });
47
+ }
48
+ else {
49
+ const paths = mapPaths(workspace);
50
+ const missing = [];
51
+ for (const [name, filePath] of Object.entries(paths)) {
52
+ if (!(await pathExists(filePath)))
53
+ missing.push(name);
54
+ }
55
+ checks.push({
56
+ label: 'missing maps',
57
+ ok: false,
58
+ detail: missing.join(', '),
59
+ });
60
+ }
61
+ const inboxCount = await countMarkdownFiles(workspace.inboxPath);
62
+ checks.push({
63
+ label: 'inbox',
64
+ ok: true,
65
+ detail: inboxCount === 0
66
+ ? 'no pending cognition notes'
67
+ : `${inboxCount} note(s) waiting for \`kgraph update\``,
68
+ });
69
+ const integrations = await listIntegrations(workspace);
70
+ checks.push({
71
+ label: 'integrations',
72
+ ok: integrations.every((integration) => integration.targetExists),
73
+ detail: integrations.length === 0
74
+ ? 'none configured'
75
+ : integrations
76
+ .map((integration) => integration.targetExists
77
+ ? `${integration.name}: ${integration.targetPath}`
78
+ : `${integration.name}: missing ${integration.targetPath}`)
79
+ .join('; '),
80
+ });
81
+ printChecks(checks);
82
+ if (checks.some((check) => !check.ok)) {
83
+ process.exitCode = 1;
84
+ }
85
+ }));
86
+ }
87
+ async function countMarkdownFiles(dirPath) {
88
+ if (!(await pathExists(dirPath))) {
89
+ return 0;
90
+ }
91
+ const entries = await readdir(dirPath, { withFileTypes: true });
92
+ return entries.filter((entry) => entry.isFile() && path.extname(entry.name) === '.md').length;
93
+ }
94
+ function printChecks(checks) {
95
+ console.log('KGraph Doctor');
96
+ console.log('');
97
+ for (const check of checks) {
98
+ console.log(`${check.ok ? 'OK' : 'FAIL'} ${check.label}: ${check.detail}`);
99
+ }
100
+ }
@@ -0,0 +1 @@
1
+ export declare function runDefaultWorkflow(query?: string): Promise<void>;
@@ -0,0 +1,42 @@
1
+ import { updateCognition } from '../../cognition/cognition-updater.js';
2
+ import { refreshCognitionReferenceStatuses } from '../../cognition/cognition-updater.js';
3
+ import { loadConfig } from '../../config/config.js';
4
+ import { queryContext } from '../../context/context-query.js';
5
+ import { scanRepository } from '../../scanner/repo-scanner.js';
6
+ import { assertWorkspace } from '../../storage/kgraph-paths.js';
7
+ import { readMaps, writeMaps } from '../../storage/map-store.js';
8
+ import { runCommand } from '../errors.js';
9
+ import { renderContextMarkdown } from './context.js';
10
+ export async function runDefaultWorkflow(query) {
11
+ await runCommand(async () => {
12
+ const topic = query?.trim();
13
+ const workspace = await assertWorkspace(process.cwd());
14
+ const config = await loadConfig(workspace);
15
+ const previousMaps = await readMaps(workspace);
16
+ const scan = await scanRepository(workspace.rootPath, config, {
17
+ files: previousMaps.fileMap.files,
18
+ symbols: previousMaps.symbolMap.symbols,
19
+ dependencies: previousMaps.dependencyMap.dependencies,
20
+ relationships: previousMaps.relationshipMap.relationships,
21
+ warnings: [],
22
+ });
23
+ await writeMaps(workspace, scan);
24
+ await refreshCognitionReferenceStatuses(workspace, {
25
+ files: scan.files,
26
+ symbols: scan.symbols,
27
+ });
28
+ const update = await updateCognition(workspace, { files: scan.files, symbols: scan.symbols }, false);
29
+ console.log(`KGraph refreshed ${scan.files.length} files, ${scan.symbols.length} symbols, and ${update.processed.length} cognition notes.`);
30
+ for (const warning of [...scan.warnings, ...update.warnings]) {
31
+ console.warn(`Warning: ${warning}`);
32
+ }
33
+ if (!topic) {
34
+ console.log('Add a topic to return compact context, for example: kgraph "auth token refresh"');
35
+ return;
36
+ }
37
+ const maps = await readMaps(workspace);
38
+ const response = await queryContext(workspace, config, maps, topic);
39
+ console.log('');
40
+ console.log(renderContextMarkdown(response));
41
+ });
42
+ }
package/dist/cli/help.js CHANGED
@@ -14,16 +14,22 @@ export function renderRootHelp(useColor = supportsColor()) {
14
14
  ` ${theme.hex('#c084fc')('and Claude Code reuse repo structure, decisions, and debugging history.')}`,
15
15
  '',
16
16
  theme.bold('Usage'),
17
+ ' kgraph [topic]',
17
18
  ' kgraph <command> [options]',
18
19
  '',
19
20
  theme.bold('Start'),
20
- command('init', 'Create .kgraph/ workspace'),
21
+ command('init', 'Required once: create .kgraph/ workspace'),
21
22
  command('init --integrations codex,cursor', 'Initialize and connect AI tools'),
22
23
  '',
24
+ theme.bold('Daily workflow'),
25
+ command('kgraph', 'Refresh scan maps and process pending cognition notes'),
26
+ command('kgraph "auth token refresh"', 'Refresh everything and return compact context for a topic'),
27
+ '',
23
28
  theme.bold('Workflows'),
24
- command('scan', 'Refresh file, symbol, import, and relationship maps'),
25
- command('context "auth token refresh"', 'Return compact context for an AI chat'),
26
- command('update', 'Process .kgraph/inbox Markdown cognition notes'),
29
+ command('scan', 'Optional: refresh only file, symbol, import, and relationship maps'),
30
+ command('context "auth token refresh"', 'Optional: return context without scanning or updating'),
31
+ command('update', 'Optional: process only .kgraph/inbox Markdown cognition notes'),
32
+ command('doctor', 'Check workspace health and next actions'),
27
33
  command('visualize', 'Interactive dependency graph at http://localhost:4242'),
28
34
  command('history', 'Timeline of processed cognition sessions'),
29
35
  '',
@@ -38,8 +44,8 @@ export function renderRootHelp(useColor = supportsColor()) {
38
44
  '',
39
45
  `${theme.yellow('Examples')}`,
40
46
  ' kgraph init --integrations codex,copilot,cursor',
41
- ' kgraph scan',
42
- ' kgraph context "blog admin token usage" --json',
47
+ ' kgraph "blog admin token usage"',
48
+ ' kgraph doctor',
43
49
  '',
44
50
  theme.dim('Docs: https://github.com/kentwynn/KGraph#readme'),
45
51
  '',
package/dist/cli/index.js CHANGED
@@ -4,12 +4,14 @@ import { realpathSync } from 'node:fs';
4
4
  import { createRequire } from 'node:module';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { registerContextCommand } from './commands/context.js';
7
+ import { registerDoctorCommand } from './commands/doctor.js';
7
8
  import { registerHistoryCommand } from './commands/history.js';
8
9
  import { registerInitCommand } from './commands/init.js';
9
10
  import { registerIntegrateCommand } from './commands/integrate.js';
10
11
  import { registerScanCommand } from './commands/scan.js';
11
12
  import { registerUpdateCommand } from './commands/update.js';
12
13
  import { registerVisualizeCommand } from './commands/visualize.js';
14
+ import { runDefaultWorkflow } from './commands/workflow.js';
13
15
  import { renderRootHelp } from './help.js';
14
16
  const require = createRequire(import.meta.url);
15
17
  const { version } = require('../../package.json');
@@ -18,9 +20,13 @@ export function createProgram() {
18
20
  program
19
21
  .name('kgraph')
20
22
  .description('Persistent repo intelligence for AI coding assistants')
23
+ .argument('[topic...]', 'Run the default refresh workflow and optionally return context for a topic')
21
24
  .version(version)
22
25
  .addHelpText('beforeAll', renderRootHelp())
23
- .helpOption(false);
26
+ .helpOption(false)
27
+ .action(async (topicParts = []) => {
28
+ await runDefaultWorkflow(topicParts.join(' '));
29
+ });
24
30
  program.option('-h, --help', 'Show this help');
25
31
  program.hook('preAction', (thisCommand) => {
26
32
  if (thisCommand.opts().help) {
@@ -35,13 +41,12 @@ export function createProgram() {
35
41
  registerIntegrateCommand(program);
36
42
  registerVisualizeCommand(program);
37
43
  registerHistoryCommand(program);
44
+ registerDoctorCommand(program);
38
45
  return program;
39
46
  }
40
47
  if (isCliEntrypoint()) {
41
48
  const program = createProgram();
42
- if (process.argv.length <= 2 ||
43
- process.argv.includes('-h') ||
44
- process.argv.includes('--help')) {
49
+ if (process.argv.includes('-h') || process.argv.includes('--help')) {
45
50
  console.log(renderRootHelp());
46
51
  }
47
52
  else {
@@ -26,6 +26,31 @@ export async function queryContext(workspace, config, maps, query) {
26
26
  { name: 'tags', value: (domain) => domain.tags },
27
27
  { name: 'path', value: (domain) => domain.pathHints },
28
28
  ]).slice(0, max);
29
+ const relatedIds = new Set([
30
+ ...relevantFiles.map((file) => file.item.path),
31
+ ...relevantSymbols.map((symbol) => symbol.item.id),
32
+ ...relevantSymbols.map((symbol) => symbol.item.filePath),
33
+ ...relevantCognition.flatMap((note) => [
34
+ ...note.item.relatedFiles,
35
+ ...note.item.relatedSymbols,
36
+ ]),
37
+ ...matchedDomains.flatMap((domain) => [
38
+ ...domain.item.files,
39
+ ...domain.item.symbols,
40
+ ]),
41
+ ]);
42
+ const rankedRelationships = rankByFields(query, maps.relationshipMap.relationships, [
43
+ { name: 'source', value: (relationship) => relationship.sourceId },
44
+ { name: 'target', value: (relationship) => relationship.targetId },
45
+ { name: 'type', value: (relationship) => relationship.relationshipType },
46
+ ]);
47
+ const relationships = [
48
+ ...maps.relationshipMap.relationships.filter((relationship) => relatedIds.has(relationship.sourceId) ||
49
+ relatedIds.has(relationship.targetId)),
50
+ ...rankedRelationships.map((relationship) => relationship.item),
51
+ ].filter((relationship, index, all) => all.findIndex((candidate) => candidate.sourceId === relationship.sourceId &&
52
+ candidate.targetId === relationship.targetId &&
53
+ candidate.relationshipType === relationship.relationshipType) === index);
29
54
  const filePaths = new Set(maps.fileMap.files.map((f) => f.path));
30
55
  const symbolNames = new Set(maps.symbolMap.symbols.map((s) => s.name));
31
56
  const staleReferences = cognition
@@ -46,7 +71,7 @@ export async function queryContext(workspace, config, maps, query) {
46
71
  relevantFiles,
47
72
  relevantSymbols,
48
73
  relevantCognition,
49
- relationships: maps.relationshipMap.relationships.slice(0, max),
74
+ relationships: relationships.slice(0, max),
50
75
  staleReferences,
51
76
  warnings: [],
52
77
  };
@@ -4,7 +4,7 @@ export const claudeCodeAdapter = {
4
4
  targetPath: 'CLAUDE.md',
5
5
  instructions: `## KGraph Workflow
6
6
 
7
- Before exploring the repository, run \`kgraph context "<topic>"\` to load existing repo intelligence. Use /kgraph for the full automated workflow. Run \`kgraph scan\` and \`kgraph update\` manually when needed.
7
+ Before exploring the repository, run \`kgraph "<topic>"\` to refresh repo intelligence and load focused context. Use /kgraph for the full automated workflow. Run \`kgraph doctor\` when setup or generated maps look wrong. Run \`kgraph scan\`, \`kgraph update\`, and \`kgraph context\` manually only when you need one specific step.
8
8
  `,
9
9
  commandFiles: [
10
10
  {
@@ -12,12 +12,13 @@ Before exploring the repository, run \`kgraph context "<topic>"\` to load existi
12
12
  content: `Use KGraph persistent repo intelligence for the current request.
13
13
 
14
14
  1. Infer the topic from the user's request.
15
- 2. Run \`kgraph context "<topic>"\`.
15
+ 2. Run \`kgraph "<topic>"\`. This refreshes maps, processes pending inbox notes, and returns focused context in one command.
16
16
  3. Use the returned files, symbols, relationships, and cognition before broad exploration.
17
- 4. **After completing the work**, write a Markdown note to \`.kgraph/inbox/<slug>.md\` **only if** you discovered something a future session would need to re-derive — a gotcha, constraint, non-obvious decision, or bug. Skip capture for read-only reviews or sessions where nothing new was found. When you do capture, immediately run \`kgraph update\`.
18
- 5. If you created, moved, deleted, or renamed files or symbols during this session, run \`kgraph scan\` and report the summary. Skip it otherwise.
19
- 6. Run \`kgraph visualize\` when the user wants to inspect the dependency graph opens an interactive graph at http://localhost:4242 with PNG export.
20
- 7. Run \`kgraph history\` to review the timeline of past cognition sessions with git author attribution.
17
+ 4. Run \`kgraph doctor\` when setup, maps, inbox processing, or integrations look wrong.
18
+ 5. **After completing the work**, write a Markdown note to \`.kgraph/inbox/<slug>.md\` **only if** you discovered something a future session would need to re-derive — a gotcha, constraint, non-obvious decision, or bug. Skip capture for read-only reviews or sessions where nothing new was found. When you do capture, immediately run \`kgraph update\` or simply run \`kgraph\`.
19
+ 6. If you created, moved, deleted, or renamed files or symbols during this session, run \`kgraph\` or \`kgraph scan\` and report the summary. Skip it otherwise.
20
+ 7. Run \`kgraph visualize\` when the user wants to inspect the dependency graph opens an interactive graph at http://localhost:4242 with PNG export.
21
+ 8. Run \`kgraph history\` to review the timeline of past cognition sessions with git author attribution.
21
22
 
22
23
  The inbox note must use this structure:
23
24
  \`\`\`markdown
@@ -35,6 +36,11 @@ One or two sentences describing what was done.
35
36
  ## Decisions
36
37
  Any architectural or implementation decisions made.
37
38
  \`\`\`
39
+ `,
40
+ },
41
+ {
42
+ path: '.claude/commands/kgraph-doctor.md',
43
+ content: `Run \`kgraph doctor\` to check whether the workspace is initialized, maps exist, inbox notes are pending, and configured integrations point to real files. Summarize any failed checks and the next command to run.
38
44
  `,
39
45
  },
40
46
  {
@@ -4,7 +4,7 @@ export const codexAdapter = {
4
4
  targetPath: 'AGENTS.md',
5
5
  instructions: `## KGraph Workflow
6
6
 
7
- Before exploring the repository, run \`kgraph context "<topic>"\` to load existing repo intelligence. The /kgraph skill handles the full automated workflow. Run \`kgraph scan\` and \`kgraph update\` manually when needed.
7
+ Before exploring the repository, run \`kgraph "<topic>"\` to refresh repo intelligence and load focused context. The /kgraph skill handles the full automated workflow. Run \`kgraph doctor\` when setup or generated maps look wrong. Run \`kgraph scan\`, \`kgraph update\`, and \`kgraph context\` manually only when you need one specific step.
8
8
  `,
9
9
  commandFiles: [
10
10
  {
@@ -19,12 +19,13 @@ description: Use KGraph persistent repo intelligence before broad repository exp
19
19
  Workflow:
20
20
 
21
21
  1. Infer the current topic from the user request.
22
- 2. Run \`kgraph context "<topic>"\` before broad repo exploration.
22
+ 2. Run \`kgraph "<topic>"\` before broad repo exploration. This refreshes maps, processes pending inbox notes, and returns focused context in one command.
23
23
  3. Use KGraph's returned files, symbols, relationships, and cognition as navigation hints.
24
- 4. **After completing the work**, write a Markdown note to \`.kgraph/inbox/<slug>.md\` **only if** you discovered something a future session would need to re-derive — a gotcha, constraint, non-obvious decision, or bug. Skip capture for read-only reviews or sessions where nothing new was found. When you do capture, immediately run \`kgraph update\`.
25
- 5. If you created, moved, deleted, or renamed files or symbols during this session, run \`kgraph scan\`. Skip it otherwise.
26
- 6. Run \`kgraph visualize\` when the user wants to inspect the dependency graph opens an interactive graph at http://localhost:4242 with PNG export.
27
- 7. Run \`kgraph history\` to review the timeline of past cognition sessions with git author attribution.
24
+ 4. Run \`kgraph doctor\` when setup, maps, inbox processing, or integrations look wrong.
25
+ 5. **After completing the work**, write a Markdown note to \`.kgraph/inbox/<slug>.md\` **only if** you discovered something a future session would need to re-derive — a gotcha, constraint, non-obvious decision, or bug. Skip capture for read-only reviews or sessions where nothing new was found. When you do capture, immediately run \`kgraph update\` or simply run \`kgraph\`.
26
+ 6. If you created, moved, deleted, or renamed files or symbols during this session, run \`kgraph\` or \`kgraph scan\`. Skip it otherwise.
27
+ 7. Run \`kgraph visualize\` when the user wants to inspect the dependency graph opens an interactive graph at http://localhost:4242 with PNG export.
28
+ 8. Run \`kgraph history\` to review the timeline of past cognition sessions with git author attribution.
28
29
 
29
30
  The inbox note must use this structure:
30
31
  \`\`\`markdown
@@ -4,9 +4,10 @@ export const copilotAdapter = {
4
4
  targetPath: '.github/copilot-instructions.md',
5
5
  instructions: `## KGraph Workflow
6
6
 
7
- 1. **Before exploring the repository**, run \`kgraph context "<topic>"\` to load existing repo intelligence. Use the returned files, symbols, and cognition before any broad exploration.
8
- 2. **After completing work**, write a Markdown note to \`.kgraph/inbox/<slug>.md\` **only if** you discovered something a future session would need to re-derive — a gotcha, architectural constraint, non-obvious decision, or bug. For routine or read-only sessions where nothing new was found, skip capture entirely. When you do capture, immediately run \`kgraph update\`.
9
- 3. If you created, moved, deleted, or renamed files or symbols, run \`kgraph scan\`.
7
+ 1. **Before exploring the repository**, run \`kgraph "<topic>"\` to refresh maps, process pending inbox notes, and load focused repo intelligence. Use the returned files, symbols, relationships, and cognition before any broad exploration.
8
+ 2. Run \`kgraph doctor\` when setup, maps, inbox processing, or integrations look wrong.
9
+ 3. **After completing work**, write a Markdown note to \`.kgraph/inbox/<slug>.md\` **only if** you discovered something a future session would need to re-derive — a gotcha, architectural constraint, non-obvious decision, or bug. For routine or read-only sessions where nothing new was found, skip capture entirely. When you do capture, immediately run \`kgraph update\` or simply run \`kgraph\`.
10
+ 4. If you created, moved, deleted, or renamed files or symbols, run \`kgraph\` or \`kgraph scan\`.
10
11
 
11
12
  The inbox note must use this structure:
12
13
  \`\`\`markdown
@@ -26,6 +27,16 @@ Any architectural or implementation decisions made.
26
27
  \`\`\`
27
28
  `,
28
29
  commandFiles: [
30
+ {
31
+ path: '.github/prompts/kgraph-doctor.prompt.md',
32
+ content: `---
33
+ description: Check KGraph workspace health and next actions
34
+ agent: agent
35
+ ---
36
+
37
+ Run \`kgraph doctor\` to check whether the workspace is initialized, maps exist, inbox notes are pending, and configured integrations point to real files. Summarize any failed checks and the next command to run.
38
+ `,
39
+ },
29
40
  {
30
41
  path: '.github/prompts/kgraph-scan.prompt.md',
31
42
  content: `---
@@ -9,9 +9,10 @@ alwaysApply: true
9
9
 
10
10
  ## KGraph Workflow
11
11
 
12
- - **Before exploring the repository**, run \`kgraph context "<topic>"\` to load existing repo intelligence. Use the returned files, symbols, and cognition before any broad exploration.
13
- - **After completing work**, write a Markdown note to \`.kgraph/inbox/<slug>.md\` (title, Key Files, Key Symbols, Decisions sections) **only if** you discovered something a future session would need to re-derive — a gotcha, constraint, non-obvious decision, or bug. Skip capture for read-only reviews or sessions where nothing new was found. When you do capture, immediately run \`kgraph update\`.
14
- - If you created, moved, deleted, or renamed files or symbols, run \`kgraph scan\`.
12
+ - **Before exploring the repository**, run \`kgraph "<topic>"\` to refresh maps, process pending inbox notes, and load focused repo intelligence. Use the returned files, symbols, relationships, and cognition before any broad exploration.
13
+ - Run \`kgraph doctor\` when setup, maps, inbox processing, or integrations look wrong.
14
+ - **After completing work**, write a Markdown note to \`.kgraph/inbox/<slug>.md\` (title, Key Files, Key Symbols, Decisions sections) **only if** you discovered something a future session would need to re-derive — a gotcha, constraint, non-obvious decision, or bug. Skip capture for read-only reviews or sessions where nothing new was found. When you do capture, immediately run \`kgraph update\` or simply run \`kgraph\`.
15
+ - If you created, moved, deleted, or renamed files or symbols, run \`kgraph\` or \`kgraph scan\`.
15
16
  - Run \`kgraph visualize\` to open the interactive dependency graph at http://localhost:4242 with PNG export.
16
17
  - Run \`kgraph history\` to review the timeline of past cognition sessions with git author attribution.
17
18
  `,
@@ -80,7 +80,7 @@ export async function scanRepository(rootPath, config, previous) {
80
80
  const extracted = extractSymbols(text, repoPath);
81
81
  symbols.push(...extracted.symbols);
82
82
  dependencies.push(...extracted.dependencies);
83
- relationships.push(...extracted.relationships);
83
+ relationships.push(...extracted.relationships.filter((relationship) => relationship.relationshipType !== 'import'));
84
84
  file.warnings.push(...extracted.warnings);
85
85
  }
86
86
  files.push(file);
@@ -100,9 +100,71 @@ export async function scanRepository(rootPath, config, previous) {
100
100
  });
101
101
  }
102
102
  }
103
+ resolveLocalDependencies(dependencies, files);
104
+ relationships.push(...buildImportRelationships(dependencies));
103
105
  relationships.push(...detectMovedFiles(previous?.files ?? [], files));
104
106
  return { files, symbols, dependencies, relationships, warnings };
105
107
  }
108
+ const SOURCE_EXTENSIONS = [
109
+ '.ts',
110
+ '.tsx',
111
+ '.js',
112
+ '.jsx',
113
+ '.mjs',
114
+ '.cjs',
115
+ '.mts',
116
+ '.cts',
117
+ '.py',
118
+ '.go',
119
+ '.rs',
120
+ '.java',
121
+ '.kt',
122
+ '.kts',
123
+ '.c',
124
+ '.h',
125
+ '.cpp',
126
+ '.cc',
127
+ '.cxx',
128
+ '.hpp',
129
+ '.hxx',
130
+ '.cs',
131
+ ];
132
+ function resolveLocalDependencies(dependencies, files) {
133
+ const filePaths = new Set(files.map((file) => file.path));
134
+ for (const dependency of dependencies) {
135
+ if (dependency.kind !== 'local') {
136
+ continue;
137
+ }
138
+ dependency.resolvedFile = resolveLocalDependencyPath(dependency.fromFile, dependency.specifier, filePaths);
139
+ }
140
+ }
141
+ function resolveLocalDependencyPath(fromFile, specifier, filePaths) {
142
+ if (!specifier.startsWith('.')) {
143
+ return undefined;
144
+ }
145
+ const base = path.posix.normalize(path.posix.join(path.posix.dirname(fromFile), specifier));
146
+ const candidates = path.posix.extname(base)
147
+ ? [base]
148
+ : [
149
+ ...SOURCE_EXTENSIONS.map((extension) => `${base}${extension}`),
150
+ ...SOURCE_EXTENSIONS.map((extension) => path.posix.join(base, `index${extension}`)),
151
+ ];
152
+ return candidates.find((candidate) => filePaths.has(candidate));
153
+ }
154
+ function buildImportRelationships(dependencies) {
155
+ return dependencies.map((dependency) => ({
156
+ sourceType: 'file',
157
+ sourceId: dependency.fromFile,
158
+ targetType: dependency.kind === 'local' ? 'file' : 'package',
159
+ targetId: dependency.resolvedFile ?? dependency.specifier,
160
+ relationshipType: 'import',
161
+ confidence: dependency.resolvedFile
162
+ ? 'high'
163
+ : dependency.kind === 'local'
164
+ ? 'low'
165
+ : 'medium',
166
+ }));
167
+ }
106
168
  function detectMovedFiles(previousFiles, currentFiles) {
107
169
  const currentPaths = new Set(currentFiles.map((file) => file.path));
108
170
  const previousByHash = new Map(previousFiles
@@ -26,7 +26,11 @@ export async function writeCognitionNote(workspace, note) {
26
26
  export async function writeDomainRecord(workspace, domain) {
27
27
  await mkdir(workspace.domainsPath, { recursive: true });
28
28
  const filePath = path.join(workspace.domainsPath, `${slugify(domain.name)}.md`);
29
- await writeFile(filePath, renderDomainRecord(domain), "utf8");
29
+ const existing = (await pathExists(filePath))
30
+ ? parseEmbeddedJson(await readFile(filePath, "utf8"))
31
+ : undefined;
32
+ const merged = existing ? mergeDomainRecords(existing, domain) : domain;
33
+ await writeFile(filePath, renderDomainRecord(merged), "utf8");
30
34
  return filePath;
31
35
  }
32
36
  export async function readCognitionNotes(workspace) {
@@ -41,9 +45,9 @@ export async function readCognitionNotes(workspace) {
41
45
  }
42
46
  const filePath = path.join(workspace.cognitionPath, entry.name);
43
47
  const raw = await readFile(filePath, "utf8");
44
- const encoded = raw.match(/```json\n([\s\S]*?)\n```/);
48
+ const encoded = parseEmbeddedJson(raw);
45
49
  if (encoded) {
46
- notes.push(JSON.parse(encoded[1]));
50
+ notes.push(encoded);
47
51
  }
48
52
  }
49
53
  return notes;
@@ -59,13 +63,35 @@ export async function readDomainRecords(workspace) {
59
63
  continue;
60
64
  }
61
65
  const raw = await readFile(path.join(workspace.domainsPath, entry.name), "utf8");
62
- const encoded = raw.match(/```json\n([\s\S]*?)\n```/);
66
+ const encoded = parseEmbeddedJson(raw);
63
67
  if (encoded) {
64
- domains.push(JSON.parse(encoded[1]));
68
+ domains.push(encoded);
65
69
  }
66
70
  }
67
71
  return domains;
68
72
  }
73
+ function parseEmbeddedJson(raw) {
74
+ const encoded = raw.match(/```json\n([\s\S]*?)\n```/);
75
+ return encoded ? JSON.parse(encoded[1]) : undefined;
76
+ }
77
+ function mergeDomainRecords(existing, next) {
78
+ return {
79
+ ...existing,
80
+ ...next,
81
+ description: next.description ?? existing.description,
82
+ pathHints: unique([...existing.pathHints, ...next.pathHints]),
83
+ tags: unique([...existing.tags, ...next.tags]),
84
+ files: unique([...existing.files, ...next.files]),
85
+ symbols: unique([...existing.symbols, ...next.symbols]),
86
+ cognitionNotes: unique([
87
+ ...existing.cognitionNotes,
88
+ ...next.cognitionNotes,
89
+ ]),
90
+ };
91
+ }
92
+ function unique(items) {
93
+ return [...new Set(items)];
94
+ }
69
95
  export function slugify(value) {
70
96
  return value
71
97
  .trim()
@@ -13,4 +13,4 @@ export interface GraphData {
13
13
  generatedAt: string;
14
14
  };
15
15
  }
16
- export declare function buildGraph(fileMap: FileMap, symbolMap: SymbolMap, dependencyMap: DependencyMap, _relationshipMap: RelationshipMap, cognitionNotes: CognitionNote[]): GraphData;
16
+ export declare function buildGraph(fileMap: FileMap, symbolMap: SymbolMap, dependencyMap: DependencyMap, relationshipMap: RelationshipMap, cognitionNotes: CognitionNote[]): GraphData;
@@ -13,10 +13,19 @@ const STATUS_COLORS = {
13
13
  stale: '#ef4444',
14
14
  unresolved: '#6b7280',
15
15
  };
16
- export function buildGraph(fileMap, symbolMap, dependencyMap, _relationshipMap, cognitionNotes) {
16
+ const SYMBOL_COLORS = {
17
+ function: '#22c55e',
18
+ class: '#a855f7',
19
+ method: '#14b8a6',
20
+ export: '#f97316',
21
+ import: '#64748b',
22
+ };
23
+ export function buildGraph(fileMap, symbolMap, dependencyMap, relationshipMap, cognitionNotes) {
17
24
  const elements = [];
18
25
  const edgeIds = new Set();
26
+ const nodeIds = new Set();
19
27
  for (const file of fileMap.files) {
28
+ nodeIds.add(file.id);
20
29
  elements.push({
21
30
  data: {
22
31
  id: file.id,
@@ -31,6 +40,21 @@ export function buildGraph(fileMap, symbolMap, dependencyMap, _relationshipMap,
31
40
  classes: `file ${file.language}`,
32
41
  });
33
42
  }
43
+ for (const symbol of symbolMap.symbols) {
44
+ nodeIds.add(symbol.id);
45
+ elements.push({
46
+ data: {
47
+ id: symbol.id,
48
+ label: symbol.name,
49
+ path: symbol.filePath,
50
+ kind: symbol.kind,
51
+ parentName: symbol.parentName ?? '',
52
+ type: 'symbol',
53
+ color: SYMBOL_COLORS[symbol.kind] ?? '#94a3b8',
54
+ },
55
+ classes: `symbol ${symbol.kind}`,
56
+ });
57
+ }
34
58
  for (const note of cognitionNotes) {
35
59
  const id = `cognition-${note.id}`;
36
60
  elements.push({
@@ -88,6 +112,27 @@ export function buildGraph(fileMap, symbolMap, dependencyMap, _relationshipMap,
88
112
  }
89
113
  }
90
114
  }
115
+ for (const relationship of relationshipMap.relationships) {
116
+ if (!nodeIds.has(relationship.sourceId) || !nodeIds.has(relationship.targetId)) {
117
+ continue;
118
+ }
119
+ const edgeId = `rel-${relationship.relationshipType}-${relationship.sourceId}-${relationship.targetId}`;
120
+ if (edgeIds.has(edgeId)) {
121
+ continue;
122
+ }
123
+ edgeIds.add(edgeId);
124
+ elements.push({
125
+ data: {
126
+ id: edgeId,
127
+ source: relationship.sourceId,
128
+ target: relationship.targetId,
129
+ type: relationship.relationshipType,
130
+ confidence: relationship.confidence,
131
+ label: relationship.relationshipType,
132
+ },
133
+ classes: `relationship ${relationship.relationshipType}`,
134
+ });
135
+ }
91
136
  return {
92
137
  elements,
93
138
  meta: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kentwynn/kgraph",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Persistent repo intelligence for AI coding assistants.",
5
5
  "type": "module",
6
6
  "bin": {