codemaxxing 1.0.0 → 1.0.2
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 +18 -12
- package/dist/agent.d.ts +4 -0
- package/dist/agent.js +91 -16
- package/dist/commands/git.d.ts +2 -0
- package/dist/commands/git.js +50 -0
- package/dist/commands/ollama.d.ts +27 -0
- package/dist/commands/ollama.js +171 -0
- package/dist/commands/output.d.ts +2 -0
- package/dist/commands/output.js +18 -0
- package/dist/commands/registry.d.ts +2 -0
- package/dist/commands/registry.js +8 -0
- package/dist/commands/skills.d.ts +18 -0
- package/dist/commands/skills.js +121 -0
- package/dist/commands/types.d.ts +5 -0
- package/dist/commands/types.js +1 -0
- package/dist/commands/ui.d.ts +16 -0
- package/dist/commands/ui.js +79 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +13 -3
- package/dist/exec.js +4 -1
- package/dist/index.js +75 -401
- package/dist/tools/files.js +58 -3
- package/dist/utils/context.js +6 -0
- package/dist/utils/mcp.d.ts +7 -2
- package/dist/utils/mcp.js +34 -6
- package/package.json +8 -5
- package/src/agent.ts +0 -894
- package/src/auth-cli.ts +0 -287
- package/src/cli.ts +0 -37
- package/src/config.ts +0 -352
- package/src/exec.ts +0 -183
- package/src/index.tsx +0 -2647
- package/src/skills/registry.ts +0 -1436
- package/src/themes.ts +0 -335
- package/src/tools/files.ts +0 -374
- package/src/utils/auth.ts +0 -606
- package/src/utils/context.ts +0 -174
- package/src/utils/git.ts +0 -117
- package/src/utils/hardware.ts +0 -131
- package/src/utils/lint.ts +0 -116
- package/src/utils/mcp.ts +0 -307
- package/src/utils/models.ts +0 -218
- package/src/utils/ollama.ts +0 -352
- package/src/utils/repomap.ts +0 -220
- package/src/utils/sessions.ts +0 -254
- package/src/utils/skills.ts +0 -241
- package/tsconfig.json +0 -16
package/src/utils/context.ts
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
2
|
-
import { join, extname } from "path";
|
|
3
|
-
import { buildRepoMap } from "./repomap.js";
|
|
4
|
-
import { buildSkillPrompts } from "./skills.js";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Load project rules from CODEMAXXING.md, .codemaxxing/CODEMAXXING.md, or .cursorrules
|
|
8
|
-
* Returns { content, source } or null if none found
|
|
9
|
-
*/
|
|
10
|
-
export function loadProjectRules(cwd: string): { content: string; source: string } | null {
|
|
11
|
-
const candidates = [
|
|
12
|
-
{ path: join(cwd, "CODEMAXXING.md"), source: "CODEMAXXING.md" },
|
|
13
|
-
{ path: join(cwd, ".codemaxxing", "CODEMAXXING.md"), source: ".codemaxxing/CODEMAXXING.md" },
|
|
14
|
-
{ path: join(cwd, ".cursorrules"), source: ".cursorrules" },
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
for (const { path, source } of candidates) {
|
|
18
|
-
if (existsSync(path)) {
|
|
19
|
-
try {
|
|
20
|
-
const content = readFileSync(path, "utf-8").trim();
|
|
21
|
-
if (content) return { content, source };
|
|
22
|
-
} catch {
|
|
23
|
-
// skip unreadable files
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Build a project context string by scanning the working directory
|
|
32
|
-
*/
|
|
33
|
-
export async function buildProjectContext(cwd: string): Promise<string> {
|
|
34
|
-
const lines: string[] = [];
|
|
35
|
-
lines.push(`Project root: ${cwd}`);
|
|
36
|
-
|
|
37
|
-
// Check for common project files
|
|
38
|
-
const markers = [
|
|
39
|
-
"package.json",
|
|
40
|
-
"Cargo.toml",
|
|
41
|
-
"pyproject.toml",
|
|
42
|
-
"go.mod",
|
|
43
|
-
"Makefile",
|
|
44
|
-
"Dockerfile",
|
|
45
|
-
"README.md",
|
|
46
|
-
"CODEMAXXING.md",
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
const found: string[] = [];
|
|
50
|
-
for (const m of markers) {
|
|
51
|
-
if (existsSync(join(cwd, m))) found.push(m);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (found.length > 0) {
|
|
55
|
-
lines.push(`Project files: ${found.join(", ")}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Read package.json for project info
|
|
59
|
-
const pkgPath = join(cwd, "package.json");
|
|
60
|
-
if (existsSync(pkgPath)) {
|
|
61
|
-
try {
|
|
62
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
63
|
-
lines.push(`\nProject: ${pkg.name ?? "unknown"} v${pkg.version ?? "0.0.0"}`);
|
|
64
|
-
if (pkg.description) lines.push(`Description: ${pkg.description}`);
|
|
65
|
-
if (pkg.scripts) {
|
|
66
|
-
lines.push(`Available scripts: ${Object.keys(pkg.scripts).join(", ")}`);
|
|
67
|
-
}
|
|
68
|
-
} catch {
|
|
69
|
-
// ignore
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Quick file tree (top level only)
|
|
74
|
-
lines.push("\nProject structure:");
|
|
75
|
-
const IGNORE = ["node_modules", ".git", "dist", ".next", "__pycache__", ".DS_Store"];
|
|
76
|
-
try {
|
|
77
|
-
const entries = readdirSync(cwd)
|
|
78
|
-
.filter((e) => !IGNORE.includes(e))
|
|
79
|
-
.slice(0, 30);
|
|
80
|
-
|
|
81
|
-
for (const entry of entries) {
|
|
82
|
-
const fullPath = join(cwd, entry);
|
|
83
|
-
const stat = statSync(fullPath);
|
|
84
|
-
if (stat.isDirectory()) {
|
|
85
|
-
const count = readdirSync(fullPath).filter(
|
|
86
|
-
(e) => !IGNORE.includes(e)
|
|
87
|
-
).length;
|
|
88
|
-
lines.push(` 📁 ${entry}/ (${count} items)`);
|
|
89
|
-
} else {
|
|
90
|
-
lines.push(` 📄 ${entry}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
} catch {
|
|
94
|
-
lines.push(" (could not read directory)");
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Build and append repo map
|
|
98
|
-
try {
|
|
99
|
-
const repoMap = await buildRepoMap(cwd);
|
|
100
|
-
lines.push("\n" + repoMap);
|
|
101
|
-
} catch (e) {
|
|
102
|
-
// Repo map failed — continue without it
|
|
103
|
-
lines.push("\n## Repository Map\n (unable to build map)");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return lines.join("\n");
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Get the system prompt for the coding agent
|
|
111
|
-
*/
|
|
112
|
-
export async function getSystemPrompt(projectContext: string, skillPrompts: string = "", projectRules: string = ""): Promise<string> {
|
|
113
|
-
const base = `You are CODEMAXXING, an AI coding assistant running in the terminal.
|
|
114
|
-
|
|
115
|
-
You help developers understand, write, debug, and refactor code. You have access to tools that let you read files, write files, list directories, search code, and run shell commands.
|
|
116
|
-
|
|
117
|
-
## Rules
|
|
118
|
-
- Always read relevant files before making changes
|
|
119
|
-
- Explain what you're about to do before doing it
|
|
120
|
-
- When writing files, show the user what will change
|
|
121
|
-
- Be concise but thorough
|
|
122
|
-
- If you're unsure, ask — don't guess
|
|
123
|
-
- Use the run_command tool for building, testing, and linters
|
|
124
|
-
- Never delete files without explicit confirmation
|
|
125
|
-
|
|
126
|
-
## Repository Map
|
|
127
|
-
The project context below includes a map of the codebase structure. Use this map to understand what files, functions, classes, and types exist where. Use read_file to see full implementations when needed.
|
|
128
|
-
|
|
129
|
-
## Project Context
|
|
130
|
-
${projectContext}
|
|
131
|
-
|
|
132
|
-
## Behavior
|
|
133
|
-
- Respond in markdown
|
|
134
|
-
- Use code blocks with language tags
|
|
135
|
-
- Be direct and helpful
|
|
136
|
-
- If the user asks to "just do it", skip explanations and execute`;
|
|
137
|
-
|
|
138
|
-
let prompt = base;
|
|
139
|
-
|
|
140
|
-
if (projectRules) {
|
|
141
|
-
prompt += "\n\n--- Project Rules (CODEMAXXING.md) ---\n" + projectRules + "\n--- End Project Rules ---";
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (skillPrompts) {
|
|
145
|
-
prompt += "\n\n## Active Skills\n" + skillPrompts;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return prompt;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Synchronous version for backwards compatibility (without repo map)
|
|
153
|
-
* @deprecated Use async buildProjectContext instead
|
|
154
|
-
*/
|
|
155
|
-
export function buildProjectContextSync(cwd: string): string {
|
|
156
|
-
const lines: string[] = [];
|
|
157
|
-
lines.push(`Project root: ${cwd}`);
|
|
158
|
-
|
|
159
|
-
const markers = [
|
|
160
|
-
"package.json", "Cargo.toml", "pyproject.toml", "go.mod",
|
|
161
|
-
"Makefile", "Dockerfile", "README.md", "CODEMAXXING.md",
|
|
162
|
-
];
|
|
163
|
-
|
|
164
|
-
const found: string[] = [];
|
|
165
|
-
for (const m of markers) {
|
|
166
|
-
if (existsSync(join(cwd, m))) found.push(m);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (found.length > 0) {
|
|
170
|
-
lines.push(`Project files: ${found.join(", ")}`);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return lines.join("\n");
|
|
174
|
-
}
|
package/src/utils/git.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { execSync } from "child_process";
|
|
2
|
-
import { existsSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Check if a directory is inside a git repo
|
|
7
|
-
*/
|
|
8
|
-
export function isGitRepo(cwd: string): boolean {
|
|
9
|
-
try {
|
|
10
|
-
execSync("git rev-parse --is-inside-work-tree", {
|
|
11
|
-
cwd,
|
|
12
|
-
stdio: "pipe",
|
|
13
|
-
encoding: "utf-8",
|
|
14
|
-
});
|
|
15
|
-
return true;
|
|
16
|
-
} catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Get current branch name
|
|
23
|
-
*/
|
|
24
|
-
export function getBranch(cwd: string): string {
|
|
25
|
-
try {
|
|
26
|
-
return execSync("git branch --show-current", {
|
|
27
|
-
cwd,
|
|
28
|
-
stdio: "pipe",
|
|
29
|
-
encoding: "utf-8",
|
|
30
|
-
}).trim();
|
|
31
|
-
} catch {
|
|
32
|
-
return "unknown";
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Get short git status (clean / dirty + count)
|
|
38
|
-
*/
|
|
39
|
-
export function getStatus(cwd: string): string {
|
|
40
|
-
try {
|
|
41
|
-
const output = execSync("git status --porcelain", {
|
|
42
|
-
cwd,
|
|
43
|
-
stdio: "pipe",
|
|
44
|
-
encoding: "utf-8",
|
|
45
|
-
}).trim();
|
|
46
|
-
if (!output) return "clean";
|
|
47
|
-
const count = output.split("\n").length;
|
|
48
|
-
return `${count} changed`;
|
|
49
|
-
} catch {
|
|
50
|
-
return "unknown";
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Auto-commit a file change with a descriptive message
|
|
56
|
-
*/
|
|
57
|
-
export function autoCommit(cwd: string, filePath: string, action: string): boolean {
|
|
58
|
-
try {
|
|
59
|
-
execSync(`git add "${filePath}"`, { cwd, stdio: "pipe" });
|
|
60
|
-
const msg = `codemaxxing: ${action} ${filePath}`;
|
|
61
|
-
execSync(`git commit -m "${msg}" --no-verify`, { cwd, stdio: "pipe" });
|
|
62
|
-
return true;
|
|
63
|
-
} catch {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Get diff of uncommitted changes (or last commit)
|
|
70
|
-
*/
|
|
71
|
-
export function getDiff(cwd: string): string {
|
|
72
|
-
try {
|
|
73
|
-
// First try uncommitted changes
|
|
74
|
-
let diff = execSync("git diff", { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
75
|
-
// Include staged changes too
|
|
76
|
-
const staged = execSync("git diff --cached", { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
77
|
-
if (staged) diff = diff ? `${diff}\n${staged}` : staged;
|
|
78
|
-
|
|
79
|
-
// If no uncommitted changes, show last commit
|
|
80
|
-
if (!diff) {
|
|
81
|
-
diff = execSync("git diff HEAD~1 HEAD", { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
82
|
-
if (diff) return `(last commit)\n${diff}`;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return diff || "No changes.";
|
|
86
|
-
} catch {
|
|
87
|
-
return "Error getting diff.";
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Undo the last codemaxxing commit
|
|
93
|
-
*/
|
|
94
|
-
export function undoLastCommit(cwd: string): { success: boolean; message: string } {
|
|
95
|
-
try {
|
|
96
|
-
// Check if last commit was from codemaxxing
|
|
97
|
-
const lastMsg = execSync("git log -1 --pretty=%s", {
|
|
98
|
-
cwd,
|
|
99
|
-
stdio: "pipe",
|
|
100
|
-
encoding: "utf-8",
|
|
101
|
-
}).trim();
|
|
102
|
-
|
|
103
|
-
if (!lastMsg.startsWith("codemaxxing:")) {
|
|
104
|
-
return {
|
|
105
|
-
success: false,
|
|
106
|
-
message: `Last commit is not from codemaxxing: "${lastMsg}"`,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
execSync("git reset --soft HEAD~1", { cwd, stdio: "pipe" });
|
|
111
|
-
execSync("git restore --staged .", { cwd, stdio: "pipe" });
|
|
112
|
-
|
|
113
|
-
return { success: true, message: `Reverted: ${lastMsg}` };
|
|
114
|
-
} catch (e: any) {
|
|
115
|
-
return { success: false, message: `Error: ${e.message}` };
|
|
116
|
-
}
|
|
117
|
-
}
|
package/src/utils/hardware.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import os from "os";
|
|
2
|
-
import { execSync } from "child_process";
|
|
3
|
-
|
|
4
|
-
export interface HardwareInfo {
|
|
5
|
-
cpu: { name: string; cores: number; speed: number };
|
|
6
|
-
ram: number; // bytes
|
|
7
|
-
gpu: { name: string; vram: number } | null; // vram in bytes, null if no GPU
|
|
8
|
-
os: "macos" | "linux" | "windows";
|
|
9
|
-
appleSilicon: boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function getOS(): "macos" | "linux" | "windows" {
|
|
13
|
-
switch (process.platform) {
|
|
14
|
-
case "darwin": return "macos";
|
|
15
|
-
case "win32": return "windows";
|
|
16
|
-
default: return "linux";
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function getCPU(): { name: string; cores: number; speed: number } {
|
|
21
|
-
const cpus = os.cpus();
|
|
22
|
-
return {
|
|
23
|
-
name: cpus[0]?.model?.trim() ?? "Unknown CPU",
|
|
24
|
-
cores: cpus.length,
|
|
25
|
-
speed: cpus[0]?.speed ?? 0, // MHz
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function getGPU(platform: "macos" | "linux" | "windows"): { name: string; vram: number } | null {
|
|
30
|
-
try {
|
|
31
|
-
if (platform === "macos") {
|
|
32
|
-
const raw = execSync("system_profiler SPDisplaysDataType -json", {
|
|
33
|
-
encoding: "utf-8",
|
|
34
|
-
timeout: 5000,
|
|
35
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
36
|
-
});
|
|
37
|
-
const data = JSON.parse(raw);
|
|
38
|
-
const displays = data?.SPDisplaysDataType;
|
|
39
|
-
if (Array.isArray(displays) && displays.length > 0) {
|
|
40
|
-
const gpu = displays[0];
|
|
41
|
-
const name: string = gpu.sppci_model ?? gpu._name ?? "Unknown GPU";
|
|
42
|
-
// On Apple Silicon, VRAM is shared (unified memory) — report total RAM
|
|
43
|
-
const vramStr: string = gpu["spdisplays_vram"] ?? gpu["spdisplays_vram_shared"] ?? "";
|
|
44
|
-
let vram = 0;
|
|
45
|
-
if (vramStr) {
|
|
46
|
-
const match = vramStr.match(/(\d+)\s*(GB|MB)/i);
|
|
47
|
-
if (match) {
|
|
48
|
-
vram = parseInt(match[1]) * (match[2].toUpperCase() === "GB" ? 1024 * 1024 * 1024 : 1024 * 1024);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// Apple Silicon unified memory — use total RAM as VRAM
|
|
52
|
-
if (vram === 0 && name.toLowerCase().includes("apple")) {
|
|
53
|
-
vram = os.totalmem();
|
|
54
|
-
}
|
|
55
|
-
return { name, vram };
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (platform === "linux") {
|
|
60
|
-
// Try NVIDIA first
|
|
61
|
-
try {
|
|
62
|
-
const raw = execSync("nvidia-smi --query-gpu=name,memory.total --format=csv,noheader", {
|
|
63
|
-
encoding: "utf-8",
|
|
64
|
-
timeout: 5000,
|
|
65
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
66
|
-
});
|
|
67
|
-
const line = raw.trim().split("\n")[0];
|
|
68
|
-
if (line) {
|
|
69
|
-
const parts = line.split(",").map(s => s.trim());
|
|
70
|
-
const name = parts[0] ?? "NVIDIA GPU";
|
|
71
|
-
const memMatch = (parts[1] ?? "").match(/(\d+)/);
|
|
72
|
-
const vram = memMatch ? parseInt(memMatch[1]) * 1024 * 1024 : 0; // MiB to bytes
|
|
73
|
-
return { name, vram };
|
|
74
|
-
}
|
|
75
|
-
} catch {
|
|
76
|
-
// No NVIDIA, try lspci
|
|
77
|
-
try {
|
|
78
|
-
const raw = execSync("lspci | grep -i vga", {
|
|
79
|
-
encoding: "utf-8",
|
|
80
|
-
timeout: 5000,
|
|
81
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
82
|
-
});
|
|
83
|
-
const line = raw.trim().split("\n")[0];
|
|
84
|
-
if (line) {
|
|
85
|
-
const name = line.split(":").slice(2).join(":").trim() || "Unknown GPU";
|
|
86
|
-
return { name, vram: 0 };
|
|
87
|
-
}
|
|
88
|
-
} catch { /* no lspci */ }
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (platform === "windows") {
|
|
93
|
-
try {
|
|
94
|
-
const raw = execSync("wmic path win32_VideoController get Name,AdapterRAM /format:csv", {
|
|
95
|
-
encoding: "utf-8",
|
|
96
|
-
timeout: 5000,
|
|
97
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
98
|
-
});
|
|
99
|
-
const lines = raw.trim().split("\n").filter(l => l.trim() && !l.startsWith("Node"));
|
|
100
|
-
if (lines.length > 0) {
|
|
101
|
-
const parts = lines[0].split(",");
|
|
102
|
-
const adapterRAM = parseInt(parts[1] ?? "0");
|
|
103
|
-
const name = parts[2]?.trim() ?? "Unknown GPU";
|
|
104
|
-
return { name, vram: isNaN(adapterRAM) ? 0 : adapterRAM };
|
|
105
|
-
}
|
|
106
|
-
} catch { /* no wmic */ }
|
|
107
|
-
}
|
|
108
|
-
} catch {
|
|
109
|
-
// GPU detection failed
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function detectHardware(): HardwareInfo {
|
|
115
|
-
const platform = getOS();
|
|
116
|
-
const cpu = getCPU();
|
|
117
|
-
const ram = os.totalmem();
|
|
118
|
-
const gpu = getGPU(platform);
|
|
119
|
-
|
|
120
|
-
// Detect Apple Silicon
|
|
121
|
-
const appleSilicon = platform === "macos" && /apple\s+m/i.test(cpu.name);
|
|
122
|
-
|
|
123
|
-
return { cpu, ram, gpu, os: platform, appleSilicon };
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** Format bytes to human-readable string */
|
|
127
|
-
export function formatBytes(bytes: number): string {
|
|
128
|
-
if (bytes >= 1024 * 1024 * 1024) return `${Math.round(bytes / (1024 * 1024 * 1024))} GB`;
|
|
129
|
-
if (bytes >= 1024 * 1024) return `${Math.round(bytes / (1024 * 1024))} MB`;
|
|
130
|
-
return `${Math.round(bytes / 1024)} KB`;
|
|
131
|
-
}
|
package/src/utils/lint.ts
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "fs";
|
|
2
|
-
import { join, extname } from "path";
|
|
3
|
-
import { execSync } from "child_process";
|
|
4
|
-
|
|
5
|
-
interface LinterInfo {
|
|
6
|
-
name: string;
|
|
7
|
-
command: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Detect the project linter based on config files in the working directory
|
|
12
|
-
*/
|
|
13
|
-
export function detectLinter(cwd: string): LinterInfo | null {
|
|
14
|
-
// JavaScript/TypeScript — check for biome first (faster), then eslint
|
|
15
|
-
if (existsSync(join(cwd, "biome.json")) || existsSync(join(cwd, "biome.jsonc"))) {
|
|
16
|
-
return { name: "Biome", command: "npx biome check" };
|
|
17
|
-
}
|
|
18
|
-
if (
|
|
19
|
-
existsSync(join(cwd, ".eslintrc")) ||
|
|
20
|
-
existsSync(join(cwd, ".eslintrc.js")) ||
|
|
21
|
-
existsSync(join(cwd, ".eslintrc.cjs")) ||
|
|
22
|
-
existsSync(join(cwd, ".eslintrc.json")) ||
|
|
23
|
-
existsSync(join(cwd, ".eslintrc.yml")) ||
|
|
24
|
-
existsSync(join(cwd, "eslint.config.js")) ||
|
|
25
|
-
existsSync(join(cwd, "eslint.config.mjs")) ||
|
|
26
|
-
existsSync(join(cwd, "eslint.config.ts"))
|
|
27
|
-
) {
|
|
28
|
-
return { name: "ESLint", command: "npx eslint" };
|
|
29
|
-
}
|
|
30
|
-
// Check package.json for eslint dependency as fallback
|
|
31
|
-
if (existsSync(join(cwd, "package.json"))) {
|
|
32
|
-
try {
|
|
33
|
-
const pkg = JSON.parse(require("fs").readFileSync(join(cwd, "package.json"), "utf-8"));
|
|
34
|
-
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
35
|
-
if (allDeps["@biomejs/biome"]) {
|
|
36
|
-
return { name: "Biome", command: "npx biome check" };
|
|
37
|
-
}
|
|
38
|
-
if (allDeps["eslint"]) {
|
|
39
|
-
return { name: "ESLint", command: "npx eslint" };
|
|
40
|
-
}
|
|
41
|
-
} catch {
|
|
42
|
-
// ignore
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Python — ruff (fast) or flake8/pylint
|
|
47
|
-
if (existsSync(join(cwd, "ruff.toml")) || existsSync(join(cwd, ".ruff.toml"))) {
|
|
48
|
-
return { name: "Ruff", command: "ruff check" };
|
|
49
|
-
}
|
|
50
|
-
if (existsSync(join(cwd, "pyproject.toml"))) {
|
|
51
|
-
try {
|
|
52
|
-
const content = require("fs").readFileSync(join(cwd, "pyproject.toml"), "utf-8");
|
|
53
|
-
if (content.includes("[tool.ruff]")) {
|
|
54
|
-
return { name: "Ruff", command: "ruff check" };
|
|
55
|
-
}
|
|
56
|
-
} catch {
|
|
57
|
-
// ignore
|
|
58
|
-
}
|
|
59
|
-
return { name: "Ruff", command: "ruff check" };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Rust
|
|
63
|
-
if (existsSync(join(cwd, "Cargo.toml"))) {
|
|
64
|
-
return { name: "Clippy", command: "cargo clippy --message-format=short --" };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Go
|
|
68
|
-
if (existsSync(join(cwd, "go.mod"))) {
|
|
69
|
-
return { name: "golangci-lint", command: "golangci-lint run" };
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Run the linter on a specific file and return errors (or null if clean)
|
|
77
|
-
*/
|
|
78
|
-
export function runLinter(linter: LinterInfo, filePath: string, cwd: string): string | null {
|
|
79
|
-
// Skip files that the linter can't handle
|
|
80
|
-
const ext = extname(filePath).toLowerCase();
|
|
81
|
-
const jsExts = new Set([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".mts", ".cts"]);
|
|
82
|
-
const pyExts = new Set([".py", ".pyi"]);
|
|
83
|
-
const rsExts = new Set([".rs"]);
|
|
84
|
-
const goExts = new Set([".go"]);
|
|
85
|
-
|
|
86
|
-
// Only lint files matching the linter's language
|
|
87
|
-
if ((linter.name === "ESLint" || linter.name === "Biome") && !jsExts.has(ext)) return null;
|
|
88
|
-
if (linter.name === "Ruff" && !pyExts.has(ext)) return null;
|
|
89
|
-
if (linter.name === "Clippy" && !rsExts.has(ext)) return null;
|
|
90
|
-
if (linter.name === "golangci-lint" && !goExts.has(ext)) return null;
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
// Clippy works on the whole project, not individual files
|
|
94
|
-
const command = linter.name === "Clippy"
|
|
95
|
-
? linter.command
|
|
96
|
-
: `${linter.command} ${filePath}`;
|
|
97
|
-
|
|
98
|
-
execSync(command, {
|
|
99
|
-
cwd,
|
|
100
|
-
encoding: "utf-8",
|
|
101
|
-
timeout: 15000,
|
|
102
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
103
|
-
});
|
|
104
|
-
return null; // No errors
|
|
105
|
-
} catch (e: any) {
|
|
106
|
-
const output = (e.stdout || "") + (e.stderr || "");
|
|
107
|
-
const trimmed = output.trim();
|
|
108
|
-
if (!trimmed) return null;
|
|
109
|
-
// Limit output to avoid flooding context
|
|
110
|
-
const lines = trimmed.split("\n");
|
|
111
|
-
if (lines.length > 30) {
|
|
112
|
-
return lines.slice(0, 30).join("\n") + `\n... (${lines.length - 30} more lines)`;
|
|
113
|
-
}
|
|
114
|
-
return trimmed;
|
|
115
|
-
}
|
|
116
|
-
}
|