@kentwynn/kgraph 0.2.11 → 0.2.13

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Persistent repository intelligence for AI coding tools.
4
4
 
5
- KGraph gives Codex, GitHub Copilot, Cursor, Claude Code, Gemini CLI, Windsurf, and Cline 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.
5
+ KGraph gives Codex, GitHub Copilot, Cursor, Claude Code, Gemini CLI, Windsurf, and Cline a local knowledge layer for your repo: file maps, symbols, imports, relationships, and durable knowledge atoms from previous AI sessions. The goal is simple: your assistant should not spend every session re-learning the same codebase.
6
6
 
7
7
  ## The Workflow
8
8
 
@@ -20,7 +20,7 @@ That second command runs the full practical workflow:
20
20
 
21
21
  1. Refreshes the repository scan.
22
22
  2. Updates file, symbol, import, and relationship maps.
23
- 3. Processes any Markdown notes waiting in `.kgraph/inbox/`.
23
+ 3. Processes any Markdown capture notes waiting in `.kgraph/inbox/` into knowledge atoms.
24
24
  4. Returns compact context for the topic you asked about.
25
25
 
26
26
  You can also run just:
@@ -29,7 +29,7 @@ You can also run just:
29
29
  kgraph
30
30
  ```
31
31
 
32
- That refreshes maps and cognition without printing topic-specific context.
32
+ That refreshes maps and durable memory without printing topic-specific context.
33
33
 
34
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.
35
35
 
@@ -52,8 +52,8 @@ KGraph stores the reusable parts locally:
52
52
  - What symbols each source file defines.
53
53
  - Which files import each other.
54
54
  - Which TypeScript/JavaScript functions and methods directly call each other when KGraph can infer it cheaply.
55
- - Which notes, decisions, debugging findings, and gotchas were captured from prior sessions.
56
- - Which cognition references are current, mixed, stale, or unresolved after code moves.
55
+ - Which decisions, debugging findings, gotchas, summaries, and relationships were captured as knowledge atoms.
56
+ - Which atoms are active, need review, stale, archived, or superseded after code moves.
57
57
 
58
58
  Then an AI assistant can ask for focused context before broad exploration:
59
59
 
@@ -61,8 +61,8 @@ Then an AI assistant can ask for focused context before broad exploration:
61
61
  kgraph "blog admin token usage"
62
62
  ```
63
63
 
64
- Instead of reading the whole repo, it gets a compact starting point: relevant files, symbols, relationships, domains, prior notes, and stale references to watch.
65
- Each context item explains why it was returned, such as a path/name match, a matched cognition reference, a domain match, or a nearby import relationship.
64
+ Instead of reading the whole repo, it gets a compact starting point: relevant files, symbols, relationships, domains, knowledge atoms, and stale references to watch.
65
+ Each context item explains why it was returned, such as a path/name match, a matched atom reference, a domain match, or a nearby import relationship.
66
66
 
67
67
  When you need change impact instead of broad context:
68
68
 
@@ -70,7 +70,7 @@ When you need change impact instead of broad context:
70
70
  kgraph impact Button
71
71
  ```
72
72
 
73
- That shows matched files/symbols, files importing the target, known callers/callees, related cognition, and simple risk signals.
73
+ That shows matched files/symbols, files importing the target, known callers/callees, related knowledge atoms, and simple risk signals.
74
74
 
75
75
  ## Install
76
76
 
@@ -125,7 +125,7 @@ kgraph "topic"
125
125
  kgraph
126
126
  ```
127
127
 
128
- Use `kgraph doctor` after setup and before trusting a repo's saved intelligence. It checks initialization, maps, pending inbox notes, integration targets, and actionable quality problems. Use `kgraph doctor --quality` and `kgraph repair --dry-run` when stale or noisy cognition references start making context harder to trust.
128
+ Use `kgraph doctor` after setup and before trusting a repo's saved intelligence. It checks initialization, maps, pending inbox notes, integration targets, and actionable quality problems. Use `kgraph doctor --quality` and `kgraph repair --dry-run` when stale or noisy atom references start making context harder to trust.
129
129
 
130
130
  Agents can also report session activity so KGraph can estimate token waste:
131
131
 
@@ -157,29 +157,29 @@ Initializes KGraph and writes local instruction files for supported AI tools.
157
157
  kgraph "some topic"
158
158
  ```
159
159
 
160
- The normal command. Scans the repo, updates cognition, and returns focused context for the topic.
160
+ The normal command. Scans the repo, updates durable memory, and returns focused context for the topic.
161
161
 
162
162
  ```bash
163
163
  kgraph
164
164
  ```
165
165
 
166
- Refreshes maps and cognition without returning topic-specific context.
166
+ Refreshes maps and durable memory without returning topic-specific context.
167
167
 
168
168
  ```bash
169
169
  kgraph doctor
170
170
  kgraph doctor --quality
171
171
  ```
172
172
 
173
- Checks whether the workspace is initialized, maps exist, inbox notes are pending, and configured integrations point to real files. Use `--quality` when context shows stale/noisy cognition references, unresolved local imports, unresolved call edges, duplicate cognition titles, or generated files in the scan.
173
+ Checks whether the workspace is initialized, maps exist, inbox notes are pending, knowledge storage is valid, and configured integrations point to real files. Use `--quality` when context shows stale/noisy atom references, unresolved local imports, unresolved call edges, duplicate atom topics, or generated files in the scan.
174
174
 
175
- The default doctor result is the main quality gate. It fails on actionable hygiene issues such as stale/noisy cognition, duplicate cognition titles, generated integration files leaking into scans, missing maps, or broken integration targets. Scanner coverage counts such as unresolved local imports or unresolved call edges remain visible in `--quality`, but they do not fail the gate by themselves because they often reflect current parser limits.
175
+ The default doctor result is the main quality gate. It fails on actionable hygiene issues such as stale/noisy atoms, duplicate atom topics, generated integration files leaking into scans, missing maps, invalid knowledge storage, or broken integration targets. Scanner coverage counts such as unresolved local imports or unresolved call edges remain visible in `--quality`, but they do not fail the gate by themselves because they often reflect current parser limits.
176
176
 
177
177
  ```bash
178
178
  kgraph repair --dry-run
179
179
  kgraph repair
180
180
  ```
181
181
 
182
- `repair --dry-run` previews cleanup for noisy cognition references, such as framework names recorded as files or local variables recorded as symbols. `repair` applies only the safe noisy-reference cleanup; broader quality findings stay report-only. Run repair intentionally when stale references make context noisy; it is not part of every normal workflow.
182
+ `repair --dry-run` previews cleanup for noisy atom references, such as framework names recorded as files or local variables recorded as symbols. `repair` applies only the safe noisy-reference cleanup; broader quality findings stay report-only. Run repair intentionally when stale references make context noisy; it is not part of every normal workflow.
183
183
 
184
184
  ```bash
185
185
  kgraph uninstall
@@ -194,7 +194,7 @@ kgraph impact "Button"
194
194
  kgraph impact "createSession" --json
195
195
  ```
196
196
 
197
- Show practical impact for a file, symbol, or topic: matched files/symbols, import users, callers, callees, ownership edges, related cognition, and risk hints.
197
+ Show practical impact for a file, symbol, or topic: matched files/symbols, import users, callers, callees, ownership edges, related knowledge atoms, and risk hints.
198
198
 
199
199
  ```bash
200
200
  kgraph session
@@ -208,7 +208,7 @@ kgraph session end --agent codex --conclude --topic "auth token refresh"
208
208
  ```
209
209
 
210
210
  Track agent-reported read/write activity, repeated reads, and estimated token cost. Supported agents are `codex`, `claude-code`, `copilot`, `cursor`, `gemini`, `windsurf`, and `cline`.
211
- The text report now includes next actions, such as using `kgraph context "<topic>"` before repeated broad file inspection. Add `--conclude` to store a durable session summary with touched files attached as related cognition.
211
+ The text report includes next actions, such as using `kgraph context "<topic>"` before repeated broad file inspection. Add `--conclude` to store a durable session summary with touched files attached as atom evidence.
212
212
 
213
213
  ```bash
214
214
  kgraph conclude "auth refresh requires rotating the session cookie" \
@@ -220,7 +220,7 @@ kgraph conclude "auth refresh requires rotating the session cookie" \
220
220
  --note "The refresh path must update both the access token and cookie expiry."
221
221
  ```
222
222
 
223
- Store durable engineering memory directly. Cognition is typed as `finding`, `decision`, `gotcha`, `summary`, or `relationship`, and confidence is `high`, `medium`, or `low`. Keep conclusions concise: preserve expensive-to-rediscover knowledge, not raw chain-of-thought, speculative exploration, or temporary reasoning.
223
+ Store durable engineering memory directly. Knowledge atoms are typed as `finding`, `decision`, `gotcha`, `summary`, or `relationship`, and confidence is `high`, `medium`, or `low`. Keep conclusions concise: preserve expensive-to-rediscover knowledge, not raw chain-of-thought, speculative exploration, or temporary reasoning.
224
224
 
225
225
  KGraph stores these conclusions as canonical knowledge atoms under `.kgraph/knowledge/` while keeping existing Markdown cognition files readable for compatibility.
226
226
 
@@ -273,8 +273,8 @@ kgraph context "auth token refresh"
273
273
  kgraph context "auth token refresh" --json
274
274
  ```
