Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@avcodes/mi

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@avcodes/mi - npm Package Compare versions

Comparing version
1.0.7
to
1.0.9
+16
-18
index.mjs

@@ -8,3 +8,3 @@ #!/usr/bin/env node

*/
import { createInterface } from 'readline'; import { spawn } from 'child_process'; import { readFileSync, writeFileSync, existsSync } from 'fs'; import { homedir } from 'os'; if (!process.env.OPENAI_API_KEY && !process.argv.includes('-h')) { console.error('OPENAI_API_KEY required'); process.exit(1); }
import { createInterface } from 'readline'; import { spawn } from 'child_process'; import { readFileSync, writeFileSync, existsSync, readdirSync } from 'fs'; import { homedir } from 'os'; const DIR = new URL('.', import.meta.url).pathname; if (!process.env.OPENAI_API_KEY && !process.argv.includes('-h')) { console.error('OPENAI_API_KEY required'); process.exit(1); }

@@ -15,3 +15,3 @@ /* Tools the agent can invoke. */

/* Run a command in a detached bash shell; resolve with combined output. */
bash: ({command}) => new Promise(resolve => { const child = spawn('bash', ['-c', command], { stdio: ['ignore', 'pipe', 'pipe'], detached: true });
bash: ({command,timeout,bg}) => { if (bg) { const log=`/tmp/mi-${Date.now()}.log`; const c=spawn('bash',['-c',`${command} >${log} 2>&1`],{stdio:'ignore',detached:true}); c.unref(); return `pid:${c.pid} log:${log}`; } return new Promise(resolve => { const child = spawn('bash', ['-c', command], { stdio: ['ignore', 'pipe', 'pipe'], detached: true });

@@ -22,6 +22,6 @@ /* Collect stdout and stderr into a single string. */

/* Kill the process group on SIGINT; remove the listener on exit. */
const cleanup = () => { try { process.kill(-child.pid) } catch (err) {} }; process.on('SIGINT', cleanup);
const cleanup = () => { try { process.kill(-child.pid) } catch (err) {} }; process.on('SIGINT', cleanup); const timer = timeout ? setTimeout(() => { cleanup(); resolve(output+'\n[timeout]') }, +timeout) : null;
/* Resolve with collected output once the child process exits. */
child.on('exit', () => { process.off('SIGINT', cleanup); resolve(output); }); }),
child.on('exit', () => { process.off('SIGINT', cleanup); if (timer) clearTimeout(timer); resolve(output); }); }); },

@@ -34,9 +34,10 @@ /* Read a file as UTF-8. */

/* Load a skill's SKILL.md from ~/.agents/skills/<name>/. */
skill: ({name}) => readFileSync(`${process.env.HOME || homedir()}/.agents/skills/${name}/SKILL.md`, 'utf8')
/* Load a skill's SKILL.md by name, or list available skills as `- name: description` bullets parsed from YAML frontmatter. */
skill: ({name}) => name ? loadSkill(name) : listSkills().join('\n'),
}; const makeParams = (...keys) => ({ type: 'object', properties: Object.fromEntries(keys.map(key => [key, { type: 'string' }])), required: keys });
}; const meta = s => ({ name: s.match(/^name:\s*(.+)$/m)?.[1], description: s.match(/^description:\s*(.+)$/m)?.[1] || '' }), skillDirs = () => [`${DIR}skills/`, `${process.env.HOME || homedir()}/.agents/skills/`];
const listSkills = () => skillDirs().flatMap(dir => existsSync(dir) ? readdirSync(dir).filter(d => existsSync(dir+d+'/SKILL.md')).map(d => { const {name,description} = meta(readFileSync(dir+d+'/SKILL.md','utf8')); return `- ${name||d}: ${description}`; }) : []), loadSkill = n => { for (const d of skillDirs()) if (existsSync(d+n+'/SKILL.md')) return readFileSync(d+n+'/SKILL.md','utf8'); }, makeParams = (...keys) => ({ type: 'object', properties: Object.fromEntries(keys.map(k => [k.replace('?',''), { type: 'string' }])), required: keys.filter(k => !k.startsWith('?')) });
/* Tool definitions formatted for the OpenAI API. */
const toolsDef = [{ name: 'bash', description: 'run bash cmd', parameters: makeParams('command') }, { name: 'read', description: 'read a file', parameters: makeParams('path') }, { name: 'write', description: 'write a file', parameters: makeParams('path', 'content') }, { name: 'skill', description: 'load skill', parameters: makeParams('name') }].map(func => ({ type: 'function', function: func }));
const toolsDef = [{ name: 'bash', description: 'run bash cmd; timeout=ms kills after delay, bg=truthy runs detached returning pid+log', parameters: makeParams('command', '?timeout', '?bg') }, { name: 'read', description: 'read a file', parameters: makeParams('path') }, { name: 'write', description: 'write a file', parameters: makeParams('path', 'content') }, { name: 'skill', description: 'load a skill\'s SKILL.md body by name', parameters: makeParams('?name') }].map(func => ({ type: 'function', function: func }));

