@kentwynn/kgraph 0.2.10 → 0.2.12

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.
Files changed (39) hide show
  1. package/README.md +59 -29
  2. package/dist/cli/commands/blame.d.ts +2 -0
  3. package/dist/cli/commands/blame.js +52 -0
  4. package/dist/cli/commands/doctor.js +42 -12
  5. package/dist/cli/commands/impact.js +11 -5
  6. package/dist/cli/commands/init.js +2 -0
  7. package/dist/cli/commands/knowledge.d.ts +2 -0
  8. package/dist/cli/commands/knowledge.js +137 -0
  9. package/dist/cli/commands/pack.d.ts +2 -0
  10. package/dist/cli/commands/pack.js +49 -0
  11. package/dist/cli/commands/repair.js +3 -3
  12. package/dist/cli/commands/stale.d.ts +2 -0
  13. package/dist/cli/commands/stale.js +33 -0
  14. package/dist/cli/commands/visualize.js +7 -6
  15. package/dist/cli/help.js +17 -11
  16. package/dist/cli/index.js +8 -0
  17. package/dist/cognition/cognition-quality.d.ts +5 -0
  18. package/dist/cognition/cognition-quality.js +98 -5
  19. package/dist/cognition/cognition-updater.js +14 -0
  20. package/dist/cognition/compact.js +129 -28
  21. package/dist/cognition/conclusion.d.ts +2 -0
  22. package/dist/cognition/conclusion.js +22 -0
  23. package/dist/context/context-pack.d.ts +3 -0
  24. package/dist/context/context-pack.js +71 -0
  25. package/dist/context/context-query.js +53 -28
  26. package/dist/integrations/adapters/claude-code.js +23 -3
  27. package/dist/integrations/adapters/codex.js +1 -1
  28. package/dist/integrations/adapters/copilot.js +46 -3
  29. package/dist/integrations/workflow-steps.js +17 -8
  30. package/dist/knowledge/atom-store.d.ts +60 -0
  31. package/dist/knowledge/atom-store.js +484 -0
  32. package/dist/storage/kgraph-paths.js +5 -2
  33. package/dist/types/config.d.ts +1 -0
  34. package/dist/types/knowledge.d.ts +92 -0
  35. package/dist/types/knowledge.js +1 -0
  36. package/dist/visualization/graph-builder.d.ts +5 -2
  37. package/dist/visualization/graph-builder.js +43 -18
  38. package/dist/visualization/html-template.js +24 -17
  39. package/package.json +1 -1
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,14 +220,41 @@ 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
+
225
+ KGraph stores these conclusions as canonical knowledge atoms under `.kgraph/knowledge/` while keeping existing Markdown cognition files readable for compatibility.
226
+
227
+ ```bash
228
+ kgraph knowledge list
229
+ kgraph knowledge list --type finding --topic auth --json
230
+ kgraph knowledge get <atom-id>
231
+ kgraph knowledge archive <atom-id>
232
+ kgraph knowledge supersede <old-id> <new-id>
233
+ ```
234
+
235
+ Inspect and manage canonical knowledge atoms. Archive and supersede update lifecycle metadata; they do not delete history.
236
+
237
+ ```bash
238
+ kgraph stale
239
+ kgraph stale --json
240
+ kgraph blame <atom-id>
241
+ ```
242
+
243
+ Refresh atom lifecycle status against the current scan and inspect atom provenance. Changed file hashes move atoms to `needs-review`; deleted files or missing symbols move atoms to `stale`; `blame` shows the source command, agent/session/commit, evidence refs, and lifecycle links.
244
+
245
+ ```bash
246
+ kgraph pack "auth token refresh" --budget 8000
247
+ kgraph pack "auth token refresh" --budget 8000 --json
248
+ ```
249
+
250
+ Build a budget-aware context pack from files, symbols, relationships, git changes, session history, and knowledge atoms. JSON output is the stable machine-readable contract for agents.
224
251
 
225
252
  ```bash
226
253
  kgraph compact --dry-run
227
254
  kgraph compact
228
255
  ```
