@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.
- package/README.md +59 -29
- package/dist/cli/commands/blame.d.ts +2 -0
- package/dist/cli/commands/blame.js +52 -0
- package/dist/cli/commands/doctor.js +42 -12
- package/dist/cli/commands/impact.js +11 -5
- package/dist/cli/commands/init.js +2 -0
- package/dist/cli/commands/knowledge.d.ts +2 -0
- package/dist/cli/commands/knowledge.js +137 -0
- package/dist/cli/commands/pack.d.ts +2 -0
- package/dist/cli/commands/pack.js +49 -0
- package/dist/cli/commands/repair.js +3 -3
- package/dist/cli/commands/stale.d.ts +2 -0
- package/dist/cli/commands/stale.js +33 -0
- package/dist/cli/commands/visualize.js +7 -6
- package/dist/cli/help.js +17 -11
- package/dist/cli/index.js +8 -0
- package/dist/cognition/cognition-quality.d.ts +5 -0
- package/dist/cognition/cognition-quality.js +98 -5
- package/dist/cognition/cognition-updater.js +14 -0
- package/dist/cognition/compact.js +129 -28
- package/dist/cognition/conclusion.d.ts +2 -0
- package/dist/cognition/conclusion.js +22 -0
- package/dist/context/context-pack.d.ts +3 -0
- package/dist/context/context-pack.js +71 -0
- package/dist/context/context-query.js +53 -28
- package/dist/integrations/adapters/claude-code.js +23 -3
- package/dist/integrations/adapters/codex.js +1 -1
- package/dist/integrations/adapters/copilot.js +46 -3
- package/dist/integrations/workflow-steps.js +17 -8
- package/dist/knowledge/atom-store.d.ts +60 -0
- package/dist/knowledge/atom-store.js +484 -0
- package/dist/storage/kgraph-paths.js +5 -2
- package/dist/types/config.d.ts +1 -0
- package/dist/types/knowledge.d.ts +92 -0
- package/dist/types/knowledge.js +1 -0
- package/dist/visualization/graph-builder.d.ts +5 -2
- package/dist/visualization/graph-builder.js +43 -18
- package/dist/visualization/html-template.js +24 -17
- 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
|
|
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
|
|
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
|
|
56
|
-
- Which
|
|
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,
|
|
65
|
-
Each context item explains why it was returned, such as a path/name match, a matched
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
250
|
-
Markdown output includes the reason each file, symbol,
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
-
|
|
358
|
-
-
|
|
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
|
|
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
|
|
467
|
+
- Team-friendly shared knowledge workflows that stay local-first.
|
|
@@ -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
|
-
|
|
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(`
|
|
143
|
-
console.log(`
|
|
144
|
-
console.log(`
|
|
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
|
|
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
|
|
203
|
+
findings.push(`${report.orphanedNoteCount} orphaned atom(s) (all refs dead); run \`kgraph repair\` to archive`);
|
|
174
204
|
}
|
|
175
|
-
if (report.
|
|
176
|
-
findings.push(`${report.
|
|
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
|
|
209
|
+
findings.push(`${report.noisyFileRefCount + report.noisySymbolRefCount} noisy atom ref(s); run \`kgraph repair --dry-run\``);
|
|
180
210
|
}
|
|
181
|
-
if (report.
|
|
182
|
-
findings.push(`${report.
|
|
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 {
|
|
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
|
|
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
|
|
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,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,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
|
+
}
|