codemaxxing 0.1.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 +209 -0
- package/dist/agent.d.ts +65 -0
- package/dist/agent.js +269 -0
- package/dist/config.d.ts +41 -0
- package/dist/config.js +174 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +659 -0
- package/dist/tools/files.d.ts +9 -0
- package/dist/tools/files.js +227 -0
- package/dist/utils/context.d.ts +13 -0
- package/dist/utils/context.js +135 -0
- package/dist/utils/git.d.ts +27 -0
- package/dist/utils/git.js +113 -0
- package/dist/utils/repomap.d.ts +18 -0
- package/dist/utils/repomap.js +195 -0
- package/dist/utils/sessions.d.ts +51 -0
- package/dist/utils/sessions.js +174 -0
- package/dist/utils/treesitter.d.ts +20 -0
- package/dist/utils/treesitter.js +710 -0
- package/package.json +51 -0
- package/src/agent.ts +322 -0
- package/src/config.ts +211 -0
- package/src/index.tsx +858 -0
- package/src/tools/files.ts +247 -0
- package/src/utils/context.ts +146 -0
- package/src/utils/git.ts +117 -0
- package/src/utils/repomap.ts +220 -0
- package/src/utils/sessions.ts +222 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { join, relative } from "path";
|
|
3
|
+
/**
|
|
4
|
+
* Tool definitions for the OpenAI function calling API
|
|
5
|
+
*/
|
|
6
|
+
export const FILE_TOOLS = [
|
|
7
|
+
{
|
|
8
|
+
type: "function",
|
|
9
|
+
function: {
|
|
10
|
+
name: "read_file",
|
|
11
|
+
description: "Read the contents of a file at the given path",
|
|
12
|
+
parameters: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {
|
|
15
|
+
path: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "Path to the file to read (relative to project root)",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: ["path"],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
type: "function",
|
|
26
|
+
function: {
|
|
27
|
+
name: "write_file",
|
|
28
|
+
description: "Write content to a file. Creates the file if it doesn't exist, overwrites if it does.",
|
|
29
|
+
parameters: {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: {
|
|
32
|
+
path: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Path to the file to write (relative to project root)",
|
|
35
|
+
},
|
|
36
|
+
content: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "Content to write to the file",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
required: ["path", "content"],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
type: "function",
|
|
47
|
+
function: {
|
|
48
|
+
name: "list_files",
|
|
49
|
+
description: "List files and directories in the given path. Returns file names and types.",
|
|
50
|
+
parameters: {
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
path: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "Directory path to list (relative to project root, defaults to '.')",
|
|
56
|
+
},
|
|
57
|
+
recursive: {
|
|
58
|
+
type: "boolean",
|
|
59
|
+
description: "Whether to list files recursively (default: false)",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
type: "function",
|
|
67
|
+
function: {
|
|
68
|
+
name: "search_files",
|
|
69
|
+
description: "Search for a text pattern across files in the project",
|
|
70
|
+
parameters: {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: {
|
|
73
|
+
pattern: {
|
|
74
|
+
type: "string",
|
|
75
|
+
description: "Text or regex pattern to search for",
|
|
76
|
+
},
|
|
77
|
+
path: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "Directory to search in (defaults to project root)",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
required: ["pattern"],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: "function",
|
|
88
|
+
function: {
|
|
89
|
+
name: "run_command",
|
|
90
|
+
description: "Execute a shell command and return the output. Use for running tests, builds, linters, etc.",
|
|
91
|
+
parameters: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
command: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "Shell command to execute",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
required: ["command"],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
/**
|
|
105
|
+
* Execute a tool call and return the result
|
|
106
|
+
*/
|
|
107
|
+
export async function executeTool(name, args, cwd) {
|
|
108
|
+
switch (name) {
|
|
109
|
+
case "read_file": {
|
|
110
|
+
const filePath = join(cwd, args.path);
|
|
111
|
+
if (!existsSync(filePath))
|
|
112
|
+
return `Error: File not found: ${args.path}`;
|
|
113
|
+
try {
|
|
114
|
+
return readFileSync(filePath, "utf-8");
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
return `Error reading file: ${e}`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
case "write_file": {
|
|
121
|
+
const filePath = join(cwd, args.path);
|
|
122
|
+
try {
|
|
123
|
+
writeFileSync(filePath, args.content, "utf-8");
|
|
124
|
+
return `✅ Wrote ${args.content.length} bytes to ${args.path}`;
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
return `Error writing file: ${e}`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
case "list_files": {
|
|
131
|
+
const dirPath = join(cwd, args.path || ".");
|
|
132
|
+
if (!existsSync(dirPath))
|
|
133
|
+
return `Error: Directory not found: ${args.path}`;
|
|
134
|
+
try {
|
|
135
|
+
const entries = listDir(dirPath, cwd, args.recursive);
|
|
136
|
+
return entries.join("\n");
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
return `Error listing files: ${e}`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
case "search_files": {
|
|
143
|
+
const searchPath = join(cwd, args.path || ".");
|
|
144
|
+
try {
|
|
145
|
+
return searchInFiles(searchPath, args.pattern, cwd);
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
return `Error searching: ${e}`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
case "run_command": {
|
|
152
|
+
try {
|
|
153
|
+
const { execSync } = await import("child_process");
|
|
154
|
+
const output = execSync(args.command, {
|
|
155
|
+
cwd,
|
|
156
|
+
encoding: "utf-8",
|
|
157
|
+
timeout: 30000,
|
|
158
|
+
maxBuffer: 1024 * 1024,
|
|
159
|
+
});
|
|
160
|
+
return output || "(no output)";
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
return `Command failed: ${e.stderr || e.message}`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
default:
|
|
167
|
+
return `Unknown tool: ${name}`;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function listDir(dirPath, cwd, recursive = false, depth = 0) {
|
|
171
|
+
const entries = [];
|
|
172
|
+
const IGNORE = ["node_modules", ".git", "dist", ".next", "__pycache__"];
|
|
173
|
+
for (const entry of readdirSync(dirPath)) {
|
|
174
|
+
if (IGNORE.includes(entry))
|
|
175
|
+
continue;
|
|
176
|
+
const fullPath = join(dirPath, entry);
|
|
177
|
+
const rel = relative(cwd, fullPath);
|
|
178
|
+
const stat = statSync(fullPath);
|
|
179
|
+
const prefix = " ".repeat(depth);
|
|
180
|
+
if (stat.isDirectory()) {
|
|
181
|
+
entries.push(`${prefix}📁 ${rel}/`);
|
|
182
|
+
if (recursive && depth < 3) {
|
|
183
|
+
entries.push(...listDir(fullPath, cwd, true, depth + 1));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
const size = stat.size > 1024 ? `${(stat.size / 1024).toFixed(1)}KB` : `${stat.size}B`;
|
|
188
|
+
entries.push(`${prefix}📄 ${rel} (${size})`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return entries;
|
|
192
|
+
}
|
|
193
|
+
function searchInFiles(dirPath, pattern, cwd) {
|
|
194
|
+
const results = [];
|
|
195
|
+
const IGNORE = ["node_modules", ".git", "dist", ".next", "__pycache__"];
|
|
196
|
+
const regex = new RegExp(pattern, "gi");
|
|
197
|
+
function search(dir) {
|
|
198
|
+
for (const entry of readdirSync(dir)) {
|
|
199
|
+
if (IGNORE.includes(entry))
|
|
200
|
+
continue;
|
|
201
|
+
const fullPath = join(dir, entry);
|
|
202
|
+
const stat = statSync(fullPath);
|
|
203
|
+
if (stat.isDirectory()) {
|
|
204
|
+
search(fullPath);
|
|
205
|
+
}
|
|
206
|
+
else if (stat.size < 100000) {
|
|
207
|
+
try {
|
|
208
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
209
|
+
const lines = content.split("\n");
|
|
210
|
+
for (let i = 0; i < lines.length; i++) {
|
|
211
|
+
if (regex.test(lines[i])) {
|
|
212
|
+
results.push(`${relative(cwd, fullPath)}:${i + 1}: ${lines[i].trim()}`);
|
|
213
|
+
}
|
|
214
|
+
regex.lastIndex = 0;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
// skip binary files
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
search(dirPath);
|
|
224
|
+
return results.length > 0
|
|
225
|
+
? results.slice(0, 50).join("\n")
|
|
226
|
+
: "No matches found.";
|
|
227
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a project context string by scanning the working directory
|
|
3
|
+
*/
|
|
4
|
+
export declare function buildProjectContext(cwd: string): Promise<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Get the system prompt for the coding agent
|
|
7
|
+
*/
|
|
8
|
+
export declare function getSystemPrompt(projectContext: string): Promise<string>;
|
|
9
|
+
/**
|
|
10
|
+
* Synchronous version for backwards compatibility (without repo map)
|
|
11
|
+
* @deprecated Use async buildProjectContext instead
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildProjectContextSync(cwd: string): string;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { buildRepoMap } from "./repomap.js";
|
|
4
|
+
/**
|
|
5
|
+
* Build a project context string by scanning the working directory
|
|
6
|
+
*/
|
|
7
|
+
export async function buildProjectContext(cwd) {
|
|
8
|
+
const lines = [];
|
|
9
|
+
lines.push(`Project root: ${cwd}`);
|
|
10
|
+
// Check for common project files
|
|
11
|
+
const markers = [
|
|
12
|
+
"package.json",
|
|
13
|
+
"Cargo.toml",
|
|
14
|
+
"pyproject.toml",
|
|
15
|
+
"go.mod",
|
|
16
|
+
"Makefile",
|
|
17
|
+
"Dockerfile",
|
|
18
|
+
"README.md",
|
|
19
|
+
"CODEMAXXING.md",
|
|
20
|
+
];
|
|
21
|
+
const found = [];
|
|
22
|
+
for (const m of markers) {
|
|
23
|
+
if (existsSync(join(cwd, m)))
|
|
24
|
+
found.push(m);
|
|
25
|
+
}
|
|
26
|
+
if (found.length > 0) {
|
|
27
|
+
lines.push(`Project files: ${found.join(", ")}`);
|
|
28
|
+
}
|
|
29
|
+
// Read PIERRE.md if it exists (like QWEN.md — project context file)
|
|
30
|
+
const contextMd = join(cwd, "CODEMAXXING.md");
|
|
31
|
+
if (existsSync(contextMd)) {
|
|
32
|
+
const content = readFileSync(contextMd, "utf-8");
|
|
33
|
+
lines.push("\n--- CODEMAXXING.md (project context) ---");
|
|
34
|
+
lines.push(content.slice(0, 4000));
|
|
35
|
+
lines.push("--- end CODEMAXXING.md ---");
|
|
36
|
+
}
|
|
37
|
+
// Read package.json for project info
|
|
38
|
+
const pkgPath = join(cwd, "package.json");
|
|
39
|
+
if (existsSync(pkgPath)) {
|
|
40
|
+
try {
|
|
41
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
42
|
+
lines.push(`\nProject: ${pkg.name ?? "unknown"} v${pkg.version ?? "0.0.0"}`);
|
|
43
|
+
if (pkg.description)
|
|
44
|
+
lines.push(`Description: ${pkg.description}`);
|
|
45
|
+
if (pkg.scripts) {
|
|
46
|
+
lines.push(`Available scripts: ${Object.keys(pkg.scripts).join(", ")}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// ignore
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Quick file tree (top level only)
|
|
54
|
+
lines.push("\nProject structure:");
|
|
55
|
+
const IGNORE = ["node_modules", ".git", "dist", ".next", "__pycache__", ".DS_Store"];
|
|
56
|
+
try {
|
|
57
|
+
const entries = readdirSync(cwd)
|
|
58
|
+
.filter((e) => !IGNORE.includes(e))
|
|
59
|
+
.slice(0, 30);
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
const fullPath = join(cwd, entry);
|
|
62
|
+
const stat = statSync(fullPath);
|
|
63
|
+
if (stat.isDirectory()) {
|
|
64
|
+
const count = readdirSync(fullPath).filter((e) => !IGNORE.includes(e)).length;
|
|
65
|
+
lines.push(` 📁 ${entry}/ (${count} items)`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
lines.push(` 📄 ${entry}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
lines.push(" (could not read directory)");
|
|
74
|
+
}
|
|
75
|
+
// Build and append repo map
|
|
76
|
+
try {
|
|
77
|
+
const repoMap = await buildRepoMap(cwd);
|
|
78
|
+
lines.push("\n" + repoMap);
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
// Repo map failed — continue without it
|
|
82
|
+
lines.push("\n## Repository Map\n (unable to build map)");
|
|
83
|
+
}
|
|
84
|
+
return lines.join("\n");
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get the system prompt for the coding agent
|
|
88
|
+
*/
|
|
89
|
+
export async function getSystemPrompt(projectContext) {
|
|
90
|
+
return `You are CODEMAXXING, an AI coding assistant running in the terminal.
|
|
91
|
+
|
|
92
|
+
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.
|
|
93
|
+
|
|
94
|
+
## Rules
|
|
95
|
+
- Always read relevant files before making changes
|
|
96
|
+
- Explain what you're about to do before doing it
|
|
97
|
+
- When writing files, show the user what will change
|
|
98
|
+
- Be concise but thorough
|
|
99
|
+
- If you're unsure, ask — don't guess
|
|
100
|
+
- Use the run_command tool for building, testing, and linters
|
|
101
|
+
- Never delete files without explicit confirmation
|
|
102
|
+
|
|
103
|
+
## Repository Map
|
|
104
|
+
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.
|
|
105
|
+
|
|
106
|
+
## Project Context
|
|
107
|
+
${projectContext}
|
|
108
|
+
|
|
109
|
+
## Behavior
|
|
110
|
+
- Respond in markdown
|
|
111
|
+
- Use code blocks with language tags
|
|
112
|
+
- Be direct and helpful
|
|
113
|
+
- If the user asks to "just do it", skip explanations and execute`;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Synchronous version for backwards compatibility (without repo map)
|
|
117
|
+
* @deprecated Use async buildProjectContext instead
|
|
118
|
+
*/
|
|
119
|
+
export function buildProjectContextSync(cwd) {
|
|
120
|
+
const lines = [];
|
|
121
|
+
lines.push(`Project root: ${cwd}`);
|
|
122
|
+
const markers = [
|
|
123
|
+
"package.json", "Cargo.toml", "pyproject.toml", "go.mod",
|
|
124
|
+
"Makefile", "Dockerfile", "README.md", "CODEMAXXING.md",
|
|
125
|
+
];
|
|
126
|
+
const found = [];
|
|
127
|
+
for (const m of markers) {
|
|
128
|
+
if (existsSync(join(cwd, m)))
|
|
129
|
+
found.push(m);
|
|
130
|
+
}
|
|
131
|
+
if (found.length > 0) {
|
|
132
|
+
lines.push(`Project files: ${found.join(", ")}`);
|
|
133
|
+
}
|
|
134
|
+
return lines.join("\n");
|
|
135
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a directory is inside a git repo
|
|
3
|
+
*/
|
|
4
|
+
export declare function isGitRepo(cwd: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Get current branch name
|
|
7
|
+
*/
|
|
8
|
+
export declare function getBranch(cwd: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Get short git status (clean / dirty + count)
|
|
11
|
+
*/
|
|
12
|
+
export declare function getStatus(cwd: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Auto-commit a file change with a descriptive message
|
|
15
|
+
*/
|
|
16
|
+
export declare function autoCommit(cwd: string, filePath: string, action: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Get diff of uncommitted changes (or last commit)
|
|
19
|
+
*/
|
|
20
|
+
export declare function getDiff(cwd: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Undo the last codemaxxing commit
|
|
23
|
+
*/
|
|
24
|
+
export declare function undoLastCommit(cwd: string): {
|
|
25
|
+
success: boolean;
|
|
26
|
+
message: string;
|
|
27
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Check if a directory is inside a git repo
|
|
4
|
+
*/
|
|
5
|
+
export function isGitRepo(cwd) {
|
|
6
|
+
try {
|
|
7
|
+
execSync("git rev-parse --is-inside-work-tree", {
|
|
8
|
+
cwd,
|
|
9
|
+
stdio: "pipe",
|
|
10
|
+
encoding: "utf-8",
|
|
11
|
+
});
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get current branch name
|
|
20
|
+
*/
|
|
21
|
+
export function getBranch(cwd) {
|
|
22
|
+
try {
|
|
23
|
+
return execSync("git branch --show-current", {
|
|
24
|
+
cwd,
|
|
25
|
+
stdio: "pipe",
|
|
26
|
+
encoding: "utf-8",
|
|
27
|
+
}).trim();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return "unknown";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get short git status (clean / dirty + count)
|
|
35
|
+
*/
|
|
36
|
+
export function getStatus(cwd) {
|
|
37
|
+
try {
|
|
38
|
+
const output = execSync("git status --porcelain", {
|
|
39
|
+
cwd,
|
|
40
|
+
stdio: "pipe",
|
|
41
|
+
encoding: "utf-8",
|
|
42
|
+
}).trim();
|
|
43
|
+
if (!output)
|
|
44
|
+
return "clean";
|
|
45
|
+
const count = output.split("\n").length;
|
|
46
|
+
return `${count} changed`;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return "unknown";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Auto-commit a file change with a descriptive message
|
|
54
|
+
*/
|
|
55
|
+
export function autoCommit(cwd, filePath, action) {
|
|
56
|
+
try {
|
|
57
|
+
execSync(`git add "${filePath}"`, { cwd, stdio: "pipe" });
|
|
58
|
+
const msg = `codemaxxing: ${action} ${filePath}`;
|
|
59
|
+
execSync(`git commit -m "${msg}" --no-verify`, { cwd, stdio: "pipe" });
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get diff of uncommitted changes (or last commit)
|
|
68
|
+
*/
|
|
69
|
+
export function getDiff(cwd) {
|
|
70
|
+
try {
|
|
71
|
+
// First try uncommitted changes
|
|
72
|
+
let diff = execSync("git diff", { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
73
|
+
// Include staged changes too
|
|
74
|
+
const staged = execSync("git diff --cached", { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
75
|
+
if (staged)
|
|
76
|
+
diff = diff ? `${diff}\n${staged}` : staged;
|
|
77
|
+
// If no uncommitted changes, show last commit
|
|
78
|
+
if (!diff) {
|
|
79
|
+
diff = execSync("git diff HEAD~1 HEAD", { cwd, stdio: "pipe", encoding: "utf-8" }).trim();
|
|
80
|
+
if (diff)
|
|
81
|
+
return `(last commit)\n${diff}`;
|
|
82
|
+
}
|
|
83
|
+
return diff || "No changes.";
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return "Error getting diff.";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Undo the last codemaxxing commit
|
|
91
|
+
*/
|
|
92
|
+
export function undoLastCommit(cwd) {
|
|
93
|
+
try {
|
|
94
|
+
// Check if last commit was from codemaxxing
|
|
95
|
+
const lastMsg = execSync("git log -1 --pretty=%s", {
|
|
96
|
+
cwd,
|
|
97
|
+
stdio: "pipe",
|
|
98
|
+
encoding: "utf-8",
|
|
99
|
+
}).trim();
|
|
100
|
+
if (!lastMsg.startsWith("codemaxxing:")) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
message: `Last commit is not from codemaxxing: "${lastMsg}"`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
execSync("git reset --soft HEAD~1", { cwd, stdio: "pipe" });
|
|
107
|
+
execSync("git restore --staged .", { cwd, stdio: "pipe" });
|
|
108
|
+
return { success: true, message: `Reverted: ${lastMsg}` };
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
return { success: false, message: `Error: ${e.message}` };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the repo map — cached for 1 min
|
|
3
|
+
*/
|
|
4
|
+
export declare function buildRepoMap(cwd: string): Promise<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Get cached map without rebuilding
|
|
7
|
+
*/
|
|
8
|
+
export declare function getCachedMap(): string;
|
|
9
|
+
/**
|
|
10
|
+
* Clear the cache
|
|
11
|
+
*/
|
|
12
|
+
export declare function clearMapCache(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Check if file is supported
|
|
15
|
+
*/
|
|
16
|
+
export declare function isSupportedFile(filePath: string): boolean;
|
|
17
|
+
export declare function getSupportedExtensions(): string[];
|
|
18
|
+
export declare function getLanguageForExt(ext: string): string | null;
|