275
275
 
276
- Return context from existing maps and cognition without scanning or updating first.
277
- Markdown output includes the reason each file, symbol, cognition note, nearby symbol, or relationship was selected. Use `--json` when an agent or script needs the same explanation data programmatically.
276
+ Return context from existing maps and knowledge atoms without scanning or updating first.
277
+ Markdown output includes the reason each file, symbol, knowledge atom, nearby symbol, or relationship was selected. Use `--json` when an agent or script needs the same explanation data programmatically.
278
278
 
279
279
  Context output includes a **Recent Git Changes** section that surfaces files with staged edits, unstaged edits, or changes in recent commits. This lets AI agents know which files are actively in flux without running a separate `git status` or `git log`.
280
280
 
@@ -283,7 +283,7 @@ kgraph update
283
283
  kgraph update --dry-run
284
284
  ```
285
285
 
286
- Process Markdown notes from `.kgraph/inbox/` into durable cognition records.
286
+ Process Markdown capture notes from `.kgraph/inbox/` into durable knowledge atoms and compatibility Markdown.
287
287
 
288
288
  ```bash
289
289
  kgraph visualize
@@ -300,7 +300,7 @@ kgraph history "blog button"
300
300
  kgraph history --json
301
301
  ```
302
302
 
303
- Show processed cognition sessions. Add a query to find historical work by title, summary, file, symbol, or note body.
303
+ Show processed capture history. Add a query to find historical work by title, summary, file, symbol, or note body.
304
304
 
305
305
  ## AI Tool Integrations
306
306
 
@@ -308,13 +308,13 @@ KGraph integrations are local files. They do not start background agents, call A
308
308
 
309
309
  ```bash
310
310
  kgraph integrate add codex copilot cursor claude-code gemini windsurf cline
311
- kgraph integrate add copilot --mode always
311
+ kgraph integrate add copilot --mode smart
312
312
  kgraph integrate set copilot --mode manual
313
313
  kgraph integrate list
314
314
  kgraph integrate remove cursor
315
315
  ```
316
316
 
317
- New integrations default to `smart` mode. Use `--mode always` to force KGraph on every chat, or `--mode manual` to run only when explicitly asked.
317
+ New integrations default to `always` mode, so every chat in the repository starts with `kgraph "<topic>"`. Use `--mode smart` to run KGraph only for repo-specific work, or `--mode manual` to run only when explicitly asked.
318
318
 
319
319
  | Mode | Behavior |
320
320
  | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -383,14 +383,13 @@ Other languages keep practical file, import, and symbol depth without full call
383
383
  kgraph visualize
384
384
  ```
385
385
 
386
- The graph shows files, symbols, imports, TypeScript/JavaScript call edges, ownership edges, cognition notes, and relationship edges. Cognition notes are colored by reference health:
386
+ The graph shows files, imports, TypeScript/JavaScript call edges, ownership edges, relationship edges, and canonical knowledge atoms. Symbols are kept out of the main rendered graph for performance and shown in the file detail panel instead. Atom nodes are capped in large memory sets and colored by lifecycle status:
387
387
 
388
- - current
389
- - mixed
388
+ - active
389
+ - needs-review
390
390
  - stale
391
- - unresolved
392
391
 
393
- Use it when you want to inspect what KGraph currently knows, find stale notes after refactors, or export a graph image for a report.
392
+ Use it when you want to inspect what KGraph currently knows, find stale atoms after refactors, or export a graph image for a report.
394
393
 
395
394
  ## Development
396
395
 
@@ -465,4 +464,4 @@ The release workflow builds, tests, packs, publishes the npm package on version
465
464
  - Stronger TypeScript path alias and package export resolution.
466
465
  - Richer graph filtering for large repositories.
467
466
  - Optional MCP and editor integration.
468
- - Team-friendly shared cognition workflows that stay local-first.
467
+ - Team-friendly shared knowledge workflows that stay local-first.
@@ -114,7 +114,8 @@ export function registerDoctorCommand(program) {
114
114
  .join('; '),
115
115
  });
116
116
  let qualityReport;
117
- if (maps) {
117
+ const knowledgeReadable = !knowledgeIssues.some((issue) => issue.code === 'invalid-jsonl' || issue.code === 'missing-schema');
118
+ if (maps && knowledgeReadable) {
118
119
  qualityReport = await analyzeCognitionQuality(workspace, maps);
119
120
  const qualityFindings = summarizeQualityFindings(qualityReport);
120
121
  const coverageNotes = summarizeCoverageNotes(qualityReport);
@@ -129,8 +130,15 @@ export function registerDoctorCommand(program) {
129
130
  : qualityFindings.join('; '),
130
131
  });
131
132
  }
133
+ else if (maps) {
134
+ checks.push({
135
+ label: 'quality gate',
136
+ ok: false,
137
+ detail: 'knowledge storage is invalid; fix knowledge check first',
138
+ });
139
+ }
132
140
  printChecks(checks);
