@kentwynn/kgraph 0.1.0 → 0.1.1

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
@@ -1,200 +1,174 @@
1
1
  # KGraph
2
2
 
3
- Persistent repo intelligence for AI coding assistants.
3
+ Persistent repository intelligence for AI coding tools.
4
4
 
5
- KGraph is a local-first CLI for building an inspectable knowledge layer around a codebase. It helps AI coding sessions stop rediscovering the same repository structure, workflows, architecture decisions, and debugging history every time a new chat starts.
5
+ KGraph is a local-first CLI that builds an inspectable knowledge layer for a codebase. It helps tools like Codex, GitHub Copilot, Cursor, and Claude Code reuse repository structure, workflow knowledge, debugging history, and architecture decisions instead of rediscovering them in every chat.
6
6
 
7
- ## Why KGraph
7
+ ## Why It Matters
8
8
 
9
- The biggest waste in AI-assisted coding is often not generation. It is repeated exploration:
9
+ AI coding sessions spend a large part of their budget finding context: reading files, tracing imports, locating the right functions, and re-learning decisions that were already discovered in previous work.
10
10
 
11
- - rereading the same files
12
- - rediscovering the same architecture
13
- - re-inferring the same workflows
14
- - repeating prior debugging conclusions
15
- - spending tokens just to find the right place to work
11
+ KGraph turns that repeated exploration into durable repository intelligence:
16
12
 
17
- KGraph stores durable repository context in a local `.kgraph/` workspace so future AI sessions can navigate directly to relevant files, symbols, domains, and prior cognition.
18
-
19
- ## What KGraph Is
20
-
21
- KGraph is:
22
-
23
- - persistent repo cognition
24
- - semantic navigation infrastructure
25
- - a context engineering layer
26
- - local filesystem-based project intelligence
27
- - an inspectable map of structure, relationships, and durable notes
28
-
29
- KGraph is not:
13
+ ```text
14
+ AI chat or developer note
15
+ -> KGraph cognition inbox
16
+ -> structured repo knowledge
17
+ -> compact context for future AI sessions
18
+ ```
30
19
 
31
- - an AI coding assistant
32
- - a chatbot
33
- - a vector database
34
- - a simple RAG wrapper
35
- - a cloud service
36
- - an autonomous agent system
20
+ The result is faster navigation, lower token waste, and more consistent understanding across coding sessions.
37
21
 
38
- ## Install And Run
22
+ ## Install
39
23
 
40
- KGraph is designed to feel like other developer-native CLI tools: one command to try it, optional global install if you use it often, and a quick version check before running commands.
24
+ Run the latest published package:
41
25
 
42
- Public npm usage is the intended distribution path once the package is published.
26
+ ```bash
27
+ npx @kentwynn/kgraph@latest init
28
+ ```
43
29
 
44
- Install and run a specific stable release:
30
+ Run a specific stable version:
45
31
 
46
32
  ```bash
47
33
  npx @kentwynn/kgraph@0.1.0 init
48
34
  ```
49
35
 
50
- Or run the latest published release:
36
+ Install globally if you use KGraph often:
51
37
 
52
38
  ```bash
53
- npx @kentwynn/kgraph@latest init
39
+ npm install -g @kentwynn/kgraph@latest
40
+ kgraph --version
54
41
  ```
55
42
 
56
- Use the CLI directly after initialization:
43
+ ## Quick Start
44
+
45
+ Initialize KGraph in a repository and connect your AI tools:
57
46
 
58
47
  ```bash
59
- npx @kentwynn/kgraph@latest scan
60
- npx @kentwynn/kgraph@latest update
61
- npx @kentwynn/kgraph@latest context "auth token refresh"
48
+ kgraph init --integrations codex,copilot,cursor
62
49
  ```
63
50
 
64
- Optional global installation:
51
+ Scan the codebase:
65
52
 
66
53
  ```bash
67
- npm install -g @kentwynn/kgraph@latest
68
- kgraph --version
69
- kgraph init
70
54
  kgraph scan
71
- kgraph update
72
- kgraph context "auth token refresh"
73
55
  ```
74
56
 
