@kentwynn/kgraph 0.1.14 → 0.1.15

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.
@@ -1,10 +1,10 @@
1
- import type { KGraphWorkspace } from "../types/config.js";
2
- import type { CognitionNote, ReferenceStatus } from "../types/cognition.js";
3
- import type { ScanResult } from "../types/maps.js";
1
+ import type { CognitionNote, ReferenceStatus } from '../types/cognition.js';
2
+ import type { KGraphWorkspace } from '../types/config.js';
3
+ import type { ScanResult } from '../types/maps.js';
4
4
  export interface UpdateResult {
5
5
  processed: CognitionNote[];
6
6
  warnings: string[];
7
7
  }
8
- export declare function updateCognition(workspace: KGraphWorkspace, currentMaps: Pick<ScanResult, "files" | "symbols">, dryRun?: boolean): Promise<UpdateResult>;
9
- export declare function refreshCognitionReferenceStatuses(workspace: KGraphWorkspace, currentMaps: Pick<ScanResult, "files" | "symbols">): Promise<void>;
10
- export declare function evaluateReferenceStatus(relatedFiles: string[], relatedSymbols: string[], currentMaps: Pick<ScanResult, "files" | "symbols">): ReferenceStatus;
8
+ export declare function updateCognition(workspace: KGraphWorkspace, currentMaps: Pick<ScanResult, 'files' | 'symbols'>, dryRun?: boolean): Promise<UpdateResult>;
9
+ export declare function refreshCognitionReferenceStatuses(workspace: KGraphWorkspace, currentMaps: Pick<ScanResult, 'files' | 'symbols'>): Promise<void>;
10
+ export declare function evaluateReferenceStatus(relatedFiles: string[], relatedSymbols: string[], currentMaps: Pick<ScanResult, 'files' | 'symbols'>): ReferenceStatus;
@@ -1,25 +1,31 @@
1
- import { readFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { parseMarkdownNote } from "./markdown-note-parser.js";
4
- import { archiveInboxNote, readCognitionNotes, listInboxNotes, slugify, writeCognitionNote, writeDomainRecord } from "../storage/cognition-store.js";
1
+ import { readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { archiveInboxNote, listInboxNotes, readCognitionNotes, slugify, writeCognitionNote, writeDomainRecord, } from '../storage/cognition-store.js';
4
+ import { parseMarkdownNote } from './markdown-note-parser.js';
5
5
  export async function updateCognition(workspace, currentMaps, dryRun = false) {
6
6
  const inboxNotes = await listInboxNotes(workspace);
7
7
  const processed = [];
8
8
  const warnings = [];
9
9
  for (const inboxPath of inboxNotes) {
10
10
  try {
11
- const raw = await readFile(inboxPath, "utf8");
11
+ const raw = await readFile(inboxPath, 'utf8');
12
12
  const parsed = parseMarkdownNote(raw);
13
- const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
14
- const id = `${timestamp}-${slugify(parsed.title) || path.basename(inboxPath, ".md")}`;
13
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
14
+ const id = `${timestamp}-${slugify(parsed.title) || path.basename(inboxPath, '.md')}`;
15
15
  const archivedPath = path.join(workspace.processedInteractionsPath, `${timestamp}-${path.basename(inboxPath)}`);
16
16
  const note = {
17
17
  ...parsed,
18
18
  id,
19
- sourceInboxPath: path.relative(workspace.rootPath, inboxPath).split(path.sep).join("/"),
20
- processedPath: path.relative(workspace.rootPath, archivedPath).split(path.sep).join("/"),
19
+ sourceInboxPath: path
20
+ .relative(workspace.rootPath, inboxPath)
21
+ .split(path.sep)
22
+ .join('/'),
23
+ processedPath: path
24
+ .relative(workspace.rootPath, archivedPath)
25
+ .split(path.sep)
26
+ .join('/'),
21
27
  createdAt: new Date().toISOString(),
22
- referencesStatus: evaluateReferenceStatus(parsed.relatedFiles, parsed.relatedSymbols, currentMaps)
28
+ referencesStatus: evaluateReferenceStatus(parsed.relatedFiles, parsed.relatedSymbols, currentMaps),
23
29
  };
24
30
  processed.push(note);
25
31
  warnings.push(...parsed.warnings.map((warning) => `${path.basename(inboxPath)}: ${warning}`));
@@ -38,9 +44,28 @@ export async function updateCognition(workspace, currentMaps, dryRun = false) {
38
44
  export async function refreshCognitionReferenceStatuses(workspace, currentMaps) {
39
45
  const notes = await readCognitionNotes(workspace);
40
46
  for (const note of notes) {
41
- const nextStatus = evaluateReferenceStatus(note.relatedFiles, note.relatedSymbols, currentMaps);
42
- if (nextStatus !== note.referencesStatus) {
43
- await writeCognitionNote(workspace, { ...note, referencesStatus: nextStatus });
47
+ // Re-parse relatedSymbols from the archived raw markdown if available.
48
+ // This migrates old notes that were parsed with the old plain-text heuristic
49
+ // (which produced false positives like JWT, CSRF, TODO) to the current
50
+ // backtick-only logic.
51
+ let relatedSymbols = note.relatedSymbols;
52
+ if (note.processedPath) {
53
+ try {
54
+ const raw = await readFile(path.join(workspace.rootPath, note.processedPath), 'utf8');
55
+ relatedSymbols = parseMarkdownNote(raw).relatedSymbols;
56
+ }
57
+ catch {
58
+ // archived file missing — keep stored symbols
59
+ }
60
+ }
61
+ const nextStatus = evaluateReferenceStatus(note.relatedFiles, relatedSymbols, currentMaps);
62
+ if (nextStatus !== note.referencesStatus ||
63
+ relatedSymbols !== note.relatedSymbols) {
64
+ await writeCognitionNote(workspace, {
65
+ ...note,
66
+ relatedSymbols,
67
+ referencesStatus: nextStatus,
68
+ });
44
69
  }
45
70
  }
46
71
  }
@@ -49,21 +74,21 @@ export function evaluateReferenceStatus(relatedFiles, relatedSymbols, currentMap
49
74
  const symbolNames = new Set(currentMaps.symbols.map((symbol) => symbol.name));
50
75
  const references = [
51
76
  ...relatedFiles.map((file) => filePaths.has(file)),
52
- ...relatedSymbols.map((symbol) => symbolNames.has(symbol))
77
+ ...relatedSymbols.map((symbol) => symbolNames.has(symbol)),
53
78
  ];
54
79
  if (references.length === 0) {
55
- return "unresolved";
80
+ return 'unresolved';
56
81
  }
57
82
  if (references.every(Boolean)) {
58
- return "current";
83
+ return 'current';
59
84
  }
60
85
  if (references.every((value) => !value)) {
61
- return "stale";
86
+ return 'stale';
62
87
  }
63
- return "mixed";
88
+ return 'mixed';
64
89
  }
65
90
  function toDomainRecord(note, currentMaps) {
66
- const name = note.domain ?? "general";
91
+ const name = note.domain ?? 'general';
67
92
  const fileSet = new Set(currentMaps.files.map((file) => file.path));
68
93
  const symbolSet = new Set(currentMaps.symbols.map((symbol) => symbol.name));
69
94
  return {
@@ -72,6 +97,6 @@ function toDomainRecord(note, currentMaps) {
72
97
  tags: note.tags,
73
98
  files: note.relatedFiles.filter((file) => fileSet.has(file)),
74
99
  symbols: note.relatedSymbols.filter((symbol) => symbolSet.has(symbol)),
75
- cognitionNotes: [note.id]
100
+ cognitionNotes: [note.id],
76
101
  };
77
102
  }
@@ -14,7 +14,7 @@ export function parseMarkdownNote(markdown) {
14
14
  summary: sections.Summary,
15
15
  sections,
16
16
  relatedFiles: unique(extractMatches(combined, PATH_REF)),
17
- relatedSymbols: unique(extractSymbolRefs(combined)),
17
+ relatedSymbols: unique(extractSymbolRefs(sections)),
18
18
  warnings,
19
19
  };
20
20
  }
@@ -59,23 +59,12 @@ function parseSections(body) {
59
59
  function extractMatches(text, regex) {
60
60
  return [...text.matchAll(regex)].map((match) => match[1]);
61
61
  }
62
- function extractSymbolRefs(text) {
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('.')));
62
+ function extractSymbolRefs(sections) {
63
+ // Prefer declared symbols in the Key Symbols section; fall back to backtick
64
+ // items across all sections. Never use plain-text heuristics — they produce
65
+ // false positives for domain vocabulary like JWT, CSRF, TODO, Next, etc.
66
+ const text = sections['Key Symbols'] ?? Object.values(sections).join('\n');
67
+ return [...text.matchAll(/`([A-Za-z_$][\w$]{2,})`/g)].map((m) => m[1]);
79
68
  }
80
69
  function unique(items) {
81
70
  return [...new Set(items)];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kentwynn/kgraph",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Persistent repo intelligence for AI coding assistants.",
5
5
  "type": "module",
6
6
  "bin": {