@kentwynn/kgraph 0.1.7 → 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 +185 -91
- package/dist/cli/commands/history.d.ts +16 -0
- package/dist/cli/commands/history.js +124 -0
- package/dist/cli/commands/visualize.d.ts +2 -0
- package/dist/cli/commands/visualize.js +73 -0
- package/dist/cli/help.js +46 -44
- package/dist/cli/index.js +4 -0
- package/dist/cognition/markdown-note-parser.d.ts +1 -1
- package/dist/cognition/markdown-note-parser.js +28 -17
- package/dist/context/context-query.d.ts +3 -4
- package/dist/context/context-query.js +30 -19
- package/dist/integrations/adapters/claude-code.js +12 -0
- package/dist/integrations/adapters/codex.js +2 -0
- package/dist/integrations/adapters/copilot.js +22 -2
- package/dist/integrations/adapters/cursor.js +1 -1
- package/dist/visualization/graph-builder.d.ts +16 -0
- package/dist/visualization/graph-builder.js +100 -0
- package/dist/visualization/html-template.d.ts +2 -0
- package/dist/visualization/html-template.js +293 -0
- package/package.json +1 -1
package/dist/cli/help.js
CHANGED
|
@@ -1,58 +1,60 @@
|
|
|
1
|
-
import { Chalk } from
|
|
2
|
-
import figlet from
|
|
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(
|
|
10
|
-
|
|
11
|
-
` ${theme.bold(
|
|
12
|
-
|
|
13
|
-
` ${theme.hex(
|
|
14
|
-
` ${theme.hex(
|
|
15
|
-
|
|
16
|
-
theme.bold(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
theme.bold(
|
|
20
|
-
command(
|
|
21
|
-
command(
|
|
22
|
-
|
|
23
|
-
theme.bold(
|
|
24
|
-
command(
|
|
25
|
-
command(
|
|
26
|
-
command(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
command(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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(
|
|
49
|
-
font:
|
|
50
|
-
horizontalLayout:
|
|
51
|
-
verticalLayout:
|
|
50
|
+
return figlet.textSync('KGraph', {
|
|
51
|
+
font: 'ANSI Shadow',
|
|
52
|
+
horizontalLayout: 'default',
|
|
53
|
+
verticalLayout: 'default',
|
|
52
54
|
});
|
|
53
55
|
}
|
|
54
56
|
catch {
|
|
55
|
-
return
|
|
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
|
|
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
|
|
2
|
-
const PATH_REF = /(?:^|\s)([\w./-]+\.(?:ts|tsx|js|jsx|json|md|yaml|yml))(?:\s|$|[)
|
|
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 ===
|
|
9
|
-
const title = extractTitle(body) ?? frontmatterTitle ??
|
|
10
|
-
const combined = Object.values(sections).join(
|
|
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 ===
|
|
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(
|
|
22
|
+
if (!markdown.startsWith('---\n')) {
|
|
24
23
|
return { frontmatter: {}, body: markdown };
|
|
25
24
|
}
|
|
26
|
-
const end = markdown.indexOf(
|
|
25
|
+
const end = markdown.indexOf('\n---', 4);
|
|
27
26
|
if (end === -1) {
|
|
28
|
-
warnings.push(
|
|
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,
|
|
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([
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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 {
|
|
2
|
-
import type {
|
|
3
|
-
import type { DependencyMap, FileMap, RelationshipMap, SymbolMap } from
|
|
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
|
|
2
|
-
import { rankByFields } from
|
|
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:
|
|
9
|
-
{ name:
|
|
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:
|
|
13
|
-
{ name:
|
|
14
|
-
{ name:
|
|
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:
|
|
18
|
-
{ name:
|
|
19
|
-
{ name:
|
|
20
|
-
{ name:
|
|
21
|
-
{ name:
|
|
22
|
-
{ name:
|
|
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:
|
|
26
|
-
{ name:
|
|
27
|
-
{ name:
|
|
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 ===
|
|
31
|
-
|
|
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
|
}
|
|
@@ -16,6 +16,8 @@ Before exploring the repository, run \`kgraph context "<topic>"\` to load existi
|
|
|
16
16
|
3. Use the returned files, symbols, relationships, and cognition before broad exploration.
|
|
17
17
|
4. Save durable discoveries to \`.kgraph/inbox/\` and immediately run \`kgraph update\`.
|
|
18
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.
|
|
19
21
|
`,
|
|
20
22
|
},
|
|
21
23
|
{
|
|
@@ -26,6 +28,16 @@ Before exploring the repository, run \`kgraph context "<topic>"\` to load existi
|
|
|
26
28
|
{
|
|
27
29
|
path: '.claude/commands/kgraph-update.md',
|
|
28
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.
|
|
29
41
|
`,
|
|
30
42
|
},
|
|
31
43
|
],
|
|
@@ -23,6 +23,8 @@ Workflow:
|
|
|
23
23
|
3. Use KGraph's returned files, symbols, relationships, and cognition as navigation hints.
|
|
24
24
|
4. After durable discoveries, write a concise Markdown note to \`.kgraph/inbox/\` and immediately run \`kgraph update\`.
|
|
25
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.
|
|
26
28
|
`,
|
|
27
29
|
},
|
|
28
30
|
],
|
|
@@ -10,8 +10,8 @@ Before exploring the repository, run \`kgraph context "<topic>"\` to load existi
|
|
|
10
10
|
{
|
|
11
11
|
path: '.github/prompts/kgraph-scan.prompt.md',
|
|
12
12
|
content: `---
|
|
13
|
-
mode: agent
|
|
14
13
|
description: Refresh KGraph file, symbol, import, and relationship maps
|
|
14
|
+
agent: agent
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
Run \`kgraph scan\` to refresh the repository maps, then summarize what changed.
|
|
@@ -20,11 +20,31 @@ Run \`kgraph scan\` to refresh the repository maps, then summarize what changed.
|
|
|
20
20
|
{
|
|
21
21
|
path: '.github/prompts/kgraph-update.prompt.md',
|
|
22
22
|
content: `---
|
|
23
|
-
mode: agent
|
|
24
23
|
description: Process KGraph inbox notes into durable cognition
|
|
24
|
+
agent: agent
|
|
25
25
|
---
|
|
26
26
|
|
|
27
27
|
Run \`kgraph update\` to process any pending Markdown notes in \`.kgraph/inbox/\` into durable cognition.
|
|
28
|
+
`,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
path: '.github/prompts/kgraph-visualize.prompt.md',
|
|
32
|
+
content: `---
|
|
33
|
+
description: Open interactive KGraph dependency graph in browser
|
|
34
|
+
agent: agent
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
Run \`kgraph visualize\` to start the interactive dependency graph at http://localhost:4242, then summarize what nodes and connections are visible.
|
|
38
|
+
`,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
path: '.github/prompts/kgraph-history.prompt.md',
|
|
42
|
+
content: `---
|
|
43
|
+
description: Show timeline of KGraph cognition sessions with git attribution
|
|
44
|
+
agent: agent
|
|
45
|
+
---
|
|
46
|
+
|
|
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.
|
|
28
48
|
`,
|
|
29
49
|
},
|
|
30
50
|
],
|
|
@@ -9,7 +9,7 @@ alwaysApply: true
|
|
|
9
9
|
|
|
10
10
|
## KGraph Workflow
|
|
11
11
|
|
|
12
|
-
Before exploring the repository, run \`kgraph context "<topic>"\` to load existing repo intelligence. Run \`kgraph scan\` and \`kgraph update\` manually when needed.
|
|
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.
|
|
13
13
|
`,
|
|
14
14
|
obsoleteCommandFiles: ['.cursor/rules/kgraph-commands.mdc'],
|
|
15
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
|
+
}
|