@kentwynn/kgraph 0.1.6 → 0.1.8

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,148 +1,220 @@
1
1
  # KGraph
2
2
 
3
- Persistent repository intelligence for AI coding tools.
3
+ > **Persistent repository intelligence for AI coding tools.**
4
+ > Stop paying the context tax on every session. KGraph gives your AI assistant a memory.
4
5
 
5
- KGraph is a local-first CLI that builds an inspectable knowledge layer for a codebase. It helps tools like Codex, GitHub Copilot, Cursor, and Claude Code reuse repository structure, workflow knowledge, debugging history, and architecture decisions instead of rediscovering them in every chat.
6
+ ---
6
7
 
7
- ## Why It Matters
8
+ ## The Problem
8
9
 
9
- AI coding sessions spend a large part of their budget finding context: reading files, tracing imports, locating the right functions, and re-learning decisions that were already discovered in previous work.
10
+ Every AI coding session starts with the same expensive ritual:
10
11
 
11
- KGraph turns that repeated exploration into durable repository intelligence:
12
-
13
- ```text
14
- AI chat or developer note
15
- -> KGraph cognition inbox
16
- -> structured repo knowledge
17
- -> compact context for future AI sessions
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..."
18
17
  ```
19
18
 
20
- The result is faster navigation, lower token waste, and more consistent understanding across coding sessions.
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.
21
20
 
22
- ## Install
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.
23
22
 
24
- Run the latest published package:
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.
25
28
 
26
- ```bash
27
- npx @kentwynn/kgraph@latest init
28
29
  ```
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"
29
34
 
30
- Run a specific stable version:
35
+ Re-learns same architecture Recalls prior decisions instantly
36
+ every single session from cognition store
31
37
 
32
- ```bash
33
- npx @kentwynn/kgraph@0.1.0 init
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
34
42
  ```
35
43
 
36
- Install globally if you use KGraph often:
44
+ ---
45
+
46
+ ## Token Savings — What This Looks Like in Practice
47
+
48
+ A typical `kgraph context` response for a focused topic:
37
49
 
38
- ```bash
39
- npm install -g @kentwynn/kgraph@latest
40
- kgraph --version
41
50
  ```
51
+ topic: auth token refresh
42
52
 
43
- ## Quick Start
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)
44
57
 
45
- Initialize KGraph in a repository and connect your AI tools:
58
+ key relationships:
59
+ POST /api/auth → createSession → writes JWT to cookie
60
+ middleware → validateToken → redirects on expiry
46
61
 
47
- ```bash
48
- kgraph init --integrations codex,copilot,cursor
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
49
66
  ```
50
67
 
51
- Scan the codebase:
68
+ **~280 tokens.** The equivalent file-by-file exploration: **4,200+ tokens.**
69
+ That's a **15x reduction** in context cost for navigation alone.
70
+
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.
72
+
73
+ ---
74
+
75
+ ## How It Works
52
76
 
53
- ```bash
54
- kgraph scan
55
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
+ └─────────────────────────────────────────────────────────────┘
102
+ ```
103
+
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.
56
105
 
57
- Ask for compact context before working on an area:
106
+ ---
107
+
108
+ ## Install
58
109
 
59
110
  ```bash
60
- kgraph context "auth token refresh"
111
+ npm install -g @kentwynn/kgraph@latest
112
+ kgraph --version
61
113
  ```
62
114
 
63
- Process saved chat notes and debugging conclusions:
115
+ Or run without installing:
64
116
 
65
117
  ```bash
66
- kgraph update
118
+ npx @kentwynn/kgraph@latest init
67
119
  ```
68
120
 
69
- ## CLI
121
+ ---
122
+
123
+ ## Quick Start
70
124
 
71
125
  ```bash
72
- kgraph init
73
- kgraph init --integrations codex,cursor
74
- kgraph integrate list
75
- kgraph integrate add codex copilot cursor claude-code
76
- kgraph integrate remove cursor
126
+ # 1. Initialize and connect your AI tools
127
+ kgraph init --integrations codex,copilot,cursor,claude-code
128
+
129
+ # 2. Scan the codebase
77
130
  kgraph scan
78
- kgraph update
131
+
132
+ # 3. Ask for context before exploring (your AI does this automatically)
79
133
  kgraph context "auth token refresh"
80
- kgraph context "auth token refresh" --json
134
+
135
+ # 4. After an AI session, save what was learned
136
+ kgraph update
81
137
  ```
82
138
 