75
- Current local development flow from a fresh clone:
57
+ Ask for compact context before working on an area:
76
58
 
77
59
  ```bash
78
- npm install
79
- npm run build
80
- npm run kgraph -- --version
81
- npm run kgraph -- init
82
- npm run kgraph -- scan
83
- npm run kgraph -- update
84
- npm run kgraph -- context "auth token refresh"
60
+ kgraph context "auth token refresh"
85
61
  ```
86
62
 
87
- Until the npm package is publicly released, use the local development commands or install directly from the release workflow artifact.
63
+ Process saved chat notes and debugging conclusions:
88
64
 
89
- ## MVP CLI
65
+ ```bash
66
+ kgraph update
67
+ ```
90
68
 
91
- The MVP command surface is intentionally small:
69
+ ## CLI
92
70
 
93
71
  ```bash
94
72
  kgraph init
73
+ kgraph init --integrations codex,cursor
74
+ kgraph integrate list
75
+ kgraph integrate add codex copilot cursor claude-code
76
+ kgraph integrate remove cursor
95
77
  kgraph scan
96
78
  kgraph update
97
79
  kgraph context "auth token refresh"
80
+ kgraph context "auth token refresh" --json
98
81
  ```
99
82
 
100
- `init` creates the local `.kgraph/` workspace. `scan` refreshes deterministic structure maps. `update` processes Markdown cognition notes. `context` returns compact repository context for a topic.
83
+ ## AI Tool Integrations
101
84
 
102
- The package is prepared for npm-style CLI distribution. The MVP includes CI and release artifact packaging, but automatic npm publishing is intentionally left for a later release policy.
85
+ KGraph writes local instruction files so AI tools know how to use the repository knowledge layer during normal coding chats.
103
86
 
104
- ## Local-First Privacy
87
+ | Integration | Instruction file |
88
+ | --- | --- |
89
+ | Codex | `AGENTS.md` |
90
+ | GitHub Copilot | `.github/copilot-instructions.md` |
91
+ | Cursor | `.cursor/rules/kgraph.mdc` |
92
+ | Claude Code | `CLAUDE.md` |
105
93
 
106
- KGraph writes project intelligence to local files in `.kgraph/`.
94
+ Example:
107
95
 
108
- The MVP does not require:
96
+ ```bash
97
+ kgraph integrate add codex cursor
98
+ kgraph integrate list
99
+ ```
109
100
 
110
- - accounts
111
- - telemetry
112
- - cloud infrastructure
113
- - hosted services
114
- - databases
115
- - LLM providers
116
- - embeddings
117
- - vector search
118
- - background daemons
101
+ Generated instructions teach AI tools to:
119
102
 
120
- Generated KGraph data is meant to be human-readable and inspectable with normal text tools.
103
+ - query `kgraph context "<topic>"` before broad repo exploration
104
+ - save useful architecture, workflow, and debugging discoveries to `.kgraph/inbox/`
105
+ - run `kgraph update` to turn notes into durable cognition
106
+ - run `kgraph scan` after refactors, file moves, or renamed functions
121
107
 
122
- ## MVP Scope
108
+ KGraph-managed instruction blocks preserve existing user-authored content.
123
109
 
124
- The first version focuses on:
110
+ ## Features
125
111
 
126
- - initializing `.kgraph/`
127
- - scanning repository files
128
- - extracting JavaScript and TypeScript symbols
129
- - writing file, symbol, dependency, and relationship maps
130
- - processing Markdown cognition notes
131
- - returning compact context for a query
132
- - keeping maps current as code changes
112
+ - Local `.kgraph/` workspace for repository intelligence
113
+ - JavaScript and TypeScript file, import, export, function, class, and method maps
114
+ - Deterministic relationship maps between files and symbols
115
+ - Markdown cognition inbox for AI chat summaries, decisions, gotchas, and debugging notes
116
+ - Compact context output for AI assistants and scripts
117
+ - JSON output for tool-friendly context retrieval
118
+ - Integration management for Codex, Copilot, Cursor, and Claude Code
119
+ - Stale-reference handling when code changes over time
120
+ - Local-first storage with human-readable JSON, YAML, and Markdown
133
121
 