229
256
 
230
- Merge duplicate cognition records and archive low-confidence stale entries. Compaction keeps memory inspectable under `.kgraph/cognition/` while reducing low-value noise in future context responses.
257
+ Merge duplicate knowledge atoms and archive low-confidence stale entries. Compaction operates on `.kgraph/knowledge/atoms.jsonl` first, then regenerates indexes and compatibility domain records so future context responses use the atom lifecycle as the source of truth.
231
258
 
232
259
  ## Optional Step Commands
233
260
 
@@ -246,8 +273,8 @@ kgraph context "auth token refresh"
246
273
  kgraph context "auth token refresh" --json
247
274
  ```
248
275
 
249
- Return context from existing maps and cognition without scanning or updating first.
250
- 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.
251
278
 
252
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`.
253
280
 
@@ -256,7 +283,7 @@ kgraph update
256
283
  kgraph update --dry-run
257
284
  ```
258
285
 
259
- 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.
260
287
 
261
288
  ```bash
262
289
  kgraph visualize
@@ -273,7 +300,7 @@ kgraph history "blog button"
273
300
  kgraph history --json
274
301
  ```
275
302
 
276
- 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.
277
304
 
278
305
  ## AI Tool Integrations
279
306
 
@@ -327,10 +354,14 @@ All runtime data lives under `.kgraph/`:
327
354
  ├── domains/
328
355
  ├── interactions/processed/
329
356
  ├── sessions/
357
+ ├── knowledge/
358
+ │ ├── atoms.jsonl
359
+ │ ├── schema.json
360
+ │ └── indexes/
330
361
  └── context/
331
362
  ```
332
363
 
333
- The files are local, inspectable, and human-readable. Core KGraph functionality is free. There is no database, telemetry, cloud service, account, API key, embedding service, model provider, or source-code upload.
364
+ The files are local, inspectable, and human-readable. `knowledge/atoms.jsonl` is the canonical durable-memory store; Markdown cognition remains a compatibility and input layer. Core KGraph functionality is free. There is no database, telemetry, cloud service, account, API key, embedding service, model provider, or source-code upload.
334
365
 
335
366
  ## Language Support
336
367
 
@@ -352,14 +383,13 @@ Other languages keep practical file, import, and symbol depth without full call
352
383
  kgraph visualize
353
384
  ```
354
385
 
355
- 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:
356
387
 
357
- - current
358
- - mixed
388
+ - active
389
+ - needs-review
359
390
  - stale
360
- - unresolved
361
391
 
362
- 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.
363
393
 
364
394
  ## Development
365
395
 
@@ -434,4 +464,4 @@ The release workflow builds, tests, packs, publishes the npm package on version
434
464
  - Stronger TypeScript path alias and package export resolution.
435
465
  - Richer graph filtering for large repositories.
436
466
  - Optional MCP and editor integration.
437
- - Team-friendly shared cognition workflows that stay local-first.
467
+ - Team-friendly shared knowledge workflows that stay local-first.
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerBlameCommand(program: Command): void;
@@ -0,0 +1,52 @@
1
+ import { readKnowledgeAtoms } from '../../knowledge/atom-store.js';
2
+ import { assertWorkspace } from '../../storage/kgraph-paths.js';
3
+ import { KGraphError, runCommand } from '../errors.js';
4
+ export function registerBlameCommand(program) {
5
+ program
6
+ .command('blame <atomId>')
7
+ .description('Show who or what created a knowledge atom and why it exists')
8
+ .option('--json', 'Print JSON output')
9
+ .action((atomId, options) => runCommand(async () => {
10
+ const workspace = await assertWorkspace(process.cwd());
11
+ const atom = (await readKnowledgeAtoms(workspace)).find((candidate) => candidate.id === atomId);
12
+ if (!atom)
13
+ throw new KGraphError(`Knowledge atom not found: ${atomId}`);
14
+ const result = {
15
+ id: atom.id,
16
+ topic: atom.topic,
17
+ claim: atom.claim,
18
+ provenance: atom.provenance,
19
+ evidenceRefs: atom.evidenceRefs,
20
+ lifecycle: atom.lifecycle,
21
+ };
22
+ if (options.json) {
23
+ console.log(JSON.stringify(result, null, 2));
24
+ return;
25
+ }
26
+ console.log(`# ${atom.topic}`);
27
+ console.log('');
28
+ console.log(`ID: ${atom.id}`);
29
+ console.log(`Claim: ${atom.claim}`);
30
+ console.log(`Source: ${atom.provenance.sourceCommand}`);
31
+ if (atom.provenance.agent)
32
+ console.log(`Agent: ${atom.provenance.agent}`);
33
+ if (atom.provenance.sessionId) {
34
+ console.log(`Session: ${atom.provenance.sessionId}`);
35
+ }
36
+ if (atom.provenance.commit)
37
+ console.log(`Commit: ${atom.provenance.commit}`);
38
+ console.log(`Created: ${atom.provenance.createdAt}`);
39
+ if (atom.provenance.updatedAt)
40
+ console.log(`Updated: ${atom.provenance.updatedAt}`);
41
+ console.log('');
42
+ console.log('Evidence:');
43
+ for (const ref of atom.evidenceRefs) {
44
+ console.log(`- ${JSON.stringify(ref)}`);
45
+ }
46
+ if (atom.lifecycle.supersededBy || atom.lifecycle.supersedes.length > 0) {
47
+ console.log('');
48
+ console.log('Lifecycle:');
49
+ console.log(JSON.stringify(atom.lifecycle, null, 2));
50
+ }
51
+ }));
52
+ }
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import { analyzeCognitionQuality, } from '../../cognition/cognition-quality.js';
4
4
  import { loadConfig } from '../../config/config.js';