83
- ## AI Tool Integrations
139
+ That's the entire loop. From session 2 onward, your AI tool loads existing intelligence before touching a single file.
84
140
 
85
- KGraph writes local instruction files and command/prompt packs so AI tools can use the repository knowledge layer during normal coding chats.
141
+ ---
86
142
 
87
- | Integration | Always-on guidance | KGraph command assets |
88
- | --- | --- | --- |
89
- | Codex | `AGENTS.md` | `.agents/skills/kgraph/SKILL.md` |
90
- | GitHub Copilot | `.github/copilot-instructions.md` | `.github/prompts/kgraph.prompt.md` |
91
- | Cursor | `.cursor/rules/kgraph.mdc` | Built into the KGraph Cursor rule |
92
- | Claude Code | `CLAUDE.md` | `.claude/commands/kgraph.md` |
143
+ ## AI Tool Integrations
93
144
 
94
- Example:
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.
95
146
 
96
147
  ```bash
97
148
  kgraph integrate add codex copilot cursor claude-code
98
149
  kgraph integrate list
99
150
  ```
100
151
 
101
- This gives each supported tool one reusable KGraph entry point similar to a Spec Kit-style command:
152
+ | Tool | Always-on instruction | Skills / commands |
153
+ | -------------- | --------------------------------- | ------------------------------------------------------------------- |
154
+ | GitHub Copilot | `.github/copilot-instructions.md` | `/kgraph-scan` · `/kgraph-update` · `/kgraph-visualize` |
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` |
102
158
 
103
- - KGraph context: query `kgraph context "<topic>"` before broad repo exploration
104
- - KGraph update: save durable chat/debugging/workflow discoveries to `.kgraph/inbox/`, then run `kgraph update`
105
- - KGraph scan: run `kgraph scan` after refactors, file moves, renamed functions, or dependency changes
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.
106
160
 
107
- The exact invocation depends on the host tool. Copilot uses one prompt file, Codex uses one skill, Cursor uses one rule, and Claude Code uses one command file. Scan and update are workflows inside that single KGraph entry point, not separate duplicated commands.
161
+ Existing user content in `AGENTS.md`, `CLAUDE.md`, etc. is preserved KGraph manages only its own clearly-marked blocks.
108
162
 
109
- KGraph-managed instruction blocks preserve existing user-authored content.
163
+ ---
110
164
 
111
- ## Features
165
+ ## CLI Reference
112
166
 
113
- - Local `.kgraph/` workspace for repository intelligence
114
- - JavaScript and TypeScript file, import, export, function, class, and method maps
115
- - Deterministic relationship maps between files and symbols
116
- - Markdown cognition inbox for AI chat summaries, decisions, gotchas, and debugging notes
117
- - Compact context output for AI assistants and scripts
118
- - JSON output for tool-friendly context retrieval
119
- - Integration management and command packs for Codex, Copilot, Cursor, and Claude Code
120
- - Stale-reference handling when code changes over time
121
- - Local-first storage with human-readable JSON, YAML, and Markdown
167
+ ```bash
168
+ kgraph init # initialize .kgraph/ workspace
169
+ kgraph init --integrations codex,copilot # init + configure integrations
122
170
 
123
- ## How KGraph Grows
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
124
175
 
125
- KGraph is designed to improve as the project changes:
176
+ kgraph integrate list # show integration status
177
+ kgraph integrate add codex copilot cursor # add integrations
178
+ kgraph integrate remove cursor # remove an integration
126
179
 
127
- ```text
128
- kgraph scan
129
- refreshes current structure
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
130
183
 
131
- AI chat or developer note
132
- captures useful reasoning in .kgraph/inbox/
184
+ kgraph history # timeline of processed cognition sessions
185
+ kgraph history --last 10 # show last 10 entries
186
+ kgraph history --json # machine-readable output
187
+ ```
133
188
 
134
- kgraph update
135
- converts notes into durable cognition
189
+ ---
136
190
 