134
- Out of scope for the MVP:
122
+ ## How KGraph Grows
135
123
 
136
- - npm publishing automation
137
- - deployment
138
- - cloud infrastructure
139
- - hosted dashboards
140
- - graph databases
141
- - vector databases
142
- - embeddings
143
- - autonomous agents
144
- - VS Code extension
124
+ KGraph is designed to improve as the project changes:
145
125
 
146
- ## CI
126
+ ```text
127
+ kgraph scan
128
+ refreshes current structure
147
129
 
148
- The CI pipeline is intentionally small and practical. It runs on pushes to `main` and pull requests targeting `main`, and validates:
130
+ AI chat or developer note
131
+ captures useful reasoning in .kgraph/inbox/
149
132
 
150
- ```bash
151
- npm ci
152
- npm run build
153
- npm test
154
- npm pack --dry-run
155
- npm run check:artifacts
156
- ```
133
+ kgraph update
134
+ converts notes into durable cognition
157
135
 
158
- `check:artifacts` fails if local/generated Spec Kit or KGraph artifacts are committed by mistake, including:
136
+ kgraph context "<topic>"
137
+ returns focused repository context for future work
138
+ ```
159
139
 
160
- - `.kgraph/`
161
- - `.specify/`
162
- - `.agents/`
163
- - `AGENTS.md`
164
- - `REQUIREMENTS.md`
165
- - `specs/`
140
+ This creates a feedback loop where normal development and AI-assisted debugging gradually improve the repository knowledge map.
166
141
 
167
- ## CD
142
+ ## Local-First
168
143
 
169
- For a CLI package, CD means packaging an intentional release, not deploying infrastructure.
144
+ KGraph stores project intelligence in local files inside `.kgraph/`. The MVP does not require accounts, telemetry, hosted services, databases, model providers, embeddings, or background daemons.
170
145
 
171
- KGraph includes a release package workflow that runs only when a maintainer pushes a version tag such as `v0.1.0` or starts the workflow manually. It repeats the core gates, creates the npm tarball, and uploads the package as a GitHub Actions artifact:
146
+ ## Development
172
147
 
173
148
  ```bash
174
- npm ci
149
+ npm install
175
150
  npm run build
176
151
  npm test
177
- npm run check:artifacts
178
- npm pack
152
+ npm run kgraph -- init --integrations codex,cursor
179
153
  ```
180
154
 
181
- This gives maintainers an inspectable package artifact before public npm publishing is enabled.
155
+ ## Release
182
156
 
183
- ## Roadmap
157
+ CI runs build, tests, package checks, and generated-artifact hygiene on pushes and pull requests.
184
158
 
185
- Near-term:
159
+ Releases are tag-driven. Bump the package version, push the commit, then push the matching tag:
186
160
 
187
- - stabilize the CLI contract
188
- - improve JS/TS symbol extraction
189
- - improve cognition note parsing
190
- - validate context quality against real repositories
191
- - make package publishing safe and intentional
161
+ ```bash
162
+ npm version patch
163
+ git push origin main --follow-tags
164
+ ```
192
165
 
193
- Later:
166
+ The release workflow verifies that the tag matches `package.json`, checks that the npm version has not already been published, publishes the package to npm, creates a GitHub Release, and attaches the packed tarball. Manual workflow runs package the project for inspection but do not publish to npm.
167
+
168
+ ## Roadmap
194
169
 
195
170
  - richer language scanners
171
+ - better cognition extraction
172
+ - graph visualization
196
173
  - Git-aware history and rename detection
197
- - optional MCP integration
198
- - optional editor integrations
199
- - visual graph exploration
200
- - optional LLM-assisted cognition extraction
174
+ - optional editor and MCP integrations
@@ -1,10 +1,29 @@
1
1
  import { writeDefaultConfig } from "../../config/config.js";
2
+ import { normalizeIntegrationNames } from "../../integrations/integration-registry.js";
3
+ import { addIntegrations } from "../../integrations/integration-store.js";
2
4
  import { ensureWorkspace } from "../../storage/kgraph-paths.js";
3
5
  import { runCommand } from "../errors.js";
