myuru 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/LICENSE +21 -0
- package/README.md +105 -0
- package/bin/myuru.js +59 -0
- package/package.json +37 -0
- package/src/commands/council.js +168 -0
- package/src/commands/init.js +67 -0
- package/src/commands/run.js +179 -0
- package/src/commands/status.js +49 -0
- package/src/index.js +18 -0
- package/src/lib/agent-runner.js +153 -0
- package/src/lib/council-server.js +193 -0
- package/src/lib/dashboard.js +111 -0
- package/src/lib/task-db.js +218 -0
- package/src/lib/tiers.js +76 -0
- package/src/providers/claude.js +49 -0
- package/src/providers/index.js +17 -0
- package/src/providers/openai.js +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wittlesus
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# MyUru
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/myuru)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Multi-provider AI agent orchestrator. Coordinate Claude, GPT, and Gemini agents to build software in parallel.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Multi-Provider**: Claude, OpenAI, Gemini
|
|
11
|
+
- **Council Mode**: Agents deliberate in a chatroom, then execute tasks together
|
|
12
|
+
- **Session Persistence**: Agents maintain context across invocations
|
|
13
|
+
- **Terminal Dashboard**: Real-time ANSI UI showing agent status
|
|
14
|
+
- **Tier System**: Free (2 sequential agents) / Pro (5 concurrent + config packs, $29 one-time)
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g myuru
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
Initialize a project:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
myuru init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Run agents on a task:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
myuru run --task "Build a login page with JWT auth"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Output:
|
|
37
|
+
```
|
|
38
|
+
[Agent Architect] Designing authentication system...
|
|
39
|
+
[Agent Frontend] Building React login form...
|
|
40
|
+
[Agent Security] Implementing JWT validation...
|
|
41
|
+
|
|
42
|
+
Tasks completed: 3/3 | Status: SUCCESS
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Start a council discussion:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
myuru council \
|
|
49
|
+
--topic "Design auth system" \
|
|
50
|
+
--agents "Architect,Security,Frontend"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Check task progress:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
myuru status
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Commands
|
|
60
|
+
|
|
61
|
+
| Command | Description |
|
|
62
|
+
|---------|-------------|
|
|
63
|
+
| `myuru init` | Initialize project with config |
|
|
64
|
+
| `myuru run --task "..."` | Run agents on a task |
|
|
65
|
+
| `myuru council --topic "..." --agents "..."` | Start council deliberation |
|
|
66
|
+
| `myuru status` | View task progress |
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
Set environment variables:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
export OPENAI_API_KEY="sk-..."
|
|
74
|
+
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
75
|
+
export GOOGLE_API_KEY="..."
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Create `.myuru.json`:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"tier": "free",
|
|
83
|
+
"providers": ["claude", "openai"],
|
|
84
|
+
"maxConcurrent": 2
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Pricing
|
|
89
|
+
|
|
90
|
+
**Free**: 2 sequential agents, basic reporting
|
|
91
|
+
**Pro**: 5 concurrent agents, config packs, priority support — $29 one-time
|
|
92
|
+
|
|
93
|
+
## Requirements
|
|
94
|
+
|
|
95
|
+
- Node.js >= 18
|
|
96
|
+
- Claude CLI (for Claude provider)
|
|
97
|
+
- API keys for your chosen providers
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT - See LICENSE file
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
Built by Wittlesus | [GitHub](https://github.com/Wittlesus/myuru)
|
package/bin/myuru.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const pkg = require('../package.json');
|
|
6
|
+
|
|
7
|
+
const program = new Command();
|
|
8
|
+
|
|
9
|
+
program
|
|
10
|
+
.name('myuru')
|
|
11
|
+
.description('Multi-provider AI agent orchestrator')
|
|
12
|
+
.version(pkg.version);
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.command('init')
|
|
16
|
+
.description('Initialize a MyUru project in the current directory')
|
|
17
|
+
.option('--template <name>', 'Config pack template to use', 'default')
|
|
18
|
+
.action(async (opts) => {
|
|
19
|
+
const { init } = require('../src/commands/init');
|
|
20
|
+
await init(opts);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
program
|
|
24
|
+
.command('run')
|
|
25
|
+
.description('Run the orchestrator on the current project')
|
|
26
|
+
.option('-t, --task <description>', 'Single task to execute')
|
|
27
|
+
.option('-f, --file <path>', 'Task file (myuru.config.mjs or JSON)')
|
|
28
|
+
.option('--provider <name>', 'Single provider: claude, openai, gemini', 'claude')
|
|
29
|
+
.option('--model <name>', 'Model override')
|
|
30
|
+
.option('--agents <n>', 'Number of builder agents', '2')
|
|
31
|
+
.option('--pro', 'Enable Pro tier (concurrent execution, more agents)')
|
|
32
|
+
.option('--budget <usd>', 'Max budget in USD')
|
|
33
|
+
.option('--dry-run', 'Show what would run without executing')
|
|
34
|
+
.action(async (opts) => {
|
|
35
|
+
const { run } = require('../src/commands/run');
|
|
36
|
+
await run(opts);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
program
|
|
40
|
+
.command('status')
|
|
41
|
+
.description('Show task progress and agent status')
|
|
42
|
+
.action(async () => {
|
|
43
|
+
const { status } = require('../src/commands/status');
|
|
44
|
+
await status();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
program
|
|
48
|
+
.command('council')
|
|
49
|
+
.description('Start a council session — agents deliberate, then execute')
|
|
50
|
+
.option('--topic <text>', 'Topic for council discussion')
|
|
51
|
+
.option('--agents <names>', 'Comma-separated agent roles', 'Architect,Reviewer,Tester')
|
|
52
|
+
.option('--rounds <n>', 'Max deliberation rounds', '3')
|
|
53
|
+
.option('--execute', 'Auto-execute tasks after deliberation')
|
|
54
|
+
.action(async (opts) => {
|
|
55
|
+
const { council } = require('../src/commands/council');
|
|
56
|
+
await council(opts);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "myuru",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Multi-provider AI agent orchestrator. Coordinate Claude, GPT, and Gemini agents to build software in parallel.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"myuru": "./bin/myuru.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node --test src/**/*.test.js",
|
|
11
|
+
"start": "node bin/myuru.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"ai",
|
|
15
|
+
"orchestrator",
|
|
16
|
+
"multi-agent",
|
|
17
|
+
"multi-provider",
|
|
18
|
+
"claude",
|
|
19
|
+
"openai",
|
|
20
|
+
"gemini",
|
|
21
|
+
"coding-agent",
|
|
22
|
+
"automation",
|
|
23
|
+
"cli"
|
|
24
|
+
],
|
|
25
|
+
"author": "Wittlesus",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/Wittlesus/myuru"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18.0.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"commander": "^12.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const { spawn } = require("child_process");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const http = require("http");
|
|
4
|
+
const CouncilServer = require("../lib/council-server");
|
|
5
|
+
const { TierManager } = require("../lib/tiers");
|
|
6
|
+
|
|
7
|
+
async function council(opts) {
|
|
8
|
+
const topic = opts.topic;
|
|
9
|
+
if (!topic) {
|
|
10
|
+
console.error("No topic specified. Use --topic \"...\"");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const agentNames = (opts.agents || "Architect,Reviewer,Tester").split(",").map(a => a.trim());
|
|
15
|
+
const maxRounds = parseInt(opts.rounds) || 3;
|
|
16
|
+
const shouldExecute = opts.execute || false;
|
|
17
|
+
|
|
18
|
+
// Tier check
|
|
19
|
+
const tier = new TierManager("FREE");
|
|
20
|
+
try {
|
|
21
|
+
tier.enforceLimit(agentNames.length);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error(err.message);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log("MyUru Council");
|
|
28
|
+
console.log("─".repeat(50));
|
|
29
|
+
console.log(`Topic: ${topic}`);
|
|
30
|
+
console.log(`Agents: ${agentNames.join(", ")}`);
|
|
31
|
+
console.log(`Rounds: ${maxRounds}`);
|
|
32
|
+
console.log("");
|
|
33
|
+
|
|
34
|
+
// Start council server
|
|
35
|
+
const server = new CouncilServer();
|
|
36
|
+
const port = await server.start();
|
|
37
|
+
console.log(`Council server on port ${port}`);
|
|
38
|
+
|
|
39
|
+
// Configure session
|
|
40
|
+
server.config.agents = agentNames;
|
|
41
|
+
server.config.maxRounds = maxRounds;
|
|
42
|
+
|
|
43
|
+
const base = `http://localhost:${port}`;
|
|
44
|
+
|
|
45
|
+
// Default role prompts
|
|
46
|
+
const rolePrompts = {
|
|
47
|
+
Architect: "Focus on technical feasibility. Challenge complexity. Push for simplicity.",
|
|
48
|
+
Reviewer: "Focus on code quality and edge cases. Challenge assumptions.",
|
|
49
|
+
Tester: "Focus on testability. What could break? Push for test coverage.",
|
|
50
|
+
Revenue: "Focus on monetization. Challenge anything without revenue path.",
|
|
51
|
+
Growth: "Focus on user acquisition. How do users find this?",
|
|
52
|
+
Security: "Focus on security implications. What attack surfaces exist?",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Spawn agents for deliberation
|
|
56
|
+
console.log("\n--- DELIBERATION ---\n");
|
|
57
|
+
|
|
58
|
+
const spawnAgent = (name) => {
|
|
59
|
+
const prompt = [
|
|
60
|
+
`You are "${name}" in a council discussion.`,
|
|
61
|
+
`TOPIC: ${topic}`,
|
|
62
|
+
`YOUR ROLE: ${name}. Stay in character. Be concise.`,
|
|
63
|
+
"CHATROOM PROTOCOL:",
|
|
64
|
+
`- Send message: curl -s -X POST ${base}/chat -H "Content-Type: application/json" -d '{"agent":"${name}","message":"YOUR MSG"}'`,
|
|
65
|
+
`- Read messages: curl -s "${base}/chat?since=-1"`,
|
|
66
|
+
`- Keep messages under 300 chars.`,
|
|
67
|
+
`- You have ${maxRounds} rounds. Each round: read, think, respond.`,
|
|
68
|
+
"- Reference other agents by name. Agree, disagree, challenge.",
|
|
69
|
+
"EFFICIENCY RULES:",
|
|
70
|
+
"- One message per round. No essays.",
|
|
71
|
+
"- Agree in 1 sentence + 1 new insight. Disagree in 2-3 sentences max.",
|
|
72
|
+
rolePrompts[name] || `You are ${name}. Contribute your perspective.`,
|
|
73
|
+
`START: Post opening position (2 sentences), then loop for ${maxRounds} rounds.`,
|
|
74
|
+
].join("\n");
|
|
75
|
+
|
|
76
|
+
console.log(` Spawning ${name}...`);
|
|
77
|
+
|
|
78
|
+
const env = { ...process.env };
|
|
79
|
+
delete env.CLAUDECODE;
|
|
80
|
+
|
|
81
|
+
// Intentional spawn — core product functionality
|
|
82
|
+
const child = spawn("claude", ["--model", "haiku", "-p"], {
|
|
83
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
84
|
+
env,
|
|
85
|
+
shell: true,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
child.stdin.write(prompt);
|
|
89
|
+
child.stdin.end();
|
|
90
|
+
|
|
91
|
+
let output = "";
|
|
92
|
+
child.stdout?.on("data", d => output += d.toString());
|
|
93
|
+
child.stderr?.on("data", d => output += d.toString());
|
|
94
|
+
|
|
95
|
+
return new Promise(resolve => {
|
|
96
|
+
child.on("close", code => {
|
|
97
|
+
console.log(` ${name} finished (exit ${code})`);
|
|
98
|
+
resolve({ name, code, output: output.substring(0, 500) });
|
|
99
|
+
});
|
|
100
|
+
child.on("error", () => {
|
|
101
|
+
resolve({ name, code: -1, output: "spawn error" });
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const results = await Promise.all(agentNames.map(spawnAgent));
|
|
107
|
+
|
|
108
|
+
// Get transcript
|
|
109
|
+
const transcript = server.messages
|
|
110
|
+
.map(m => `[R${m.round}] **${m.agent}**: ${m.message}`)
|
|
111
|
+
.join("\n\n");
|
|
112
|
+
|
|
113
|
+
console.log(`\nDeliberation complete: ${server.messages.length} messages across ${server.currentRound} rounds.\n`);
|
|
114
|
+
|
|
115
|
+
if (transcript) {
|
|
116
|
+
console.log("--- TRANSCRIPT ---\n");
|
|
117
|
+
console.log(transcript.substring(0, 3000));
|
|
118
|
+
console.log("");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Task extraction
|
|
122
|
+
if (shouldExecute && server.messages.length > 0) {
|
|
123
|
+
console.log("--- TASK EXTRACTION ---\n");
|
|
124
|
+
|
|
125
|
+
const extractPrompt = [
|
|
126
|
+
"Read this council debate and extract 3-6 concrete tasks.",
|
|
127
|
+
`Output a JSON array: [{id, title, description, assignee, priority}].`,
|
|
128
|
+
`Assignee must be one of: ${agentNames.join(", ")}.`,
|
|
129
|
+
"Only output the JSON array.",
|
|
130
|
+
"",
|
|
131
|
+
"TRANSCRIPT:",
|
|
132
|
+
transcript.substring(0, 4000),
|
|
133
|
+
].join("\n");
|
|
134
|
+
|
|
135
|
+
const env = { ...process.env };
|
|
136
|
+
delete env.CLAUDECODE;
|
|
137
|
+
|
|
138
|
+
const taskOutput = await new Promise(resolve => {
|
|
139
|
+
const child = spawn("claude", ["--model", "haiku", "-p"], {
|
|
140
|
+
stdio: ["pipe", "pipe", "pipe"], env, shell: true,
|
|
141
|
+
});
|
|
142
|
+
child.stdin.write(extractPrompt);
|
|
143
|
+
child.stdin.end();
|
|
144
|
+
let out = "";
|
|
145
|
+
child.stdout?.on("data", d => out += d.toString());
|
|
146
|
+
child.stderr?.on("data", d => out += d.toString());
|
|
147
|
+
child.on("close", () => resolve(out));
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
let tasks = [];
|
|
151
|
+
try {
|
|
152
|
+
const jsonMatch = taskOutput.match(/\[[\s\S]*\]/);
|
|
153
|
+
tasks = jsonMatch ? JSON.parse(jsonMatch[0]) : [];
|
|
154
|
+
} catch {
|
|
155
|
+
console.log("Failed to parse tasks from output.");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (tasks.length > 0) {
|
|
159
|
+
console.log(`Extracted ${tasks.length} tasks:`);
|
|
160
|
+
tasks.forEach(t => console.log(` [${t.assignee}] ${t.title}`));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
await server.stop();
|
|
165
|
+
console.log("\nCouncil session complete.");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = { council };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
async function init(opts) {
|
|
5
|
+
const cwd = process.cwd();
|
|
6
|
+
const configFile = path.join(cwd, "myuru.config.mjs");
|
|
7
|
+
const stateDir = path.join(cwd, ".myuru");
|
|
8
|
+
|
|
9
|
+
if (fs.existsSync(configFile)) {
|
|
10
|
+
console.log("myuru.config.mjs already exists in this directory.");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Create .myuru state directory
|
|
15
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
16
|
+
fs.writeFileSync(path.join(stateDir, ".gitignore"), "*\n");
|
|
17
|
+
|
|
18
|
+
// Write default config
|
|
19
|
+
const template = opts.template || "default";
|
|
20
|
+
const config = getTemplate(template);
|
|
21
|
+
fs.writeFileSync(configFile, config);
|
|
22
|
+
|
|
23
|
+
console.log("Initialized MyUru project:");
|
|
24
|
+
console.log(` ${configFile}`);
|
|
25
|
+
console.log(` ${stateDir}/`);
|
|
26
|
+
console.log("");
|
|
27
|
+
console.log("Next: myuru run --task \"Build a login page\"");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getTemplate(name) {
|
|
31
|
+
const templates = {
|
|
32
|
+
default: `// myuru.config.mjs — MyUru orchestrator configuration
|
|
33
|
+
export default {
|
|
34
|
+
// Provider: "claude", "openai", or "gemini" (coming soon)
|
|
35
|
+
provider: "claude",
|
|
36
|
+
|
|
37
|
+
// Model to use for execution
|
|
38
|
+
model: "sonnet",
|
|
39
|
+
|
|
40
|
+
// Number of builder agents
|
|
41
|
+
agents: 2,
|
|
42
|
+
|
|
43
|
+
// Agent roles and system prompts
|
|
44
|
+
roles: {
|
|
45
|
+
Builder: "You are a software engineer. Write clean, working code.",
|
|
46
|
+
Reviewer: "You review code for bugs, security issues, and quality.",
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// Budget limit per run (USD)
|
|
50
|
+
budget: 5,
|
|
51
|
+
|
|
52
|
+
// Max turns per agent invocation
|
|
53
|
+
maxTurns: 10,
|
|
54
|
+
};
|
|
55
|
+
`,
|
|
56
|
+
minimal: `export default {
|
|
57
|
+
provider: "claude",
|
|
58
|
+
model: "sonnet",
|
|
59
|
+
agents: 1,
|
|
60
|
+
};
|
|
61
|
+
`,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return templates[name] || templates.default;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = { init };
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { createProvider } = require("../providers");
|
|
4
|
+
const AgentRunner = require("../lib/agent-runner");
|
|
5
|
+
const TaskDB = require("../lib/task-db");
|
|
6
|
+
const Dashboard = require("../lib/dashboard");
|
|
7
|
+
const { TierManager } = require("../lib/tiers");
|
|
8
|
+
|
|
9
|
+
async function run(opts) {
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const stateDir = path.join(cwd, ".myuru");
|
|
12
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
13
|
+
|
|
14
|
+
// Load config
|
|
15
|
+
let config = {};
|
|
16
|
+
const configFile = path.join(cwd, "myuru.config.mjs");
|
|
17
|
+
if (fs.existsSync(configFile)) {
|
|
18
|
+
try {
|
|
19
|
+
config = (await import(`file://${configFile.replace(/\\/g, "/")}`)).default;
|
|
20
|
+
} catch {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// CLI overrides
|
|
24
|
+
const providerName = opts.provider || config.provider || "claude";
|
|
25
|
+
const model = opts.model || config.model || "sonnet";
|
|
26
|
+
const agentCount = parseInt(opts.agents) || config.agents || 2;
|
|
27
|
+
const budget = opts.budget || config.budget || "5";
|
|
28
|
+
const maxTurns = config.maxTurns || 10;
|
|
29
|
+
const isPro = opts.pro || false;
|
|
30
|
+
|
|
31
|
+
// Tier enforcement
|
|
32
|
+
const tier = new TierManager(isPro ? "PRO" : "FREE");
|
|
33
|
+
const tierInfo = tier.info();
|
|
34
|
+
|
|
35
|
+
console.log(`MyUru v0.1.0 | ${tierInfo.tier} tier | ${providerName}/${model}`);
|
|
36
|
+
console.log(`Agents: ${agentCount} | Execution: ${tierInfo.concurrency}`);
|
|
37
|
+
console.log("─".repeat(50));
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
tier.enforceLimit(agentCount, isPro);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
console.error(err.message);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Resolve task
|
|
47
|
+
let task = opts.task;
|
|
48
|
+
if (!task && opts.file) {
|
|
49
|
+
task = fs.readFileSync(opts.file, "utf-8");
|
|
50
|
+
}
|
|
51
|
+
if (!task) {
|
|
52
|
+
console.error("No task specified. Use --task \"...\" or --file <path>");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (opts.dryRun) {
|
|
57
|
+
console.log("\nDry run — would execute:");
|
|
58
|
+
console.log(` Task: ${task}`);
|
|
59
|
+
console.log(` Provider: ${providerName}/${model}`);
|
|
60
|
+
console.log(` Agents: ${agentCount}`);
|
|
61
|
+
console.log(` Budget: $${budget}`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Create provider and DB
|
|
66
|
+
const provider = createProvider(providerName);
|
|
67
|
+
const db = new TaskDB(stateDir);
|
|
68
|
+
|
|
69
|
+
// Create task in DB
|
|
70
|
+
const taskId = `task-${Date.now()}`;
|
|
71
|
+
db.createTask({
|
|
72
|
+
id: taskId,
|
|
73
|
+
title: task.substring(0, 100),
|
|
74
|
+
description: task,
|
|
75
|
+
priority: 1,
|
|
76
|
+
createdBy: "user",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Create agents
|
|
80
|
+
const agents = [];
|
|
81
|
+
const roles = config.roles || { Builder: "You are a software engineer. Write clean, working code." };
|
|
82
|
+
const roleNames = Object.keys(roles).slice(0, agentCount);
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < agentCount; i++) {
|
|
85
|
+
const roleName = roleNames[i % roleNames.length];
|
|
86
|
+
agents.push(new AgentRunner({
|
|
87
|
+
id: `${roleName}-${i}`,
|
|
88
|
+
model,
|
|
89
|
+
provider,
|
|
90
|
+
systemPrompt: roles[roleName] || `You are ${roleName}. Complete the task efficiently.`,
|
|
91
|
+
maxTurns,
|
|
92
|
+
budgetUsd: budget,
|
|
93
|
+
cwd,
|
|
94
|
+
stateless: true,
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Dashboard
|
|
99
|
+
const dashboard = new Dashboard();
|
|
100
|
+
const startTime = Date.now();
|
|
101
|
+
|
|
102
|
+
dashboard.start(() => {
|
|
103
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(0);
|
|
104
|
+
const running = agents.filter(a => a.busy).length;
|
|
105
|
+
const done = agents.filter(a => a.invocationCount > 0 && !a.busy).length;
|
|
106
|
+
return [
|
|
107
|
+
`\x1b[36mMyUru\x1b[0m | ${providerName}/${model} | ${tierInfo.tier}`,
|
|
108
|
+
`Task: ${task.substring(0, 60)}`,
|
|
109
|
+
`Agents: ${running} running, ${done} done | ${elapsed}s elapsed`,
|
|
110
|
+
"─".repeat(dashboard.cols),
|
|
111
|
+
];
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Execute
|
|
115
|
+
console.log(`\nExecuting with ${agentCount} agent(s)...\n`);
|
|
116
|
+
|
|
117
|
+
const runAgent = async (agent, idx) => {
|
|
118
|
+
const prompt = [
|
|
119
|
+
`PROJECT DIRECTORY: ${cwd}`,
|
|
120
|
+
`TASK: ${task}`,
|
|
121
|
+
"",
|
|
122
|
+
`You are agent ${idx + 1} of ${agentCount}.`,
|
|
123
|
+
agentCount > 1 ? `Focus on your part. Agent roles: ${roleNames.join(", ")}.` : "",
|
|
124
|
+
"",
|
|
125
|
+
"Complete the task. Be efficient. Ship working code.",
|
|
126
|
+
].filter(Boolean).join("\n");
|
|
127
|
+
|
|
128
|
+
db.assignTask(taskId, agent.id);
|
|
129
|
+
db.startTask(taskId, agent.id);
|
|
130
|
+
dashboard.log(`[${agent.id}] Starting...`);
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const output = await agent.invoke(prompt, taskId);
|
|
134
|
+
dashboard.log(`[${agent.id}] Done (${output.length} chars)`);
|
|
135
|
+
return { agent: agent.id, success: true, output };
|
|
136
|
+
} catch (err) {
|
|
137
|
+
dashboard.log(`[${agent.id}] Failed: ${err.message}`);
|
|
138
|
+
return { agent: agent.id, success: false, error: err.message };
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
let results;
|
|
143
|
+
if (isPro && agentCount > 1) {
|
|
144
|
+
// Concurrent execution (PRO)
|
|
145
|
+
results = await Promise.all(agents.map((a, i) => runAgent(a, i)));
|
|
146
|
+
} else {
|
|
147
|
+
// Sequential execution (FREE)
|
|
148
|
+
results = [];
|
|
149
|
+
for (let i = 0; i < agents.length; i++) {
|
|
150
|
+
results.push(await runAgent(agents[i], i));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
dashboard.stop();
|
|
155
|
+
|
|
156
|
+
// Summary
|
|
157
|
+
const successes = results.filter(r => r.success).length;
|
|
158
|
+
const failures = results.filter(r => !r.success).length;
|
|
159
|
+
|
|
160
|
+
if (successes > 0) {
|
|
161
|
+
db.completeTask(taskId, "orchestrator", `${successes} agents completed`);
|
|
162
|
+
} else {
|
|
163
|
+
db.failTask(taskId, "orchestrator", `All ${failures} agents failed`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
console.log("\nResults:");
|
|
167
|
+
console.log("─".repeat(50));
|
|
168
|
+
for (const r of results) {
|
|
169
|
+
const icon = r.success ? "OK" : "XX";
|
|
170
|
+
console.log(` [${icon}] ${r.agent}`);
|
|
171
|
+
if (r.output) console.log(` ${r.output.substring(0, 200)}`);
|
|
172
|
+
if (r.error) console.log(` Error: ${r.error}`);
|
|
173
|
+
}
|
|
174
|
+
console.log(`\n${successes} succeeded, ${failures} failed.`);
|
|
175
|
+
|
|
176
|
+
db.close();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = { run };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const TaskDB = require("../lib/task-db");
|
|
4
|
+
|
|
5
|
+
async function status() {
|
|
6
|
+
const stateDir = path.join(process.cwd(), ".myuru");
|
|
7
|
+
|
|
8
|
+
if (!fs.existsSync(stateDir)) {
|
|
9
|
+
console.log("No MyUru project found. Run: myuru init");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const db = new TaskDB(stateDir);
|
|
14
|
+
const tasks = db.getAllTasks();
|
|
15
|
+
const stats = db.getTaskStats();
|
|
16
|
+
|
|
17
|
+
if (tasks.length === 0) {
|
|
18
|
+
console.log("No tasks yet. Run: myuru run --task \"...\"");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log("MyUru Task Status");
|
|
23
|
+
console.log("─".repeat(60));
|
|
24
|
+
|
|
25
|
+
const statusIcons = {
|
|
26
|
+
pending: " ",
|
|
27
|
+
assigned: ">>",
|
|
28
|
+
in_progress: "**",
|
|
29
|
+
done: "OK",
|
|
30
|
+
failed: "XX",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
for (const task of tasks) {
|
|
34
|
+
const icon = statusIcons[task.status] || "??";
|
|
35
|
+
const agent = task.assigned_to ? ` (${task.assigned_to})` : "";
|
|
36
|
+
console.log(` [${icon}] ${task.title}${agent}`);
|
|
37
|
+
if (task.result) {
|
|
38
|
+
console.log(` -> ${task.result.substring(0, 80)}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log("");
|
|
43
|
+
console.log("Summary:");
|
|
44
|
+
for (const s of stats) {
|
|
45
|
+
console.log(` ${s.status}: ${s.count}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { status };
|