@kentwynn/kgraph 0.2.32 → 0.2.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -253,9 +253,10 @@ kgraph repair
253
253
  kgraph uninstall
254
254
  kgraph uninstall --yes
255
255
  kgraph uninstall --keep-integrations --yes
256
+ kgraph uninstall --yes --memory
256
257
  ```
257
258
 
258
- `uninstall` previews repo-local removal and does not delete anything unless `--yes` is passed. `uninstall --yes` removes `.kgraph/` and KGraph-managed integration blocks/files while preserving source files and user-authored text outside managed blocks. Use `--keep-integrations --yes` to remove only `.kgraph/` while leaving AI tool instruction files in place. After uninstalling, `kgraph init` can be run again for a fresh setup.
259
+ `uninstall` previews repo-local removal and does not delete anything unless `--yes` is passed. `uninstall --yes` removes `.kgraph/` and KGraph-managed integration blocks/files while preserving source files and user-authored text outside managed blocks. Use `--keep-integrations --yes` to remove only `.kgraph/` while leaving AI tool instruction files in place. Add `--memory` to also remove the Copilot memory rule that `init` installs; without this flag the memory entry is preserved across uninstalls. After uninstalling, `kgraph init` can be run again for a fresh setup.
259
260
 
260
261
  ```bash
261
262
  kgraph impact "Button"
@@ -1,4 +1,5 @@
1
- import { loadConfig, writeDefaultConfig } from '../../config/config.js';
1
+ import { loadConfig, saveConfig, writeDefaultConfig, } from '../../config/config.js';
2
+ import { installCopilotMemory } from '../../integrations/copilot-memory.js';
2
3
  import { normalizeIntegrationNames } from '../../integrations/integration-registry.js';
3
4
  import { addIntegrations } from '../../integrations/integration-store.js';
4
5
  import { ensureKnowledgeStore } from '../../knowledge/atom-store.js';
@@ -6,9 +7,10 @@ import { scanRepository } from '../../scanner/repo-scanner.js';
6
7
  import { ensureWorkspace } from '../../storage/kgraph-paths.js';
7
8
  import { readMaps, writeMaps } from '../../storage/map-store.js';
8
9
  import { KGraphError, runCommand } from '../errors.js';
9
- import { promptForInitIntegrations, shouldPromptForInitIntegrations, } from '../init-prompt.js';
10
+ import { promptForInitIntegrations, promptScopeConfirmation, promptWorkspaceSetup, shouldPromptForInitIntegrations, } from '../init-prompt.js';
10
11
  import { detectMachineIntegrationRecommendations, recommendedIntegrationsForInit, } from '../init-recommendations.js';
11
12
  import { renderInitSummary } from '../init-summary.js';
13
+ import { countScopeFiles, detectWorkspaces } from '../workspace-detection.js';
12
14
  export function registerInitCommand(program) {
13
15
  program
14
16
  .command('init')
@@ -33,6 +35,26 @@ export function registerInitCommand(program) {
33
35
  console.log(`Configured integrations: ${changed.map((item) => `${item.name}:${item.mode}`).join(', ')}`);
34
36
  }
35
37
  let config = await loadConfig(workspace);
38
+ // Workspace detection — only for fresh init, non-destructive
39
+ const workspaceInfo = await detectWorkspaces(workspace.rootPath);
40
+ if (workspaceInfo && Object.keys(config.domainHints).length === 0) {
41
+ const result = await promptWorkspaceSetup(workspaceInfo);
42
+ if (result.applyDomains && result.domainHints) {
43
+ config = { ...config, domainHints: result.domainHints };
44
+ await saveConfig(workspace, config);
45
+ }
46
+ }
47
+ // Pre-scan scope check — fast file count before heavy scan
48
+ const fileCount = await countScopeFiles(workspace.rootPath, config);
49
+ const scopeResult = await promptScopeConfirmation(fileCount);
50
+ if (!scopeResult.proceed) {
51
+ console.log('Init cancelled. Edit .kgraph/config.yaml to adjust scope, then run `kgraph init` again.');
52
+ return;
53
+ }
54
+ if (scopeResult.narrowedInclude) {
55
+ config = { ...config, include: scopeResult.narrowedInclude };
56
+ await saveConfig(workspace, config);
57
+ }
36
58
  const previousMaps = await readMaps(workspace);
37
59
  const result = await scanRepository(workspace.rootPath, config, {
38
60
  files: previousMaps.fileMap.files,
@@ -64,6 +86,11 @@ export function registerInitCommand(program) {
64
86
  });
65
87
  }
66
88
  }