5
5
  import { listIntegrations } from '../../integrations/integration-store.js';
6
+ import { validateKnowledgeStore } from '../../knowledge/atom-store.js';
6
7
  import { getCurrentCommit, isGitRepo } from '../../scanner/git-utils.js';
7
8
  import { assertWorkspace, pathExists, resolveWorkspace, } from '../../storage/kgraph-paths.js';
8
9
  import { mapPaths, mapsExist, readMaps } from '../../storage/map-store.js';
@@ -76,6 +77,22 @@ export function registerDoctorCommand(program) {
76
77
  detail: missing.join(', '),
77
78
  });
78
79
  }
80
+ const knowledgeIssues = await validateKnowledgeStore(workspace, maps
81
+ ? { fileMap: maps.fileMap, symbolMap: maps.symbolMap }
82
+ : undefined);
83
+ checks.push({
84
+ label: 'knowledge',
85
+ ok: knowledgeIssues.length === 0,
86
+ detail: knowledgeIssues.length === 0
87
+ ? 'knowledge atoms, schema, and refs are valid'
88
+ : knowledgeIssues
89
+ .slice(0, 3)
90
+ .map((issue) => issue.message)
91
+ .join('; ') +
92
+ (knowledgeIssues.length > 3
93
+ ? `; and ${knowledgeIssues.length - 3} more`
94
+ : ''),
95
+ });
79
96
  const inboxCount = await countMarkdownFiles(workspace.inboxPath);
