@latentforce/latentgraph 1.0.0
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 +320 -0
- package/build/cli/commands/add.js +350 -0
- package/build/cli/commands/config.js +142 -0
- package/build/cli/commands/init.js +285 -0
- package/build/cli/commands/start.js +65 -0
- package/build/cli/commands/status.js +76 -0
- package/build/cli/commands/stop.js +18 -0
- package/build/cli/commands/update-drg.js +194 -0
- package/build/daemon/daemon-manager.js +136 -0
- package/build/daemon/daemon.js +119 -0
- package/build/daemon/tools-executor.js +462 -0
- package/build/daemon/websocket-client.js +334 -0
- package/build/index.js +205 -0
- package/build/mcp-server.js +484 -0
- package/build/utils/api-client.js +147 -0
- package/build/utils/auth-resolver.js +184 -0
- package/build/utils/config.js +260 -0
- package/build/utils/machine-id.js +46 -0
- package/build/utils/prompts.js +114 -0
- package/build/utils/shiftignore.js +108 -0
- package/build/utils/tree-scanner.js +165 -0
- package/package.json +49 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import ignore from 'ignore';
|
|
4
|
+
const SHIFTIGNORE_FILE = '.shiftignore';
|
|
5
|
+
/**
|
|
6
|
+
* Load and parse a .shiftignore file from the project root.
|
|
7
|
+
* Returns an `ignore` instance that can test paths, or null if no .shiftignore exists.
|
|
8
|
+
*
|
|
9
|
+
* .shiftignore uses the same syntax as .gitignore:
|
|
10
|
+
* - Blank lines are ignored
|
|
11
|
+
* - Lines starting with # are comments
|
|
12
|
+
* - Standard glob patterns (*, **, ?)
|
|
13
|
+
* - Trailing / matches directories only
|
|
14
|
+
* - Leading / anchors to root
|
|
15
|
+
* - ! negates a pattern
|
|
16
|
+
*/
|
|
17
|
+
const DEFAULT_SHIFTIGNORE = `# .shiftignore — files and directories to exclude from Shift indexing
|
|
18
|
+
# Uses the same syntax as .gitignore
|
|
19
|
+
|
|
20
|
+
# Dependencies
|
|
21
|
+
node_modules/
|
|
22
|
+
vendor/
|
|
23
|
+
bower_components/
|
|
24
|
+
|
|
25
|
+
# Build output
|
|
26
|
+
dist/
|
|
27
|
+
build/
|
|
28
|
+
out/
|
|
29
|
+
.next/
|
|
30
|
+
|
|
31
|
+
# Test & coverage
|
|
32
|
+
coverage/
|
|
33
|
+
.nyc_output/
|
|
34
|
+
|
|
35
|
+
# Environment & secrets
|
|
36
|
+
.env
|
|
37
|
+
.env.*
|
|
38
|
+
*.pem
|
|
39
|
+
*.key
|
|
40
|
+
|
|
41
|
+
# Logs
|
|
42
|
+
*.log
|
|
43
|
+
logs/
|
|
44
|
+
|
|
45
|
+
# OS files
|
|
46
|
+
.DS_Store
|
|
47
|
+
Thumbs.db
|
|
48
|
+
|
|
49
|
+
# IDE
|
|
50
|
+
.vscode/
|
|
51
|
+
.idea/
|
|
52
|
+
*.swp
|
|
53
|
+
*.swo
|
|
54
|
+
|
|
55
|
+
# Python
|
|
56
|
+
__pycache__/
|
|
57
|
+
*.pyc
|
|
58
|
+
venv/
|
|
59
|
+
.venv/
|
|
60
|
+
|
|
61
|
+
# Misc
|
|
62
|
+
*.min.js
|
|
63
|
+
*.min.css
|
|
64
|
+
*.map
|
|
65
|
+
`;
|
|
66
|
+
/**
|
|
67
|
+
* Create a default .shiftignore file in the project root if one doesn't exist.
|
|
68
|
+
* Returns true if a new file was created, false if it already exists.
|
|
69
|
+
*/
|
|
70
|
+
export function scaffoldShiftIgnore(projectRoot) {
|
|
71
|
+
const ignorePath = path.join(projectRoot, SHIFTIGNORE_FILE);
|
|
72
|
+
if (fs.existsSync(ignorePath)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
fs.writeFileSync(ignorePath, DEFAULT_SHIFTIGNORE, 'utf-8');
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
console.warn(`[ShiftIgnore] Warning: Could not create ${SHIFTIGNORE_FILE}: ${err.message}`);
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function loadShiftIgnore(projectRoot) {
|
|
85
|
+
const ignorePath = path.join(projectRoot, SHIFTIGNORE_FILE);
|
|
86
|
+
if (!fs.existsSync(ignorePath)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const content = fs.readFileSync(ignorePath, 'utf-8');
|
|
91
|
+
const ig = ignore();
|
|
92
|
+
ig.add(content);
|
|
93
|
+
return ig;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
console.warn(`[ShiftIgnore] Warning: Could not read ${SHIFTIGNORE_FILE}: ${err.message}`);
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Filter an array of file paths through the shiftIgnore rules.
|
|
102
|
+
* Returns only paths that are NOT ignored.
|
|
103
|
+
*/
|
|
104
|
+
export function filterPaths(ig, paths) {
|
|
105
|
+
if (!ig)
|
|
106
|
+
return paths;
|
|
107
|
+
return paths.filter(p => !ig.ignores(p.replace(/\\/g, '/')));
|
|
108
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
// Matching extension's file-tools.js exclude patterns
|
|
4
|
+
const DEFAULT_EXCLUDE_PATTERNS = [
|
|
5
|
+
'.git',
|
|
6
|
+
'node_modules',
|
|
7
|
+
'__pycache__',
|
|
8
|
+
'.vscode',
|
|
9
|
+
'dist',
|
|
10
|
+
'build',
|
|
11
|
+
'.shift',
|
|
12
|
+
'.next',
|
|
13
|
+
'.cache',
|
|
14
|
+
'coverage',
|
|
15
|
+
'.pytest_cache',
|
|
16
|
+
'venv',
|
|
17
|
+
'env',
|
|
18
|
+
'.env',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Get project tree - matching extension's getProjectTree function
|
|
22
|
+
*/
|
|
23
|
+
export function getProjectTree(workspaceRoot, options = {}) {
|
|
24
|
+
const { depth = 0, exclude_patterns = DEFAULT_EXCLUDE_PATTERNS, } = options;
|
|
25
|
+
let file_count = 0;
|
|
26
|
+
let dir_count = 0;
|
|
27
|
+
let total_size = 0;
|
|
28
|
+
const max_depth = depth === 0 ? Infinity : depth;
|
|
29
|
+
function scanDirectory(dirPath, currentDepth, relativePath) {
|
|
30
|
+
if (currentDepth >= max_depth) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const items = [];
|
|
34
|
+
try {
|
|
35
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
36
|
+
for (const entry of entries) {
|
|
37
|
+
// Check if should be excluded by built-in patterns
|
|
38
|
+
if (exclude_patterns.some(pattern => entry.name.includes(pattern))) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const itemPath = path.join(dirPath, entry.name);
|
|
42
|
+
const itemRelativePath = relativePath ? path.join(relativePath, entry.name) : entry.name;
|
|
43
|
+
if (entry.isDirectory()) {
|
|
44
|
+
dir_count++;
|
|
45
|
+
const children = scanDirectory(itemPath, currentDepth + 1, itemRelativePath);
|
|
46
|
+
items.push({
|
|
47
|
+
name: entry.name,
|
|
48
|
+
type: 'directory',
|
|
49
|
+
path: itemRelativePath.replace(/\\/g, '/'), // Normalize to forward slashes
|
|
50
|
+
depth: currentDepth,
|
|
51
|
+
children: children,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
else if (entry.isFile()) {
|
|
55
|
+
file_count++;
|
|
56
|
+
try {
|
|
57
|
+
const stats = fs.statSync(itemPath);
|
|
58
|
+
total_size += stats.size;
|
|
59
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
60
|
+
items.push({
|
|
61
|
+
name: entry.name,
|
|
62
|
+
type: 'file',
|
|
63
|
+
path: itemRelativePath.replace(/\\/g, '/'), // Normalize to forward slashes
|
|
64
|
+
depth: currentDepth,
|
|
65
|
+
size: stats.size,
|
|
66
|
+
extension: ext,
|
|
67
|
+
modified: stats.mtime,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Skip files we can't read
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
console.error(`Error scanning ${dirPath}:`, err.message);
|
|
78
|
+
}
|
|
79
|
+
return items;
|
|
80
|
+
}
|
|
81
|
+
const tree = scanDirectory(workspaceRoot, 0, '');
|
|
82
|
+
const total_size_mb = (total_size / (1024 * 1024)).toFixed(2);
|
|
83
|
+
return {
|
|
84
|
+
status: 'success',
|
|
85
|
+
tree: tree,
|
|
86
|
+
file_count: file_count,
|
|
87
|
+
dir_count: dir_count,
|
|
88
|
+
total_size_bytes: total_size,
|
|
89
|
+
total_size_mb: total_size_mb,
|
|
90
|
+
scanned_from: workspaceRoot,
|
|
91
|
+
depth_limit: depth,
|
|
92
|
+
actual_max_depth: depth === 0 ? 'unlimited' : String(depth),
|
|
93
|
+
excluded_patterns: exclude_patterns,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Extract all file paths from tree structure
|
|
98
|
+
* Matching extension's extractAllFilePaths function
|
|
99
|
+
*/
|
|
100
|
+
export function extractAllFilePaths(tree, basePath = '') {
|
|
101
|
+
let files = [];
|
|
102
|
+
for (const item of tree) {
|
|
103
|
+
const itemPath = basePath ? `${basePath}/${item.name}` : item.name;
|
|
104
|
+
if (item.type === 'file') {
|
|
105
|
+
files.push(itemPath);
|
|
106
|
+
}
|
|
107
|
+
else if (item.type === 'directory' && item.children) {
|
|
108
|
+
files = files.concat(extractAllFilePaths(item.children, itemPath));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return files;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Categorize files by type
|
|
115
|
+
* Matching extension's categorizeFiles function
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* Count total lines of code across all project files
|
|
119
|
+
*/
|
|
120
|
+
export function countProjectLOC(rootPath, filePaths) {
|
|
121
|
+
let totalLOC = 0;
|
|
122
|
+
for (const filePath of filePaths) {
|
|
123
|
+
try {
|
|
124
|
+
const fullPath = path.join(rootPath, filePath);
|
|
125
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
126
|
+
totalLOC += content.split('\n').length;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Skip binary/unreadable files
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return totalLOC;
|
|
133
|
+
}
|
|
134
|
+
export function categorizeFiles(tree, basePath = '') {
|
|
135
|
+
const categories = {
|
|
136
|
+
source_files: [],
|
|
137
|
+
config_files: [],
|
|
138
|
+
asset_files: [],
|
|
139
|
+
};
|
|
140
|
+
const sourceExts = ['.js', '.jsx', '.ts', '.tsx', '.py', '.java', '.cpp', '.cs', '.go', '.c', '.h'];
|
|
141
|
+
const configExts = ['.json', '.yaml', '.yml', '.toml', '.ini', '.config', '.xml'];
|
|
142
|
+
const assetExts = ['.png', '.jpg', '.jpeg', '.svg', '.gif', '.css', '.scss', '.less', '.sass'];
|
|
143
|
+
for (const item of tree) {
|
|
144
|
+
const itemPath = basePath ? `${basePath}/${item.name}` : item.name;
|
|
145
|
+
if (item.type === 'file') {
|
|
146
|
+
const ext = path.extname(item.name).toLowerCase();
|
|
147
|
+
if (sourceExts.includes(ext)) {
|
|
148
|
+
categories.source_files.push(itemPath);
|
|
149
|
+
}
|
|
150
|
+
else if (configExts.includes(ext)) {
|
|
151
|
+
categories.config_files.push(itemPath);
|
|
152
|
+
}
|
|
153
|
+
else if (assetExts.includes(ext)) {
|
|
154
|
+
categories.asset_files.push(itemPath);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (item.type === 'directory' && item.children) {
|
|
158
|
+
const subCategories = categorizeFiles(item.children, itemPath);
|
|
159
|
+
categories.source_files.push(...subCategories.source_files);
|
|
160
|
+
categories.config_files.push(...subCategories.config_files);
|
|
161
|
+
categories.asset_files.push(...subCategories.asset_files);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return categories;
|
|
165
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@latentforce/latentgraph",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shift CLI - AI-powered code intelligence with MCP support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./build/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./build/index.js"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"lgraph": "./build/index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"build"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"prepublishOnly": "npm run build",
|
|
19
|
+
"test": "echo \"No tests yet\""
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"model-context-protocol",
|
|
24
|
+
"cli",
|
|
25
|
+
"node",
|
|
26
|
+
"typescript"
|
|
27
|
+
],
|
|
28
|
+
"author": "Latentforce",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": ""
|
|
33
|
+
},
|
|
34
|
+
"homepage": "",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
40
|
+
"commander": "^12.0.0",
|
|
41
|
+
"ws": "^8.14.0",
|
|
42
|
+
"zod": "^3.25.76"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/node": "^25.0.9",
|
|
46
|
+
"@types/ws": "^8.5.10",
|
|
47
|
+
"typescript": "^5.9.3"
|
|
48
|
+
}
|
|
49
|
+
}
|