89
+ // Install Copilot memory entry
90
+ const installed = await installCopilotMemory();
91
+ if (installed) {
92
+ console.log('Copilot memory rule installed.');
93
+ }
67
94
  console.log('');
68
95
  console.log(renderInitSummary({
69
96
  files: result.files,
@@ -1,6 +1,7 @@
1
1
  import { readdir, rm, rmdir } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { loadConfig } from '../../config/config.js';
4
+ import { removeCopilotMemory } from '../../integrations/copilot-memory.js';
4
5
  import { removeIntegrations } from '../../integrations/integration-store.js';
5
6
  import { pathExists, resolveWorkspace } from '../../storage/kgraph-paths.js';
6
7
  import { runCommand } from '../errors.js';
@@ -15,6 +16,7 @@ export function registerUninstallCommand(program) {
15
16
  .description('Remove KGraph from this repository')
16
17
  .option('--yes', 'Apply the uninstall after previewing what will be removed')
17
18
  .option('--keep-integrations', 'Remove only .kgraph/ and preserve generated AI tool instruction files')
19
+ .option('--memory', 'Also remove the global Copilot memory entry for this repo')
18
20
  .action((options) => runCommand(async () => {
19
21
  const workspace = resolveWorkspace(process.cwd());
20
22
  const initialized = await pathExists(workspace.kgraphPath);
@@ -25,6 +27,7 @@ export function registerUninstallCommand(program) {
25
27
  initialized,
26
28
  integrations: configuredIntegrations,
27
29
  keepIntegrations: options.keepIntegrations === true,
30
+ removeMemory: options.memory === true,
28
31
  applying: options.yes === true,
29
32
  });
30
33
  if (!options.yes) {
@@ -39,6 +42,13 @@ export function registerUninstallCommand(program) {
39
42
  if (initialized) {
40
43
  await rm(workspace.kgraphPath, { recursive: true, force: true });
41
44
  }
45
+ // Remove Copilot memory entry only if --memory flag is set
46
+ if (options.memory) {
47
+ const memoryRemoved = await removeCopilotMemory();
48
+ if (memoryRemoved) {
49
+ console.log('Removed Copilot memory rule.');
50
+ }
51
+ }
42
52
  console.log('');
43
53
  console.log('KGraph uninstall complete.');
44
54
  console.log('Run `kgraph init` to set up this repository again.');
@@ -84,6 +94,9 @@ function printUninstallPreview(input) {
84
94
  console.log('- No configured integration blocks/files found');
85
95
  }
86
96
  }
97
+ if (input.removeMemory) {
98
+ console.log('- Copilot memory rule for this repo');
99
+ }
87
100
  console.log('');
88
101
  console.log('Will preserve:');
89
102
  console.log('- Repository source files');
package/dist/cli/help.js CHANGED
@@ -11,7 +11,10 @@ export function renderRootHelp(useColor = supportsColor()) {
11
11
  ['purpose', 'durable engineering memory for AI coding tools'],
12
12
  ['storage', '.kgraph/ atoms, maps, indexes, and session history'],
13
13
  ['stance', 'local-first · deterministic-first · inspectable'],
14
- ['agents', 'Codex · Copilot · Cursor · Claude Code · Gemini · Windsurf · Cline'],
14
+ [
15
+ 'agents',
16
+ 'Codex · Copilot · Cursor · Claude Code · Gemini · Windsurf · Cline',
17
+ ],
15
18
  ]),
16
19
  '',
17
20
  sectionTitle(theme, `${accent} Usage`),
@@ -49,6 +52,7 @@ export function renderRootHelp(useColor = supportsColor()) {
49
52
  command('repair', 'Clean noisy stale atom references'),
50
53
  command('uninstall', 'Preview repo-local KGraph removal'),
51
54
  command('uninstall --yes', 'Remove .kgraph/ and managed integrations'),
55
+ command('uninstall --yes --memory', 'Also remove Copilot memory rule'),
52
56
  command('visualize', 'Interactive dependency graph at http://localhost:4242'),
53
57
  command('history "blog button"', 'Search processed cognition sessions'),
54
58
  '',
@@ -1,8 +1,27 @@
1
- import type { IntegrationConfig, IntegrationName } from '../types/config.js';
1
+ import type { DomainHint, IntegrationConfig, IntegrationName } from '../types/config.js';
2
2
  import type { InitIntegrationRecommendation } from './init-recommendations.js';
3
+ import type { WorkspaceInfo } from './workspace-detection.js';
3
4
  export declare function shouldPromptForInitIntegrations(options: {
4
5
  explicitIntegrationsRequested: boolean;
5
6
  configuredIntegrations: Pick<IntegrationConfig, 'name'>[];
6
7
  interactive?: boolean;
7
8
  }): boolean;
8
9
  export declare function promptForInitIntegrations(recommendations: InitIntegrationRecommendation[]): Promise<IntegrationName[]>;
10
+ export interface ScopeConfirmResult {
11
+ proceed: boolean;
12
+ narrowedInclude?: string[];
13
+ }
14
+ /**
15
+ * If file count exceeds threshold, ask user whether to proceed or narrow scope.
16
+ * For small repos, returns { proceed: true } without prompting.
17
+ */
18
+ export declare function promptScopeConfirmation(fileCount: number, threshold?: number): Promise<ScopeConfirmResult>;
19
+ export interface WorkspacePromptResult {
20
+ applyDomains: boolean;
21
+ domainHints?: Record<string, DomainHint>;
22
+ }
23
+ /**
24
+ * If a monorepo workspace is detected, offer to configure domain hints.
25
+ * For simple projects (no workspace detected), skips silently.
26
+ */
27
+ export declare function promptWorkspaceSetup(info: WorkspaceInfo): Promise<WorkspacePromptResult>;
@@ -59,3 +59,60 @@ export async function promptForInitIntegrations(recommendations) {
59
59
  function isInteractiveTerminal() {
60
60
  return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
61
61
  }
62
+ /**
63
+ * If file count exceeds threshold, ask user whether to proceed or narrow scope.
64
+ * For small repos, returns { proceed: true } without prompting.
65
+ */
66
+ export async function promptScopeConfirmation(fileCount, threshold = 500) {
67
+ if (fileCount <= threshold || !isInteractiveTerminal()) {
68
+ return { proceed: true };
69
+ }
70
+ const action = await clack.select({
71
+ message: `Found ${fileCount.toLocaleString()} files in scope`,
72
+ options: [
73
+ { value: 'proceed', label: 'Continue with all files' },
74
+ {
75
+ value: 'narrow',
76
+ label: 'Narrow to src/ only',
77
+ hint: 'include: ["src/**"]',
78
+ },
79
+ { value: 'cancel', label: "Cancel — I'll edit config.yaml manually" },
80
+ ],
81
+ });
82
+ if (clack.isCancel(action) || action === 'cancel') {
83
+ return { proceed: false };
84
+ }
85
+ if (action === 'narrow') {
86
+ return { proceed: true, narrowedInclude: ['src/**'] };
87
+ }
88
+ return { proceed: true };
89
+ }
90
+ /**
91
+ * If a monorepo workspace is detected, offer to configure domain hints.
92
+ * For simple projects (no workspace detected), skips silently.
93
+ */
94
+ export async function promptWorkspaceSetup(info) {
95
+ if (!isInteractiveTerminal()) {
96
+ return { applyDomains: false };
97
+ }
98
+ const packageNames = info.packages.map((p) => p.name).join(', ');
99
+ const action = await clack.select({
100
+ message: `Detected ${info.tool} workspace (${info.packages.length} packages: ${packageNames})`,
101
+ options: [
102
+ {
103
+ value: 'apply',
104
+ label: 'Configure domain hints from packages',
105
+ hint: 'context packs will prefer the active package',
106
+ },
107
+ { value: 'skip', label: 'Skip — scan everything flat' },
108
+ ],
109
+ });
110
+ if (clack.isCancel(action) || action === 'skip') {
111
+ return { applyDomains: false };
112
+ }
113
+ const hints = {};
114
+ for (const pkg of info.packages) {
115
+ hints[pkg.name] = { paths: [pkg.path + '/**'] };
116
+ }
117
+ return { applyDomains: true, domainHints: hints };
118
+ }
@@ -0,0 +1,23 @@
1
+ import type { DomainHint, KGraphConfig } from '../types/config.js';
2
+ export interface WorkspaceInfo {
3
+ tool: string;
4
+ packages: WorkspacePackage[];
5
+ }
6
+ export interface WorkspacePackage {
7
+ name: string;
8
+ path: string;
9
+ }
10
+ /**
11
+ * Detect monorepo workspace tools and their packages.
12
+ * Returns null for simple single-package projects.
13
+ */
14
+ export declare function detectWorkspaces(rootPath: string): Promise<WorkspaceInfo | null>;
15
+ /**
16
+ * Convert detected workspace packages into domainHints.
17
+ */
18
+ export declare function workspacesToDomainHints(info: WorkspaceInfo): Record<string, DomainHint>;
19
+ /**
20
+ * Quick file count using fast-glob (no content read).
21
+ * Used to warn about large scopes before a full scan.
22
+ */
23
+ export declare function countScopeFiles(rootPath: string, config: KGraphConfig): Promise<number>;
@@ -0,0 +1,169 @@
1
+ import fg from 'fast-glob';
2
+ import { readFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { buildFastGlobIgnore, readGitignorePatterns, } from '../scanner/file-classifier.js';
5
+ import { pathExists } from '../storage/kgraph-paths.js';
6
+ /**
7
+ * Detect monorepo workspace tools and their packages.
8
+ * Returns null for simple single-package projects.
9
+ */
10
+ export async function detectWorkspaces(rootPath) {
11
+ // pnpm
12
+ const pnpmPath = path.join(rootPath, 'pnpm-workspace.yaml');
13
+ if (await pathExists(pnpmPath)) {
14
+ const packages = await resolvePnpmPackages(rootPath, pnpmPath);
15
+ if (packages.length > 1)
16
+ return { tool: 'pnpm', packages };
17
+ }
18
+ // nx
19
+ const nxPath = path.join(rootPath, 'nx.json');
20
+ if (await pathExists(nxPath)) {
21
+ const packages = await resolveNxPackages(rootPath);
22
+ if (packages.length > 1)
23
+ return { tool: 'nx', packages };
24
+ }
25
+ // lerna
26
+ const lernaPath = path.join(rootPath, 'lerna.json');
27
+ if (await pathExists(lernaPath)) {
28
+ const packages = await resolveLernaPackages(rootPath, lernaPath);
29
+ if (packages.length > 1)
30
+ return { tool: 'lerna', packages };
31
+ }
32
+ // rush
33
+ const rushPath = path.join(rootPath, 'rush.json');
34
+ if (await pathExists(rushPath)) {
35
+ const packages = await resolveRushPackages(rootPath, rushPath);
36
+ if (packages.length > 1)
37
+ return { tool: 'rush', packages };
38
+ }
39
+ // npm/yarn workspaces (package.json)
40
+ const pkgPath = path.join(rootPath, 'package.json');
41
+ if (await pathExists(pkgPath)) {
42
+ const packages = await resolveNpmWorkspaces(rootPath, pkgPath);
43
+ if (packages.length > 1)
44
+ return { tool: 'npm', packages };
45
+ }
46
+ return null;
47
+ }
48
+ /**
49
+ * Convert detected workspace packages into domainHints.
50
+ */
51
+ export function workspacesToDomainHints(info) {
52
+ const hints = {};
53
+ for (const pkg of info.packages) {
54
+ hints[pkg.name] = { paths: [pkg.path + '/**'] };
55
+ }
56
+ return hints;
57
+ }
58
+ /**
59
+ * Quick file count using fast-glob (no content read).
60
+ * Used to warn about large scopes before a full scan.
61
+ */
62
+ export async function countScopeFiles(rootPath, config) {
63
+ const gitignorePatterns = await readGitignorePatterns(rootPath);
64
+ const allExcludes = [...config.exclude, ...gitignorePatterns];
65
+ const entries = await fg(config.include, {
66
+ cwd: rootPath,
67
+ dot: true,
68
+ onlyFiles: true,
69
+ unique: true,
70
+ ignore: buildFastGlobIgnore(allExcludes),
71
+ stats: false,
72
+ });
73
+ return entries.length;
74
+ }
75
+ // --- Resolvers ---
76
+ async function resolvePnpmPackages(rootPath, filePath) {
77
+ try {
78
+ const content = await readFile(filePath, 'utf8');
79
+ // Simple YAML parsing for packages: array
80
+ const lines = content.split(/\r?\n/);
81
+ const globs = [];
82
+ let inPackages = false;
83
+ for (const line of lines) {
84
+ if (/^packages:/i.test(line.trim())) {
85
+ inPackages = true;
86
+ continue;
87
+ }
88
+ if (inPackages) {
89
+ const match = line.match(/^\s+-\s+['"]?([^'"]+)['"]?\s*$/);
90
+ if (match) {
91
+ globs.push(match[1]);
92
+ }
93
+ else if (/^\S/.test(line) && line.trim().length > 0) {
94
+ break;
95
+ }
96
+ }
97
+ }
98
+ if (globs.length === 0)
99
+ globs.push('packages/*');
100
+ return await resolvePackageGlobs(rootPath, globs);
101
+ }
102
+ catch {
103
+ return [];
104
+ }
105
+ }
106
+ async function resolveNxPackages(rootPath) {
107
+ // Nx projects live in common dirs
108
+ const candidates = ['apps/*', 'libs/*', 'packages/*'];
109
+ return await resolvePackageGlobs(rootPath, candidates);
110
+ }
111
+ async function resolveLernaPackages(rootPath, filePath) {
112
+ try {
113
+ const content = await readFile(filePath, 'utf8');
114
+ const parsed = JSON.parse(content);
115
+ const globs = Array.isArray(parsed.packages)
116
+ ? parsed.packages
117
+ : ['packages/*'];
118
+ return await resolvePackageGlobs(rootPath, globs);
119
+ }
120
+ catch {
121
+ return [];
122
+ }
123
+ }
124
+ async function resolveRushPackages(rootPath, filePath) {
125
+ try {
126
+ const content = await readFile(filePath, 'utf8');
127
+ const parsed = JSON.parse(content);
128
+ if (!Array.isArray(parsed.projects))
129
+ return [];
130
+ return parsed.projects
131
+ .filter((p) => p.projectFolder)
132
+ .map((p) => ({
133
+ name: p.packageName || path.basename(p.projectFolder),
134
+ path: p.projectFolder,
135
+ }));
136
+ }
137
+ catch {
138
+ return [];
139
+ }
140
+ }
141
+ async function resolveNpmWorkspaces(rootPath, filePath) {
142
+ try {
143
+ const content = await readFile(filePath, 'utf8');
144
+ const parsed = JSON.parse(content);
145
+ const workspaces = parsed.workspaces;
146
+ if (!workspaces)
147
+ return [];
148
+ const globs = Array.isArray(workspaces)
149
+ ? workspaces
150
+ : (workspaces.packages ?? []);
151
+ if (globs.length === 0)
152
+ return [];
153
+ return await resolvePackageGlobs(rootPath, globs);
154
+ }
155
+ catch {
156
+ return [];
157
+ }
158
+ }
159
+ async function resolvePackageGlobs(rootPath, globs) {
160
+ const dirs = await fg(globs, {
161
+ cwd: rootPath,
162
+ onlyDirectories: true,
163
+ deep: 1,
164
+ });
165
+ return dirs.sort().map((dir) => ({
166
+ name: path.basename(dir),
167
+ path: dir.split(path.sep).join('/'),
168
+ }));
169
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Resolve the Copilot memory-tool memories directory for the current OS.
3
+ * Returns undefined if the globalStorage root cannot be determined.
4
+ * Respects KGRAPH_COPILOT_MEMORY_DIR env override for testing.
5
+ */
6
+ export declare function resolveCopilotMemoryDir(): string | undefined;
7
+ /**
8
+ * Install the KGraph memory entry into the Copilot memory store.
9
+ * Returns true if written, false if the directory could not be resolved.
10
+ */
11
+ export declare function installCopilotMemory(): Promise<boolean>;
12
+ /**
13
+ * Remove the KGraph memory entry from the Copilot memory store.
14
+ * Returns true if removed or did not exist, false if the directory could not be resolved.
15
+ */
16
+ export declare function removeCopilotMemory(): Promise<boolean>;
17
+ /**
18
+ * Check whether the KGraph memory entry exists.
19
+ */
20
+ export declare function hasCopilotMemory(): Promise<boolean>;
21
+ /**
22
+ * Read the KGraph memory content, if it exists.
23
+ */
24
+ export declare function readCopilotMemory(): Promise<string | undefined>;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Manages the GitHub Copilot memory-tool entry for KGraph.
3
+ * Installs/removes a persistent preference so Copilot always follows the
4
+ * KGraph workflow in repos where KGraph is initialised.
5
+ */
6
+ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
7
+ import { homedir, platform } from 'node:os';
8
+ import path from 'node:path';
9
+ import { pathExists } from '../storage/kgraph-paths.js';
10
+ const MEMORY_FILE = 'kgraph.md';
11
+ const MEMORY_CONTENT = `# KGraph
12
+
13
+ - KGraph is installed. Follow the copilot-instructions.md and @kgraph agent for this repo.
14
+ `;
15
+ /**
16
+ * Resolve the Copilot memory-tool memories directory for the current OS.
17
+ * Returns undefined if the globalStorage root cannot be determined.
18
+ * Respects KGRAPH_COPILOT_MEMORY_DIR env override for testing.
19
+ */
20
+ export function resolveCopilotMemoryDir() {
21
+ if (process.env['KGRAPH_COPILOT_MEMORY_DIR']) {
22
+ return process.env['KGRAPH_COPILOT_MEMORY_DIR'];
23
+ }
24
+ const home = homedir();
25
+ switch (platform()) {
26
+ case 'win32':
27
+ return path.join(process.env['APPDATA'] ?? path.join(home, 'AppData', 'Roaming'), 'Code', 'User', 'globalStorage', 'github.copilot-chat', 'memory-tool', 'memories');
28
+ case 'darwin':
29
+ return path.join(home, 'Library', 'Application Support', 'Code', 'User', 'globalStorage', 'github.copilot-chat', 'memory-tool', 'memories');
30
+ case 'linux':
31
+ return path.join(process.env['XDG_CONFIG_HOME'] ?? path.join(home, '.config'), 'Code', 'User', 'globalStorage', 'github.copilot-chat', 'memory-tool', 'memories');
32
+ default:
33
+ return undefined;
34
+ }
35
+ }
36
+ /**
37
+ * Install the KGraph memory entry into the Copilot memory store.
38
+ * Returns true if written, false if the directory could not be resolved.
39
+ */
40
+ export async function installCopilotMemory() {
41
+ const memoryDir = resolveCopilotMemoryDir();
42
+ if (!memoryDir) {
43
+ return false;
44
+ }
45
+ await mkdir(memoryDir, { recursive: true });
46
+ await writeFile(path.join(memoryDir, MEMORY_FILE), MEMORY_CONTENT, 'utf8');
47
+ return true;
48
+ }
49
+ /**
50
+ * Remove the KGraph memory entry from the Copilot memory store.
51
+ * Returns true if removed or did not exist, false if the directory could not be resolved.
52
+ */
53
+ export async function removeCopilotMemory() {
54
+ const memoryDir = resolveCopilotMemoryDir();
55
+ if (!memoryDir) {
56
+ return false;
57
+ }
58
+ const memoryFile = path.join(memoryDir, MEMORY_FILE);
59
+ if (await pathExists(memoryFile)) {
60
+ await rm(memoryFile, { force: true });
61
+ }
62
+ return true;
63
+ }
64
+ /**
65
+ * Check whether the KGraph memory entry exists.
66
+ */
67
+ export async function hasCopilotMemory() {
68
+ const memoryDir = resolveCopilotMemoryDir();
69
+ if (!memoryDir) {
70
+ return false;
71
+ }
72
+ return pathExists(path.join(memoryDir, MEMORY_FILE));
73
+ }
74
+ /**
75
+ * Read the KGraph memory content, if it exists.
76
+ */
77
+ export async function readCopilotMemory() {
78
+ const memoryDir = resolveCopilotMemoryDir();
79
+ if (!memoryDir) {
80
+ return undefined;
81
+ }
82
+ const memoryFile = path.join(memoryDir, MEMORY_FILE);
83
+ if (!(await pathExists(memoryFile))) {
84
+ return undefined;
85
+ }
86
+ return readFile(memoryFile, 'utf8');
87
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kentwynn/kgraph",
3
- "version": "0.2.32",
3
+ "version": "0.2.34",
4
4
  "description": "Persistent repo intelligence for AI coding assistants.",
5
5
  "type": "module",
6
6
  "bin": {