@latentforce/latentgraph 1.0.0 → 1.0.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 +16 -16
- package/build/cli/commands/add.js +31 -31
- package/build/cli/commands/config.js +7 -7
- package/build/cli/commands/init.js +11 -11
- package/build/cli/commands/start.js +3 -3
- package/build/cli/commands/status.js +1 -1
- package/build/cli/commands/stop.js +1 -1
- package/build/cli/commands/update-drg.js +1 -1
- package/build/daemon/daemon.js +1 -1
- package/build/index.js +15 -15
- package/build/mcp-server.js +14 -14
- package/build/utils/auth-resolver.js +2 -2
- package/build/utils/config.js +27 -27
- package/build/utils/prompts.js +4 -4
- package/build/utils/tree-scanner.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
AI-powered code intelligence CLI and MCP server for Claude.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Latentgraph indexes your codebase, builds a dependency relationship graph (DRG), and provides AI-powered insights via MCP tools — enabling Claude to understand your project's structure, dependencies, and blast radius of changes.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -59,18 +59,18 @@ If a project named "My App" already exists in your account, it will be reused. O
|
|
|
59
59
|
| `lgraph status` | Show current status |
|
|
60
60
|
| `lgraph update-drg` | Update the dependency relationship graph |
|
|
61
61
|
| `lgraph stop` | Stop the daemon |
|
|
62
|
-
| `lgraph add <tool>` | Add
|
|
62
|
+
| `lgraph add <tool>` | Add Latentgraph MCP server to an AI coding tool |
|
|
63
63
|
| `lgraph config` | Manage configuration |
|
|
64
64
|
|
|
65
65
|
### `lgraph start`
|
|
66
66
|
|
|
67
|
-
Resolves authentication, configures the project, and launches a background daemon that maintains a WebSocket connection to the
|
|
67
|
+
Resolves authentication, configures the project, and launches a background daemon that maintains a WebSocket connection to the Latentgraph backend.
|
|
68
68
|
|
|
69
69
|
**When run interactively (no flags):**
|
|
70
70
|
1. Prompts you to enter your API key or choose guest mode
|
|
71
71
|
2. Prompts you to **select an existing project** or **create a new one**
|
|
72
72
|
- If creating: asks for a project name and lets you pick a migration template
|
|
73
|
-
3. Saves config to `.
|
|
73
|
+
3. Saves config to `.lgraph/config.json`
|
|
74
74
|
4. Launches the background daemon
|
|
75
75
|
|
|
76
76
|
If you already created a project on the dashboard (https://dev-shift-lite.latentforce.ai), you can select it from the list. Otherwise, create one directly from the CLI.
|
|
@@ -91,7 +91,7 @@ lgraph start --api-key <key> --project-name "My App" # Non-interactive
|
|
|
91
91
|
|
|
92
92
|
### `lgraph init`
|
|
93
93
|
|
|
94
|
-
Performs a full project scan — collects the file tree, categorizes files (source, config, assets), gathers git info, and sends everything to the
|
|
94
|
+
Performs a full project scan — collects the file tree, categorizes files (source, config, assets), gathers git info, and sends everything to the Latentgraph backend for indexing. Automatically starts the daemon if not running.
|
|
95
95
|
|
|
96
96
|
If the project is already indexed, you will be prompted to re-index. Use `--force` to skip the prompt.
|
|
97
97
|
|
|
@@ -111,7 +111,7 @@ lgraph init --force # Force re-index without prompt
|
|
|
111
111
|
|
|
112
112
|
### `lgraph status`
|
|
113
113
|
|
|
114
|
-
Displays comprehensive information about the current
|
|
114
|
+
Displays comprehensive information about the current Latentgraph setup — API key status, project details, backend indexing state, and daemon health.
|
|
115
115
|
|
|
116
116
|
```bash
|
|
117
117
|
lgraph status
|
|
@@ -153,7 +153,7 @@ lgraph stop
|
|
|
153
153
|
|
|
154
154
|
### `lgraph config`
|
|
155
155
|
|
|
156
|
-
Manage
|
|
156
|
+
Manage Latentgraph configuration (URLs, API key).
|
|
157
157
|
|
|
158
158
|
| Action | Description |
|
|
159
159
|
|--------|-------------|
|
|
@@ -171,7 +171,7 @@ lgraph config clear # Clear all config
|
|
|
171
171
|
```
|
|
172
172
|
## MCP Integration
|
|
173
173
|
|
|
174
|
-
After initializing your project, use `lgraph add` to configure
|
|
174
|
+
After initializing your project, use `lgraph add` to configure Latent's MCP server in your AI coding tool:
|
|
175
175
|
|
|
176
176
|
```bash
|
|
177
177
|
lgraph add <tool>
|
|
@@ -197,19 +197,19 @@ Add to your config file (`%APPDATA%\Claude\claude_desktop_config.json` on Window
|
|
|
197
197
|
```json
|
|
198
198
|
{
|
|
199
199
|
"mcpServers": {
|
|
200
|
-
"
|
|
200
|
+
"lgraph": {
|
|
201
201
|
"command": "lgraph",
|
|
202
202
|
"args": ["mcp"],
|
|
203
203
|
"env": {
|
|
204
|
-
"
|
|
204
|
+
"LGRAPH_PROJECT_ID": "YOUR_PROJECT_ID"
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
```
|
|
210
|
-
## `.
|
|
210
|
+
## `.lgraph/scan_target.json`
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
Latentgraph uses a `scan_target.json` file inside the `.lgraph/` directory to let you specify which parts of your codebase to scan and in which language. This is especially useful for monorepos or projects with multiple languages.
|
|
213
213
|
|
|
214
214
|
A default template is **automatically created** the first time you run `lgraph init` or `lgraph start`. By default, it is unconfigured and the server will auto-detect scan targets. Edit the file to manually specify targets.
|
|
215
215
|
|
|
@@ -309,12 +309,12 @@ When left as-is (single entry with `language: null` and `path: ""`), the server
|
|
|
309
309
|
| `dependencies` | Get all dependencies for a file with relationship summaries |
|
|
310
310
|
| `blast_radius` | Analyze what files would be affected if a file is modified or deleted |
|
|
311
311
|
|
|
312
|
-
Each tool accepts an optional `project_id` parameter. If not provided, it falls back to the `
|
|
312
|
+
Each tool accepts an optional `project_id` parameter. If not provided, it falls back to the `LGRAPH_PROJECT_ID` environment variable.
|
|
313
313
|
|
|
314
314
|
## Configuration Files
|
|
315
315
|
|
|
316
316
|
| File | Location | Description |
|
|
317
317
|
|------|----------|-------------|
|
|
318
|
-
| Global config | `~/.
|
|
319
|
-
| Project config | `.
|
|
320
|
-
| Scan targets | `.
|
|
318
|
+
| Global config | `~/.lgraph/config.json` | API key and server URLs |
|
|
319
|
+
| Project config | `.lgraph/config.json` | Project ID, name, and agent info |
|
|
320
|
+
| Scan targets | `.lgraph/scan_target.json` | Language and path targets for scanning (auto-created) |
|
|
@@ -23,14 +23,14 @@ function getMcpConfig(projectId) {
|
|
|
23
23
|
return {
|
|
24
24
|
command: 'lgraph',
|
|
25
25
|
args: ['mcp'],
|
|
26
|
-
env: {
|
|
26
|
+
env: { LGRAPH_PROJECT_ID: projectId },
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
const CLAUDE_MD_SECTION_MARKER = '<!--
|
|
29
|
+
const CLAUDE_MD_SECTION_MARKER = '<!-- lgraph-mcp-instructions -->';
|
|
30
30
|
const CLAUDE_MD_CONTENT = `${CLAUDE_MD_SECTION_MARKER}
|
|
31
|
-
##
|
|
31
|
+
## Latentgraph MCP Tools — MANDATORY USAGE RULES
|
|
32
32
|
|
|
33
|
-
This project has the **
|
|
33
|
+
This project has the **Latentgraph MCP server** (\`lgraph\`) configured with a pre-built knowledge graph of the entire codebase. You MUST use its tools as the primary way to understand code. Do NOT rely solely on reading raw files.
|
|
34
34
|
|
|
35
35
|
### Tools
|
|
36
36
|
|
|
@@ -104,9 +104,9 @@ blast_radius(file_path="src/server.ts")
|
|
|
104
104
|
\`\`\`
|
|
105
105
|
|
|
106
106
|
> Run \`lgraph update-drg\` after significant code changes to keep the knowledge graph current.
|
|
107
|
-
<!-- end-
|
|
107
|
+
<!-- end-lgraph-mcp-instructions -->
|
|
108
108
|
`;
|
|
109
|
-
const CLAUDE_MD_END_MARKER = '<!-- end-
|
|
109
|
+
const CLAUDE_MD_END_MARKER = '<!-- end-lgraph-mcp-instructions -->';
|
|
110
110
|
function createClaudeMd(projectRoot) {
|
|
111
111
|
const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
|
|
112
112
|
if (fs.existsSync(claudeMdPath)) {
|
|
@@ -126,16 +126,16 @@ function createClaudeMd(projectRoot) {
|
|
|
126
126
|
const before = existing.slice(0, existing.indexOf(CLAUDE_MD_SECTION_MARKER)).trimEnd();
|
|
127
127
|
fs.writeFileSync(claudeMdPath, (before ? before + '\n\n' : '') + CLAUDE_MD_CONTENT);
|
|
128
128
|
}
|
|
129
|
-
console.log(' Updated
|
|
129
|
+
console.log(' Updated Latentgraph MCP instructions in CLAUDE.md.');
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
132
132
|
// Append section to existing file
|
|
133
133
|
fs.writeFileSync(claudeMdPath, existing.trimEnd() + '\n\n' + CLAUDE_MD_CONTENT);
|
|
134
|
-
console.log(' Updated CLAUDE.md with
|
|
134
|
+
console.log(' Updated CLAUDE.md with Latentgraph MCP instructions.');
|
|
135
135
|
}
|
|
136
136
|
else {
|
|
137
137
|
fs.writeFileSync(claudeMdPath, CLAUDE_MD_CONTENT);
|
|
138
|
-
console.log(' Created CLAUDE.md with
|
|
138
|
+
console.log(' Created CLAUDE.md with Latentgraph MCP instructions.');
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
async function addClaudeCode(projectId, projectRoot) {
|
|
@@ -143,35 +143,35 @@ async function addClaudeCode(projectId, projectRoot) {
|
|
|
143
143
|
type: 'stdio',
|
|
144
144
|
command: 'lgraph',
|
|
145
145
|
args: ['mcp'],
|
|
146
|
-
env: {
|
|
146
|
+
env: { LGRAPH_PROJECT_ID: projectId },
|
|
147
147
|
});
|
|
148
148
|
try {
|
|
149
|
-
await execAsync(buildCommand('claude', ['mcp', 'add-json', '
|
|
150
|
-
console.log(' Added
|
|
149
|
+
await execAsync(buildCommand('claude', ['mcp', 'add-json', 'lgraph', jsonPayload]));
|
|
150
|
+
console.log(' Added Latentgraph MCP server to Claude Code.');
|
|
151
151
|
console.log('\n Verify with: claude mcp list');
|
|
152
152
|
}
|
|
153
153
|
catch (e) {
|
|
154
154
|
const err = e;
|
|
155
155
|
const stderr = getStderr(err);
|
|
156
156
|
if (/already exists/.test(stderr)) {
|
|
157
|
-
console.log('
|
|
158
|
-
console.log(' To update, remove it first: claude mcp remove
|
|
157
|
+
console.log(' Latentgraph MCP server is already configured in Claude Code.');
|
|
158
|
+
console.log(' To update, remove it first: claude mcp remove lgraph');
|
|
159
159
|
}
|
|
160
160
|
else if (isNotFound(err)) {
|
|
161
161
|
console.log(' "claude" CLI not found. Add manually:\n');
|
|
162
|
-
console.log(` claude mcp add-json
|
|
162
|
+
console.log(` claude mcp add-json lgraph '${jsonPayload}'`);
|
|
163
163
|
}
|
|
164
164
|
else {
|
|
165
165
|
console.log(` Failed: ${stderr}`);
|
|
166
166
|
console.log('\n Try adding manually:\n');
|
|
167
|
-
console.log(` claude mcp add-json
|
|
167
|
+
console.log(` claude mcp add-json lgraph '${jsonPayload}'`);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
createClaudeMd(projectRoot);
|
|
171
171
|
}
|
|
172
172
|
async function addCodex(projectId) {
|
|
173
173
|
const config = getMcpConfig(projectId);
|
|
174
|
-
const args = ['mcp', 'add', '
|
|
174
|
+
const args = ['mcp', 'add', 'lgraph'];
|
|
175
175
|
for (const [k, v] of Object.entries(config.env)) {
|
|
176
176
|
args.push('--env', `${k}=${v}`);
|
|
177
177
|
}
|
|
@@ -179,7 +179,7 @@ async function addCodex(projectId) {
|
|
|
179
179
|
const manualCmd = `codex ${args.join(' ')}`;
|
|
180
180
|
try {
|
|
181
181
|
await execAsync(buildCommand('codex', args));
|
|
182
|
-
console.log(' Added
|
|
182
|
+
console.log(' Added Latentgraph MCP server to Codex.');
|
|
183
183
|
console.log('\n Verify with: codex mcp list');
|
|
184
184
|
}
|
|
185
185
|
catch (e) {
|
|
@@ -190,8 +190,8 @@ async function addCodex(projectId) {
|
|
|
190
190
|
console.log(' Once installed, add manually:\n');
|
|
191
191
|
}
|
|
192
192
|
else if (/already exists/.test(stderr)) {
|
|
193
|
-
console.log('
|
|
194
|
-
console.log(' To update, remove it first: codex mcp remove
|
|
193
|
+
console.log(' Latentgraph MCP server is already configured in Codex.');
|
|
194
|
+
console.log(' To update, remove it first: codex mcp remove lgraph');
|
|
195
195
|
return;
|
|
196
196
|
}
|
|
197
197
|
else {
|
|
@@ -203,14 +203,14 @@ async function addCodex(projectId) {
|
|
|
203
203
|
}
|
|
204
204
|
async function addFactoryDroid(projectId) {
|
|
205
205
|
const config = getMcpConfig(projectId);
|
|
206
|
-
const args = ['mcp', 'add', '
|
|
206
|
+
const args = ['mcp', 'add', 'lgraph', config.command];
|
|
207
207
|
for (const [k, v] of Object.entries(config.env)) {
|
|
208
208
|
args.push('--env', `${k}=${v}`);
|
|
209
209
|
}
|
|
210
210
|
const manualCmd = `droid ${args.join(' ')}`;
|
|
211
211
|
try {
|
|
212
212
|
await execAsync(buildCommand('droid', args));
|
|
213
|
-
console.log(' Added
|
|
213
|
+
console.log(' Added Latentgraph MCP server to Factory Droid.');
|
|
214
214
|
console.log('\n Verify: type /mcp within droid to see configured servers');
|
|
215
215
|
}
|
|
216
216
|
catch (e) {
|
|
@@ -265,14 +265,14 @@ function addOpencode(projectId, projectRoot) {
|
|
|
265
265
|
fs.mkdirSync(dir, { recursive: true });
|
|
266
266
|
fs.writeFileSync(filePath, JSON.stringify(existing, null, 2) + '\n');
|
|
267
267
|
}
|
|
268
|
-
mergeJsonConfig(filePath, ['mcp', '
|
|
268
|
+
mergeJsonConfig(filePath, ['mcp', 'lgraph'], {
|
|
269
269
|
type: 'local',
|
|
270
270
|
command: ["lgraph", "mcp"],
|
|
271
271
|
enabled: true,
|
|
272
|
-
environment: { "
|
|
272
|
+
environment: { "LGRAPH_PROJECT_ID": projectId },
|
|
273
273
|
});
|
|
274
274
|
console.log(` Updated ${filePath}`);
|
|
275
|
-
console.log('\n Verify: check "mcp.
|
|
275
|
+
console.log('\n Verify: check "mcp.lgraph" in opencode.json');
|
|
276
276
|
}
|
|
277
277
|
function addShiftcode(projectId, projectRoot) {
|
|
278
278
|
const filePath = path.join(projectRoot, 'shiftcode.json');
|
|
@@ -290,25 +290,25 @@ function addShiftcode(projectId, projectRoot) {
|
|
|
290
290
|
fs.mkdirSync(dir, { recursive: true });
|
|
291
291
|
fs.writeFileSync(filePath, JSON.stringify(existing, null, 2) + '\n');
|
|
292
292
|
}
|
|
293
|
-
mergeJsonConfig(filePath, ['mcp', '
|
|
293
|
+
mergeJsonConfig(filePath, ['mcp', 'lgraph'], {
|
|
294
294
|
type: 'local',
|
|
295
295
|
command: ["lgraph", "mcp"],
|
|
296
296
|
enabled: true,
|
|
297
|
-
environment: { "
|
|
297
|
+
environment: { "LGRAPH_PROJECT_ID": projectId },
|
|
298
298
|
});
|
|
299
299
|
console.log(` Updated ${filePath}`);
|
|
300
|
-
console.log('\n Verify: check "mcp.
|
|
300
|
+
console.log('\n Verify: check "mcp.lgraph" in shiftcode.json');
|
|
301
301
|
}
|
|
302
302
|
function addCopilot(projectId, projectRoot) {
|
|
303
303
|
const config = getMcpConfig(projectId);
|
|
304
304
|
const filePath = path.join(projectRoot, '.vscode', 'mcp.json');
|
|
305
|
-
mergeJsonConfig(filePath, ['servers', '
|
|
305
|
+
mergeJsonConfig(filePath, ['servers', 'lgraph'], {
|
|
306
306
|
command: config.command,
|
|
307
307
|
args: config.args,
|
|
308
308
|
env: config.env,
|
|
309
309
|
});
|
|
310
310
|
console.log(` Updated ${filePath}`);
|
|
311
|
-
console.log('\n Verify: check "servers.
|
|
311
|
+
console.log('\n Verify: check "servers.lgraph" in .vscode/mcp.json');
|
|
312
312
|
}
|
|
313
313
|
export async function addCommand(tool) {
|
|
314
314
|
if (!SUPPORTED_TOOLS.includes(tool)) {
|
|
@@ -325,7 +325,7 @@ export async function addCommand(tool) {
|
|
|
325
325
|
console.error('\n No project configured. Run "lgraph init" first to set up your project.\n');
|
|
326
326
|
process.exit(1);
|
|
327
327
|
}
|
|
328
|
-
console.log(`\n Configuring
|
|
328
|
+
console.log(`\n Configuring Latentgraph MCP for ${tool}...\n`);
|
|
329
329
|
switch (tool) {
|
|
330
330
|
case 'shiftcode':
|
|
331
331
|
addShiftcode(projectId, projectRoot);
|
|
@@ -21,10 +21,10 @@ export async function configCommand(action, key, value) {
|
|
|
21
21
|
console.log(' clear [key] Clear configuration (all or specific key)');
|
|
22
22
|
console.log('');
|
|
23
23
|
console.log('Keys:');
|
|
24
|
-
console.log(' api-key Your
|
|
25
|
-
console.log(' api-url
|
|
26
|
-
console.log(' orch-url
|
|
27
|
-
console.log(' ws-url
|
|
24
|
+
console.log(' api-key Your LGRAPH API key');
|
|
25
|
+
console.log(' api-url LGRAPH_API_URL');
|
|
26
|
+
console.log(' orch-url LGRAPH_ORCH_URL');
|
|
27
|
+
console.log(' ws-url LGRAPH_WS_URL');
|
|
28
28
|
console.log('');
|
|
29
29
|
console.log('Examples:');
|
|
30
30
|
console.log(' lgraph config');
|
|
@@ -37,7 +37,7 @@ export async function configCommand(action, key, value) {
|
|
|
37
37
|
}
|
|
38
38
|
function showConfig() {
|
|
39
39
|
console.log('╔════════════════════════════════════════════╗');
|
|
40
|
-
console.log('║
|
|
40
|
+
console.log('║ Latentgraph Configuration ║');
|
|
41
41
|
console.log('╚════════════════════════════════════════════╝\n');
|
|
42
42
|
const globalConfig = readGlobalConfig();
|
|
43
43
|
const urls = getConfiguredUrls();
|
|
@@ -59,10 +59,10 @@ function showConfig() {
|
|
|
59
59
|
console.log(` WS URL: ${urls.ws_url}`);
|
|
60
60
|
console.log('');
|
|
61
61
|
// Source info
|
|
62
|
-
console.log('Config file: ~/.
|
|
62
|
+
console.log('Config file: ~/.lgraph/config.json');
|
|
63
63
|
console.log('');
|
|
64
64
|
console.log('Tip: URLs can also be set via environment variables:');
|
|
65
|
-
console.log('
|
|
65
|
+
console.log(' LGRAPH_API_URL, LGRAPH_ORCH_URL, LGRAPH_WS_URL');
|
|
66
66
|
}
|
|
67
67
|
async function setConfigValue(key, value) {
|
|
68
68
|
if (!key) {
|
|
@@ -32,7 +32,7 @@ export async function initCommand(options = {}) {
|
|
|
32
32
|
const isAuthInteractive = !options.guest && !options.apiKey;
|
|
33
33
|
const isProjectInteractive = !!(process.stdin.isTTY);
|
|
34
34
|
console.log('╔═══════════════════════════════════════════════╗');
|
|
35
|
-
console.log('║
|
|
35
|
+
console.log('║ Initializing Latentgraph Project ║');
|
|
36
36
|
console.log('╚═══════════════════════════════════════════════╝\n');
|
|
37
37
|
// Step 1: Resolve API key
|
|
38
38
|
console.log('[Init] Step 1/5: Checking API key...');
|
|
@@ -65,7 +65,7 @@ export async function initCommand(options = {}) {
|
|
|
65
65
|
console.error('\n❌ Failed to configure project.');
|
|
66
66
|
process.exit(1);
|
|
67
67
|
}
|
|
68
|
-
// Ensure .
|
|
68
|
+
// Ensure .lgraph/scan_target.json exists (template for user customization)
|
|
69
69
|
ensureScanTargetFile(projectRoot);
|
|
70
70
|
// Check if project is already indexed (skip check if --force flag is used)
|
|
71
71
|
if (!options.force) {
|
|
@@ -134,7 +134,7 @@ export async function initCommand(options = {}) {
|
|
|
134
134
|
'.vscode',
|
|
135
135
|
'dist',
|
|
136
136
|
'build',
|
|
137
|
-
'.
|
|
137
|
+
'.lgraph',
|
|
138
138
|
'.next',
|
|
139
139
|
'coverage',
|
|
140
140
|
'venv',
|
|
@@ -147,8 +147,8 @@ export async function initCommand(options = {}) {
|
|
|
147
147
|
// Get git info (matching extension)
|
|
148
148
|
let gitInfo = {
|
|
149
149
|
current_branch: '',
|
|
150
|
-
original_branch: '
|
|
151
|
-
migrate_branch: '
|
|
150
|
+
original_branch: 'lgraph_original',
|
|
151
|
+
migrate_branch: 'lgraph_migrated',
|
|
152
152
|
has_uncommitted_changes: false,
|
|
153
153
|
};
|
|
154
154
|
try {
|
|
@@ -172,9 +172,9 @@ export async function initCommand(options = {}) {
|
|
|
172
172
|
console.log(`[Init] Asset files: ${categorized.asset_files.length}\n`);
|
|
173
173
|
// Step 5: Send scan to backend (matching extension's Step 9)
|
|
174
174
|
console.log('[Init] Step 5/5: Sending scan to backend...');
|
|
175
|
-
// Read scan targets from .
|
|
175
|
+
// Read scan targets from .lgraph/scan_target.json if it exists
|
|
176
176
|
let scanTargets = null;
|
|
177
|
-
const scanTargetPath = path.join(projectRoot, '.
|
|
177
|
+
const scanTargetPath = path.join(projectRoot, '.lgraph', 'scan_target.json');
|
|
178
178
|
if (existsSync(scanTargetPath)) {
|
|
179
179
|
try {
|
|
180
180
|
const raw = readFileSync(scanTargetPath, 'utf-8');
|
|
@@ -188,24 +188,24 @@ export async function initCommand(options = {}) {
|
|
|
188
188
|
// Treat default template (single entry with language=null, path="") as unconfigured
|
|
189
189
|
const isDefault = mapped.length === 1 && mapped[0].language === null && mapped[0].path === '';
|
|
190
190
|
if (isDefault) {
|
|
191
|
-
console.log('[Init] .
|
|
191
|
+
console.log('[Init] .lgraph/scan_target.json is default template — server will auto-detect scan targets');
|
|
192
192
|
console.log('[Init] Edit the file to manually specify scan targets');
|
|
193
193
|
}
|
|
194
194
|
else {
|
|
195
195
|
scanTargets = mapped;
|
|
196
|
-
console.log(`[Init] ✓ Loaded ${scanTargets.length} scan target(s) from .
|
|
196
|
+
console.log(`[Init] ✓ Loaded ${scanTargets.length} scan target(s) from .lgraph/scan_target.json`);
|
|
197
197
|
for (const t of scanTargets) {
|
|
198
198
|
console.log(`[Init] → language=${t.language ?? 'auto'}, path="${t.path || '(root)'}"`);
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
202
|
catch (err) {
|
|
203
|
-
console.log(`[Init] ⚠️ Failed to read .
|
|
203
|
+
console.log(`[Init] ⚠️ Failed to read .lgraph/scan_target.json: ${err.message}`);
|
|
204
204
|
console.log('[Init] Proceeding without scan targets (server will auto-detect)');
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
else {
|
|
208
|
-
console.log('[Init] No .
|
|
208
|
+
console.log('[Init] No .lgraph/scan_target.json found — server will auto-detect scan targets');
|
|
209
209
|
}
|
|
210
210
|
// Read source file contents so orchestrator can run pipeline inline (e.g. LOCAL mode) and index via init-scan
|
|
211
211
|
const MAX_FILES_TO_SEND = 800;
|
|
@@ -6,7 +6,7 @@ export async function startCommand(options = {}) {
|
|
|
6
6
|
const isAuthInteractive = !options.guest && !options.apiKey;
|
|
7
7
|
const isProjectInteractive = !!(process.stdin.isTTY);
|
|
8
8
|
console.log('╔════════════════════════════════════════════╗');
|
|
9
|
-
console.log('║
|
|
9
|
+
console.log('║ Starting Latentgraph ║');
|
|
10
10
|
console.log('╚════════════════════════════════════════════╝\n');
|
|
11
11
|
// Step 1: Resolve API key
|
|
12
12
|
console.log('[Start] Step 1/4: Checking API key...');
|
|
@@ -37,7 +37,7 @@ export async function startCommand(options = {}) {
|
|
|
37
37
|
console.error('❌ Failed to configure project');
|
|
38
38
|
process.exit(1);
|
|
39
39
|
}
|
|
40
|
-
// Ensure .
|
|
40
|
+
// Ensure .lgraph/scan_target.json exists (template for user customization)
|
|
41
41
|
ensureScanTargetFile(projectRoot);
|
|
42
42
|
// Step 3: Check if daemon is already running
|
|
43
43
|
console.log('[Start] Step 3/4: Checking daemon status...');
|
|
@@ -57,7 +57,7 @@ export async function startCommand(options = {}) {
|
|
|
57
57
|
}
|
|
58
58
|
console.log(`[Start] ✓ Daemon started (PID: ${result.pid})`);
|
|
59
59
|
console.log('\n╔════════════════════════════════════════════╗');
|
|
60
|
-
console.log('║
|
|
60
|
+
console.log('║ Latentgraph is now running! ║');
|
|
61
61
|
console.log('╚════════════════════════════════════════════╝');
|
|
62
62
|
console.log('\nUse "lgraph status" to check connection status.');
|
|
63
63
|
console.log('Use "lgraph init" to scan and index the project.');
|
|
@@ -4,7 +4,7 @@ import { fetchProjectStatus } from '../../utils/api-client.js';
|
|
|
4
4
|
export async function statusCommand() {
|
|
5
5
|
const projectRoot = process.cwd();
|
|
6
6
|
console.log('\n╔════════════════════════════════════════════╗');
|
|
7
|
-
console.log('║
|
|
7
|
+
console.log('║ Latentgraph Status ║');
|
|
8
8
|
console.log('╚════════════════════════════════════════════╝\n');
|
|
9
9
|
// Check API key
|
|
10
10
|
const apiKey = getApiKey();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getDaemonStatus, stopDaemon } from '../../daemon/daemon-manager.js';
|
|
2
2
|
export async function stopCommand() {
|
|
3
3
|
const projectRoot = process.cwd();
|
|
4
|
-
console.log('\nStopping
|
|
4
|
+
console.log('\nStopping Latentgraph daemon...\n');
|
|
5
5
|
// Check if daemon is running
|
|
6
6
|
const status = getDaemonStatus(projectRoot);
|
|
7
7
|
if (!status.running) {
|
package/build/daemon/daemon.js
CHANGED
|
@@ -54,7 +54,7 @@ process.on('unhandledRejection', (reason) => {
|
|
|
54
54
|
});
|
|
55
55
|
async function main() {
|
|
56
56
|
console.log('╔════════════════════════════════════════════╗');
|
|
57
|
-
console.log('║
|
|
57
|
+
console.log('║ Latentgraph Daemon Starting ║');
|
|
58
58
|
console.log('╚════════════════════════════════════════════╝');
|
|
59
59
|
console.log(`[Daemon] Project ID: ${projectId}`);
|
|
60
60
|
console.log(`[Daemon] Project root: ${projectRoot}`);
|
package/build/index.js
CHANGED
|
@@ -6,8 +6,8 @@ const { version } = require('../package.json');
|
|
|
6
6
|
const program = new Command();
|
|
7
7
|
program
|
|
8
8
|
.name('lgraph')
|
|
9
|
-
.description('
|
|
10
|
-
'
|
|
9
|
+
.description('Latentgraph CLI - AI-powered code intelligence and dependency analysis.\n\n' +
|
|
10
|
+
'Latentgraph indexes your codebase, builds a dependency relationship graph (DRG),\n' +
|
|
11
11
|
'and provides AI-powered insights via MCP tools.\n\n' +
|
|
12
12
|
'Quick start:\n' +
|
|
13
13
|
' lgraph start Start the daemon and configure project\n' +
|
|
@@ -15,8 +15,8 @@ program
|
|
|
15
15
|
' lgraph update-drg Build/update the dependency graph\n' +
|
|
16
16
|
' lgraph status Check current status\n\n' +
|
|
17
17
|
'Configuration:\n' +
|
|
18
|
-
' Global config: ~/.
|
|
19
|
-
' Project config: .
|
|
18
|
+
' Global config: ~/.lgraph/config.json\n' +
|
|
19
|
+
' Project config: .lgraph/config.json')
|
|
20
20
|
.version(version);
|
|
21
21
|
// MCP server mode (default when run via MCP host)
|
|
22
22
|
program
|
|
@@ -34,9 +34,9 @@ program
|
|
|
34
34
|
// --- start ---
|
|
35
35
|
const startCmd = program
|
|
36
36
|
.command('start')
|
|
37
|
-
.description('Start the
|
|
37
|
+
.description('Start the Latentgraph daemon for this project')
|
|
38
38
|
.option('--guest', 'Use guest authentication (auto-creates a temporary project)')
|
|
39
|
-
.option('--api-key <key>', 'Provide your
|
|
39
|
+
.option('--api-key <key>', 'Provide your Latentgraph API key directly instead of interactive prompt')
|
|
40
40
|
.option('--project-name <name>', 'Create a new project or match an existing one by name')
|
|
41
41
|
.option('--project-id <id>', 'Link to an existing project by its UUID')
|
|
42
42
|
.option('--template <id>', 'Use a specific migration template when creating the project')
|
|
@@ -54,7 +54,7 @@ startCmd.addHelpText('after', `
|
|
|
54
54
|
Details:
|
|
55
55
|
Resolves authentication, configures the project, and launches a
|
|
56
56
|
background daemon that maintains a WebSocket connection to the
|
|
57
|
-
|
|
57
|
+
Latentgraph backend.
|
|
58
58
|
|
|
59
59
|
Examples:
|
|
60
60
|
lgraph start Interactive setup
|
|
@@ -67,7 +67,7 @@ const initCmd = program
|
|
|
67
67
|
.description('Initialize and scan the project for file indexing')
|
|
68
68
|
.option('-f, --force', 'Force re-indexing even if the project is already indexed')
|
|
69
69
|
.option('--guest', 'Use guest authentication (auto-creates a temporary project)')
|
|
70
|
-
.option('--api-key <key>', 'Provide your
|
|
70
|
+
.option('--api-key <key>', 'Provide your Latentgraph API key directly instead of interactive prompt')
|
|
71
71
|
.option('--project-name <name>', 'Create a new project or match an existing one by name')
|
|
72
72
|
.option('--project-id <id>', 'Link to an existing project by its UUID')
|
|
73
73
|
.option('--template <id>', 'Use a specific migration template when creating the project')
|
|
@@ -86,7 +86,7 @@ initCmd.addHelpText('after', `
|
|
|
86
86
|
Details:
|
|
87
87
|
Performs a full project scan — collects the file tree, categorizes files
|
|
88
88
|
(source, config, assets), gathers git info, and sends everything to the
|
|
89
|
-
|
|
89
|
+
Latentgraph backend for indexing. Automatically starts the daemon if not running.
|
|
90
90
|
|
|
91
91
|
If the project is already indexed, you will be prompted to re-index.
|
|
92
92
|
Use --force to skip the prompt.
|
|
@@ -99,7 +99,7 @@ Examples:
|
|
|
99
99
|
// --- stop ---
|
|
100
100
|
const stopCmd = program
|
|
101
101
|
.command('stop')
|
|
102
|
-
.description('Stop the
|
|
102
|
+
.description('Stop the Latentgraph daemon for this project')
|
|
103
103
|
.action(async () => {
|
|
104
104
|
const { stopCommand } = await import('./cli/commands/stop.js');
|
|
105
105
|
await stopCommand();
|
|
@@ -112,7 +112,7 @@ Details:
|
|
|
112
112
|
// --- status ---
|
|
113
113
|
const statusCmd = program
|
|
114
114
|
.command('status')
|
|
115
|
-
.description('Show the current
|
|
115
|
+
.description('Show the current Latentgraph status')
|
|
116
116
|
.action(async () => {
|
|
117
117
|
const { statusCommand } = await import('./cli/commands/status.js');
|
|
118
118
|
await statusCommand();
|
|
@@ -152,7 +152,7 @@ Examples:
|
|
|
152
152
|
// --- add mcp servers---
|
|
153
153
|
const addCmd = program
|
|
154
154
|
.command('add <tool>')
|
|
155
|
-
.description('Add
|
|
155
|
+
.description('Add Latentgraph MCP server to an AI coding tool')
|
|
156
156
|
.action(async (tool) => {
|
|
157
157
|
const { addCommand } = await import('./cli/commands/add.js');
|
|
158
158
|
await addCommand(tool);
|
|
@@ -175,7 +175,7 @@ Examples:
|
|
|
175
175
|
// --- config ---
|
|
176
176
|
const configCmd = program
|
|
177
177
|
.command('config [action] [key] [value]')
|
|
178
|
-
.description('Manage
|
|
178
|
+
.description('Manage Latentgraph configuration (URLs, API key)')
|
|
179
179
|
.action(async (action, key, value) => {
|
|
180
180
|
const { configCommand } = await import('./cli/commands/config.js');
|
|
181
181
|
await configCommand(action, key, value);
|
|
@@ -187,13 +187,13 @@ Actions:
|
|
|
187
187
|
clear [key] Clear a specific key or all configuration
|
|
188
188
|
|
|
189
189
|
Configurable keys:
|
|
190
|
-
api-key Your
|
|
190
|
+
api-key Your Latentgraph API key
|
|
191
191
|
api-url Backend API URL
|
|
192
192
|
orch-url Orchestrator URL
|
|
193
193
|
ws-url WebSocket URL
|
|
194
194
|
|
|
195
195
|
URLs can also be set via environment variables:
|
|
196
|
-
|
|
196
|
+
LGRAPH_API_URL, LGRAPH_ORCH_URL, LGRAPH_WS_URL
|
|
197
197
|
|
|
198
198
|
Examples:
|
|
199
199
|
lgraph config Show config
|
package/build/mcp-server.js
CHANGED
|
@@ -5,15 +5,15 @@ import { createRequire } from 'module';
|
|
|
5
5
|
import { getApiKey } from './utils/config.js';
|
|
6
6
|
const require = createRequire(import.meta.url);
|
|
7
7
|
const { version } = require('../package.json');
|
|
8
|
-
const BASE_URL = process.env.
|
|
8
|
+
const BASE_URL = process.env.LGRAPH_BACKEND_URL || "https://dev-shift-lite.latentforce.ai";
|
|
9
9
|
function getApiKeyFromEnv() {
|
|
10
|
-
return process.env.
|
|
10
|
+
return process.env.LGRAPH_API_KEY?.trim() || getApiKey();
|
|
11
11
|
}
|
|
12
12
|
function getProjectIdFromEnv() {
|
|
13
|
-
const projectId = process.env.
|
|
13
|
+
const projectId = process.env.LGRAPH_PROJECT_ID;
|
|
14
14
|
if (!projectId || projectId.trim() === "") {
|
|
15
|
-
throw new Error("
|
|
16
|
-
"Set it to your
|
|
15
|
+
throw new Error("LGRAPH_PROJECT_ID environment variable is not set. " +
|
|
16
|
+
"Set it to your Latentgraph project UUID, or pass project_id in each tool call.");
|
|
17
17
|
}
|
|
18
18
|
return projectId.trim();
|
|
19
19
|
}
|
|
@@ -395,7 +395,7 @@ function formatProjectOverview(data) {
|
|
|
395
395
|
// ============= MCP SERVER =============
|
|
396
396
|
export async function startMcpServer() {
|
|
397
397
|
const server = new McpServer({
|
|
398
|
-
name: "
|
|
398
|
+
name: "lgraph",
|
|
399
399
|
version,
|
|
400
400
|
});
|
|
401
401
|
// ---- blast_radius ----
|
|
@@ -403,7 +403,7 @@ export async function startMcpServer() {
|
|
|
403
403
|
description: "Use this BEFORE editing or refactoring any source file. Returns every file in the project that imports or depends on the given file, grouped by dependency depth (level 1 = direct importers, level 2 = their importers, etc.). Also returns the affected modules/folders. Use this to understand the full impact of a change before making it. Only works on indexed source files: .js .jsx .ts .tsx .py .java .cpp .cs .go .c .h .css .scss .html — do not call for .json, .yaml, .md or other non-source files.",
|
|
404
404
|
inputSchema: z.object({
|
|
405
405
|
file_path: z.string().describe("Path to the file (relative to project root)"),
|
|
406
|
-
project_id: z.string().optional().describe("
|
|
406
|
+
project_id: z.string().optional().describe("Latentgraph project UUID; overrides LGRAPH_PROJECT_ID if provided"),
|
|
407
407
|
level: z.number().optional().describe("Max depth of blast radius (optional)"),
|
|
408
408
|
})
|
|
409
409
|
}, async (args) => {
|
|
@@ -420,7 +420,7 @@ export async function startMcpServer() {
|
|
|
420
420
|
description: "Returns bidirectional dependencies of a source file: what it imports (with usage pattern, data flow, and summary per dependency) AND what imports it (reverse dependencies). Use this to trace data flow, understand module relationships, follow a bug through the call chain, or map out what a file relies on before modifying it. Only works on indexed source files: .js .jsx .ts .tsx .py .java .cpp .cs .go .c .h .css .scss .html — do not call for .json, .yaml, .md or other non-source files.",
|
|
421
421
|
inputSchema: z.object({
|
|
422
422
|
file_path: z.string().describe("Path to the file"),
|
|
423
|
-
project_id: z.string().optional().describe("
|
|
423
|
+
project_id: z.string().optional().describe("Latentgraph project UUID; overrides LGRAPH_PROJECT_ID if provided"),
|
|
424
424
|
})
|
|
425
425
|
}, async (args) => {
|
|
426
426
|
const projectId = resolveProjectId(args);
|
|
@@ -435,7 +435,7 @@ export async function startMcpServer() {
|
|
|
435
435
|
description: "Returns an AI-generated summary of a source file: what it does, its key exports and functions, internal state, error handling, constraints, what it imports, who imports it, and which module it belongs to. Always call this before reading a raw file — it gives you the essential context without having to parse the full source. Use the 'level' parameter to include summaries of parent directories for broader context. Only works on indexed source files: .js .jsx .ts .tsx .py .java .cpp .cs .go .c .h .css .scss .html — for .json, .yaml, .md, or other non-source files, read them directly instead.",
|
|
436
436
|
inputSchema: z.object({
|
|
437
437
|
file_path: z.string().describe("Path to the file to summarize"),
|
|
438
|
-
project_id: z.string().optional().describe("
|
|
438
|
+
project_id: z.string().optional().describe("Latentgraph project UUID; overrides LGRAPH_PROJECT_ID if provided"),
|
|
439
439
|
level: z.number().optional().describe("Number of parent directory levels to include (default 0)"),
|
|
440
440
|
})
|
|
441
441
|
}, async (args) => {
|
|
@@ -452,7 +452,7 @@ export async function startMcpServer() {
|
|
|
452
452
|
description: "Given any file path, returns the full documentation for the module that owns it, the list of files in that module, the corresponding DRG cluster summary and metadata, and parent/child module relationships. Use this after file_summary to get broader module-level context before editing — especially useful when a change may affect an entire module rather than just one file.",
|
|
453
453
|
inputSchema: z.object({
|
|
454
454
|
file_path: z.string().describe("Path to any file in the module (relative to project root)"),
|
|
455
|
-
project_id: z.string().optional().describe("
|
|
455
|
+
project_id: z.string().optional().describe("Latentgraph project UUID; overrides LGRAPH_PROJECT_ID if provided"),
|
|
456
456
|
})
|
|
457
457
|
}, async (args) => {
|
|
458
458
|
const projectId = resolveProjectId(args);
|
|
@@ -466,7 +466,7 @@ export async function startMcpServer() {
|
|
|
466
466
|
server.registerTool("project_overview", {
|
|
467
467
|
description: "Returns the highest-level context for the project: architecture summary, layers, tech stack, entry points, constraints, top-level modules with their files, and the project documentation overview. Call this first at the start of a session — before exploring any specific file — to understand the overall structure and where things live.",
|
|
468
468
|
inputSchema: z.object({
|
|
469
|
-
project_id: z.string().optional().describe("
|
|
469
|
+
project_id: z.string().optional().describe("Latentgraph project UUID; overrides LGRAPH_PROJECT_ID if provided"),
|
|
470
470
|
})
|
|
471
471
|
}, async (args) => {
|
|
472
472
|
const projectId = resolveProjectId(args);
|
|
@@ -477,8 +477,8 @@ export async function startMcpServer() {
|
|
|
477
477
|
});
|
|
478
478
|
const transport = new StdioServerTransport();
|
|
479
479
|
await server.connect(transport);
|
|
480
|
-
console.error("
|
|
481
|
-
if (!process.env.
|
|
482
|
-
console.error("Warning:
|
|
480
|
+
console.error("Latentgraph MCP Server running on stdio");
|
|
481
|
+
if (!process.env.LGRAPH_PROJECT_ID) {
|
|
482
|
+
console.error("Warning: LGRAPH_PROJECT_ID is not set. Pass project_id in each tool call, or set the env var.");
|
|
483
483
|
}
|
|
484
484
|
}
|
|
@@ -7,7 +7,7 @@ import { promptApiKey, promptKeyChoice, promptSelectProject, promptProjectName,
|
|
|
7
7
|
* Guest flow returns both apiKey and projectId (from guest-key endpoint).
|
|
8
8
|
*/
|
|
9
9
|
export async function resolveApiKey(opts = {}) {
|
|
10
|
-
const label = opts.commandLabel || '
|
|
10
|
+
const label = opts.commandLabel || 'lgraph';
|
|
11
11
|
const interactive = opts.interactive ?? true;
|
|
12
12
|
// If both --guest and --api-key, api-key wins
|
|
13
13
|
if (opts.guest && opts.apiKey) {
|
|
@@ -74,7 +74,7 @@ async function doGuestFlow(label) {
|
|
|
74
74
|
* Can create new projects when --project-name is given.
|
|
75
75
|
*/
|
|
76
76
|
export async function resolveProject(opts) {
|
|
77
|
-
const label = opts.commandLabel || '
|
|
77
|
+
const label = opts.commandLabel || 'lgraph';
|
|
78
78
|
const interactive = opts.interactive ?? true;
|
|
79
79
|
const projectRoot = opts.projectRoot || process.cwd();
|
|
80
80
|
// 1. Check existing local config
|
package/build/utils/config.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as os from 'os';
|
|
4
|
-
// Global config location: ~/.
|
|
5
|
-
const
|
|
6
|
-
const GLOBAL_CONFIG_FILE = path.join(
|
|
7
|
-
// Local config location: .
|
|
8
|
-
const
|
|
4
|
+
// Global config location: ~/.lgraph/config.json
|
|
5
|
+
const GLOBAL_LGRAPH_DIR = path.join(os.homedir(), '.lgraph');
|
|
6
|
+
const GLOBAL_CONFIG_FILE = path.join(GLOBAL_LGRAPH_DIR, 'config.json');
|
|
7
|
+
// Local config location: .lgraph/ in current directory
|
|
8
|
+
const LOCAL_LGRAPH_DIR = '.lgraph';
|
|
9
9
|
const LOCAL_CONFIG_FILE = 'config.json'; // Changed from project.json to match extension
|
|
10
10
|
const DAEMON_PID_FILE = 'daemon.pid';
|
|
11
11
|
const DAEMON_STATUS_FILE = 'daemon.status.json';
|
|
@@ -39,13 +39,13 @@ function readGlobalConfigInternal() {
|
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
41
|
// API URLs - Priority: env var > global config > default
|
|
42
|
-
export const API_BASE_URL = getConfiguredUrl('
|
|
43
|
-
export const API_BASE_URL_ORCH = getConfiguredUrl('
|
|
44
|
-
export const WS_URL = getConfiguredUrl('
|
|
42
|
+
export const API_BASE_URL = getConfiguredUrl('LGRAPH_API_URL', 'api_url', DEFAULT_API_URL);
|
|
43
|
+
export const API_BASE_URL_ORCH = getConfiguredUrl('LGRAPH_ORCH_URL', 'orch_url', DEFAULT_ORCH_URL);
|
|
44
|
+
export const WS_URL = getConfiguredUrl('LGRAPH_WS_URL', 'ws_url', DEFAULT_WS_URL);
|
|
45
45
|
// --- Global Config Functions ---
|
|
46
|
-
export function
|
|
47
|
-
if (!fs.existsSync(
|
|
48
|
-
fs.mkdirSync(
|
|
46
|
+
export function ensureGlobalLgraphDir() {
|
|
47
|
+
if (!fs.existsSync(GLOBAL_LGRAPH_DIR)) {
|
|
48
|
+
fs.mkdirSync(GLOBAL_LGRAPH_DIR, { recursive: true });
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
export function readGlobalConfig() {
|
|
@@ -61,7 +61,7 @@ export function readGlobalConfig() {
|
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
63
|
export function writeGlobalConfig(config) {
|
|
64
|
-
|
|
64
|
+
ensureGlobalLgraphDir();
|
|
65
65
|
fs.writeFileSync(GLOBAL_CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
66
66
|
}
|
|
67
67
|
export function getApiKey() {
|
|
@@ -122,12 +122,12 @@ export function getConfiguredUrls() {
|
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
124
|
// --- Local Config Functions ---
|
|
125
|
-
export function
|
|
125
|
+
export function getLocalLgraphDir(projectRoot) {
|
|
126
126
|
const root = projectRoot || process.cwd();
|
|
127
|
-
return path.join(root,
|
|
127
|
+
return path.join(root, LOCAL_LGRAPH_DIR);
|
|
128
128
|
}
|
|
129
|
-
export function
|
|
130
|
-
const dir =
|
|
129
|
+
export function ensureLocalLgraphDir(projectRoot) {
|
|
130
|
+
const dir = getLocalLgraphDir(projectRoot);
|
|
131
131
|
if (!fs.existsSync(dir)) {
|
|
132
132
|
fs.mkdirSync(dir, { recursive: true });
|
|
133
133
|
}
|
|
@@ -139,18 +139,18 @@ const SCAN_TARGET_TEMPLATE = [
|
|
|
139
139
|
},
|
|
140
140
|
];
|
|
141
141
|
/**
|
|
142
|
-
* Create .
|
|
142
|
+
* Create .lgraph/scan_target.json with a template if it doesn't exist.
|
|
143
143
|
* Users can edit this file to manually specify scan targets.
|
|
144
144
|
*/
|
|
145
145
|
export function ensureScanTargetFile(projectRoot) {
|
|
146
|
-
const filePath = path.join(
|
|
146
|
+
const filePath = path.join(getLocalLgraphDir(projectRoot), 'scan_target.json');
|
|
147
147
|
if (!fs.existsSync(filePath)) {
|
|
148
148
|
fs.writeFileSync(filePath, JSON.stringify(SCAN_TARGET_TEMPLATE, null, 2));
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
export function readProjectConfig(projectRoot) {
|
|
152
152
|
try {
|
|
153
|
-
const filePath = path.join(
|
|
153
|
+
const filePath = path.join(getLocalLgraphDir(projectRoot), LOCAL_CONFIG_FILE);
|
|
154
154
|
if (fs.existsSync(filePath)) {
|
|
155
155
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
156
156
|
return JSON.parse(content);
|
|
@@ -162,11 +162,11 @@ export function readProjectConfig(projectRoot) {
|
|
|
162
162
|
return null;
|
|
163
163
|
}
|
|
164
164
|
export function writeProjectConfig(config, projectRoot) {
|
|
165
|
-
|
|
166
|
-
const filePath = path.join(
|
|
165
|
+
ensureLocalLgraphDir(projectRoot);
|
|
166
|
+
const filePath = path.join(getLocalLgraphDir(projectRoot), LOCAL_CONFIG_FILE);
|
|
167
167
|
fs.writeFileSync(filePath, JSON.stringify(config, null, 2));
|
|
168
|
-
// Also create .gitignore in .
|
|
169
|
-
const gitignorePath = path.join(
|
|
168
|
+
// Also create .gitignore in .lgraph folder (matching extension)
|
|
169
|
+
const gitignorePath = path.join(getLocalLgraphDir(projectRoot), '.gitignore');
|
|
170
170
|
if (!fs.existsSync(gitignorePath)) {
|
|
171
171
|
fs.writeFileSync(gitignorePath, '*\n!.gitignore\n!config.json\n!scan_target.json\n');
|
|
172
172
|
}
|
|
@@ -193,10 +193,10 @@ export function setProject(projectId, projectName, projectRoot) {
|
|
|
193
193
|
}
|
|
194
194
|
// --- Daemon Status Functions ---
|
|
195
195
|
export function getDaemonPidPath(projectRoot) {
|
|
196
|
-
return path.join(
|
|
196
|
+
return path.join(getLocalLgraphDir(projectRoot), DAEMON_PID_FILE);
|
|
197
197
|
}
|
|
198
198
|
export function getDaemonStatusPath(projectRoot) {
|
|
199
|
-
return path.join(
|
|
199
|
+
return path.join(getLocalLgraphDir(projectRoot), DAEMON_STATUS_FILE);
|
|
200
200
|
}
|
|
201
201
|
export function readDaemonPid(projectRoot) {
|
|
202
202
|
try {
|
|
@@ -213,7 +213,7 @@ export function readDaemonPid(projectRoot) {
|
|
|
213
213
|
return null;
|
|
214
214
|
}
|
|
215
215
|
export function writeDaemonPid(pid, projectRoot) {
|
|
216
|
-
|
|
216
|
+
ensureLocalLgraphDir(projectRoot);
|
|
217
217
|
const filePath = getDaemonPidPath(projectRoot);
|
|
218
218
|
fs.writeFileSync(filePath, String(pid));
|
|
219
219
|
}
|
|
@@ -237,7 +237,7 @@ export function readDaemonStatus(projectRoot) {
|
|
|
237
237
|
return null;
|
|
238
238
|
}
|
|
239
239
|
export function writeDaemonStatus(status, projectRoot) {
|
|
240
|
-
|
|
240
|
+
ensureLocalLgraphDir(projectRoot);
|
|
241
241
|
const filePath = getDaemonStatusPath(projectRoot);
|
|
242
242
|
fs.writeFileSync(filePath, JSON.stringify(status, null, 2));
|
|
243
243
|
}
|
package/build/utils/prompts.js
CHANGED
|
@@ -15,8 +15,8 @@ export function prompt(question) {
|
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
export async function promptApiKey() {
|
|
18
|
-
console.log('\nNo
|
|
19
|
-
const apiKey = await prompt('Please paste your
|
|
18
|
+
console.log('\nNo Latentgraph API key found.');
|
|
19
|
+
const apiKey = await prompt('Please paste your Latentgraph API key: ');
|
|
20
20
|
if (!apiKey) {
|
|
21
21
|
console.error('Error: API key is required.');
|
|
22
22
|
process.exit(1);
|
|
@@ -25,7 +25,7 @@ export async function promptApiKey() {
|
|
|
25
25
|
}
|
|
26
26
|
export async function promptProjectId() {
|
|
27
27
|
console.log('\nNo project configured for this directory.');
|
|
28
|
-
const projectId = await prompt('Please enter your
|
|
28
|
+
const projectId = await prompt('Please enter your Latentgraph project ID: ');
|
|
29
29
|
if (!projectId) {
|
|
30
30
|
console.error('Error: Project ID is required.');
|
|
31
31
|
process.exit(1);
|
|
@@ -49,7 +49,7 @@ export async function promptSelectProject(projects) {
|
|
|
49
49
|
return projects[selection - 1];
|
|
50
50
|
}
|
|
51
51
|
export async function promptKeyChoice() {
|
|
52
|
-
console.log('\nNo
|
|
52
|
+
console.log('\nNo Latentgraph API key found.');
|
|
53
53
|
console.log('');
|
|
54
54
|
console.log(' 1. I have an API key');
|
|
55
55
|
console.log(' 2. Continue as guest');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@latentforce/latentgraph",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Latentgraph CLI - AI-powered code intelligence with MCP support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./build/index.js",
|
|
7
7
|
"exports": {
|