4
6
  export function registerInitCommand(program) {
5
- program.command("init").description("Initialize a .kgraph workspace").action(() => runCommand(async () => {
7
+ program
8
+ .command("init")
9
+ .description("Initialize a .kgraph workspace")
10
+ .option("--integration <name>", "Configure an AI tool integration", collectOption, [])
11
+ .option("--integrations <names>", "Configure comma-separated AI tool integrations")
12
+ .action((options) => runCommand(async () => {
6
13
  const workspace = await ensureWorkspace(process.cwd());
7
14
  const wroteConfig = await writeDefaultConfig(workspace);
8
15
  console.log(wroteConfig ? "Initialized .kgraph workspace." : ".kgraph workspace already initialized.");
16
+ const names = normalizeIntegrationNames([
17
+ ...(options.integration ?? []),
18
+ ...(options.integrations ? [options.integrations] : [])
19
+ ]);
20
+ if (names.length > 0) {
21
+ const changed = await addIntegrations(workspace, names);
22
+ console.log(`Configured integrations: ${changed.map((item) => item.name).join(", ")}`);
23
+ }
9
24
  }));
10
25
  }
26
+ function collectOption(value, previous) {
27
+ previous.push(value);
28
+ return previous;
29
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerIntegrateCommand(program: Command): void;
@@ -0,0 +1,36 @@
1
+ import { addIntegrations, listIntegrations, removeIntegrations } from "../../integrations/integration-store.js";
2
+ import { normalizeIntegrationNames } from "../../integrations/integration-registry.js";
3
+ import { assertWorkspace } from "../../storage/kgraph-paths.js";
4
+ import { KGraphError, runCommand } from "../errors.js";
5
+ export function registerIntegrateCommand(program) {
6
+ const integrate = program.command("integrate").description("Manage AI tool integrations");
7
+ integrate.command("list").description("List configured integrations").action(() => runCommand(async () => {
8
+ const workspace = await assertWorkspace(process.cwd());
9
+ const integrations = await listIntegrations(workspace);
10
+ if (integrations.length === 0) {
11
+ console.log("No integrations configured.");
12
+ return;
13
+ }
14
+ for (const integration of integrations) {
15
+ console.log(`${integration.name} ${integration.enabled ? "enabled" : "disabled"} ${integration.targetPath} ${integration.targetExists ? "present" : "missing"}`);
16
+ }
17
+ }));
18
+ integrate.command("add").description("Add AI tool integrations").argument("<names...>").action((names) => runCommand(async () => {
19
+ const workspace = await assertWorkspace(process.cwd());
20
+ const normalized = normalizeIntegrationNames(names);
21
+ if (normalized.length === 0) {
22
+ throw new KGraphError("Provide at least one integration name.");
23
+ }
24
+ const changed = await addIntegrations(workspace, normalized);
25
+ console.log(`Configured integrations: ${changed.map((item) => item.name).join(", ")}`);
26
+ }));
27
+ integrate.command("remove").description("Remove AI tool integrations").argument("<names...>").action((names) => runCommand(async () => {
28
+ const workspace = await assertWorkspace(process.cwd());
29
+ const normalized = normalizeIntegrationNames(names);
30
+ if (normalized.length === 0) {
31
+ throw new KGraphError("Provide at least one integration name.");
32
+ }
33
+ const removed = await removeIntegrations(workspace, normalized);
34
+ console.log(`Removed integrations: ${removed.join(", ")}`);
35
+ }));
36
+ }
package/dist/cli/index.js CHANGED
@@ -4,6 +4,7 @@ import { registerInitCommand } from "./commands/init.js";
4
4
  import { registerScanCommand } from "./commands/scan.js";
5
5
  import { registerUpdateCommand } from "./commands/update.js";
6
6
  import { registerContextCommand } from "./commands/context.js";
7
+ import { registerIntegrateCommand } from "./commands/integrate.js";
7
8
  export function createProgram() {
8
9
  const program = new Command();
9
10
  program
@@ -14,6 +15,7 @@ export function createProgram() {
14
15
  registerScanCommand(program);
15
16
  registerUpdateCommand(program);
16
17
  registerContextCommand(program);
18
+ registerIntegrateCommand(program);
17
19
  return program;
18
20
  }
19
21
  if (import.meta.url === `file://${process.argv[1]}`) {
@@ -1,5 +1,6 @@
1
1
  import type { KGraphConfig, KGraphWorkspace } from "../types/config.js";
2
2
  export declare const DEFAULT_CONFIG: KGraphConfig;
3
3
  export declare function writeDefaultConfig(workspace: KGraphWorkspace): Promise<boolean>;
4
+ export declare function saveConfig(workspace: KGraphWorkspace, config: KGraphConfig): Promise<void>;
4
5
  export declare function loadConfig(workspace: KGraphWorkspace): Promise<KGraphConfig>;
5
6
  export declare function normalizeConfig(config: Partial<KGraphConfig>): KGraphConfig;
@@ -9,7 +9,8 @@ export const DEFAULT_CONFIG = {
9
9
  precise: [".js", ".jsx", ".ts", ".tsx"]
10
10
  },
11
11
  maxContextItems: 8,
12
- domainHints: {}
12
+ domainHints: {},
13
+ integrations: []
13
14
  };
14
15
  export async function writeDefaultConfig(workspace) {
15
16
  if (await pathExists(workspace.configPath)) {
@@ -18,6 +19,9 @@ export async function writeDefaultConfig(workspace) {
18
19
  await writeFile(workspace.configPath, YAML.stringify(DEFAULT_CONFIG), "utf8");
19
20
  return true;
20
21
  }
22
+ export async function saveConfig(workspace, config) {
23
+ await writeFile(workspace.configPath, YAML.stringify(config), "utf8");
24
+ }
21
25
  export async function loadConfig(workspace) {
22
26
  if (!(await pathExists(workspace.configPath))) {
23
27
  return DEFAULT_CONFIG;
@@ -44,6 +48,35 @@ export function normalizeConfig(config) {
44
48
  maxContextItems: typeof config.maxContextItems === "number" && config.maxContextItems > 0
45
49
  ? config.maxContextItems
46
50
  : DEFAULT_CONFIG.maxContextItems,
47
- domainHints: config.domainHints && typeof config.domainHints === "object" ? config.domainHints : {}
51
+ domainHints: config.domainHints && typeof config.domainHints === "object" ? config.domainHints : {},
52
+ integrations: normalizeIntegrations(config.integrations)
48
53
  };
49
54
  }
55
+ function normalizeIntegrations(value) {
56
+ if (!Array.isArray(value)) {
57
+ return [];
58
+ }
59
+ const seen = new Set();
60
+ const integrations = [];
61
+ for (const item of value) {
62
+ if (!item || typeof item !== "object") {
63
+ continue;
64
+ }
65
+ const candidate = item;
66
+ if (typeof candidate.name !== "string" ||
67
+ typeof candidate.targetPath !== "string" ||
68
+ seen.has(candidate.name)) {
69
+ continue;
70
+ }
71
+ if (!["claude-code", "codex", "copilot", "cursor"].includes(candidate.name)) {
72
+ continue;
73
+ }
74
+ seen.add(candidate.name);
75
+ integrations.push({
76
+ name: candidate.name,
77
+ enabled: candidate.enabled !== false,
78
+ targetPath: candidate.targetPath
79
+ });
80
+ }
81
+ return integrations;
82
+ }
@@ -0,0 +1,2 @@
1
+ import type { IntegrationAdapter } from "../integration-registry.js";
2
+ export declare const claudeCodeAdapter: IntegrationAdapter;
@@ -0,0 +1,13 @@
1
+ export const claudeCodeAdapter = {
2
+ name: "claude-code",
3
+ label: "Claude Code",
4
+ targetPath: "CLAUDE.md",
5
+ instructions: `## KGraph Workflow
6
+
7
+ - Start repository work by checking \`kgraph context "<topic>"\` when the user asks about a domain, bug, workflow, or feature.
8
+ - Convert stable discoveries from chat into Markdown notes under \`.kgraph/inbox/\`.
9
+ - Run \`kgraph update\` to preserve those notes as durable cognition.
10
+ - Run \`kgraph scan\` after structural code changes.
11
+ - Run \`kgraph visualize\` when visualization support is available and the user wants to inspect the current knowledge map.
12
+ `
13
+ };
@@ -0,0 +1,2 @@
1
+ import type { IntegrationAdapter } from "../integration-registry.js";
2
+ export declare const codexAdapter: IntegrationAdapter;
@@ -0,0 +1,13 @@
1
+ export const codexAdapter = {
2
+ name: "codex",
3
+ label: "Codex",
4
+ targetPath: "AGENTS.md",
5
+ instructions: `## KGraph Workflow
6
+
7
+ - Before broad repository exploration, run \`kgraph context "<topic>"\` to load current repo intelligence.
8
+ - After useful debugging, architecture discovery, workflow learning, or implementation work, write a concise Markdown note into \`.kgraph/inbox/\`.
9
+ - Run \`kgraph update\` after adding inbox notes so KGraph preserves durable cognition.
10
+ - Run \`kgraph scan\` after file moves, function renames, refactors, or other structural changes.
11
+ - When visualization support is available and the developer asks to inspect KGraph, run \`kgraph visualize\` and report the generated local artifact.
12
+ `
13
+ };
@@ -0,0 +1,2 @@
1
+ import type { IntegrationAdapter } from "../integration-registry.js";
2
+ export declare const copilotAdapter: IntegrationAdapter;
@@ -0,0 +1,13 @@
1
+ export const copilotAdapter = {
2
+ name: "copilot",
3
+ label: "GitHub Copilot",
4
+ targetPath: ".github/copilot-instructions.md",
5
+ instructions: `## KGraph Workflow
6
+
7
+ - Use \`kgraph context "<topic>"\` before scanning many files manually.
8
+ - Preserve stable findings by creating Markdown notes in \`.kgraph/inbox/\`.
9
+ - Use \`kgraph update\` to process chat summaries and debugging conclusions into durable cognition.
10
+ - Use \`kgraph scan\` when code structure changes.
11
+ - Use \`kgraph visualize\` when visualization support is available and the developer asks to inspect the repository knowledge map.
12
+ `
13
+ };
@@ -0,0 +1,2 @@
1
+ import type { IntegrationAdapter } from "../integration-registry.js";
2
+ export declare const cursorAdapter: IntegrationAdapter;
@@ -0,0 +1,18 @@
1
+ export const cursorAdapter = {
2
+ name: "cursor",
3
+ label: "Cursor",
4
+ targetPath: ".cursor/rules/kgraph.mdc",
5
+ instructions: `---
6
+ description: Use KGraph persistent repo intelligence before broad repository exploration
7
+ alwaysApply: true
8
+ ---
9
+
10
+ ## KGraph Workflow
11
+
12
+ - Query \`kgraph context "<topic>"\` before broad file searches when repo cognition may already exist.
13
+ - Store durable chat, debugging, architecture, and workflow discoveries as Markdown notes in \`.kgraph/inbox/\`.
14
+ - Run \`kgraph update\` after adding useful notes.
15
+ - Run \`kgraph scan\` after refactors, moved folders, renamed functions, or other structure changes.
16
+ - Run \`kgraph visualize\` when visualization support is available and the developer asks to inspect the KGraph map.
17
+ `
18
+ };
@@ -0,0 +1,2 @@
1
+ export declare function upsertManagedBlock(content: string, integrationName: string, instructions: string): string;
2
+ export declare function removeManagedBlock(content: string, integrationName: string): string;
@@ -0,0 +1,27 @@
1
+ const MARKER_PREFIX = "<!--";
2
+ const MARKER_SUFFIX = "-->";
3
+ export function upsertManagedBlock(content, integrationName, instructions) {
4
+ const normalized = content.trimEnd();
5
+ const block = renderManagedBlock(integrationName, instructions);
6
+ const pattern = managedBlockPattern(integrationName);
7
+ if (pattern.test(content)) {
8
+ return content.replace(pattern, block);
9
+ }
10
+ return `${normalized}${normalized ? "\n\n" : ""}${block}\n`;
11
+ }
12
+ export function removeManagedBlock(content, integrationName) {
13
+ return content.replace(managedBlockPattern(integrationName), "").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
14
+ }
15
+ function renderManagedBlock(integrationName, instructions) {
16
+ return [
17
+ `${MARKER_PREFIX} BEGIN KGRAPH ${integrationName} ${MARKER_SUFFIX}`,
18
+ instructions.trim(),
19
+ `${MARKER_PREFIX} END KGRAPH ${integrationName} ${MARKER_SUFFIX}`
20
+ ].join("\n");
21
+ }
22
+ function managedBlockPattern(integrationName) {
23
+ return new RegExp(`${escapeRegExp(MARKER_PREFIX)} BEGIN KGRAPH ${escapeRegExp(integrationName)} ${escapeRegExp(MARKER_SUFFIX)}[\\s\\S]*?${escapeRegExp(MARKER_PREFIX)} END KGRAPH ${escapeRegExp(integrationName)} ${escapeRegExp(MARKER_SUFFIX)}\\n?`, "m");
24
+ }
25
+ function escapeRegExp(value) {
26
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27
+ }
@@ -0,0 +1,10 @@
1
+ import type { IntegrationName } from "../types/config.js";
2
+ export interface IntegrationAdapter {
3
+ name: IntegrationName;
4
+ label: string;
5
+ targetPath: string;
6
+ instructions: string;
7
+ }
8
+ export declare function listIntegrationAdapters(): IntegrationAdapter[];
9
+ export declare function getIntegrationAdapter(name: string): IntegrationAdapter;
10
+ export declare function normalizeIntegrationNames(values: string[] | undefined): IntegrationName[];
@@ -0,0 +1,39 @@
1
+ import { claudeCodeAdapter } from "./adapters/claude-code.js";
2
+ import { codexAdapter } from "./adapters/codex.js";
3
+ import { copilotAdapter } from "./adapters/copilot.js";
4
+ import { cursorAdapter } from "./adapters/cursor.js";
5
+ const ADAPTERS = [
6
+ claudeCodeAdapter,
7
+ codexAdapter,
8
+ copilotAdapter,
9
+ cursorAdapter
10
+ ].sort((left, right) => left.name.localeCompare(right.name));
11
+ export function listIntegrationAdapters() {
12
+ return ADAPTERS;
13
+ }
14
+ export function getIntegrationAdapter(name) {
15
+ const adapter = ADAPTERS.find((item) => item.name === name);
16
+ if (!adapter) {
17
+ throw new Error(`Unsupported integration "${name}". Supported integrations: ${ADAPTERS.map((item) => item.name).join(", ")}`);
18
+ }
19
+ return adapter;
20
+ }
21
+ export function normalizeIntegrationNames(values) {
22
+ if (!values || values.length === 0) {
23
+ return [];
24
+ }
25
+ const names = [];
26
+ const seen = new Set();
27
+ for (const value of values) {
28
+ for (const raw of value.split(",")) {
29
+ const name = raw.trim();
30
+ if (!name || seen.has(name)) {
31
+ continue;
32
+ }
33
+ const adapter = getIntegrationAdapter(name);
34
+ seen.add(adapter.name);
35
+ names.push(adapter.name);
36
+ }
37
+ }
38
+ return names;
39
+ }
@@ -0,0 +1,10 @@
1
+ import type { IntegrationConfig, IntegrationName, KGraphWorkspace } from "../types/config.js";
2
+ export interface IntegrationStatus {
3
+ name: IntegrationName;
4
+ enabled: boolean;
5
+ targetPath: string;
6
+ targetExists: boolean;
7
+ }
8
+ export declare function listIntegrations(workspace: KGraphWorkspace): Promise<IntegrationStatus[]>;
9
+ export declare function addIntegrations(workspace: KGraphWorkspace, names: IntegrationName[]): Promise<IntegrationConfig[]>;
10
+ export declare function removeIntegrations(workspace: KGraphWorkspace, names: IntegrationName[]): Promise<IntegrationName[]>;
@@ -0,0 +1,68 @@
1
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { loadConfig, saveConfig } from "../config/config.js";
4
+ import { pathExists } from "../storage/kgraph-paths.js";
5
+ import { getIntegrationAdapter } from "./integration-registry.js";
6
+ import { removeManagedBlock, upsertManagedBlock } from "./instruction-blocks.js";
7
+ export async function listIntegrations(workspace) {
8
+ const config = await loadConfig(workspace);
9
+ const statuses = await Promise.all(config.integrations.map(async (integration) => ({
10
+ name: integration.name,
11
+ enabled: integration.enabled,
12
+ targetPath: integration.targetPath,
13
+ targetExists: await pathExists(path.join(workspace.rootPath, integration.targetPath))
14
+ })));
15
+ return statuses.sort((left, right) => left.name.localeCompare(right.name));
16
+ }
17
+ export async function addIntegrations(workspace, names) {
18
+ const config = await loadConfig(workspace);
19
+ const byName = new Map(config.integrations.map((integration) => [integration.name, integration]));
20
+ const changed = [];
21
+ for (const name of names) {
22
+ const adapter = getIntegrationAdapter(name);
23
+ const next = {
24
+ name: adapter.name,
25
+ enabled: true,
26
+ targetPath: adapter.targetPath
27
+ };
28
+ byName.set(adapter.name, next);
29
+ await writeIntegrationInstructions(workspace.rootPath, adapter.targetPath, adapter.name, adapter.instructions);
30
+ changed.push(next);
31
+ }
32
+ config.integrations = [...byName.values()].sort((left, right) => left.name.localeCompare(right.name));
33
+ await saveConfig(workspace, config);
34
+ return changed;
35
+ }
36
+ export async function removeIntegrations(workspace, names) {
37
+ const config = await loadConfig(workspace);
38
+ const removeNames = new Set(names);
39
+ const removed = [];
40
+ for (const name of removeNames) {
41
+ const adapter = getIntegrationAdapter(name);
42
+ await removeIntegrationInstructions(workspace.rootPath, adapter.targetPath, adapter.name);
43
+ removed.push(adapter.name);
44
+ }
45
+ config.integrations = config.integrations.filter((integration) => !removeNames.has(integration.name));
46
+ await saveConfig(workspace, config);
47
+ return removed.sort((left, right) => left.localeCompare(right));
48
+ }
49
+ async function writeIntegrationInstructions(rootPath, targetPath, integrationName, instructions) {
50
+ const fullPath = path.join(rootPath, targetPath);
51
+ const existing = (await pathExists(fullPath)) ? await readFile(fullPath, "utf8") : "";
52
+ const next = upsertManagedBlock(existing, integrationName, instructions);
53
+ await mkdir(path.dirname(fullPath), { recursive: true });
54
+ await writeFile(fullPath, next, "utf8");
55
+ }
56
+ async function removeIntegrationInstructions(rootPath, targetPath, integrationName) {
57
+ const fullPath = path.join(rootPath, targetPath);
58
+ if (!(await pathExists(fullPath))) {
59
+ return;
60
+ }
61
+ const existing = await readFile(fullPath, "utf8");
62
+ const next = removeManagedBlock(existing, integrationName);
63
+ if (next.trim().length === 0) {
64
+ await rm(fullPath, { force: true });
65
+ return;
66
+ }
67
+ await writeFile(fullPath, next, "utf8");
68
+ }
@@ -6,11 +6,18 @@ export interface KGraphConfig {
6
6
  };
7
7
  maxContextItems: number;
8
8
  domainHints: Record<string, DomainHint>;
9
+ integrations: IntegrationConfig[];
9
10
  }
10
11
  export interface DomainHint {
11
12
  paths?: string[];
12
13
  tags?: string[];
13
14
  }
15
+ export type IntegrationName = "claude-code" | "codex" | "copilot" | "cursor";
16
+ export interface IntegrationConfig {
17
+ name: IntegrationName;
18
+ enabled: boolean;
19
+ targetPath: string;
20
+ }
14
21
  export interface KGraphWorkspace {
15
22
  rootPath: string;
16
23
  kgraphPath: string;
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@kentwynn/kgraph",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Persistent repo intelligence for AI coding assistants.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "kgraph": "./dist/cli/index.js"
7
+ "kgraph": "dist/cli/index.js"
8
8
  },
9
9
  "files": [
10
10
  "dist",