137
- kgraph context "<topic>"
138
- returns focused repository context for future work
139
- ```
191
+ ## What KGraph Tracks
192
+
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 |
200
+
201
+ Supported languages: TypeScript, JavaScript, Python, Go, Rust, Java, Kotlin, C/C++, C#, Ruby, PHP, Swift, and 30+ more — detected by file extension, no configuration needed.
202
+
203
+ ---
204
+
205
+ ## Local-First, Zero Dependencies
140
206
 
141
- This creates a feedback loop where normal development and AI-assisted debugging gradually improve the repository knowledge map.
207
+ KGraph requires nothing beyond Node.js 20:
142
208
 
143
- ## Local-First
209
+ - No accounts or API keys
210
+ - No embeddings or vector databases
211
+ - No cloud services or telemetry
212
+ - No background daemons
213
+ - No model provider
144
214
 
145
- KGraph stores project intelligence in local files inside `.kgraph/`. The MVP does not require accounts, telemetry, hosted services, databases, model providers, embeddings, or background daemons.
215
+ All data lives in `.kgraph/` as human-readable JSON, YAML, and Markdown. Commit it, diff it, inspect it anytime.
216
+
217
+ ---
146
218
 
147
219
  ## Development
148
220
 
@@ -150,26 +222,62 @@ KGraph stores project intelligence in local files inside `.kgraph/`. The MVP doe
150
222
  npm install
151
223
  npm run build
152
224
  npm test
225
+ ```
226
+
227
+ Test a command without installing:
228
+
229
+ ```bash
153
230
  npm run kgraph -- init --integrations codex,cursor
231
+ npm run kgraph -- context "auth token refresh"
154
232
  ```
155
233
 
156
- ## Release
234
+ Install the local build globally to test the `kgraph` binary end-to-end:
157
235
 
158
- CI runs build, tests, package checks, and generated-artifact hygiene on pushes and pull requests.
236
+ ```bash
237
+ npm install -g .
238
+ kgraph --version
239
+ kgraph init --integrations codex,copilot
240
+ ```
159
241
 
160
- Releases are tag-driven. Bump the package version, push the commit, then push the matching tag:
242
+ ---
243
+
244
+ ## Release
245
+
246
+ Releases are tag-driven. Bump the version, push the commit and tag:
161
247
 
162
248
  ```bash
163
249
  npm version patch
164
250
  git push origin main --follow-tags
165
251
  ```
166
252
 
167
- The release workflow verifies that the tag matches `package.json`, checks that the npm version has not already been published, publishes the package to npm, creates a GitHub Release, and attaches the packed tarball. Manual workflow runs package the project for inspection but do not publish to npm.
253
+ CI verifies the tag matches `package.json`, checks the version is unpublished, publishes to npm, creates a GitHub Release, and attaches the tarball.
254
+
255
+ ---
256
+
257
+ ## Visualization
258
+
259
+ ```bash
260
+ kgraph visualize
261
+ ```
262
+
263
+ 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.
264
+
265
+ **What you see:**
266
+
267
+ - File nodes colored by language (TypeScript, JavaScript, Markdown, YAML, …)
268
+ - Cognition notes as diamonds, colored by health (green = current, amber = mixed, red = stale)
269
+ - Import edges showing real dependency flow
270
+ - Dashed blue edges linking cognition notes to the files they describe
271
+ - Click any node for a metadata panel (path, size, domain, related symbols)
272
+ - Toggle cognition overlay on/off
273
+ - Switch layout: Hierarchical (default), Force-directed, Grid, Concentric
274
+ - **Export PNG** — 2× resolution, dark background, ready for reports or slides
275
+
276
+ ---
168
277
 
169
278
  ## Roadmap
170
279
 