133
- if (options.quality && maps) {
141
+ if (options.quality && maps && knowledgeReadable) {
134
142
  console.log('');
135
143
  console.log('KGraph Cognition Quality');
136
144
  console.log('');
@@ -156,14 +164,19 @@ function printChecks(checks) {
156
164
  }
157
165
  }
158
166
  export function printQualityReport(report) {
159
- console.log(`Notes: ${report.noteCount}`);
160
- console.log(`Mixed/stale/unresolved notes: ${report.mixedOrStaleCount}`);
161
- console.log(`Orphaned notes (all refs dead): ${report.orphanedNoteCount}`);
167
+ console.log(`Atoms: ${report.atomCount}`);
168
+ console.log(`Needs-review atoms: ${report.needsReviewAtomCount}`);
169
+ console.log(`Stale atoms: ${report.staleAtomCount}`);
170
+ console.log(`Archived atoms: ${report.archivedAtomCount}`);
171
+ console.log(`Duplicate atom topics: ${report.duplicateAtomTopicCount}`);
172
+ console.log(`Compatibility notes: ${report.noteCount}`);
173
+ console.log(`Mixed/stale/unresolved compatibility notes: ${report.mixedOrStaleCount}`);
174
+ console.log(`Orphaned atoms (all refs dead): ${report.orphanedNoteCount}`);
162
175
  console.log(`Noisy file refs: ${report.noisyFileRefCount}`);
163
176
  console.log(`Noisy symbol refs: ${report.noisySymbolRefCount}`);
164
177
  console.log(`Unresolved local imports: ${report.unresolvedLocalImportCount}`);
165
178
  console.log(`Unresolved call edges: ${report.unresolvedCallCount}`);
166
- console.log(`Duplicate cognition titles: ${report.duplicateTitleCount}`);
179
+ console.log(`Duplicate compatibility note titles: ${report.duplicateTitleCount}`);
167
180
  console.log(`Generated files scanned: ${report.generatedFileScanCount}`);
168
181
  console.log(`Expensive files: ${report.expensiveFileCount}`);
169
182
  console.log(`Session repeated reads: ${report.sessionRepeatedReadCount}`);
@@ -187,16 +200,16 @@ export function printQualityReport(report) {
187
200
  function summarizeQualityFindings(report) {
188
201
  const findings = [];
189
202
  if (report.orphanedNoteCount > 0) {
190
- findings.push(`${report.orphanedNoteCount} orphaned cognition note(s) (all refs dead); run \`kgraph repair\` to archive`);
203
+ findings.push(`${report.orphanedNoteCount} orphaned atom(s) (all refs dead); run \`kgraph repair\` to archive`);
191
204
  }
192
- if (report.mixedOrStaleCount > 0) {
193
- findings.push(`${report.mixedOrStaleCount} stale/mixed/unresolved note(s)`);
205
+ if (report.staleAtomCount > 0 || report.needsReviewAtomCount > 0) {
206
+ findings.push(`${report.staleAtomCount} stale atom(s), ${report.needsReviewAtomCount} needs-review atom(s)`);
194
207
  }
195
208
  if (report.noisyFileRefCount > 0 || report.noisySymbolRefCount > 0) {
196
- findings.push(`${report.noisyFileRefCount + report.noisySymbolRefCount} noisy cognition ref(s); run \`kgraph repair --dry-run\``);
209
+ findings.push(`${report.noisyFileRefCount + report.noisySymbolRefCount} noisy atom ref(s); run \`kgraph repair --dry-run\``);
197
210
  }
198
- if (report.duplicateTitleCount > 0) {
199
- findings.push(`${report.duplicateTitleCount} duplicate cognition title(s)`);
211
+ if (report.duplicateAtomTopicCount > 0) {
212
+ findings.push(`${report.duplicateAtomTopicCount} duplicate atom topic(s)`);
200
213
  }
201
214
  if (report.generatedFileScanCount > 0) {
202
215
  findings.push(`${report.generatedFileScanCount} generated/integration file(s) scanned; update excludes`);
@@ -1,6 +1,6 @@
1
1
  import { loadConfig } from '../../config/config.js';
2
2
  import { analyzeImpact } from '../../context/impact.js';
3
- import { readCognitionNotes } from '../../storage/cognition-store.js';
3
+ import { atomToCognitionNote, refreshKnowledgeAtomStatuses, } from '../../knowledge/atom-store.js';
4
4
  import { assertWorkspace } from '../../storage/kgraph-paths.js';
5
5
  import { mapsExist, readMaps } from '../../storage/map-store.js';
6
6
  import { KGraphError, runCommand } from '../errors.js';
@@ -17,11 +17,17 @@ export function registerImpactCommand(program) {
17
17
  if (!(await mapsExist(workspace))) {
18
18
  throw new KGraphError('KGraph maps are missing. Run `kgraph scan` first.');
19
19
  }
20
- const [config, maps, cognition] = await Promise.all([
20
+ const [config, maps] = await Promise.all([
21
21
  loadConfig(workspace),
22
22
  readMaps(workspace),
23
- readCognitionNotes(workspace),
24
23
  ]);
24
+ const { atoms } = await refreshKnowledgeAtomStatuses(workspace, {
25
+ fileMap: maps.fileMap,
26
+ symbolMap: maps.symbolMap,
27
+ });
28
+ const cognition = atoms
29
+ .filter((atom) => atom.status !== 'archived')
30
+ .map(atomToCognitionNote);
25
31
  const response = analyzeImpact(query, maps, cognition, config.maxContextItems);
26
32
  console.log(options.json ? JSON.stringify(response, null, 2) : renderImpactMarkdown(response));
27
33
  }));
@@ -40,8 +46,8 @@ export function renderImpactMarkdown(response) {
40
46
  lines.push(...formatList(response.calls.map((rel) => `- ${rel.sourceId} calls ${rel.targetId} (${rel.confidence})`)));
41
47
  lines.push('', '## Ownership', '');
42
48
  lines.push(...formatList(response.ownership.map((rel) => `- ${rel.sourceId} owns ${rel.targetId} (${rel.confidence})`)));
43
- lines.push('', '## Related Cognition', '');
44
- lines.push(...formatList(response.relatedCognition.map((note) => `- ${note.title} [${note.referencesStatus}]`)));
49
+ lines.push('', '## Related Knowledge', '');
50
+ lines.push(...formatList(response.relatedCognition.map((note) => `- ${note.title} [${note.referencesStatus}, ${note.confidence}]`)));
45
51
  lines.push('', '## Risk', '');
46
52
  lines.push(...formatList(response.risk.map((item) => `- ${item}`)));
47
53
  return lines.join('\n');
@@ -15,7 +15,7 @@ export function registerInitCommand(program) {
15
15
  .description('Initialize a .kgraph workspace')
16
16
  .option('--integration <name>', 'Configure an AI tool integration', collectOption, [])
17
17
  .option('--integrations <names>', 'Configure comma-separated AI tool integrations')
18
- .option('--mode <mode>', 'Integration mode: always, smart, manual, or off', 'smart')
18
+ .option('--mode <mode>', 'Integration mode: always, smart, manual, or off', 'always')
19
19
  .action((options) => runCommand(async () => {
20
20
  const workspace = await ensureWorkspace(process.cwd());
21
21
  await ensureKnowledgeStore(workspace);
@@ -55,7 +55,7 @@ export function registerInitCommand(program) {
55
55
  })) {
56
56
  const selected = await promptForInitIntegrations(recommendedIntegrations);
57
57
  if (selected.length > 0) {
58
- const changed = await addIntegrations(workspace, selected, 'smart');
58
+ const changed = await addIntegrations(workspace, selected, 'always');
59
59
  console.log(`Configured integrations: ${changed.map((item) => `${item.name}:${item.mode}`).join(', ')}`);
60
60
  config = await loadConfig(workspace);
61
61
  recommendedIntegrations = recommendedIntegrationsForInit({
@@ -25,7 +25,7 @@ export function registerIntegrateCommand(program) {
25
25
  .command('add')
26
26
  .description('Add AI tool integrations')
27
27
  .argument('<names...>')
28
- .option('--mode <mode>', 'always, smart, manual, or off', 'smart')
28
+ .option('--mode <mode>', 'always, smart, manual, or off', 'always')
29
29
  .action((names, options) => runCommand(async () => {
30
30
  const workspace = await assertWorkspace(process.cwd());
31
31
  const normalized = normalizeIntegrationNames(names);
@@ -6,8 +6,8 @@ import { printQualityReport } from './doctor.js';
6
6
  export function registerRepairCommand(program) {
7
7
  program
8
8
  .command('repair')
9
- .description('Clean noisy stale references from KGraph cognition')
10
- .option('--dry-run', 'Show proposed cognition cleanup without writing files')
9
+ .description('Clean noisy stale references from KGraph knowledge atoms')
10
+ .option('--dry-run', 'Show proposed atom cleanup without writing files')
11
11
  .action((options) => runCommand(async () => {
12
12
  const workspace = await assertWorkspace(process.cwd());
13
13
  if (!(await mapsExist(workspace))) {
@@ -21,7 +21,7 @@ export function registerRepairCommand(program) {
21
21
  console.log('');
22
22
  printQualityReport(report);
23
23
  if (report.changes.length === 0) {
24
- console.log('No noisy cognition references found.');
24
+ console.log('No noisy atom references found.');
25
25
  }
26
26
  else if (options.dryRun) {
27
27
  console.log('');
@@ -1,8 +1,13 @@
1
1
  import { rm } from 'node:fs/promises';
2
+ import path from 'node:path';
2
3
  import { loadConfig } from '../../config/config.js';
3
4
  import { removeIntegrations } from '../../integrations/integration-store.js';
4
5
  import { pathExists, resolveWorkspace } from '../../storage/kgraph-paths.js';
5
6
  import { runCommand } from '../errors.js';
7
+ const LEGACY_GENERATED_FILES = [
8
+ '.github/agents/kgraph.agent.md',
9
+ '.github/kgraph.agent.md',
10
+ ];
6
11
  export function registerUninstallCommand(program) {
7
12
  program
8
13
  .command('uninstall')
@@ -28,6 +33,7 @@ export function registerUninstallCommand(program) {
28
33
  !options.keepIntegrations &&
29
34
  configuredIntegrations.length > 0) {
30
35
  await removeIntegrations(workspace, configuredIntegrations);
36
+ await removeLegacyGeneratedFiles(workspace.rootPath);
31
37
  }
32
38
  if (initialized) {
33
39
  await rm(workspace.kgraphPath, { recursive: true, force: true });
@@ -37,6 +43,9 @@ export function registerUninstallCommand(program) {
37
43
  console.log('Run `kgraph init` to set up this repository again.');
38
44
  }));
39
45
  }
46
+ async function removeLegacyGeneratedFiles(rootPath) {
47
+ await Promise.all(LEGACY_GENERATED_FILES.map((filePath) => rm(path.join(rootPath, filePath), { force: true })));
48
+ }
40
49
  function printUninstallPreview(input) {
41
50
  console.log('KGraph Uninstall Preview');
42
51
  console.log('');
@@ -1,7 +1,7 @@
1
1
  import { exec } from 'node:child_process';
2
2
  import { createServer } from 'node:http';
3
3
  import { loadConfig } from '../../config/config.js';
4
- import { readCognitionNotes } from '../../storage/cognition-store.js';
4
+ import { refreshKnowledgeAtomStatuses } from '../../knowledge/atom-store.js';
5
5
  import { assertWorkspace } from '../../storage/kgraph-paths.js';
6
6
  import { mapsExist, readMaps } from '../../storage/map-store.js';
7
7
  import { buildGraph } from '../../visualization/graph-builder.js';
@@ -22,12 +22,13 @@ export function registerVisualizeCommand(program) {
22
22
  if (!(await mapsExist(workspace))) {
23
23
  throw new KGraphError('KGraph maps are missing. Run `kgraph scan` first.');
24
24
  }
25
- const [maps, cognition] = await Promise.all([
26
- readMaps(workspace),
27
- readCognitionNotes(workspace),
28
- ]);
25
+ const maps = await readMaps(workspace);
26
+ const { atoms } = await refreshKnowledgeAtomStatuses(workspace, {
27
+ fileMap: maps.fileMap,
28
+ symbolMap: maps.symbolMap,
29
+ });
29
30
  await loadConfig(workspace); // ensure workspace is valid
30
- const graphData = buildGraph(maps.fileMap, maps.symbolMap, maps.dependencyMap, maps.relationshipMap, cognition);
31
+ const graphData = buildGraph(maps.fileMap, maps.symbolMap, maps.dependencyMap, maps.relationshipMap, atoms);
31
32
  const html = renderHtml(graphData, workspace.rootPath);
32
33
  await serveGraph(html, port, options.open);
33
34
  }));
package/dist/cli/help.js CHANGED
@@ -22,7 +22,7 @@ export function renderRootHelp(useColor = supportsColor()) {
22
22
  command('init --integrations codex,gemini', 'Initialize and connect AI tools'),
23
23
  '',
24
24
  theme.bold('Daily workflow'),
25
- command('kgraph', 'Refresh scan maps and process pending cognition notes'),
25
+ command('kgraph', 'Refresh scan maps and process pending capture notes'),
26
26
  command('kgraph "auth token refresh"', 'Refresh everything and return compact context for a topic'),
27
27
  '',
28
28
  theme.bold('Workflows'),
@@ -30,19 +30,19 @@ export function renderRootHelp(useColor = supportsColor()) {
30
30
  command('session', 'Show agent read/write activity and token estimates'),
31
31
  command('session read src/auth.ts --agent codex', 'Record an agent file read'),
32
32
  command('session end --agent codex --conclude', 'End tracking and store a durable session summary'),
33
- command('conclude "auth refresh gotcha"', 'Store typed engineering cognition'),
34
- command('compact', 'Merge duplicate cognition and archive stale noise'),
33
+ command('conclude "auth refresh gotcha"', 'Store typed engineering knowledge'),
34
+ command('compact', 'Merge duplicate atoms and archive stale noise'),
35
35
  command('knowledge list', 'Inspect canonical knowledge atoms'),
36
36
  command('pack "auth task" --budget 8000', 'Build a budget-aware context pack'),
37
37
  command('stale', 'Show atoms invalidated by changed or missing refs'),
38
38
  command('blame <atom-id>', 'Show atom provenance and evidence'),
39
39
  command('context "auth token refresh"', 'Optional: return context without scanning or updating'),
40
- command('impact "Button"', 'Show imports, callers, calls, cognition, and risk'),
41
- command('update', 'Optional: process only .kgraph/inbox Markdown cognition notes'),
40
+ command('impact "Button"', 'Show imports, callers, calls, knowledge, and risk'),
41
+ command('update', 'Optional: process only .kgraph/inbox capture notes'),
42
42
  command('doctor', 'Check workspace health and next actions'),
43
- command('doctor --quality', 'Report stale/noisy cognition references'),
44
- command('repair --dry-run', 'Preview cognition reference cleanup'),
45
- command('repair', 'Clean noisy stale cognition references'),
43
+ command('doctor --quality', 'Report stale/noisy atom references'),
44
+ command('repair --dry-run', 'Preview atom reference cleanup'),
45
+ command('repair', 'Clean noisy stale atom references'),
46
46
  command('uninstall', 'Preview repo-local KGraph removal'),
47
47
  command('uninstall --yes', 'Remove .kgraph/ and managed integrations'),
48
48
  command('visualize', 'Interactive dependency graph at http://localhost:4242'),
@@ -51,7 +51,7 @@ export function renderRootHelp(useColor = supportsColor()) {
51
51
  theme.bold('Integrations'),
52
52
  command('integrate list', 'Show configured AI tool integrations'),
53
53
  command('integrate add gemini windsurf cline', 'Write KGraph instructions using always mode by default'),
54
- command('integrate add copilot --mode always', 'Every Copilot chat starts with kgraph "<topic>"'),
54
+ command('integrate add copilot --mode smart', 'Run KGraph for repo-specific Copilot work only'),
55
55
  command('integrate set copilot --mode manual', 'Only run KGraph when explicitly requested'),
56
56
  command('integrate remove cursor', 'Remove KGraph-managed instruction blocks'),
57
57
  command('--mode smart|always|manual|off', 'Control automatic KGraph involvement per integration'),
@@ -91,13 +91,13 @@ export function renderWorkflowBanner(stats, useColor = supportsColor()) {
91
91
  ? ` (${stats.skippedFiles} unchanged, skipped)`
92
92
  : '')),
93
93
  command('symbols', String(stats.symbols)),
94
- command('cognition notes processed', String(stats.cognitionNotes)),
94
+ command('capture notes processed', String(stats.cognitionNotes)),
95
95
  command('integration modes', integrationLine),
96
96
  '',
97
97
  theme.bold('Next'),
98
98
  command('kgraph "auth token refresh"', 'Return compact context for a topic'),
99
99
  command('kgraph doctor', 'Check workspace health'),
100
- command('kgraph doctor --quality', 'Check cognition quality'),
100
+ command('kgraph doctor --quality', 'Check atom quality'),
101
101
  command('kgraph knowledge list', 'Inspect knowledge atoms'),
102
102
  command('kgraph pack "auth task"', 'Build budget-aware context'),
103
103
  command('kgraph session', 'Check session token waste'),
@@ -9,6 +9,11 @@ export interface CognitionRepairChange {
9
9
  nextStatus: ReferenceStatus;
10
10
  }
11
11
  export interface CognitionQualityReport {
12
+ atomCount: number;
13
+ staleAtomCount: number;
14
+ needsReviewAtomCount: number;
15
+ archivedAtomCount: number;
16
+ duplicateAtomTopicCount: number;
12
17
  noteCount: number;
13
18
  mixedOrStaleCount: number;
14
19
  noisyFileRefCount: number;
@@ -1,9 +1,13 @@
1
1
  import { mkdir, rename } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
+ import { atomToCognitionNote, refreshKnowledgeAtomStatuses, writeKnowledgeAtoms, } from '../knowledge/atom-store.js';
3
4
  import { buildSessionReport } from '../session/session-store.js';
4
- import { overwriteDomainRecord, readCognitionNotes, readDomainRecords, writeCognitionNote, } from '../storage/cognition-store.js';
5
+ import { overwriteDomainRecord, readDomainRecords, writeCognitionNote, } from '../storage/cognition-store.js';
5
6
  export async function analyzeCognitionQuality(workspace, maps) {
6
- const notes = await readCognitionNotes(workspace);
7
+ const refreshed = await refreshKnowledgeAtomStatuses(workspace, { fileMap: maps.fileMap, symbolMap: maps.symbolMap }, true);
8
+ const atoms = refreshed.atoms;
9
+ const activeAtoms = atoms.filter((atom) => atom.status !== 'archived');
10
+ const notes = activeAtoms.map(atomToCognitionNote);
7
11
  const session = await buildSessionReport(workspace);
8
12
  const changes = notes
9
13
  .map((note) => analyzeNote(note, maps))
@@ -11,13 +15,18 @@ export async function analyzeCognitionQuality(workspace, maps) {
11
15
  change.removedSymbolRefs.length > 0);
12
16
  const orphanedNoteCount = notes.filter((note) => note.referencesStatus === 'stale').length;
13
17
  return {
18
+ atomCount: activeAtoms.length,
19
+ staleAtomCount: activeAtoms.filter((atom) => atom.status === 'stale').length,
20
+ needsReviewAtomCount: activeAtoms.filter((atom) => atom.status === 'needs-review').length,
21
+ archivedAtomCount: atoms.filter((atom) => atom.status === 'archived').length,
22
+ duplicateAtomTopicCount: countDuplicateAtomTopics(activeAtoms),
14
23
  noteCount: notes.length,
15
24
  mixedOrStaleCount: notes.filter((note) => ['mixed', 'stale', 'unresolved'].includes(note.referencesStatus)).length,
16
25
  noisyFileRefCount: changes.reduce((total, change) => total + change.removedFileRefs.length, 0),
17
26
  noisySymbolRefCount: changes.reduce((total, change) => total + change.removedSymbolRefs.length, 0),
18
27
  unresolvedLocalImportCount: countUnresolvedLocalImports(maps.dependencyMap),
19
28
  unresolvedCallCount: countUnresolvedCalls(maps.symbolMap, maps.relationshipMap),
20
- duplicateTitleCount: countDuplicateTitles(notes),
29
+ duplicateTitleCount: countDuplicateAtomTopics(activeAtoms),
21
30
  generatedFileScanCount: countGeneratedScannedFiles(maps.fileMap),
22
31
  expensiveFileCount: countExpensiveFiles(maps.fileMap),
23
32
  sessionRepeatedReadCount: session.repeatedReadCount,
@@ -28,10 +37,14 @@ export async function analyzeCognitionQuality(workspace, maps) {
28
37
  };
29
38
  }
30
39
  export async function repairCognition(workspace, maps, dryRun = false) {
31
- const notes = await readCognitionNotes(workspace);
40
+ const refreshed = await refreshKnowledgeAtomStatuses(workspace, { fileMap: maps.fileMap, symbolMap: maps.symbolMap }, dryRun);
41
+ const atoms = refreshed.atoms;
42
+ const activeAtoms = atoms.filter((atom) => atom.status !== 'archived');
43
+ const notes = activeAtoms.map(atomToCognitionNote);
32
44
  const session = await buildSessionReport(workspace);
33
45
  const nextNotes = [];
34
46
  const changes = [];
47
+ const changesById = new Map();
35
48
  for (const note of notes) {
36
49
  const change = analyzeNote(note, maps);
37
50
  const nextNote = applyChange(note, change);
@@ -39,6 +52,7 @@ export async function repairCognition(workspace, maps, dryRun = false) {
39
52
  if (change.removedFileRefs.length > 0 ||
40
53
  change.removedSymbolRefs.length > 0) {
41
54
  changes.push(change);
55
+ changesById.set(change.noteId, change);
42
56
  if (!dryRun) {
43
57
  await writeCognitionNote(workspace, nextNote);
44
58
  }
@@ -47,8 +61,58 @@ export async function repairCognition(workspace, maps, dryRun = false) {
47
61
  // Archive fully-orphaned notes (all refs dead) so they no longer appear in context
48
62
  const orphanedNotes = nextNotes.filter((note) => note.referencesStatus === 'stale');
49
63
  if (!dryRun && (changes.length > 0 || orphanedNotes.length > 0)) {
64
+ const now = new Date().toISOString();
65
+ const nextAtoms = atoms.map((atom) => {
66
+ if (atom.status === 'archived')
67
+ return atom;
68
+ const change = changesById.get(atom.id);
69
+ if (!change && !orphanedNotes.some((note) => note.id === atom.id)) {
70
+ return atom;
71
+ }
72
+ if (orphanedNotes.some((note) => note.id === atom.id)) {
73
+ return {
74
+ ...atom,
75
+ status: 'archived',
76
+ confidence: 'low',
77
+ lifecycle: { ...atom.lifecycle, archivedAt: now },
78
+ provenance: { ...atom.provenance, updatedAt: now },
79
+ };
80
+ }
81
+ const removedFiles = new Set(change?.removedFileRefs ?? []);
82
+ const removedSymbols = new Set(change?.removedSymbolRefs ?? []);
83
+ const nextStatus = atomStatusFromReferenceStatus(change?.nextStatus ?? 'current');
84
+ return {
85
+ ...atom,
86
+ status: nextStatus,
87
+ confidence: atom.confidence === 'low' && atom.status === 'stale' && nextStatus !== 'stale'
88
+ ? 'medium'
89
+ : atom.confidence,
90
+ scopeRefs: {
91
+ ...atom.scopeRefs,
92
+ files: atom.scopeRefs.files.filter((file) => !removedFiles.has(file)),
93
+ symbols: atom.scopeRefs.symbols.filter((symbol) => !removedSymbols.has(symbol)),
94
+ },
95
+ evidenceRefs: atom.evidenceRefs.filter((ref) => {
96
+ if (ref.type === 'file')
97
+ return !removedFiles.has(ref.path);
98
+ if (ref.type === 'symbol')
99
+ return !removedSymbols.has(ref.name);
100
+ return true;
101
+ }),
102
+ lifecycle: {
103
+ ...atom.lifecycle,
104
+ invalidatedBy: change?.nextStatus === 'current'
105
+ ? undefined
106
+ : atom.lifecycle.invalidatedBy,
107
+ },
108
+ provenance: { ...atom.provenance, updatedAt: now },
109
+ };
110
+ });
111
+ await writeKnowledgeAtoms(workspace, nextAtoms);
50
112
  // Exclude fully-orphaned notes from domain records — they are being archived
51
- await repairDomainRecords(workspace, nextNotes.filter((n) => n.referencesStatus !== 'stale'), maps);
113
+ await repairDomainRecords(workspace, nextAtoms
114
+ .filter((atom) => atom.status !== 'archived')
115
+ .map(atomToCognitionNote), maps);
52
116
  }
53
117
  if (!dryRun) {
54
118
  for (const note of orphanedNotes) {
@@ -57,6 +121,11 @@ export async function repairCognition(workspace, maps, dryRun = false) {
57
121
  }
58
122
  const orphanedNoteCount = orphanedNotes.length;
59
123
  return {
124
+ atomCount: activeAtoms.length,
125
+ staleAtomCount: nextNotes.filter((note) => note.referencesStatus === 'stale').length,
126
+ needsReviewAtomCount: nextNotes.filter((note) => note.referencesStatus === 'mixed').length,
127
+ archivedAtomCount: atoms.filter((atom) => atom.status === 'archived').length + orphanedNoteCount,
128
+ duplicateAtomTopicCount: countDuplicateTitles(nextNotes),
60
129
  noteCount: notes.length,
61
130
  mixedOrStaleCount: nextNotes.filter((note) => ['mixed', 'stale', 'unresolved'].includes(note.referencesStatus)).length,
62
131
  noisyFileRefCount: changes.reduce((total, change) => total + change.removedFileRefs.length, 0),
@@ -98,6 +167,23 @@ function countDuplicateTitles(notes) {
98
167
  }
99
168
  return duplicates.size;
100
169
  }
170
+ function countDuplicateAtomTopics(atoms) {
171
+ const seen = new Set();
172
+ const duplicates = new Set();
173
+ for (const atom of atoms) {
174
+ const key = [
175
+ atom.type,
176
+ atom.topic.trim().toLowerCase(),
177
+ atom.claim.trim().toLowerCase(),
178
+ ].join('\0');
179
+ if (!atom.topic.trim())
180
+ continue;
181
+ if (seen.has(key))
182
+ duplicates.add(key);
183
+ seen.add(key);
184
+ }
185
+ return duplicates.size;
186
+ }
101
187
  function countGeneratedScannedFiles(fileMap) {
102
188
  return fileMap.files.filter((file) => [
103
189
  '.agents/',
@@ -175,6 +261,13 @@ function evaluateReferenceStatus(relatedFiles, relatedSymbols, maps) {
175
261
  return 'stale';
176
262
  return 'mixed';
177
263
  }
264
+ function atomStatusFromReferenceStatus(status) {
265
+ if (status === 'current')
266
+ return 'active';
267
+ if (status === 'mixed')
268
+ return 'needs-review';
269
+ return 'stale';
270
+ }
178
271
  function isNoisyFileRef(ref) {
179
272
  return (!ref.includes('/') && /^[A-Z][A-Za-z0-9_-]*\.[A-Za-z0-9_-]+$/.test(ref));
180
273
  }
@@ -157,7 +157,7 @@ function normalizeIntegrations(value) {
157
157
  return integrations;
158
158
  }
159
159
  function normalizeIntegrationMode(value) {
160
- return value === 'always' || value === 'manual' || value === 'off'
160
+ return value === 'smart' || value === 'manual' || value === 'off'
161
161
  ? value
162
- : 'smart';
162
+ : 'always';
163
163
  }
@@ -1,4 +1,3 @@
1
- import { numberedWorkflow } from '../workflow-steps.js';
2
1
  export const claudeCodeAdapter = {
3
2
  name: 'claude-code',
4
3
  label: 'Claude Code',
@@ -6,13 +5,22 @@ export const claudeCodeAdapter = {
6
5
  instructions: `## KGraph Workflow
7
6
 
8
7
  {{KGRAPH_CONTEXT_POLICY}} Use /kgraph for the full automated workflow. Run \`kgraph pack "<task>" --budget 8000 --json\` for a machine-readable token-budgeted context pack, \`kgraph knowledge list\` or \`kgraph knowledge get <atom-id>\` to inspect durable atoms, \`kgraph stale\` and \`kgraph blame <atom-id>\` when lifecycle/provenance matters, \`kgraph conclude\` for durable typed engineering memory, and \`kgraph compact --dry-run\` when cognition looks duplicated or stale. 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
+
9
+ {{KGRAPH_CAPTURE_POLICY}}
9
10
  `,
10
11
  commandFiles: [
11
12
  {
12
13
  path: '.claude/commands/kgraph.md',
13
- content: `Use KGraph persistent repo intelligence for the current request.
14
+ content: `Use KGraph persistent repo intelligence through the single normal \`kgraph "<topic>"\` entry point.
14
15
 
15
- ${numberedWorkflow('claude-code', { sessionQualifier: 'when native hooks are unavailable' })}
16
+ 1. Infer a concise topic from the user's request.
17
+ 2. Run exactly one command from the repository root: \`kgraph "<topic>"\`.
18
+ 3. Treat the returned files, symbols, relationships, atoms, and warnings as the first-pass source of truth.
19
+ 4. If the user asked for an edit, inspect only the returned candidate file or the smallest necessary range, then make the edit.
20
+ 5. Verify the change actually landed before claiming completion. Prefer a narrow read of the changed range or \`git diff -- <path>\`; if there is no diff or the expected text is missing, say the edit did not apply and fix it before summarizing.
21
+ 6. Do not run \`kgraph\` again, \`kgraph context\`, \`kgraph pack\`, \`kgraph knowledge\`, \`kgraph stale\`, \`kgraph blame\`, \`kgraph scan\`, \`kgraph update\`, \`kgraph compact\`, or \`kgraph repair\` unless the user explicitly asks for that lower-level command.
22
+ 7. Do not continue broad repository search after the target file is identified. If a path must be located, prefer \`rg --files\` and quote paths containing spaces or parentheses.
23
+ 8. At the end of repository-file changes, store durable engineering memory with \`kgraph conclude "<topic>" --type <finding|decision|gotcha|summary|relationship> --confidence <high|medium|low>\` only when the work created reusable engineering knowledge.
16
24
  `,
17
25
  },
18
26
  {
@@ -22,7 +30,7 @@ ${numberedWorkflow('claude-code', { sessionQualifier: 'when native hooks are una
22
30
  },
23
31
  {
24
32
  path: '.claude/commands/kgraph-repair.md',
25
- content: `Run \`kgraph repair --dry-run\` first and summarize the proposed cognition cleanup. Run \`kgraph repair\` only when the user asks to apply the cleanup.
33
+ content: `Run \`kgraph repair --dry-run\` first and summarize the proposed atom-reference cleanup. Run \`kgraph repair\` only when the user asks to apply the cleanup.
26
34
  `,
27
35
  },
28
36
  {
@@ -67,7 +75,7 @@ ${numberedWorkflow('claude-code', { sessionQualifier: 'when native hooks are una
67
75
  },
68
76
  {
69
77
  path: '.claude/commands/kgraph-impact.md',
70
- content: `Run \`kgraph impact "$ARGUMENTS"\` to show matched files/symbols, import users, callers, callees, related cognition, and risk hints.
78
+ content: `Run \`kgraph impact "$ARGUMENTS"\` to show matched files/symbols, import users, callers, callees, related knowledge atoms, and risk hints.
71
79
  `,
72
80
  },
73
81
  {
@@ -8,26 +8,6 @@ export const copilotAdapter = {
8
8
  ${numberedWorkflow('copilot')}
9
9
  `,
10
10
  commandFiles: [
11
- {
12
- path: '.github/agents/kgraph.agent.md',
13
- content: `---
14
- name: kgraph
15
- description: Use KGraph persistent repo intelligence to answer questions about this codebase. Runs kgraph context, pack, knowledge, stale, blame, scan, update, conclude, compact, impact, history, and session commands to ground responses in durable local knowledge.
16
- tools:
17
- - run_in_terminal
18
- - read_file
19
- - file_search
20
- - grep_search
21
- - semantic_search
22
- ---
23
-
24
- ## KGraph Agent
25
-
26
- You are a KGraph-powered agent. Before exploring the repository freely, always:
27
-
28
- ${numberedWorkflow('copilot')}
29
- `,
30
- },
31
11
  {
32
12
  path: '.github/prompts/kgraph-doctor.prompt.md',
33
13
  content: `---
@@ -46,7 +26,7 @@ agent: agent
46
26
  argument-hint: "--dry-run or apply"
47
27
  ---
48
28
 
49
- Run \`kgraph repair --dry-run\` first and summarize the proposed cognition cleanup. Run \`kgraph repair\` only when the user asks to apply the cleanup.
29
+ Run \`kgraph repair --dry-run\` first and summarize the proposed atom-reference cleanup. Run \`kgraph repair\` only when the user asks to apply the cleanup.
50
30
  `,
51
31
  },
52
32
  {
@@ -165,7 +145,7 @@ agent: agent
165
145
  argument-hint: "File, symbol, or topic"
166
146
  ---
167
147
 
168
- Run \`kgraph impact "$ARGUMENTS"\` to show matched files/symbols, import users, callers, callees, related cognition, and risk hints.
148
+ Run \`kgraph impact "$ARGUMENTS"\` to show matched files/symbols, import users, callers, callees, related knowledge atoms, and risk hints.
169
149
  `,
170
150
  },
171
151
  {
@@ -36,16 +36,17 @@ export function applyContextPolicy(content, mode) {
36
36
  .replaceAll(KGRAPH_CAPTURE_POLICY_PLACEHOLDER, renderCapturePolicy());
37
37
  }
38
38
  export function renderContextPolicy(mode) {
39
+ const useResultBoundary = 'Use the returned KGraph candidate files as the first-pass source of truth. Do not rerun the same KGraph query just to tail or reformat output, do not continue broad repository search after the target file is identified, do not retry malformed shell commands with broader variants, and do not run broad `find`, recursive `grep`, or repeated full-file dumps after KGraph has narrowed the target.';
39
40
  switch (mode) {
40
41
  case 'always':
41
- return 'Every chat in this repository must start by running `kgraph "<topic>"` before answering or exploring files. Infer the topic from the user\'s message; no exceptions, including simple or conversational requests.';
42
+ return `Every chat in this repository must start by running \`kgraph "<topic>"\` before answering or exploring files. Infer the topic from the user's message; no exceptions, including simple or conversational requests. ${useResultBoundary}`;
42
43
  case 'manual':
43
44
  return 'Do not run KGraph automatically. Run `kgraph "<topic>"` only when the user explicitly asks for KGraph context or invokes the KGraph command.';
44
45
  case 'off':
45
46
  return 'KGraph is disabled for this integration.';
46
47
  case 'smart':
47
48
  default:
48
- return 'For repo-specific coding, debugging, architecture, refactor, review, or file-exploration requests, run `kgraph "<topic>"` before broad repository exploration. Infer the topic from the user\'s message. Skip KGraph for simple conversational requests that do not depend on repo knowledge.';
49
+ return `For repo-specific coding, debugging, architecture, refactor, review, or file-exploration requests, run \`kgraph "<topic>"\` before broad repository exploration. Infer the topic from the user's message. Skip KGraph for simple conversational requests that do not depend on repo knowledge. ${useResultBoundary}`;
49
50
  }
50
51
  }
51
52
  export function renderCapturePolicy() {
@@ -15,7 +15,7 @@ export async function listIntegrations(workspace) {
15
15
  })));
16
16
  return statuses.sort((left, right) => left.name.localeCompare(right.name));
17
17
  }
18
- export async function addIntegrations(workspace, names, mode = 'smart') {
18
+ export async function addIntegrations(workspace, names, mode = 'always') {
19
19
  const config = await loadConfig(workspace);
20
20
  const byName = new Map(config.integrations.map((integration) => [integration.name, integration]));
21
21
  const changed = [];
@@ -4,12 +4,14 @@
4
4
  */
5
5
  const DOCTOR_STEP = `Run \`kgraph doctor\` when setup, maps, inbox processing, or integrations look wrong. Run \`kgraph doctor --quality\` when context shows stale/noisy cognition references.`;
6
6
  const IMPACT_STEP = `Run \`kgraph impact "<file-or-symbol>"\` when the user asks what a change may affect. Run \`kgraph history "<topic>"\` when prior work or decisions matter.`;
7
- const REPAIR_STEP = `Run \`kgraph repair --dry-run\` before cleanup when stale/noisy cognition needs fixing. Run \`kgraph repair\` only when the user asks to apply that cleanup.`;
7
+ const REPAIR_STEP = `Run \`kgraph repair --dry-run\` before cleanup when stale/noisy atom refs need fixing. Run \`kgraph repair\` only when the user asks to apply that cleanup.`;
8
8
  const COMPACT_STEP = `Run \`kgraph compact --dry-run\` when cognition looks duplicated, noisy, or stale. Run \`kgraph compact\` only when the user asks to merge/archive cognition.`;
9
9
  const HISTORY_STEP = `Run \`kgraph history\` or \`kgraph history "<topic>"\` to review past cognition sessions with git author attribution.`;
10
10
  const KNOWLEDGE_STEP = `Run \`kgraph knowledge list --topic "<topic>"\` or \`kgraph knowledge get <atom-id>\` when the user asks what KGraph remembers or atom provenance/lifecycle matters.`;
11
11
  const PACK_STEP = `Run \`kgraph pack "<task>" --budget 8000 --json\` when an agent needs a machine-readable, token-budgeted context pack instead of human Markdown context.`;
12
12
  const STALE_STEP = `Run \`kgraph stale\` when changed or deleted code may have invalidated durable knowledge. Run \`kgraph blame <atom-id>\` when provenance or evidence for a memory matters.`;
13
+ const EXPLORATION_BOUNDARY_STEP = `Keep exploration bounded by the task. For simple edits, use KGraph to identify the likely file, then read only that file or a narrow range and make the edit. Do not keep searching after the target file is found, do not retry malformed shell commands with broader variants, and do not run broad \`find\`, recursive \`grep\`, or repeated full-file dumps after KGraph already returned candidate files. Use \`rg --files\` and quoted paths when a path must be located.`;
14
+ const VERIFY_EDIT_STEP = `After editing, verify the change actually landed before claiming completion. Prefer a narrow read of the changed range or \`git diff -- <path>\`; if there is no diff or the expected text is missing, say the edit did not apply and fix it before summarizing.`;
13
15
  function sessionStep(agentName, qualifier) {
14
16
  const base = `Track meaningful session activity with \`kgraph session start --agent ${agentName}\`, \`kgraph session read <path> --agent ${agentName}\`, \`kgraph session write <path> --agent ${agentName}\`, and \`kgraph session end --agent ${agentName} --conclude --topic "<topic>"\` when durable session memory is useful`;
15
17
  return qualifier ? `${base} ${qualifier}.` : `${base}.`;
@@ -22,19 +24,21 @@ export function numberedWorkflow(agentName, options = {}) {
22
24
  return `1. Infer the topic from the user's request.
23
25
  2. {{KGRAPH_CONTEXT_POLICY}}
24
26
  3. Use the returned files, symbols, relationships, and cognition before broad exploration.
25
- 4. ${PACK_STEP}
26
- 5. ${KNOWLEDGE_STEP}
27
- 6. ${DOCTOR_STEP}
28
- 7. ${STALE_STEP}
29
- 8. ${sessionStep(agentName, options.sessionQualifier)}
30
- 9. ${IMPACT_STEP}
27
+ 4. ${EXPLORATION_BOUNDARY_STEP}
28
+ 5. ${VERIFY_EDIT_STEP}
29
+ 6. ${PACK_STEP}
30
+ 7. ${KNOWLEDGE_STEP}
31
+ 8. ${DOCTOR_STEP}
32
+ 9. ${STALE_STEP}
33
+ 10. ${sessionStep(agentName, options.sessionQualifier)}
34
+ 11. ${IMPACT_STEP}
31
35
 
32
36
  {{KGRAPH_CAPTURE_POLICY}}
33
37
 
34
- 10. ${REPAIR_STEP}
35
- 11. ${COMPACT_STEP}
36
- 12. Run \`kgraph visualize\` when the user wants to inspect the dependency graph — opens an interactive graph at http://localhost:4242 with PNG export.
37
- 13. ${HISTORY_STEP}`;
38
+ 12. ${REPAIR_STEP}
39
+ 13. ${COMPACT_STEP}
40
+ 14. Run \`kgraph visualize\` when the user wants to inspect the dependency graph — opens an interactive graph at http://localhost:4242 with PNG export.
41
+ 15. ${HISTORY_STEP}`;
38
42
  }
39
43
  /**
40
44
  * Returns the bullet-list workflow for rules files.
@@ -42,6 +46,8 @@ export function numberedWorkflow(agentName, options = {}) {
42
46
  */
43
47
  export function bulletWorkflow(agentName, options = {}) {
44
48
  return `- {{KGRAPH_CONTEXT_POLICY}}
49
+ - ${EXPLORATION_BOUNDARY_STEP}
50
+ - ${VERIFY_EDIT_STEP}
45
51
  - ${PACK_STEP}
46
52
  - ${KNOWLEDGE_STEP}
47
53
  - ${DOCTOR_STEP}
@@ -1,4 +1,4 @@
1
- import type { CognitionNote } from '../types/cognition.js';
1
+ import type { KnowledgeAtom } from '../types/knowledge.js';
2
2
  import type { DependencyMap, FileMap, RelationshipMap, SymbolMap } from '../types/maps.js';
3
3
  export interface CytoscapeElement {
4
4
  data: Record<string, unknown>;
@@ -9,9 +9,12 @@ export interface GraphData {
9
9
  meta: {
10
10
  fileCount: number;
11
11
  symbolCount: number;
12
+ atomCount: number;
13
+ hiddenAtomCount: number;
14
+ /** @deprecated Kept for older tests/consumers that still read cognitionCount. */
12
15
  cognitionCount: number;
13
16
  tokenEstimate: number;
14
17
  generatedAt: string;
15
18
  };
16
19
  }
17
- export declare function buildGraph(fileMap: FileMap, symbolMap: SymbolMap, dependencyMap: DependencyMap, relationshipMap: RelationshipMap, cognitionNotes: CognitionNote[]): GraphData;
20
+ export declare function buildGraph(fileMap: FileMap, symbolMap: SymbolMap, dependencyMap: DependencyMap, relationshipMap: RelationshipMap, knowledgeAtoms: KnowledgeAtom[]): GraphData;
@@ -8,10 +8,10 @@ const LANGUAGE_COLORS = {
8
8
  html: '#f97316',
9
9
  };
10
10
  const STATUS_COLORS = {
11
- current: '#10b981',
12
- mixed: '#f59e0b',
11
+ active: '#10b981',
12
+ 'needs-review': '#f59e0b',
13
13
  stale: '#ef4444',
14
- unresolved: '#6b7280',
14
+ archived: '#6b7280',
15
15
  };
16
16
  const SYMBOL_COLORS = {
17
17
  function: '#22c55e',
@@ -20,7 +20,8 @@ const SYMBOL_COLORS = {
20
20
  export: '#f97316',
21
21
  import: '#64748b',
22
22
  };
23
- export function buildGraph(fileMap, symbolMap, dependencyMap, relationshipMap, cognitionNotes) {
23
+ const MAX_ATOM_NODES = 250;
24
+ export function buildGraph(fileMap, symbolMap, dependencyMap, relationshipMap, knowledgeAtoms) {
24
25
  const elements = [];
25
26
  const edgeIds = new Set();
26
27
  const nodeIds = new Set();
@@ -59,22 +60,30 @@ export function buildGraph(fileMap, symbolMap, dependencyMap, relationshipMap, c
59
60
  classes: `symbol ${symbol.kind}`,
60
61
  });
61
62
  }
62
- for (const note of cognitionNotes) {
63
- const id = `cognition-${note.id}`;
63
+ const atomsForGraph = selectAtomsForGraph(knowledgeAtoms);
64
+ for (const atom of atomsForGraph) {
65
+ const id = `atom-${atom.id}`;
64
66
  elements.push({
65
67
  data: {
66
68
  id,
67
- label: note.title,
68
- color: STATUS_COLORS[note.referencesStatus] ?? STATUS_COLORS.unresolved,
69
- type: 'cognition',
70
- referencesStatus: note.referencesStatus,
71
- domain: note.domain ?? '',
72
- relatedFiles: note.relatedFiles,
73
- relatedSymbols: note.relatedSymbols,
69
+ label: atom.topic,
70
+ color: STATUS_COLORS[atom.status] ?? STATUS_COLORS.archived,
71
+ type: 'atom',
72
+ atomId: atom.id,
73
+ atomType: atom.type,
74
+ confidence: atom.confidence,
75
+ status: atom.status,
76
+ sourceCommand: atom.provenance.sourceCommand,
77
+ domain: atom.scopeRefs.domains[0] ?? '',
78
+ relatedFiles: atom.scopeRefs.files,
79
+ relatedSymbols: atom.scopeRefs.symbols,
80
+ supersededBy: atom.lifecycle.supersededBy ?? '',
81
+ supersedes: atom.lifecycle.supersedes,
82
+ invalidatedBy: atom.lifecycle.invalidatedBy ?? [],
74
83
  },
75
- classes: `cognition ${note.referencesStatus}`,
84
+ classes: `atom ${atom.status}`,
76
85
  });
77
- for (const filePath of note.relatedFiles) {
86
+ for (const filePath of atom.scopeRefs.files) {
78
87
  const target = fileMap.files.find((f) => f.path === filePath);
79
88
  if (target) {
80
89
  const edgeId = `${id}-ref-${target.id}`;
@@ -85,10 +94,10 @@ export function buildGraph(fileMap, symbolMap, dependencyMap, relationshipMap, c
85
94
  id: edgeId,
86
95
  source: id,
87
96
  target: target.id,
88
- type: 'cognition-ref',
97
+ type: 'atom-ref',
89
98
  label: '',
90
99
  },
91
- classes: 'cognition-ref',
100
+ classes: 'atom-ref',
92
101
  });
93
102
  }
94
103
  }
@@ -142,12 +151,28 @@ export function buildGraph(fileMap, symbolMap, dependencyMap, relationshipMap, c
142
151
  meta: {
143
152
  fileCount: fileMap.files.length,
144
153
  symbolCount: symbolMap.symbols.length,
145
- cognitionCount: cognitionNotes.length,
154
+ atomCount: knowledgeAtoms.filter((atom) => atom.status !== 'archived').length,
155
+ hiddenAtomCount: Math.max(0, knowledgeAtoms.filter((atom) => atom.status !== 'archived').length -
156
+ atomsForGraph.length),
157
+ cognitionCount: knowledgeAtoms.filter((atom) => atom.status !== 'archived').length,
146
158
  tokenEstimate,
147
159
  generatedAt: new Date().toISOString(),
148
160
  },
149
161
  };
150
162
  }
163
+ function selectAtomsForGraph(atoms) {
164
+ return atoms
165
+ .filter((atom) => atom.status !== 'archived')
166
+ .sort((left, right) => atomGraphScore(right) - atomGraphScore(left))
167
+ .slice(0, MAX_ATOM_NODES);
168
+ }
169
+ function atomGraphScore(atom) {
170
+ const statusScore = atom.status === 'active' ? 6 : atom.status === 'needs-review' ? 4 : 2;
171
+ const confidenceScore = atom.confidence === 'high' ? 3 : atom.confidence === 'medium' ? 2 : 0;
172
+ const evidenceScore = Math.min(4, atom.scopeRefs.files.length + atom.scopeRefs.symbols.length);
173
+ const typeScore = atom.type === 'decision' || atom.type === 'gotcha' ? 1 : 0;
174
+ return statusScore + confidenceScore + evidenceScore + typeScore;
175
+ }
151
176
  function getTokenBucket(tokenEstimate) {
152
177
  const tokens = tokenEstimate ?? 0;
153
178
  if (tokens >= 1000)
@@ -51,10 +51,10 @@ select:hover,button:hover{background:#475569}
51
51
  <body>
52
52
  <div id="toolbar">
53
53
  <span id="t-title">\u29e1 KGraph \u00b7 ${repoName}</span>
54
- <span id="t-stats">${meta.fileCount} files &middot; ${meta.symbolCount} symbols &middot; ${meta.cognitionCount} notes &middot; ~${meta.tokenEstimate} tokens</span>
54
+ <span id="t-stats">${meta.fileCount} files &middot; ${meta.symbolCount} symbols &middot; ${meta.atomCount} atoms${meta.hiddenAtomCount ? ' (' + meta.hiddenAtomCount + ' hidden)' : ''} &middot; ~${meta.tokenEstimate} tokens</span>
55
55
  <div id="t-controls">
56
56
  <label class="clabel"><input type="checkbox" id="tog-lbl" checked> Labels</label>
57
- <label class="clabel"><input type="checkbox" id="tog-cog" checked> Cognition</label>
57
+ <label class="clabel"><input type="checkbox" id="tog-cog" checked> Memory</label>
58
58
  <select id="sel-layout" title="Graph layout algorithm">
59
59
  <option value="dagre">Hierarchical</option>
60
60
  <option value="cose">Force-directed</option>
@@ -85,9 +85,9 @@ select:hover,button:hover{background:#475569}
85
85
  <span class="li"><span class="li-dot" style="background:#475569"></span>200+ tok</span>
86
86
  <span class="li"><span class="li-dot" style="background:#ef4444"></span>1000+ tok</span>
87
87
  <span class="li-sep"></span>
88
- <span class="li-head">Cognition</span>
89
- <span class="li"><span class="li-dia" style="background:#10b981"></span>Current</span>
90
- <span class="li"><span class="li-dia" style="background:#f59e0b"></span>Mixed</span>
88
+ <span class="li-head">Atoms</span>
89
+ <span class="li"><span class="li-dia" style="background:#10b981"></span>Active</span>
90
+ <span class="li"><span class="li-dia" style="background:#f59e0b"></span>Review</span>
91
91
  <span class="li"><span class="li-dia" style="background:#ef4444"></span>Stale</span>
92
92
  <span class="li-sep"></span>
93
93
  <span class="li" style="margin-left:auto;color:#334155;font-size:10px">KGraph v${meta.generatedAt.slice(0, 10)}</span>
@@ -166,7 +166,7 @@ select:hover,button:hover{background:#475569}
166
166
  }
167
167
  },
168
168
  {
169
- selector: 'node.cognition',
169
+ selector: 'node.atom',
170
170
  style: {
171
171
  shape: 'diamond',
172
172
  width: 40,
@@ -212,7 +212,7 @@ select:hover,button:hover{background:#475569}
212
212
  }
213
213
  },
214
214
  {
215
- selector: 'edge.cognition-ref',
215
+ selector: 'edge.atom-ref',
216
216
  style: {
217
217
  width: 1.5,
218
218
  'line-color': '#7dd3fc',
@@ -267,25 +267,32 @@ select:hover,button:hover{background:#475569}
267
267
  symHtml;
268
268
  }
269
269
 
270
- function renderCognitionPanel(d) {
271
- var sc = { current: '#10b981', mixed: '#f59e0b', stale: '#ef4444', unresolved: '#6b7280' }[d.referencesStatus] || '#6b7280';
270
+ function renderAtomPanel(d) {
271
+ var sc = { active: '#10b981', 'needs-review': '#f59e0b', stale: '#ef4444', archived: '#6b7280' }[d.status] || '#6b7280';
272
272
  var files = d.relatedFiles && d.relatedFiles.length
273
273
  ? '<ul class="sb-list">' + d.relatedFiles.map(function (f) { return '<li>' + esc(f) + '</li>'; }).join('') + '</ul>'
274
274
  : '<span class="sb-val">none</span>';
275
275
  var syms = d.relatedSymbols && d.relatedSymbols.length
276
276
  ? d.relatedSymbols.slice(0, 15).map(function (s) { return '<span class="sb-code">' + esc(s) + '</span>'; }).join(' ')
277
277
  : '<span class="sb-val">none</span>';
278
- return '<div class="sb-badge" style="background:' + sc + '22;color:' + sc + ';border:1px solid ' + sc + '44">' + esc(d.referencesStatus) + '</div>' +
278
+ var invalidated = d.invalidatedBy && d.invalidatedBy.length
279
+ ? '<div class="sb-sect"><div class="sb-lbl">Invalidated By</div><ul class="sb-list">' + d.invalidatedBy.map(function (r) { return '<li>' + esc(r) + '</li>'; }).join('') + '</ul></div>'
280
+ : '';
281
+ return '<div class="sb-badge" style="background:' + sc + '22;color:' + sc + ';border:1px solid ' + sc + '44">' + esc(d.status) + '</div>' +
279
282
  '<div class="sb-title">' + esc(d.label) + '</div>' +
283
+ '<div class="sb-sect"><div class="sb-lbl">Atom</div><div class="sb-val"><span class="sb-code">' + esc(d.atomId) + '</span></div></div>' +
284
+ '<div class="sb-sect"><div class="sb-lbl">Type / Confidence</div><div class="sb-val">' + esc(d.atomType) + ' / ' + esc(d.confidence) + '</div></div>' +
285
+ '<div class="sb-sect"><div class="sb-lbl">Source</div><div class="sb-val">' + esc(d.sourceCommand) + '</div></div>' +
280
286
  (d.domain ? '<div class="sb-sect"><div class="sb-lbl">Domain</div><div class="sb-val">' + esc(d.domain) + '</div></div>' : '') +
281
287
  '<div class="sb-sect"><div class="sb-lbl">Related Files</div>' + files + '</div>' +
282
- '<div class="sb-sect"><div class="sb-lbl">Symbols</div><div class="sb-val">' + syms + '</div></div>';
288
+ '<div class="sb-sect"><div class="sb-lbl">Symbols</div><div class="sb-val">' + syms + '</div></div>' +
289
+ invalidated;
283
290
  }
284
291
 
285
292
  cy.on('tap', 'node', function (evt) {
286
293
  var d = evt.target.data();
287
- document.getElementById('sb-type').textContent = d.type === 'cognition' ? 'Cognition Note' : 'File';
288
- document.getElementById('sb-body').innerHTML = d.type === 'cognition' ? renderCognitionPanel(d) : renderFilePanel(d);
294
+ document.getElementById('sb-type').textContent = d.type === 'atom' ? 'Knowledge Atom' : 'File';
295
+ document.getElementById('sb-body').innerHTML = d.type === 'atom' ? renderAtomPanel(d) : renderFilePanel(d);
289
296
  document.getElementById('sidebar').classList.add('open');
290
297
  });
291
298
 
@@ -305,11 +312,11 @@ select:hover,button:hover{background:#475569}
305
312
 
306
313
  document.getElementById('tog-cog').addEventListener('change', function (e) {
307
314
  if (e.target.checked) {
308
- cy.nodes('.cognition').removeClass('hidden');
309
- cy.edges('.cognition-ref').removeClass('hidden');
315
+ cy.nodes('.atom').removeClass('hidden');
316
+ cy.edges('.atom-ref').removeClass('hidden');
310
317
  } else {
311
- cy.nodes('.cognition').addClass('hidden');
312
- cy.edges('.cognition-ref').addClass('hidden');
318
+ cy.nodes('.atom').addClass('hidden');
319
+ cy.edges('.atom-ref').addClass('hidden');
313
320
  }
314
321
  });
315
322
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kentwynn/kgraph",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "Persistent repo intelligence for AI coding assistants.",
5
5
  "type": "module",
6
6
  "bin": {