80
97
  checks.push({
81
98
  label: 'inbox',
@@ -97,7 +114,8 @@ export function registerDoctorCommand(program) {
97
114
  .join('; '),
98
115
  });
99
116
  let qualityReport;
100
- if (maps) {
117
+ const knowledgeReadable = !knowledgeIssues.some((issue) => issue.code === 'invalid-jsonl' || issue.code === 'missing-schema');
118
+ if (maps && knowledgeReadable) {
101
119
  qualityReport = await analyzeCognitionQuality(workspace, maps);
102
120
  const qualityFindings = summarizeQualityFindings(qualityReport);
103
121
  const coverageNotes = summarizeCoverageNotes(qualityReport);
@@ -112,8 +130,15 @@ export function registerDoctorCommand(program) {
112
130
  : qualityFindings.join('; '),
113
131
  });
114
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
+ }
115
140
  printChecks(checks);
116
- if (options.quality && maps) {
141
+ if (options.quality && maps && knowledgeReadable) {
117
142
  console.log('');
118
143
  console.log('KGraph Cognition Quality');
119
144
  console.log('');
@@ -139,14 +164,19 @@ function printChecks(checks) {
139
164
  }
140
165
  }
141
166
  export function printQualityReport(report) {
142
- console.log(`Notes: ${report.noteCount}`);
143
- console.log(`Mixed/stale/unresolved notes: ${report.mixedOrStaleCount}`);
144
- 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}`);
145
175
  console.log(`Noisy file refs: ${report.noisyFileRefCount}`);
146
176
  console.log(`Noisy symbol refs: ${report.noisySymbolRefCount}`);
147
177
  console.log(`Unresolved local imports: ${report.unresolvedLocalImportCount}`);
148
178
  console.log(`Unresolved call edges: ${report.unresolvedCallCount}`);
149
- console.log(`Duplicate cognition titles: ${report.duplicateTitleCount}`);
179
+ console.log(`Duplicate compatibility note titles: ${report.duplicateTitleCount}`);
150
180
  console.log(`Generated files scanned: ${report.generatedFileScanCount}`);
151
181
  console.log(`Expensive files: ${report.expensiveFileCount}`);
152
182
  console.log(`Session repeated reads: ${report.sessionRepeatedReadCount}`);
@@ -170,16 +200,16 @@ export function printQualityReport(report) {
170
200
  function summarizeQualityFindings(report) {
171
201
  const findings = [];
172
202
  if (report.orphanedNoteCount > 0) {
173
- 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`);
174
204
  }
175
- if (report.mixedOrStaleCount > 0) {
176
- 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)`);
177
207
  }
178
208
  if (report.noisyFileRefCount > 0 || report.noisySymbolRefCount > 0) {
179
- 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\``);
180
210
  }