@@ -50,10 +51,7 @@ /*

/* POST to the completions endpoint; parse the JSON response. */
const response = await fetch(`${(process.env.OPENAI_BASE_URL || 'https://api.openai.com').replace(/\/+$/, '')}/v1/chat/completions`, { method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.OPENAI_API_KEY}` }, body: JSON.stringify({ model: process.env.MODEL || 'gpt-5.4', messages, tools: toolsDef }) }).then(res => res.json());
const response = await fetch(`${(process.env.OPENAI_BASE_URL || 'https://api.openai.com').replace(/\/+$/, '')}/v1/chat/completions`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.OPENAI_API_KEY}` }, body: JSON.stringify({ model: process.env.MODEL || 'gpt-5.4', messages, tools: toolsDef }) }).then(res => res.json());
/* Throw on API error; return text content once no tool calls remain. */
if (response.error) throw new Error(response.error.message || JSON.stringify(response.error)); const message = response.choices?.[0]?.message; if (!message) throw new Error(JSON.stringify(response));
/* Throw on API error; push the message, return content once no tool calls remain. */
if (response.error) throw new Error(response.error.message || JSON.stringify(response.error)); const message = response.choices?.[0]?.message; if (!message) throw new Error(JSON.stringify(response)); messages.push(message); if (!message.tool_calls) return message.content;
messages.push(message); if (!message.tool_calls) return message.content;
for (const toolCall of message.tool_calls) {

@@ -69,3 +67,3 @@ const {name} = toolCall.function, args = JSON.parse(toolCall.function.arguments), formatDim = str => `\x1b[90m${str}\x1b[0m`;

/* System prompt: built-in instructions plus current directory and date. */
const SYSTEM = (process.env.SYSTEM_PROMPT || 'You are an autonomous agent. Prefer action over speculation—use tools to answer questions and complete tasks.\nbash runs any shell command: curl/wget for HTTP, git, package managers, compilers, anything available on the system.\nread/write operate on local files. Always read before editing; write complete files.\nApproach: explore, plan, act one step at a time, verify. Be concise.') + `\nCWD: ${process.cwd()}\nDate: ${new Date().toISOString()}`;
const SYSTEM = (process.env.SYSTEM_PROMPT || 'You are an autonomous agent. Prefer action over speculation—use tools to answer questions and complete tasks.\nA skill is a SKILL.md file containing a procedure for a particular kind of task, paired with a one-line description of when it applies. The skill tool loads a skill\'s body by name.\nWhen handling a request, compare it against each skill description listed below. If a description covers what the user is asking for, call skill(name) to load that skill and follow its body as your plan.\nbash runs any shell command: curl/wget for HTTP, git, package managers, compilers, anything available on the system.\nread/write operate on local files. Always read before editing; write complete files.\nApproach: explore, plan, act one step at a time, verify. Be concise.') + `\nCWD: ${process.cwd()}\nDate: ${new Date().toISOString()}`;

@@ -75,6 +73,6 @@ /* History seeded with the system prompt; getArg reads a named CLI flag. */

if (process.argv.includes('-h')) { console.log('usage: mi [-p prompt] [-f file] [-h]\n pipe: echo "..." | mi repl: /reset clears history\nenv: OPENAI_API_KEY, MODEL, OPENAI_BASE_URL, SYSTEM_PROMPT'); process.exit(0); }
if (process.argv.includes('-h')) { console.log('usage: mi [-p prompt] [-f file] [-h]\n pipe: echo "..." | mi repl: /reset clears history\nenv: OPENAI_API_KEY, MODEL, OPENAI_BASE_URL, SYSTEM_PROMPT\nbash tool args: timeout=<ms> kills after delay · bg=truthy detaches and returns pid+log'); process.exit(0); }
/* Prepend -f file and AGENTS.md (if present) to the system message. */
const fileArg = getArg('-f'); if (fileArg) history[0].content += `\n\nFile (${fileArg}):\n` + readFileSync(fileArg, 'utf8'); if (existsSync('AGENTS.md')) history[0].content += '\n' + readFileSync('AGENTS.md', 'utf8');
/* Prepend -f file, AGENTS.md, and the skills index (if present) to the system message. */
const fileArg = getArg('-f'); if (fileArg) history[0].content += `\n\nFile (${fileArg}):\n` + readFileSync(fileArg, 'utf8'); if (existsSync('AGENTS.md')) history[0].content += '\n' + readFileSync('AGENTS.md', 'utf8'); const sl = listSkills(); if (sl.length) history[0].content += '\n\nSkill descriptions:\n' + sl.join('\n');

@@ -81,0 +79,0 @@ if (getArg('-p')) { history.push({ role: 'user', content: getArg('-p') }); console.log(await run(history)); process.exit(0); }

{
"name": "@avcodes/mi",
"version": "1.0.7",
"description": "agentic coding in 30 loc. a loop, three tools, and an llm.",
"version": "1.0.9",
"description": "agentic coding in 30 loc. a loop, four tools, and an llm.",
"type": "module",

@@ -6,0 +6,0 @@ "bin": {

@@ -9,4 +9,4 @@ ![Splash image](./assets/splash.png)

- `bash`, `read`, `write`, and `skill` tools
- `skill` tool loads agent skills from `~/.agents/skills/`
- `bash` (optional `timeout=<ms>` kills after delay, `bg=truthy` detaches and returns pid+log), `read`, `write`, and `skill` tools
- `skill` tool loads `SKILL.md` playbooks from `~/.agents/skills/` (descriptions auto-advertised in system prompt so the model matches tasks to skills)
- accepts stdin via pipes (e.g. `echo "do this" | mi`)

@@ -68,3 +68,3 @@ - file context ingestion via `-f <file>` argument

const tools = {
bash: ({ command }) => execShell(command), // run any shell command
bash: ({ command, timeout, bg }) => execShell(command, timeout, bg), // run any shell command
read: ({ path }) => readFileSync(path, 'utf8'), // read a file

@@ -76,3 +76,3 @@ write: ({ path, content }) => (writeFileSync(path, content), 'ok'), // write a file

`bash` gives the agent access to the entire system: git, curl, compilers, package managers. `read` and `write` handle files. `skill` gives the agent specialized workflows. every tool returns a string because that's what goes back into the conversation.
`bash` gives the agent access to the entire system: git, curl, compilers, package managers. optional `timeout=<ms>` kills the process after the given delay and resolves with `[timeout]`. optional `bg=truthy` runs the command detached and returns `pid:X log:/tmp/mi-*.log` immediately. `read` and `write` handle files. `skill` gives the agent specialized workflows. every tool returns a string because that's what goes back into the conversation.

@@ -79,0 +79,0 @@ ### tool definitions