@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 +28 -29
- package/dist/cli/commands/doctor.js +25 -12
- package/dist/cli/commands/impact.js +11 -5
- package/dist/cli/commands/init.js +2 -2
- package/dist/cli/commands/integrate.js +1 -1
- package/dist/cli/commands/repair.js +3 -3
- package/dist/cli/commands/uninstall.js +9 -0
- package/dist/cli/commands/visualize.js +7 -6
- package/dist/cli/help.js +11 -11
- package/dist/cognition/cognition-quality.d.ts +5 -0
- package/dist/cognition/cognition-quality.js +98 -5
- package/dist/config/config.js +2 -2
- package/dist/integrations/adapters/claude-code.js +13 -5
- package/dist/integrations/adapters/copilot.js +2 -22
- package/dist/integrations/instruction-blocks.js +3 -2
- package/dist/integrations/integration-store.js +1 -1
- package/dist/integrations/workflow-steps.js +17 -11
- 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,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.
|
|
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
|
|
277
|
-
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.
|
|
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
|
|
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
|
|
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
|
|
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 `
|
|
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,
|
|
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
|
-
-
|
|
389
|
-
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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(`
|
|
160
|
-
console.log(`
|
|
161
|
-
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}`);
|
|
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
|
|
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
|
|
203
|
+
findings.push(`${report.orphanedNoteCount} orphaned atom(s) (all refs dead); run \`kgraph repair\` to archive`);
|
|
191
204
|
}
|
|
192
|
-
if (report.
|
|
193
|
-
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)`);
|
|
194
207
|
}
|
|
195
208
|
if (report.noisyFileRefCount > 0 || report.noisySymbolRefCount > 0) {
|
|
196
|
-
findings.push(`${report.noisyFileRefCount + report.noisySymbolRefCount} noisy
|
|
209
|
+
findings.push(`${report.noisyFileRefCount + report.noisySymbolRefCount} noisy atom ref(s); run \`kgraph repair --dry-run\``);
|
|
197
210
|
}
|
|
198
|
-
if (report.
|
|
199
|
-
findings.push(`${report.
|
|
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 {
|
|
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');
|
|
@@ -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', '
|
|
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, '
|
|
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', '
|
|
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
|
|
10
|
-
.option('--dry-run', 'Show proposed
|
|
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
|
|
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 {
|
|
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
|
|
26
|
-
|
|
27
|
-
|
|
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,
|
|
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
|
|
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
|
|
34
|
-
command('compact', 'Merge duplicate
|
|
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,
|
|
41
|
-
command('update', 'Optional: process only .kgraph/inbox
|
|
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
|
|
44
|
-
command('repair --dry-run', 'Preview
|
|
45
|
-
command('repair', 'Clean noisy stale
|
|
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
|
|
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('
|
|
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
|
|
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,
|
|
5
|
+
import { overwriteDomainRecord, readDomainRecords, writeCognitionNote, } from '../storage/cognition-store.js';
|
|
5
6
|
export async function analyzeCognitionQuality(workspace, maps) {
|
|
6
|
-
const
|
|
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:
|
|
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
|
|
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,
|
|
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
|
}
|
package/dist/config/config.js
CHANGED
|
@@ -157,7 +157,7 @@ function normalizeIntegrations(value) {
|
|
|
157
157
|
return integrations;
|
|
158
158
|
}
|
|
159
159
|
function normalizeIntegrationMode(value) {
|
|
160
|
-
return value === '
|
|
160
|
+
return value === 'smart' || value === 'manual' || value === 'off'
|
|
161
161
|
? value
|
|
162
|
-
: '
|
|
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
|
|
14
|
+
content: `Use KGraph persistent repo intelligence through the single normal \`kgraph "<topic>"\` entry point.
|
|
14
15
|
|
|
15
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 = '
|
|
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
|
|
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. ${
|
|
26
|
-
5. ${
|
|
27
|
-
6. ${
|
|
28
|
-
7. ${
|
|
29
|
-
8. ${
|
|
30
|
-
9. ${
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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 {
|
|
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,
|
|
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
|
-
|
|
12
|
-
|
|
11
|
+
active: '#10b981',
|
|
12
|
+
'needs-review': '#f59e0b',
|
|
13
13
|
stale: '#ef4444',
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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:
|
|
68
|
-
color: STATUS_COLORS[
|
|
69
|
-
type: '
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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: `
|
|
84
|
+
classes: `atom ${atom.status}`,
|
|
76
85
|
});
|
|
77
|
-
for (const filePath of
|
|
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: '
|
|
97
|
+
type: 'atom-ref',
|
|
89
98
|
label: '',
|
|
90
99
|
},
|
|
91
|
-
classes: '
|
|
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
|
-
|
|
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 · ${meta.symbolCount} symbols · ${meta.
|
|
54
|
+
<span id="t-stats">${meta.fileCount} files · ${meta.symbolCount} symbols · ${meta.atomCount} atoms${meta.hiddenAtomCount ? ' (' + meta.hiddenAtomCount + ' hidden)' : ''} · ~${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>
|
|
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">
|
|
89
|
-
<span class="li"><span class="li-dia" style="background:#10b981"></span>
|
|
90
|
-
<span class="li"><span class="li-dia" style="background:#f59e0b"></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.
|
|
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.
|
|
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
|
|
271
|
-
var sc = {
|
|
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
|
-
|
|
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 === '
|
|
288
|
-
document.getElementById('sb-body').innerHTML = d.type === '
|
|
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('.
|
|
309
|
-
cy.edges('.
|
|
315
|
+
cy.nodes('.atom').removeClass('hidden');
|
|
316
|
+
cy.edges('.atom-ref').removeClass('hidden');
|
|
310
317
|
} else {
|
|
311
|
-
cy.nodes('.
|
|
312
|
-
cy.edges('.
|
|
318
|
+
cy.nodes('.atom').addClass('hidden');
|
|
319
|
+
cy.edges('.atom-ref').addClass('hidden');
|
|
313
320
|
}
|
|
314
321
|
});
|
|
315
322
|
|