181
- if (report.duplicateTitleCount > 0) {
182
- findings.push(`${report.duplicateTitleCount} duplicate cognition title(s)`);
211
+ if (report.duplicateAtomTopicCount > 0) {
212
+ findings.push(`${report.duplicateAtomTopicCount} duplicate atom topic(s)`);
183
213
  }
184
214
  if (report.generatedFileScanCount > 0) {
185
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');
@@ -1,6 +1,7 @@
1
1
  import { loadConfig, writeDefaultConfig } from '../../config/config.js';
2
2
  import { normalizeIntegrationNames } from '../../integrations/integration-registry.js';
3
3
  import { addIntegrations } from '../../integrations/integration-store.js';
4
+ import { ensureKnowledgeStore } from '../../knowledge/atom-store.js';
4
5
  import { scanRepository } from '../../scanner/repo-scanner.js';
5
6
  import { ensureWorkspace } from '../../storage/kgraph-paths.js';
6
7
  import { readMaps, writeMaps } from '../../storage/map-store.js';
@@ -17,6 +18,7 @@ export function registerInitCommand(program) {
17
18
  .option('--mode <mode>', 'Integration mode: always, smart, manual, or off', 'smart')
18
19
  .action((options) => runCommand(async () => {
19
20
  const workspace = await ensureWorkspace(process.cwd());
21
+ await ensureKnowledgeStore(workspace);
20
22
  const wroteConfig = await writeDefaultConfig(workspace);
21
23
  console.log(wroteConfig
22
24
  ? 'Initialized .kgraph workspace.'
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerKnowledgeCommand(program: Command): void;
@@ -0,0 +1,137 @@
1
+ import { readKnowledgeAtoms, updateKnowledgeAtom, } from '../../knowledge/atom-store.js';
2
+ import { assertWorkspace } from '../../storage/kgraph-paths.js';
3
+ import { KGraphError, runCommand } from '../errors.js';
4
+ export function registerKnowledgeCommand(program) {
5
+ const knowledge = program
6
+ .command('knowledge')
7
+ .description('Manage canonical KGraph knowledge atoms');
8
+ knowledge
9
+ .command('list')
10
+ .option('--type <type>', 'Filter by atom type')
11
+ .option('--topic <topic>', 'Filter by topic substring')
12
+ .option('--status <status>', 'Filter by active, stale, needs-review, or archived')
13
+ .option('--json', 'Print JSON output')
14
+ .action((options) => runCommand(async () => {
15
+ const workspace = await assertWorkspace(process.cwd());
16
+ const atoms = filterAtoms(await readKnowledgeAtoms(workspace), options);
17
+ if (options.json) {
18
+ console.log(JSON.stringify(atoms, null, 2));
19
+ return;
20
+ }
21
+ console.log('KGraph Knowledge');
22
+ console.log('');
23
+ for (const atom of atoms) {
24
+ console.log(`- ${atom.id} [${atom.type}, ${atom.confidence}, ${atom.status}] ${atom.topic}`);
25
+ console.log(` ${atom.claim}`);
26
+ }
27
+ if (atoms.length === 0)
28
+ console.log('- None');
29
+ }));
30
+ knowledge
31
+ .command('get <atomId>')
32
+ .option('--json', 'Print JSON output')
33
+ .action((atomId, options) => runCommand(async () => {
34
+ const workspace = await assertWorkspace(process.cwd());
35
+ const atom = await requireAtom(workspace, atomId);
36
+ if (options.json) {
37
+ console.log(JSON.stringify(atom, null, 2));
38
+ return;
39
+ }
40
+ console.log(`# ${atom.topic}`);
41
+ console.log('');
42
+ console.log(`ID: ${atom.id}`);
43
+ console.log(`Type: ${atom.type}`);
44
+ console.log(`Confidence: ${atom.confidence}`);
45
+ console.log(`Status: ${atom.status}`);
46
+ console.log(`Claim: ${atom.claim}`);
47
+ if (atom.summary)
48
+ console.log(`Summary: ${atom.summary}`);
49
+ console.log('');
50
+ console.log('Evidence:');
51
+ for (const ref of atom.evidenceRefs) {
52
+ console.log(`- ${JSON.stringify(ref)}`);
53
+ }
54
+ console.log('');
55
+ console.log('Provenance:');
56
+ console.log(JSON.stringify(atom.provenance, null, 2));
57
+ console.log('');
58
+ console.log('Lifecycle:');
59
+ console.log(JSON.stringify(atom.lifecycle, null, 2));
60
+ }));
61
+ knowledge
62
+ .command('archive <atomId>')
63
+ .option('--json', 'Print JSON output')
64
+ .action((atomId, options) => runCommand(async () => {
65
+ const workspace = await assertWorkspace(process.cwd());
66
+ const now = new Date().toISOString();
67
+ const atom = await updateKnowledgeAtom(workspace, atomId, (current) => ({
68
+ ...current,
69
+ status: 'archived',
70
+ provenance: { ...current.provenance, updatedAt: now },
71
+ lifecycle: { ...current.lifecycle, archivedAt: now },
72
+ }));
73
+ console.log(options.json
74
+ ? JSON.stringify(atom, null, 2)
75
+ : `Archived knowledge atom: ${atom.id}`);
76
+ }));
77
+ knowledge
78
+ .command('supersede <oldId> <newId>')
79
+ .option('--json', 'Print JSON output')
80
+ .action((oldId, newId, options) => runCommand(async () => {
81
+ const workspace = await assertWorkspace(process.cwd());
82
+ await requireAtom(workspace, newId);
83
+ const now = new Date().toISOString();
84
+ const oldAtom = await updateKnowledgeAtom(workspace, oldId, (current) => ({
85
+ ...current,
86
+ status: 'archived',
87
+ provenance: { ...current.provenance, updatedAt: now },
88
+ lifecycle: {
89
+ ...current.lifecycle,
90
+ supersededBy: newId,
91
+ archivedAt: now,
92
+ },
93
+ }));
94
+ const newAtom = await updateKnowledgeAtom(workspace, newId, (current) => ({
95
+ ...current,
96
+ provenance: { ...current.provenance, updatedAt: now },
97
+ lifecycle: {
98
+ ...current.lifecycle,
99
+ supersedes: [...new Set([...current.lifecycle.supersedes, oldId])],
100
+ },
101
+ }));
102
+ const result = { old: oldAtom, new: newAtom };
103
+ console.log(options.json
104
+ ? JSON.stringify(result, null, 2)
105
+ : `Superseded ${oldId} with ${newId}`);
106
+ }));
107
+ }
108
+ async function requireAtom(workspace, atomId) {
109
+ const atom = (await readKnowledgeAtoms(workspace)).find((candidate) => candidate.id === atomId);
110
+ if (!atom)
111
+ throw new KGraphError(`Knowledge atom not found: ${atomId}`);
112
+ return atom;
113
+ }
114
+ function filterAtoms(atoms, options) {
115
+ return atoms.filter((atom) => {
116
+ if (options.type && atom.type !== options.type)
117
+ return false;
118
+ if (options.status &&
119
+ atom.status !== normalizeStatus(options.status)) {
120
+ return false;
121
+ }
122
+ if (options.topic &&
123
+ !atom.topic.toLowerCase().includes(options.topic.toLowerCase())) {
124
+ return false;
125
+ }
126
+ return true;
127
+ });
128
+ }
129
+ function normalizeStatus(value) {
130
+ if (value === 'active' ||
131
+ value === 'stale' ||
132
+ value === 'needs-review' ||
133
+ value === 'archived') {
134
+ return value;
135
+ }
136
+ throw new KGraphError('--status must be active, stale, needs-review, or archived.');
137
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerPackCommand(program: Command): void;
@@ -0,0 +1,49 @@
1
+ import { buildContextPack } from '../../context/context-pack.js';
2
+ import { queryContext } from '../../context/context-query.js';
3
+ import { loadConfig } from '../../config/config.js';
4
+ import { assertWorkspace } from '../../storage/kgraph-paths.js';
5
+ import { mapsExist, readMaps } from '../../storage/map-store.js';
6
+ import { KGraphError, runCommand } from '../errors.js';
7
+ export function registerPackCommand(program) {
8
+ program
9
+ .command('pack <task>')
10
+ .description('Build a budget-aware KGraph context pack for a task')
11
+ .option('--budget <tokens>', 'Maximum estimated tokens to include', '8000')
12
+ .option('--json', 'Print JSON output')
13
+ .action((task, options) => runCommand(async () => {
14
+ if (!task.trim())
15
+ throw new KGraphError('Task cannot be empty.');
16
+ const budget = Number.parseInt(options.budget ?? '8000', 10);
17
+ if (!Number.isFinite(budget) || budget < 1) {
18
+ throw new KGraphError('--budget must be a positive integer.');
19
+ }
20
+ const workspace = await assertWorkspace(process.cwd());
21
+ if (!(await mapsExist(workspace))) {
22
+ throw new KGraphError('KGraph maps are missing. Run `kgraph scan` first.');
23
+ }
24
+ const [config, maps] = await Promise.all([
25
+ loadConfig(workspace),
26
+ readMaps(workspace),
27
+ ]);
28
+ const response = await queryContext(workspace, config, maps, task);
29
+ const pack = buildContextPack(response, budget);
30
+ if (options.json) {
31
+ console.log(JSON.stringify(pack, null, 2));
32
+ return;
33
+ }
34
+ console.log(`# KGraph Context Pack`);
35
+ console.log('');
36
+ console.log(`Task: ${pack.task}`);
37
+ console.log(`Budget: ${pack.budget}`);
38
+ console.log(`Used: ${pack.usedTokens}`);
39
+ console.log('');
40
+ for (const item of pack.items) {
41
+ console.log(`- [${item.kind}] ${item.title} (~${item.tokenEstimate} tokens)`);
42
+ console.log(` because ${item.reasons.slice(0, 3).join('; ')}`);
43
+ }
44
+ if (pack.omitted.length > 0) {
45
+ console.log('');
46
+ console.log(`Omitted: ${pack.omitted.length} item(s) over budget`);
47
+ }
48
+ }));
49
+ }