@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/dist/cli/help.js CHANGED
@@ -1,58 +1,60 @@
1
- import { Chalk } from "chalk";
2
- import figlet from "figlet";
1
+ import { Chalk } from 'chalk';
2
+ import figlet from 'figlet';
3
3
  export function renderRootHelp(useColor = supportsColor()) {
4
4
  const theme = new Chalk({ level: useColor ? 3 : 0 });
5
5
  const command = (name, description) => ` ${theme.green(name.padEnd(30))} ${description}`;
6
6
  const logo = renderLogo();
7
7
  return [
8
- "",
9
- theme.hex("#7dd3fc").bold(logo),
10
- "",
11
- ` ${theme.bold("KGraph")} ${theme.dim("Persistent repo intelligence for AI coding tools")}`,
12
- "",
13
- ` ${theme.hex("#c084fc")("Build a local knowledge layer that helps Codex, Copilot, Cursor,")}`,
14
- ` ${theme.hex("#c084fc")("and Claude Code reuse repo structure, decisions, and debugging history.")}`,
15
- "",
16
- theme.bold("Usage"),
17
- " kgraph <command> [options]",
18
- "",
19
- theme.bold("Start"),
20
- command("init", "Create .kgraph/ workspace"),
21
- command("init --integrations codex,cursor", "Initialize and connect AI tools"),
22
- "",
23
- 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"),
27
- "",
28
- theme.bold("Integrations"),
29
- command("integrate list", "Show configured AI tool integrations"),
30
- command("integrate add codex copilot", "Write KGraph instructions for AI tools"),
31
- command("integrate remove cursor", "Remove KGraph-managed instruction blocks"),
32
- "",
33
- theme.bold("Options"),
34
- command("-V, --version", "Show version"),
35
- command("-h, --help", "Show this help"),
36
- "",
37
- `${theme.yellow("Examples")}`,
38
- " kgraph init --integrations codex,copilot,cursor",
39
- " kgraph scan",
40
- " kgraph context \"blog admin token usage\" --json",
41
- "",
42
- theme.dim("Docs: https://github.com/kentwynn/KGraph#readme"),
43
- ""
44
- ].join("\n");
8
+ '',
9
+ theme.hex('#7dd3fc').bold(logo),
10
+ '',
11
+ ` ${theme.bold('KGraph')} ${theme.dim('Persistent repo intelligence for AI coding tools')}`,
12
+ '',
13
+ ` ${theme.hex('#c084fc')('Build a local knowledge layer that helps Codex, Copilot, Cursor,')}`,
14
+ ` ${theme.hex('#c084fc')('and Claude Code reuse repo structure, decisions, and debugging history.')}`,
15
+ '',
16
+ theme.bold('Usage'),
17
+ ' kgraph <command> [options]',
18
+ '',
19
+ theme.bold('Start'),
20
+ command('init', 'Create .kgraph/ workspace'),
21
+ command('init --integrations codex,cursor', 'Initialize and connect AI tools'),
22
+ '',
23
+ 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'),
27
+ command('visualize', 'Interactive dependency graph at http://localhost:4242'),
28
+ command('history', 'Timeline of processed cognition sessions'),
29
+ '',
30
+ theme.bold('Integrations'),
31
+ command('integrate list', 'Show configured AI tool integrations'),
32
+ command('integrate add codex copilot', 'Write KGraph instructions for AI tools'),
33
+ command('integrate remove cursor', 'Remove KGraph-managed instruction blocks'),
34
+ '',
35
+ theme.bold('Options'),
36
+ command('-V, --version', 'Show version'),
37
+ command('-h, --help', 'Show this help'),
38
+ '',
39
+ `${theme.yellow('Examples')}`,
40
+ ' kgraph init --integrations codex,copilot,cursor',
41
+ ' kgraph scan',
42
+ ' kgraph context "blog admin token usage" --json',
43
+ '',
44
+ theme.dim('Docs: https://github.com/kentwynn/KGraph#readme'),
45
+ '',
46
+ ].join('\n');
45
47
  }
