junecoder 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 +2 -0
- package/agent.mjs +650 -0
- package/checkpoint.mjs +43 -0
- package/cli.js +199 -0
- package/config.mjs +118 -0
- package/context.mjs +160 -0
- package/distill.mjs +178 -0
- package/executor.mjs +207 -0
- package/mcp.mjs +209 -0
- package/memory.mjs +218 -0
- package/metaTools.mjs +523 -0
- package/package.json +26 -0
- package/provider.mjs +145 -0
- package/session.mjs +114 -0
- package/skills.mjs +147 -0
- package/tools/bash.mjs +41 -0
- package/tools/delete.mjs +57 -0
- package/tools/edit.mjs +71 -0
- package/tools/fetch.mjs +55 -0
- package/tools/glob.mjs +83 -0
- package/tools/grep.mjs +58 -0
- package/tools/index.mjs +41 -0
- package/tools/ls.mjs +62 -0
- package/tools/read.mjs +50 -0
- package/tools/websearch.mjs +58 -0
- package/tools/write.mjs +54 -0
- package/tools.mjs +15 -0
- package/tui.mjs +437 -0
package/tools/ls.mjs
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ls tool — list directory contents with type, size, and modification time.
|
|
3
|
+
*/
|
|
4
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
5
|
+
import { resolve, relative } from 'node:path';
|
|
6
|
+
|
|
7
|
+
export const lsTool = {
|
|
8
|
+
name: 'ls',
|
|
9
|
+
description:
|
|
10
|
+
'List directory contents with type, size, and modification time. Directories listed first.',
|
|
11
|
+
parameters: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
path: { type: 'string', description: 'Directory path (default cwd)' },
|
|
15
|
+
},
|
|
16
|
+
required: [],
|
|
17
|
+
},
|
|
18
|
+
readonly: true,
|
|
19
|
+
parallel: true,
|
|
20
|
+
|
|
21
|
+
async execute(args, agent) {
|
|
22
|
+
const cwd = agent?.cwd || process.cwd();
|
|
23
|
+
const dir = args.path ? resolve(cwd, args.path) : cwd;
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
27
|
+
const results = [];
|
|
28
|
+
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const fullPath = resolve(dir, entry.name);
|
|
31
|
+
let info;
|
|
32
|
+
try {
|
|
33
|
+
const st = statSync(fullPath);
|
|
34
|
+
info = {
|
|
35
|
+
name: entry.name + (entry.isDirectory() ? '/' : ''),
|
|
36
|
+
size: st.size,
|
|
37
|
+
mtime: st.mtime.toISOString().slice(0, 19),
|
|
38
|
+
};
|
|
39
|
+
} catch {
|
|
40
|
+
info = { name: entry.name, size: 0, mtime: '???' };
|
|
41
|
+
}
|
|
42
|
+
info.isDir = entry.isDirectory();
|
|
43
|
+
results.push(info);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Sort: directories first, then alphabetically
|
|
47
|
+
results.sort((a, b) => {
|
|
48
|
+
if (a.isDir && !b.isDir) return -1;
|
|
49
|
+
if (!a.isDir && b.isDir) return 1;
|
|
50
|
+
return a.name.localeCompare(b.name);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const lines = results.map(
|
|
54
|
+
(r) => `${r.isDir ? 'd' : '-'} ${String(r.size).padStart(8)} ${r.mtime} ${r.name}`,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return `Directory: ${dir}\n${lines.join('\n') || '(empty)'}`;
|
|
58
|
+
} catch (err) {
|
|
59
|
+
return `Error listing directory: ${err.message}`;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
package/tools/read.mjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* read tool — read a text file with optional offset/limit, returning numbered lines.
|
|
3
|
+
*/
|
|
4
|
+
import { readFileSync, existsSync, statSync } from 'node:fs';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
|
|
7
|
+
export const readTool = {
|
|
8
|
+
name: 'read',
|
|
9
|
+
description:
|
|
10
|
+
'Read a text file. Returns numbered lines. Use offset/limit to page large files.',
|
|
11
|
+
parameters: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
path: { type: 'string', description: 'File path, relative to cwd or absolute' },
|
|
15
|
+
offset: { type: 'number', description: '1-based line number to start from' },
|
|
16
|
+
limit: { type: 'number', description: 'Max lines to return (default 2000)' },
|
|
17
|
+
},
|
|
18
|
+
required: ['path'],
|
|
19
|
+
},
|
|
20
|
+
readonly: true,
|
|
21
|
+
parallel: true,
|
|
22
|
+
|
|
23
|
+
async execute(args, agent) {
|
|
24
|
+
const cwd = agent?.cwd || process.cwd();
|
|
25
|
+
const abs = resolve(cwd, args.path);
|
|
26
|
+
|
|
27
|
+
if (!existsSync(abs)) {
|
|
28
|
+
return `Error: file not found: ${args.path}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const st = statSync(abs);
|
|
32
|
+
if (st.isDirectory()) {
|
|
33
|
+
return `Error: "${args.path}" is a directory, not a file.`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const content = readFileSync(abs, 'utf-8');
|
|
38
|
+
const lines = content.split('\n');
|
|
39
|
+
const start = (args.offset || 1) - 1;
|
|
40
|
+
const end = args.limit ? start + args.limit : start + 2000;
|
|
41
|
+
const slice = lines.slice(Math.max(0, start), end);
|
|
42
|
+
|
|
43
|
+
return slice
|
|
44
|
+
.map((line, i) => `${String(start + i + 1).padStart(4, ' ')}| ${line}`)
|
|
45
|
+
.join('\n');
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return `Error reading file: ${err.message}`;
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* websearch tool — search the web (Bing).
|
|
3
|
+
*/
|
|
4
|
+
export const websearchTool = {
|
|
5
|
+
name: 'websearch',
|
|
6
|
+
description:
|
|
7
|
+
'Search the web (Bing). Returns result titles, URLs, and snippets.',
|
|
8
|
+
parameters: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
query: { type: 'string', description: 'Search query' },
|
|
12
|
+
limit: { type: 'number', description: 'Max results (default 8)' },
|
|
13
|
+
},
|
|
14
|
+
required: ['query'],
|
|
15
|
+
},
|
|
16
|
+
readonly: true,
|
|
17
|
+
parallel: true,
|
|
18
|
+
|
|
19
|
+
async execute(args, _agent) {
|
|
20
|
+
try {
|
|
21
|
+
const url = `https://www.bing.com/search?q=${encodeURIComponent(args.query)}`;
|
|
22
|
+
const response = await fetch(url, {
|
|
23
|
+
headers: {
|
|
24
|
+
'User-Agent': 'Mozilla/5.0 (compatible; junecode/1.0)',
|
|
25
|
+
'Accept': 'text/html',
|
|
26
|
+
},
|
|
27
|
+
signal: AbortSignal.timeout(15_000),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
return `Error: HTTP ${response.status}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const html = await response.text();
|
|
35
|
+
|
|
36
|
+
// Parse Bing results (simple regex extraction)
|
|
37
|
+
const results = [];
|
|
38
|
+
const snippetRegex = /<li class="b_algo"[^>]*>[\s\S]*?<h2[^>]*><a[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?<p[^>]*>([\s\S]*?)<\/p>/gi;
|
|
39
|
+
let match;
|
|
40
|
+
|
|
41
|
+
while ((match = snippetRegex.exec(html)) !== null && results.length < (args.limit || 8)) {
|
|
42
|
+
const title = match[2].replace(/<[^>]+>/g, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').trim();
|
|
43
|
+
const snippet = match[3].replace(/<[^>]+>/g, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').trim();
|
|
44
|
+
if (title && snippet) {
|
|
45
|
+
results.push({ title, url: match[1], snippet });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (results.length === 0) return '(no search results found)';
|
|
50
|
+
|
|
51
|
+
return results
|
|
52
|
+
.map((r) => `${r.title}\n ${r.url}\n ${r.snippet}`)
|
|
53
|
+
.join('\n\n');
|
|
54
|
+
} catch (err) {
|
|
55
|
+
return `Error searching web: ${err.message}`;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
};
|
package/tools/write.mjs
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* write tool — write content to a file, creating parent directories as needed.
|
|
3
|
+
*
|
|
4
|
+
* Uses execSync (cat > file) for disk writes to avoid virtual-filesystem isolation
|
|
5
|
+
* that can cause writes to be lost when the agent session ends.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
8
|
+
import { resolve, dirname } from 'node:path';
|
|
9
|
+
import { execSync } from 'node:child_process';
|
|
10
|
+
|
|
11
|
+
/** Escape a path for safe use in a single-quoted shell string. */
|
|
12
|
+
function shellEscape(str) {
|
|
13
|
+
return "'" + str.replace(/'/g, "'\\''") + "'";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const writeTool = {
|
|
17
|
+
name: 'write',
|
|
18
|
+
description:
|
|
19
|
+
'Write content to a file. Creates parent directories; overwrites existing file.',
|
|
20
|
+
parameters: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
path: { type: 'string', description: 'File path, relative to cwd or absolute' },
|
|
24
|
+
content: { type: 'string', description: 'Full content to write' },
|
|
25
|
+
},
|
|
26
|
+
required: ['path', 'content'],
|
|
27
|
+
},
|
|
28
|
+
readonly: false,
|
|
29
|
+
parallel: false,
|
|
30
|
+
|
|
31
|
+
async execute(args, agent) {
|
|
32
|
+
const cwd = agent?.cwd || process.cwd();
|
|
33
|
+
const abs = resolve(cwd, args.path);
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
mkdirSync(dirname(abs), { recursive: true });
|
|
37
|
+
execSync(`cat > ${shellEscape(abs)}`, {
|
|
38
|
+
cwd,
|
|
39
|
+
input: args.content,
|
|
40
|
+
encoding: 'utf-8',
|
|
41
|
+
timeout: 10_000,
|
|
42
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
43
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
44
|
+
});
|
|
45
|
+
// Verify the write actually landed
|
|
46
|
+
if (existsSync(abs)) {
|
|
47
|
+
return `Wrote ${args.content.length} chars to ${args.path}`;
|
|
48
|
+
}
|
|
49
|
+
return `Error writing file: write command completed but file not found at ${args.path}`;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return `Error writing file: ${err.message}`;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
package/tools.mjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools index — schema conversion + base tool collection.
|
|
3
|
+
* Each tool lives in tools/*.mjs with its real implementation.
|
|
4
|
+
*/
|
|
5
|
+
export { readTool } from './tools/read.mjs';
|
|
6
|
+
export { writeTool } from './tools/write.mjs';
|
|
7
|
+
export { editTool } from './tools/edit.mjs';
|
|
8
|
+
export { bashTool } from './tools/bash.mjs';
|
|
9
|
+
export { globTool } from './tools/glob.mjs';
|
|
10
|
+
export { grepTool } from './tools/grep.mjs';
|
|
11
|
+
export { lsTool } from './tools/ls.mjs';
|
|
12
|
+
export { fetchTool } from './tools/fetch.mjs';
|
|
13
|
+
export { websearchTool } from './tools/websearch.mjs';
|
|
14
|
+
export { deleteTool } from './tools/delete.mjs';
|
|
15
|
+
export { toOpenAISchema, baseTools } from './tools/index.mjs';
|