171
- - richer language scanners
172
- - better cognition extraction
173
- - graph visualization
174
- - Git-aware history and rename detection
175
- - optional editor and MCP integrations
280
+ - Git-aware history and rename tracking
281
+ - richer language scanners (deeper AST, cross-file type resolution)
282
+ - MCP server for editor tool-call access
283
+ - team-shared cognition via committed `.kgraph/`
@@ -0,0 +1,16 @@
1
+ import type { Command } from 'commander';
2
+ export interface HistoryEntry {
3
+ timestamp: Date;
4
+ filename: string;
5
+ title: string;
6
+ author?: string;
7
+ }
8
+ export declare function registerHistoryCommand(program: Command): void;
9
+ export declare function readHistoryEntries(processedPath: string, rootPath: string): Promise<HistoryEntry[]>;
10
+ /**
11
+ * Parses the UTC timestamp embedded in a processed interaction filename.
12
+ * Filename format: 2026-05-09T09-36-06-247Z-slug.md
13
+ * (colons and dot replaced by dashes when written to disk)
14
+ */
15
+ export declare function parseTimestampFromFilename(filename: string): Date | undefined;
16
+ export declare function renderHistory(entries: HistoryEntry[], useColor?: boolean): string;
@@ -0,0 +1,124 @@
1
+ import { Chalk } from 'chalk';
2
+ import { execFile } from 'node:child_process';
3
+ import { readFile, readdir } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { promisify } from 'node:util';
6
+ import { assertWorkspace, pathExists } from '../../storage/kgraph-paths.js';
7
+ import { KGraphError, runCommand } from '../errors.js';
8
+ const execFileAsync = promisify(execFile);
9
+ export function registerHistoryCommand(program) {
10
+ program
11
+ .command('history')
12
+ .description('Show a timeline of processed cognition sessions')
13
+ .option('--last <n>', 'Show only the last N entries')
14
+ .option('--json', 'Print JSON output')
15
+ .action((options) => runCommand(async () => {
16
+ const workspace = await assertWorkspace(process.cwd());
17
+ const entries = await readHistoryEntries(workspace.processedInteractionsPath, workspace.rootPath);
18
+ const limit = options.last !== undefined ? parseInt(options.last, 10) : 0;
19
+ if (options.last !== undefined && (isNaN(limit) || limit < 1)) {
20
+ throw new KGraphError('--last must be a positive integer.');
21
+ }
22
+ const shown = limit > 0 ? entries.slice(-limit) : entries;
23
+ if (options.json) {
24
+ console.log(JSON.stringify(shown.map((e) => ({
25
+ timestamp: e.timestamp.toISOString(),
26
+ filename: e.filename,
27
+ title: e.title,
28
+ ...(e.author !== undefined ? { author: e.author } : {}),
29
+ })), null, 2));
30
+ }
31
+ else {
32
+ console.log(renderHistory(shown));
33
+ }
34
+ }));
35
+ }
36
+ export async function readHistoryEntries(processedPath, rootPath) {
37
+ if (!(await pathExists(processedPath))) {
38
+ return [];
39
+ }
40
+ const dirents = await readdir(processedPath, { withFileTypes: true });
41
+ const filenames = dirents
42
+ .filter((e) => e.isFile() && e.name.endsWith('.md'))
43
+ .map((e) => e.name)
44
+ .sort(); // ISO-prefixed filenames sort chronologically
45
+ const entries = [];
46
+ for (const filename of filenames) {
47
+ const timestamp = parseTimestampFromFilename(filename);
48
+ if (!timestamp)
49
+ continue;
50
+ const filePath = path.join(processedPath, filename);
51
+ const content = await readFile(filePath, 'utf8');
52
+ const title = content.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? filename;
53
+ const relPath = path.relative(rootPath, filePath);
54
+ const author = await getGitAuthor(rootPath, relPath);
55
+ entries.push({ timestamp, filename, title, author });
56
+ }
57
+ return entries;
58
+ }
59
+ /**
60
+ * Parses the UTC timestamp embedded in a processed interaction filename.
61
+ * Filename format: 2026-05-09T09-36-06-247Z-slug.md
62
+ * (colons and dot replaced by dashes when written to disk)
63
+ */
64
+ export function parseTimestampFromFilename(filename) {
65
+ const match = filename.match(/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z-/);
66
+ if (!match)
67
+ return undefined;
68
+ const iso = `${match[1]}T${match[2]}:${match[3]}:${match[4]}.${match[5]}Z`;
69
+ const d = new Date(iso);
70
+ return isNaN(d.getTime()) ? undefined : d;
71
+ }
72
+ export function renderHistory(entries, useColor = supportsColor()) {
73
+ const chalk = new Chalk({ level: useColor ? 3 : 0 });
74
+ if (entries.length === 0) {
75
+ return ('\n' +
76
+ chalk.dim(' No processed cognition notes found. Write Markdown notes to .kgraph/inbox/ and run `kgraph update`.') +
77
+ '\n');
78
+ }
79
+ const header = ` ${chalk.bold('KGraph History')} ${chalk.dim(`· ${entries.length} ${entries.length === 1 ? 'entry' : 'entries'}`)}`;
80
+ const lines = ['', header, ''];
81
+ const titleWidth = Math.max(...entries.map((e) => e.title.length));
82
+ for (const entry of entries) {
83
+ const when = chalk.dim(`${formatDate(entry.timestamp)} ${formatTime(entry.timestamp)}`);
84
+ const title = chalk.bold(entry.title.padEnd(titleWidth));
85
+ const who = entry.author !== undefined
86
+ ? chalk.cyan(`by ${entry.author}`)
87
+ : chalk.dim('(uncommitted)');
88
+ lines.push(` ${when} ${title} ${who}`);
89
+ }
90
+ lines.push('');
91
+ return lines.join('\n');
92
+ }
93
+ async function getGitAuthor(rootPath, relFilePath) {
94
+ try {
95
+ const { stdout } = await execFileAsync('git', ['log', '--format=%an', '-1', '--', relFilePath], { cwd: rootPath, timeout: 3000 });
96
+ return stdout.trim() || undefined;
97
+ }
98
+ catch {
99
+ return undefined;
100
+ }
101
+ }
102
+ function formatDate(d) {
103
+ const months = [
104
+ 'Jan',
105
+ 'Feb',
106
+ 'Mar',
107
+ 'Apr',
108
+ 'May',
109
+ 'Jun',
110
+ 'Jul',
111
+ 'Aug',
112
+ 'Sep',
113
+ 'Oct',
114
+ 'Nov',
115
+ 'Dec',
116
+ ];
117
+ return `${months[d.getUTCMonth()]} ${String(d.getUTCDate()).padStart(2, ' ')}, ${d.getUTCFullYear()}`;
118
+ }
119
+ function formatTime(d) {
120
+ return `${String(d.getUTCHours()).padStart(2, '0')}:${String(d.getUTCMinutes()).padStart(2, '0')}`;
121
+ }
122
+ function supportsColor() {
123
+ return Boolean(process.stdout.isTTY) && process.env.NO_COLOR === undefined;
124
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerVisualizeCommand(program: Command): void;
@@ -0,0 +1,73 @@
1
+ import { exec } from 'node:child_process';
2
+ import { createServer } from 'node:http';
3
+ import { loadConfig } from '../../config/config.js';
4
+ import { readCognitionNotes } from '../../storage/cognition-store.js';
5
+ import { assertWorkspace } from '../../storage/kgraph-paths.js';
6
+ import { mapsExist, readMaps } from '../../storage/map-store.js';
7
+ import { buildGraph } from '../../visualization/graph-builder.js';
8
+ import { renderHtml } from '../../visualization/html-template.js';
9
+ import { KGraphError, runCommand } from '../errors.js';
10
+ export function registerVisualizeCommand(program) {
11
+ program
12
+ .command('visualize')
13
+ .description('Start a local server and open the interactive dependency graph in browser')
14
+ .option('--port <port>', 'Port to listen on', '4242')
15
+ .option('--no-open', 'Print URL without opening browser')
16
+ .action((options) => runCommand(async () => {
17
+ const port = parseInt(options.port, 10);
18
+ if (isNaN(port) || port < 1 || port > 65535) {
19
+ throw new KGraphError('Invalid port. Use a value between 1 and 65535.');
20
+ }
21
+ const workspace = await assertWorkspace(process.cwd());
22
+ if (!(await mapsExist(workspace))) {
23
+ throw new KGraphError('KGraph maps are missing. Run `kgraph scan` first.');
24
+ }
25
+ const [maps, cognition] = await Promise.all([
26
+ readMaps(workspace),
27
+ readCognitionNotes(workspace),
28
+ ]);
29
+ await loadConfig(workspace); // ensure workspace is valid
30
+ const graphData = buildGraph(maps.fileMap, maps.symbolMap, maps.dependencyMap, maps.relationshipMap, cognition);
31
+ const html = renderHtml(graphData, workspace.rootPath);
32
+ await serveGraph(html, port, options.open);
33
+ }));
34
+ }
35
+ async function serveGraph(html, port, autoOpen) {
36
+ return new Promise((resolve, reject) => {
37
+ const server = createServer((_req, res) => {
38
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
39
+ res.end(html);
40
+ });
41
+ server.on('error', (err) => {
42
+ if (err.code === 'EADDRINUSE') {
43
+ reject(new KGraphError(`Port ${port} is already in use. Try --port <number>.`));
44
+ }
45
+ else {
46
+ reject(err);
47
+ }
48
+ });
49
+ server.listen(port, '127.0.0.1', () => {
50
+ const url = `http://localhost:${port}`;
51
+ console.log(`\nKGraph visualization at ${url}\n`);
52
+ console.log('Press Ctrl+C to stop.');
53
+ if (autoOpen) {
54
+ openBrowser(url);
55
+ }
56
+ });
57
+ process.on('SIGINT', () => {
58
+ server.close(() => {
59
+ console.log('\nVisualization server stopped.');
60
+ resolve();
61
+ process.exit(0);
62
+ });
63
+ });
64
+ });
65
+ }
66
+ function openBrowser(url) {
67
+ const cmd = process.platform === 'darwin'
68
+ ? `open "${url}"`
69
+ : process.platform === 'win32'
70
+ ? `start "" "${url}"`
71
+ : `xdg-open "${url}"`;
72
+ exec(cmd);
73
+ }