46
48
  function renderLogo() {
47
49
  try {
48
- return figlet.textSync("KGraph", {
49
- font: "ANSI Shadow",
50
- horizontalLayout: "default",
51
- verticalLayout: "default"
50
+ return figlet.textSync('KGraph', {
51
+ font: 'ANSI Shadow',
52
+ horizontalLayout: 'default',
53
+ verticalLayout: 'default',
52
54
  });
53
55
  }
54
56
  catch {
55
- return "KGraph";
57
+ return 'KGraph';
56
58
  }
57
59
  }
58
60
  function supportsColor() {
package/dist/cli/index.js CHANGED
@@ -4,10 +4,12 @@ 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 { registerHistoryCommand } from './commands/history.js';
7
8
  import { registerInitCommand } from './commands/init.js';
8
9
  import { registerIntegrateCommand } from './commands/integrate.js';
9
10
  import { registerScanCommand } from './commands/scan.js';
10
11
  import { registerUpdateCommand } from './commands/update.js';
12
+ import { registerVisualizeCommand } from './commands/visualize.js';
11
13
  import { renderRootHelp } from './help.js';
12
14
  const require = createRequire(import.meta.url);
13
15
  const { version } = require('../../package.json');
@@ -31,6 +33,8 @@ export function createProgram() {
31
33
  registerUpdateCommand(program);
32
34
  registerContextCommand(program);
33
35
  registerIntegrateCommand(program);
36
+ registerVisualizeCommand(program);
37
+ registerHistoryCommand(program);
34
38
  return program;
35
39
  }
36
40
  if (isCliEntrypoint()) {
@@ -1,2 +1,2 @@
1
- import type { ParsedCognitionNote } from "../types/cognition.js";
1
+ import type { ParsedCognitionNote } from '../types/cognition.js';
2
2
  export declare function parseMarkdownNote(markdown: string): ParsedCognitionNote;
@@ -1,37 +1,36 @@
1
- import YAML from "yaml";
2
- const PATH_REF = /(?:^|\s)([\w./-]+\.(?:ts|tsx|js|jsx|json|md|yaml|yml))(?:\s|$|[),.;])/g;
3
- const SYMBOL_REF = /`?([A-Za-z_$][\w$]{2,})`?/g;
1
+ import YAML from 'yaml';
2
+ const PATH_REF = /(?:^|\s|`?)([\w./-]+\.(?:ts|tsx|js|jsx|json|md|yaml|yml))(?:\s|$|[),.;`])/g;
4
3
  export function parseMarkdownNote(markdown) {
5
4
  const warnings = [];
6
5
  const { frontmatter, body } = splitFrontmatter(markdown, warnings);
7
6
  const sections = parseSections(body);
8
- const frontmatterTitle = typeof frontmatter.title === "string" ? frontmatter.title : undefined;
9
- const title = extractTitle(body) ?? frontmatterTitle ?? "Untitled Cognition Note";
10
- const combined = Object.values(sections).join("\n");
7
+ const frontmatterTitle = typeof frontmatter.title === 'string' ? frontmatter.title : undefined;
8
+ const title = extractTitle(body) ?? frontmatterTitle ?? 'Untitled Cognition Note';
9
+ const combined = Object.values(sections).join('\n');
11
10
  return {
12
11
  title,
13
- domain: typeof frontmatter.domain === "string" ? frontmatter.domain : undefined,
12
+ domain: typeof frontmatter.domain === 'string' ? frontmatter.domain : undefined,
14
13
  tags: Array.isArray(frontmatter.tags) ? frontmatter.tags.map(String) : [],
15
14
  summary: sections.Summary,
16
15
  sections,
17
16
  relatedFiles: unique(extractMatches(combined, PATH_REF)),
18
17
  relatedSymbols: unique(extractSymbolRefs(combined)),
19
- warnings
18
+ warnings,
20
19
  };
21
20
  }
22
21
  function splitFrontmatter(markdown, warnings) {
23
- if (!markdown.startsWith("---\n")) {
22
+ if (!markdown.startsWith('---\n')) {
24
23
  return { frontmatter: {}, body: markdown };
25
24
  }
26
- const end = markdown.indexOf("\n---", 4);
25
+ const end = markdown.indexOf('\n---', 4);
27
26
  if (end === -1) {
28
- warnings.push("Frontmatter start found without closing delimiter.");
27
+ warnings.push('Frontmatter start found without closing delimiter.');
29
28
  return { frontmatter: {}, body: markdown };
30
29
  }
31
30
  try {
32
31
  return {
33
32
  frontmatter: (YAML.parse(markdown.slice(4, end)) ?? {}),
34
- body: markdown.slice(end + 4)
33
+ body: markdown.slice(end + 4),
35
34
  };
36
35
  }
37
36
  catch (error) {
@@ -53,7 +52,7 @@ function parseSections(body) {
53
52
  sections[heading] = body.slice(start, end).trim();
54
53
  }
55
54
  if (Object.keys(sections).length === 0) {
56
- sections.Summary = body.replace(/^#\s+.+$/m, "").trim();
55
+ sections.Summary = body.replace(/^#\s+.+$/m, '').trim();
57
56
  }
58
57
  return sections;
59
58
  }
@@ -61,10 +60,22 @@ function extractMatches(text, regex) {
61
60
  return [...text.matchAll(regex)].map((match) => match[1]);
62
61
  }
63
62
  function extractSymbolRefs(text) {
64
- const stopwords = new Set(["Summary", "Related", "Files", "Functions", "Decisions", "Debugging", "Conclusions"]);
65
- return [...text.matchAll(SYMBOL_REF)]
66
- .map((match) => match[1])
67
- .filter((item) => !stopwords.has(item) && !item.includes("."));
63
+ const stopwords = new Set([
64
+ 'Summary',
65
+ 'Related',
66
+ 'Files',
67
+ 'Functions',
68
+ 'Decisions',
69
+ 'Debugging',
70
+ 'Conclusions',
71
+ ]);
72
+ // Backtick-quoted: accept any identifier in backticks
73
+ const backtickSymbols = [...text.matchAll(/`([A-Za-z_$][\w$]{2,})`/g)].map((m) => m[1]);
74
+ // Plain text: only camelCase, PascalCase, ALL_CAPS, or snake_case (must contain uppercase or underscore after first char)
75
+ const plainSymbols = [...text.matchAll(/\b([A-Za-z_$][\w$]{2,})\b/g)]
76
+ .map((m) => m[1])
77
+ .filter((item) => /[A-Z_]/.test(item.slice(1)));
78
+ return unique([...backtickSymbols, ...plainSymbols].filter((item) => !stopwords.has(item) && !item.includes('.')));
68
79
  }
69
80
  function unique(items) {
70
81
  return [...new Set(items)];
@@ -1,7 +1,6 @@
1
- import type { KGraphConfig } from "../types/config.js";
2
- import type { ContextResponse } from "../types/cognition.js";
3
- import type { DependencyMap, FileMap, RelationshipMap, SymbolMap } from "../types/maps.js";
4
- import type { KGraphWorkspace } from "../types/config.js";
1
+ import type { ContextResponse } from '../types/cognition.js';
2
+ import type { KGraphConfig, KGraphWorkspace } from '../types/config.js';
3
+ import type { DependencyMap, FileMap, RelationshipMap, SymbolMap } from '../types/maps.js';
5
4
  export declare function queryContext(workspace: KGraphWorkspace, config: KGraphConfig, maps: {
6
5
  fileMap: FileMap;
7
6
  symbolMap: SymbolMap;
@@ -1,34 +1,45 @@
1
- import { readCognitionNotes, readDomainRecords } from "../storage/cognition-store.js";
2
- import { rankByFields } from "./ranking.js";
1
+ import { readCognitionNotes, readDomainRecords, } from '../storage/cognition-store.js';
2
+ import { rankByFields } from './ranking.js';
3
3
  export async function queryContext(workspace, config, maps, query) {
4
4
  const cognition = await readCognitionNotes(workspace);
5
5
  const domains = await readDomainRecords(workspace);
6
6
  const max = config.maxContextItems;
7
7
  const relevantFiles = rankByFields(query, maps.fileMap.files, [
8
- { name: "path", value: (file) => file.path },
9
- { name: "language", value: (file) => file.language }
8
+ { name: 'path', value: (file) => file.path },
9
+ { name: 'language', value: (file) => file.language },
10
10
  ]).slice(0, max);
11
11
  const relevantSymbols = rankByFields(query, maps.symbolMap.symbols, [
12
- { name: "name", value: (symbol) => symbol.name },
13
- { name: "path", value: (symbol) => symbol.filePath },
14
- { name: "kind", value: (symbol) => symbol.kind }
12
+ { name: 'name', value: (symbol) => symbol.name },
13
+ { name: 'path', value: (symbol) => symbol.filePath },
14
+ { name: 'kind', value: (symbol) => symbol.kind },
15
15
  ]).slice(0, max);
16
16
  const relevantCognition = rankByFields(query, cognition, [
17
- { name: "title", value: (note) => note.title },
18
- { name: "domain", value: (note) => note.domain },
19
- { name: "tags", value: (note) => note.tags },
20
- { name: "files", value: (note) => note.relatedFiles },
21
- { name: "symbols", value: (note) => note.relatedSymbols },
22
- { name: "summary", value: (note) => note.summary }
17
+ { name: 'title', value: (note) => note.title },
18
+ { name: 'domain', value: (note) => note.domain },
19
+ { name: 'tags', value: (note) => note.tags },
20
+ { name: 'files', value: (note) => note.relatedFiles },
21
+ { name: 'symbols', value: (note) => note.relatedSymbols },
22
+ { name: 'summary', value: (note) => note.summary },
23
23
  ]).slice(0, max);
24
24
  const matchedDomains = rankByFields(query, domains, [
25
- { name: "name", value: (domain) => domain.name },
26
- { name: "tags", value: (domain) => domain.tags },
27
- { name: "path", value: (domain) => domain.pathHints }
25
+ { name: 'name', value: (domain) => domain.name },
26
+ { name: 'tags', value: (domain) => domain.tags },
27
+ { name: 'path', value: (domain) => domain.pathHints },
28
28
  ]).slice(0, max);
29
+ const filePaths = new Set(maps.fileMap.files.map((f) => f.path));
30
+ const symbolNames = new Set(maps.symbolMap.symbols.map((s) => s.name));
29
31
  const staleReferences = cognition
30
- .filter((note) => note.referencesStatus === "stale" || note.referencesStatus === "unresolved" || note.referencesStatus === "mixed")
31
- .flatMap((note) => [...note.relatedFiles, ...note.relatedSymbols].map((ref) => `${note.title}: ${ref}`));
32
+ .filter((note) => note.referencesStatus === 'stale' ||
33
+ note.referencesStatus === 'unresolved' ||
34
+ note.referencesStatus === 'mixed')
35
+ .flatMap((note) => [
36
+ ...note.relatedFiles
37
+ .filter((f) => !filePaths.has(f))
38
+ .map((ref) => `${note.title}: ${ref}`),
39
+ ...note.relatedSymbols
40
+ .filter((s) => !symbolNames.has(s))
41
+ .map((ref) => `${note.title}: ${ref}`),
42
+ ]);
32
43
  return {
33
44
  query,
34
45
  matchedDomains,
@@ -37,6 +48,6 @@ export async function queryContext(workspace, config, maps, query) {
37
48
  relevantCognition,
38
49
  relationships: maps.relationshipMap.relationships.slice(0, max),
39
50
  staleReferences,
40
- warnings: []
51
+ warnings: [],
41
52
  };
42
53
  }
@@ -4,11 +4,7 @@ export const claudeCodeAdapter = {
4
4
  targetPath: 'CLAUDE.md',
5
5
  instructions: `## KGraph Workflow
6
6
 
7
- - Start repository work by checking \`kgraph context "<topic>"\` when the user asks about a domain, bug, workflow, or feature.
8
- - Convert stable discoveries from chat into Markdown notes under \`.kgraph/inbox/\`.
9
- - Run \`kgraph update\` to preserve those notes as durable cognition.
10
- - Run \`kgraph scan\` after structural code changes.
11
- - Run \`kgraph visualize\` when visualization support is available and the user wants to inspect the current knowledge map.
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.
12
8
  `,
13
9
  commandFiles: [
14
10
  {
@@ -18,8 +14,10 @@ export const claudeCodeAdapter = {
18
14
  1. Infer the topic from the user's request.
19
15
  2. Run \`kgraph context "<topic>"\`.
20
16
  3. Use the returned files, symbols, relationships, and cognition before broad exploration.
21
- 4. Save durable discoveries to \`.kgraph/inbox/\` and run \`kgraph update\` when appropriate.
22
- 5. Run \`kgraph scan\` after structural changes and report the scan summary.
17
+ 4. Save durable discoveries to \`.kgraph/inbox/\` and 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.
23
21
  `,
24
22
  },
25
23
  {
@@ -30,6 +28,16 @@ export const claudeCodeAdapter = {
30
28
  {
31
29
  path: '.claude/commands/kgraph-update.md',
32
30
  content: `Run \`kgraph update\` to process any pending Markdown notes in \`.kgraph/inbox/\` into durable cognition.
31
+ `,
32
+ },
33
+ {
34
+ path: '.claude/commands/kgraph-visualize.md',
35
+ content: `Run \`kgraph visualize\` to start an interactive dependency graph at http://localhost:4242. Opens in browser automatically. Use \`--no-open\` to print URL only, \`--port <n>\` for a custom port.
36
+ `,
37
+ },
38
+ {
39
+ path: '.claude/commands/kgraph-history.md',
40
+ content: `Run \`kgraph history\` to show a timeline of all processed cognition sessions. Includes git author attribution when available. Use \`--last <n>\` to limit entries, \`--json\` for machine-readable output.
33
41
  `,
34
42
  },
35
43
  ],
@@ -1,2 +1,2 @@
1
- import type { IntegrationAdapter } from "../integration-registry.js";
1
+ import type { IntegrationAdapter } from '../integration-registry.js';
2
2
  export declare const codexAdapter: IntegrationAdapter;
@@ -1,35 +1,35 @@
1
1
  export const codexAdapter = {
2
- name: "codex",
3
- label: "Codex",
4
- targetPath: "AGENTS.md",
2
+ name: 'codex',
3
+ label: 'Codex',
4
+ targetPath: 'AGENTS.md',
5
5
  instructions: `## KGraph Workflow
6
6
 
7
- - Before broad repository exploration, run \`kgraph context "<topic>"\` to load current repo intelligence.
8
- - After useful debugging, architecture discovery, workflow learning, or implementation work, write a concise Markdown note into \`.kgraph/inbox/\`.
9
- - Run \`kgraph update\` after adding inbox notes so KGraph preserves durable cognition.
10
- - Run \`kgraph scan\` after file moves, function renames, refactors, or other structural changes.
11
- - When visualization support is available and the developer asks to inspect KGraph, run \`kgraph visualize\` and report the generated local artifact.
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.
12
8
  `,
13
9
  commandFiles: [
14
10
  {
15
- path: ".agents/skills/kgraph/SKILL.md",
11
+ path: '.agents/skills/kgraph/SKILL.md',
16
12
  content: `---
17
- name: "kgraph"
18
- description: "Use KGraph persistent repo intelligence for the current coding task."
13
+ name: kgraph
14
+ description: Use KGraph persistent repo intelligence before broad repository exploration. Use when asked about repo structure, debugging context, architecture decisions, or to avoid rediscovering what is already known.
19
15
  ---
20
16
 
21
- Use this skill when the user asks to use KGraph, requests repo context, or asks you to avoid rediscovering repository structure.
17
+ # KGraph Skill
22
18
 
23
19
  Workflow:
24
20
 
25
21
  1. Infer the current topic from the user request.
26
22
  2. Run \`kgraph context "<topic>"\` before broad repo exploration.
27
- 3. Use KGraph's files, symbols, relationships, and cognition as navigation hints.
28
- 4. After durable discoveries, write a concise Markdown note to \`.kgraph/inbox/\`.
29
- 5. Run \`kgraph update\` if you created an inbox note.
30
- 6. Run \`kgraph scan\` after structural changes.
31
- `
32
- }
23
+ 3. Use KGraph's returned files, symbols, relationships, and cognition as navigation hints.
24
+ 4. After durable discoveries, write a concise Markdown note to \`.kgraph/inbox/\` and 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.
28
+ `,
29
+ },
30
+ ],
31
+ obsoleteCommandFiles: [
32
+ '.agents/skills/kgraph-update',
33
+ '.agents/skills/kgraph-scan',
33
34
  ],
34
- obsoleteCommandFiles: [".agents/skills/kgraph-update", ".agents/skills/kgraph-scan"]
35
35
  };
@@ -4,49 +4,49 @@ export const copilotAdapter = {
4
4
  targetPath: '.github/copilot-instructions.md',
5
5
  instructions: `## KGraph Workflow
6
6
 
7
- - Use \`kgraph context "<topic>"\` before scanning many files manually.
8
- - Preserve stable findings by creating Markdown notes in \`.kgraph/inbox/\`.
9
- - Use \`kgraph update\` to process chat summaries and debugging conclusions into durable cognition.
10
- - Use \`kgraph scan\` when code structure changes.
11
- - Use \`kgraph visualize\` when visualization support is available and the developer asks to inspect the repository knowledge map.
7
+ Before exploring the repository, run \`kgraph context "<topic>"\` to load existing repo intelligence. Use /kgraph-scan and /kgraph-update for manual maintenance.
12
8
  `,
13
9
  commandFiles: [
14
10
  {
15
- path: '.github/prompts/kgraph.prompt.md',
11
+ path: '.github/prompts/kgraph-scan.prompt.md',
16
12
  content: `---
17
- mode: agent
18
- description: Use KGraph persistent repo intelligence for this request
13
+ description: Refresh KGraph file, symbol, import, and relationship maps
14
+ agent: agent
19
15
  ---
20
16
 
21
- Use KGraph for the current task.
17
+ Run \`kgraph scan\` to refresh the repository maps, then summarize what changed.
18
+ `,
19
+ },
20
+ {
21
+ path: '.github/prompts/kgraph-update.prompt.md',
22
+ content: `---
23
+ description: Process KGraph inbox notes into durable cognition
24
+ agent: agent
25
+ ---
22
26
 
23
- 1. If the user provided a topic, run \`kgraph context "<topic>"\` first. If not, infer a concise topic from the request.
24
- 2. Use the returned files, symbols, relationships, and cognition before broad repository exploration.
25
- 3. If you discover durable architecture, debugging, workflow, or gotcha knowledge, create a Markdown note in \`.kgraph/inbox/\`.
26
- 4. If you add an inbox note, run \`kgraph update\`.
27
- 5. If code structure changed, run \`kgraph scan\`.
27
+ Run \`kgraph update\` to process any pending Markdown notes in \`.kgraph/inbox/\` into durable cognition.
28
28
  `,
29
29
  },
30
30
  {
31
- path: '.github/prompts/kgraph-scan.prompt.md',
31
+ path: '.github/prompts/kgraph-visualize.prompt.md',
32
32
  content: `---
33
- mode: agent
34
- description: Refresh KGraph file, symbol, import, and relationship maps
33
+ description: Open interactive KGraph dependency graph in browser
34
+ agent: agent
35
35
  ---
36
36
 
37
- Run \`kgraph scan\` to refresh the repository maps, then summarize what changed.
37
+ Run \`kgraph visualize\` to start the interactive dependency graph at http://localhost:4242, then summarize what nodes and connections are visible.
38
38
  `,
39
39
  },
40
40
  {
41
- path: '.github/prompts/kgraph-update.prompt.md',
41
+ path: '.github/prompts/kgraph-history.prompt.md',
42
42
  content: `---
43
- mode: agent
44
- description: Process KGraph inbox notes into durable cognition
43
+ description: Show timeline of KGraph cognition sessions with git attribution
44
+ agent: agent
45
45
  ---
46
46
 
47
- Run \`kgraph update\` to process any pending Markdown notes in \`.kgraph/inbox/\` into durable cognition.
47
+ Run \`kgraph history\` to display the timeline of all processed cognition sessions. Summarize who contributed what and when. Use \`--last <n>\` to limit entries.
48
48
  `,
49
49
  },
50
50
  ],
51
- obsoleteCommandFiles: [],
51
+ obsoleteCommandFiles: ['.github/prompts/kgraph.prompt.md'],
52
52
  };
@@ -1,2 +1,2 @@
1
- import type { IntegrationAdapter } from "../integration-registry.js";
1
+ import type { IntegrationAdapter } from '../integration-registry.js';
2
2
  export declare const cursorAdapter: IntegrationAdapter;
@@ -1,7 +1,7 @@
1
1
  export const cursorAdapter = {
2
- name: "cursor",
3
- label: "Cursor",
4
- targetPath: ".cursor/rules/kgraph.mdc",
2
+ name: 'cursor',
3
+ label: 'Cursor',
4
+ targetPath: '.cursor/rules/kgraph.mdc',
5
5
  instructions: `---
6
6
  description: Use KGraph persistent repo intelligence before broad repository exploration
7
7
  alwaysApply: true
@@ -9,11 +9,7 @@ alwaysApply: true
9
9
 
10
10
  ## KGraph Workflow
11
11
 
12
- - Query \`kgraph context "<topic>"\` before broad file searches when repo cognition may already exist.
13
- - Store durable chat, debugging, architecture, and workflow discoveries as Markdown notes in \`.kgraph/inbox/\`.
14
- - Run \`kgraph update\` after adding useful notes.
15
- - Run \`kgraph scan\` after refactors, moved folders, renamed functions, or other structure changes.
16
- - Run \`kgraph visualize\` when visualization support is available and the developer asks to inspect the KGraph map.
12
+ Before exploring the repository, run \`kgraph context "<topic>"\` to load existing repo intelligence. Run \`kgraph scan\` and \`kgraph update\` manually when needed. Run \`kgraph visualize\` to open the interactive dependency graph at http://localhost:4242. Run \`kgraph history\` to review the timeline of past cognition sessions.
17
13
  `,
18
- obsoleteCommandFiles: [".cursor/rules/kgraph-commands.mdc"]
14
+ obsoleteCommandFiles: ['.cursor/rules/kgraph-commands.mdc'],
19
15
  };
@@ -0,0 +1,16 @@
1
+ import type { CognitionNote } from '../types/cognition.js';
2
+ import type { DependencyMap, FileMap, RelationshipMap, SymbolMap } from '../types/maps.js';
3
+ export interface CytoscapeElement {
4
+ data: Record<string, unknown>;
5
+ classes?: string;
6
+ }
7
+ export interface GraphData {
8
+ elements: CytoscapeElement[];
9
+ meta: {
10
+ fileCount: number;
11
+ symbolCount: number;
12
+ cognitionCount: number;
13
+ generatedAt: string;
14
+ };
15
+ }
16
+ export declare function buildGraph(fileMap: FileMap, symbolMap: SymbolMap, dependencyMap: DependencyMap, _relationshipMap: RelationshipMap, cognitionNotes: CognitionNote[]): GraphData;
@@ -0,0 +1,100 @@
1
+ const LANGUAGE_COLORS = {
2
+ typescript: '#3b82f6',
3
+ javascript: '#f59e0b',
4
+ json: '#6b7280',
5
+ markdown: '#10b981',
6
+ yaml: '#8b5cf6',
7
+ css: '#06b6d4',
8
+ html: '#f97316',
9
+ };
10
+ const STATUS_COLORS = {
11
+ current: '#10b981',
12
+ mixed: '#f59e0b',
13
+ stale: '#ef4444',
14
+ unresolved: '#6b7280',
15
+ };
16
+ export function buildGraph(fileMap, symbolMap, dependencyMap, _relationshipMap, cognitionNotes) {
17
+ const elements = [];
18
+ const edgeIds = new Set();
19
+ for (const file of fileMap.files) {
20
+ elements.push({
21
+ data: {
22
+ id: file.id,
23
+ label: file.path.split('/').pop() ?? file.path,
24
+ path: file.path,
25
+ language: file.language,
26
+ color: LANGUAGE_COLORS[file.language] ?? '#94a3b8',
27
+ type: 'file',
28
+ size: file.sizeBytes,
29
+ scanStatus: file.scanStatus,
30
+ },
31
+ classes: `file ${file.language}`,
32
+ });
33
+ }
34
+ for (const note of cognitionNotes) {
35
+ const id = `cognition-${note.id}`;
36
+ elements.push({
37
+ data: {
38
+ id,
39
+ label: note.title,
40
+ color: STATUS_COLORS[note.referencesStatus] ?? STATUS_COLORS.unresolved,
41
+ type: 'cognition',
42
+ referencesStatus: note.referencesStatus,
43
+ domain: note.domain ?? '',
44
+ relatedFiles: note.relatedFiles,
45
+ relatedSymbols: note.relatedSymbols,
46
+ },
47
+ classes: `cognition ${note.referencesStatus}`,
48
+ });
49
+ for (const filePath of note.relatedFiles) {
50
+ const target = fileMap.files.find((f) => f.path === filePath);
51
+ if (target) {
52
+ const edgeId = `${id}-ref-${target.id}`;
53
+ if (!edgeIds.has(edgeId)) {
54
+ edgeIds.add(edgeId);
55
+ elements.push({
56
+ data: {
57
+ id: edgeId,
58
+ source: id,
59
+ target: target.id,
60
+ type: 'cognition-ref',
61
+ label: '',
62
+ },
63
+ classes: 'cognition-ref',
64
+ });
65
+ }
66
+ }
67
+ }
68
+ }
69
+ for (const dep of dependencyMap.dependencies) {
70
+ if (dep.kind !== 'local' || !dep.resolvedFile)
71
+ continue;
72
+ const src = fileMap.files.find((f) => f.path === dep.fromFile);
73
+ const tgt = fileMap.files.find((f) => f.path === dep.resolvedFile);
74
+ if (src && tgt && src.id !== tgt.id) {
75
+ const edgeId = `import-${src.id}-${tgt.id}`;
76
+ if (!edgeIds.has(edgeId)) {
77
+ edgeIds.add(edgeId);
78
+ elements.push({
79
+ data: {
80
+ id: edgeId,
81
+ source: src.id,
82
+ target: tgt.id,
83
+ type: 'import',
84
+ label: '',
85
+ },
86
+ classes: 'import',
87
+ });
88
+ }
89
+ }
90
+ }
91
+ return {
92
+ elements,
93
+ meta: {
94
+ fileCount: fileMap.files.length,
95
+ symbolCount: symbolMap.symbols.length,
96
+ cognitionCount: cognitionNotes.length,
97
+ generatedAt: new Date().toISOString(),
98
+ },
99
+ };
100
+ }
@@ -0,0 +1,2 @@
1
+ import type { GraphData } from './graph-builder.js';
2
+ export declare function renderHtml(graphData: GraphData, rootPath: string): string;