@sean.holung/minicode 0.1.1 → 0.2.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 +47 -6
- package/dist/src/agent/config.js +1 -1
- package/dist/src/cli/args.js +65 -0
- package/dist/src/index.js +109 -26
- package/dist/src/session/session-store.js +82 -0
- package/dist/src/tools/find-references.js +1 -1
- package/dist/src/tools/get-dependencies.js +1 -1
- package/dist/src/tools/read-symbol.js +1 -2
- package/dist/src/tools/registry.js +26 -61
- package/dist/src/tools/search-code-map.js +1 -1
- package/dist/src/ui/cli-ink.js +91 -19
- package/dist/tests/agent.test.js +2 -3
- package/dist/tests/cli-args.test.js +73 -0
- package/dist/tests/cli-oneshot.integration.test.js +26 -0
- package/dist/tests/dependency-graph.test.js +12 -12
- package/dist/tests/file-tools.test.js +2 -3
- package/dist/tests/find-references.test.js +6 -6
- package/dist/tests/guardrails.test.js +1 -1
- package/dist/tests/indexer.test.js +9 -9
- package/dist/tests/model-client-openai.test.js +1 -1
- package/dist/tests/read-symbol.test.js +16 -17
- package/dist/tests/search-code-map.test.js +2 -2
- package/dist/tests/session-store.test.js +115 -0
- package/dist/tests/session.test.js +1 -1
- package/dist/tests/system-prompt.test.js +1 -1
- package/dist/tests/tool-registry.test.js +1 -1
- package/package.json +7 -2
- package/dist/src/agent/agent.js +0 -209
- package/dist/src/agent/types.js +0 -1
- package/dist/src/model/client.js +0 -374
- package/dist/src/prompt/system-prompt.js +0 -91
- package/dist/src/safety/guardrails.js +0 -55
- package/dist/src/session/session.js +0 -95
- package/dist/src/tools/edit-file.js +0 -73
- package/dist/src/tools/helpers.js +0 -42
- package/dist/src/tools/list-files.js +0 -63
- package/dist/src/tools/read-file.js +0 -79
- package/dist/src/tools/run-command.js +0 -92
- package/dist/src/tools/search.js +0 -153
- package/dist/src/tools/write-file.js +0 -44
package/dist/src/tools/search.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { resolveWorkspacePath } from "../safety/guardrails.js";
|
|
4
|
-
import { expectNonEmptyString } from "./helpers.js";
|
|
5
|
-
function runCommand(command, args, cwd, timeoutMs) {
|
|
6
|
-
return new Promise((resolve, reject) => {
|
|
7
|
-
const child = spawn(command, args, { cwd });
|
|
8
|
-
let stdout = "";
|
|
9
|
-
let stderr = "";
|
|
10
|
-
let timedOut = false;
|
|
11
|
-
const timeout = setTimeout(() => {
|
|
12
|
-
timedOut = true;
|
|
13
|
-
child.kill("SIGTERM");
|
|
14
|
-
}, timeoutMs);
|
|
15
|
-
child.stdout.on("data", (chunk) => {
|
|
16
|
-
stdout += chunk.toString();
|
|
17
|
-
});
|
|
18
|
-
child.stderr.on("data", (chunk) => {
|
|
19
|
-
stderr += chunk.toString();
|
|
20
|
-
});
|
|
21
|
-
child.on("error", (error) => {
|
|
22
|
-
clearTimeout(timeout);
|
|
23
|
-
reject(error);
|
|
24
|
-
});
|
|
25
|
-
child.on("close", (code) => {
|
|
26
|
-
clearTimeout(timeout);
|
|
27
|
-
if (timedOut) {
|
|
28
|
-
reject(new Error(`Search command timed out after ${timeoutMs} ms.`));
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
resolve({
|
|
32
|
-
stdout,
|
|
33
|
-
stderr,
|
|
34
|
-
code,
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
function getOptionalString(input, key) {
|
|
40
|
-
const value = input[key];
|
|
41
|
-
if (value === undefined) {
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
if (typeof value !== "string" || value.trim().length === 0) {
|
|
45
|
-
throw new Error(`Input "${key}" must be a non-empty string.`);
|
|
46
|
-
}
|
|
47
|
-
return value;
|
|
48
|
-
}
|
|
49
|
-
export function createSearchTool(config) {
|
|
50
|
-
return {
|
|
51
|
-
name: "search",
|
|
52
|
-
description: "Search file contents using ripgrep. Use when you don't know the symbol name. When results show a function/class name, use read_symbol next (not read_file).",
|
|
53
|
-
inputSchema: {
|
|
54
|
-
type: "object",
|
|
55
|
-
properties: {
|
|
56
|
-
pattern: {
|
|
57
|
-
type: "string",
|
|
58
|
-
description: "Regex pattern to search for.",
|
|
59
|
-
},
|
|
60
|
-
path: {
|
|
61
|
-
type: "string",
|
|
62
|
-
description: "Optional path to search under, relative to workspace root.",
|
|
63
|
-
},
|
|
64
|
-
include: {
|
|
65
|
-
type: "string",
|
|
66
|
-
description: "Optional glob include filter, e.g. *.ts",
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
required: ["pattern"],
|
|
70
|
-
additionalProperties: false,
|
|
71
|
-
},
|
|
72
|
-
execute: async (input) => {
|
|
73
|
-
const maxOutputChars = 12_000;
|
|
74
|
-
const pattern = expectNonEmptyString(input, "pattern");
|
|
75
|
-
const requestedPath = getOptionalString(input, "path") ?? ".";
|
|
76
|
-
const include = getOptionalString(input, "include");
|
|
77
|
-
const targetPath = resolveWorkspacePath(requestedPath, config.workspaceRoot);
|
|
78
|
-
const relativeTarget = path.relative(config.workspaceRoot, targetPath) || ".";
|
|
79
|
-
const rgArgs = [
|
|
80
|
-
"--line-number",
|
|
81
|
-
"--color",
|
|
82
|
-
"never",
|
|
83
|
-
"--no-heading",
|
|
84
|
-
"--binary-files",
|
|
85
|
-
"without-match",
|
|
86
|
-
"--glob",
|
|
87
|
-
"!.minicode/**",
|
|
88
|
-
"--glob",
|
|
89
|
-
"!node_modules/**",
|
|
90
|
-
"--glob",
|
|
91
|
-
"!package-lock.json",
|
|
92
|
-
"--glob",
|
|
93
|
-
"!yarn.lock",
|
|
94
|
-
"--glob",
|
|
95
|
-
"!pnpm-lock.yaml",
|
|
96
|
-
"--glob",
|
|
97
|
-
"!*.min.js",
|
|
98
|
-
"-m",
|
|
99
|
-
"50",
|
|
100
|
-
];
|
|
101
|
-
if (include) {
|
|
102
|
-
rgArgs.push("--glob", include);
|
|
103
|
-
}
|
|
104
|
-
rgArgs.push(pattern, relativeTarget);
|
|
105
|
-
try {
|
|
106
|
-
const result = await runCommand("rg", rgArgs, config.workspaceRoot, config.commandTimeoutMs);
|
|
107
|
-
if (result.code === 1 || result.stdout.trim().length === 0) {
|
|
108
|
-
return "No matches found.";
|
|
109
|
-
}
|
|
110
|
-
if (result.code !== 0) {
|
|
111
|
-
throw new Error(result.stderr || "ripgrep search failed.");
|
|
112
|
-
}
|
|
113
|
-
const output = result.stdout.trimEnd();
|
|
114
|
-
if (output.length > maxOutputChars) {
|
|
115
|
-
return `${output.slice(0, maxOutputChars)}\n\n[... output truncated, ${output.length - maxOutputChars} more chars ...]`;
|
|
116
|
-
}
|
|
117
|
-
return output;
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
const commandError = error;
|
|
121
|
-
if (commandError.code !== "ENOENT") {
|
|
122
|
-
throw error;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// Minimal fallback for systems without rg installed.
|
|
126
|
-
const grepArgs = [
|
|
127
|
-
"-RIn",
|
|
128
|
-
"--exclude-dir=.minicode",
|
|
129
|
-
"--exclude-dir=node_modules",
|
|
130
|
-
"--exclude-dir=.git",
|
|
131
|
-
"--exclude=package-lock.json",
|
|
132
|
-
"--exclude=yarn.lock",
|
|
133
|
-
"--exclude=pnpm-lock.yaml",
|
|
134
|
-
"-m",
|
|
135
|
-
"50",
|
|
136
|
-
pattern,
|
|
137
|
-
relativeTarget,
|
|
138
|
-
];
|
|
139
|
-
const fallbackResult = await runCommand("grep", grepArgs, config.workspaceRoot, config.commandTimeoutMs);
|
|
140
|
-
if (fallbackResult.code === 1 || fallbackResult.stdout.trim().length === 0) {
|
|
141
|
-
return "No matches found.";
|
|
142
|
-
}
|
|
143
|
-
if (fallbackResult.code !== 0) {
|
|
144
|
-
throw new Error(fallbackResult.stderr || "grep search failed.");
|
|
145
|
-
}
|
|
146
|
-
const fallbackOutput = fallbackResult.stdout.trimEnd();
|
|
147
|
-
if (fallbackOutput.length > maxOutputChars) {
|
|
148
|
-
return `${fallbackOutput.slice(0, maxOutputChars)}\n\n[... output truncated, ${fallbackOutput.length - maxOutputChars} more chars ...]`;
|
|
149
|
-
}
|
|
150
|
-
return fallbackOutput;
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { resolveWorkspacePath } from "../safety/guardrails.js";
|
|
4
|
-
import { expectNonEmptyString } from "./helpers.js";
|
|
5
|
-
function expectString(input, key) {
|
|
6
|
-
const value = input[key];
|
|
7
|
-
if (typeof value !== "string") {
|
|
8
|
-
throw new Error(`Input "${key}" must be a string.`);
|
|
9
|
-
}
|
|
10
|
-
return value;
|
|
11
|
-
}
|
|
12
|
-
export function createWriteFileTool(config, projectIndex) {
|
|
13
|
-
return {
|
|
14
|
-
name: "write_file",
|
|
15
|
-
description: "Create or overwrite a file with the provided content.",
|
|
16
|
-
inputSchema: {
|
|
17
|
-
type: "object",
|
|
18
|
-
properties: {
|
|
19
|
-
path: {
|
|
20
|
-
type: "string",
|
|
21
|
-
description: "Path to the file relative to workspace root.",
|
|
22
|
-
},
|
|
23
|
-
content: {
|
|
24
|
-
type: "string",
|
|
25
|
-
description: "The full file content to write.",
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
required: ["path", "content"],
|
|
29
|
-
additionalProperties: false,
|
|
30
|
-
},
|
|
31
|
-
execute: async (input) => {
|
|
32
|
-
const requestedPath = expectNonEmptyString(input, "path");
|
|
33
|
-
const content = expectString(input, "content");
|
|
34
|
-
const filePath = resolveWorkspacePath(requestedPath, config.workspaceRoot);
|
|
35
|
-
await mkdir(path.dirname(filePath), { recursive: true });
|
|
36
|
-
await writeFile(filePath, content, "utf8");
|
|
37
|
-
if (projectIndex) {
|
|
38
|
-
const relPath = path.relative(config.workspaceRoot, filePath);
|
|
39
|
-
projectIndex.reindexFile(relPath, content);
|
|
40
|
-
}
|
|
41
|
-
return `Wrote ${content.length} characters to "${requestedPath}".`;
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
}
|