@yemi33/minions 0.1.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +819 -0
  2. package/LICENSE +21 -0
  3. package/README.md +598 -0
  4. package/agents/dallas/charter.md +56 -0
  5. package/agents/lambert/charter.md +67 -0
  6. package/agents/ralph/charter.md +45 -0
  7. package/agents/rebecca/charter.md +57 -0
  8. package/agents/ripley/charter.md +47 -0
  9. package/bin/minions.js +467 -0
  10. package/config.template.json +28 -0
  11. package/dashboard.html +4822 -0
  12. package/dashboard.js +2623 -0
  13. package/docs/auto-discovery.md +416 -0
  14. package/docs/blog-first-successful-dispatch.md +128 -0
  15. package/docs/command-center.md +156 -0
  16. package/docs/demo/01-dashboard-overview.gif +0 -0
  17. package/docs/demo/02-command-center.gif +0 -0
  18. package/docs/demo/03-work-items.gif +0 -0
  19. package/docs/demo/04-plan-docchat.gif +0 -0
  20. package/docs/demo/05-prd-progress.gif +0 -0
  21. package/docs/demo/06-inbox-metrics.gif +0 -0
  22. package/docs/deprecated.json +83 -0
  23. package/docs/distribution.md +96 -0
  24. package/docs/engine-restart.md +92 -0
  25. package/docs/human-vs-automated.md +108 -0
  26. package/docs/index.html +221 -0
  27. package/docs/plan-lifecycle.md +140 -0
  28. package/docs/self-improvement.md +344 -0
  29. package/engine/ado-mcp-wrapper.js +42 -0
  30. package/engine/ado.js +383 -0
  31. package/engine/check-status.js +23 -0
  32. package/engine/cli.js +754 -0
  33. package/engine/consolidation.js +417 -0
  34. package/engine/github.js +331 -0
  35. package/engine/lifecycle.js +1113 -0
  36. package/engine/llm.js +116 -0
  37. package/engine/queries.js +677 -0
  38. package/engine/shared.js +397 -0
  39. package/engine/spawn-agent.js +151 -0
  40. package/engine.js +3227 -0
  41. package/minions.js +556 -0
  42. package/package.json +48 -0
  43. package/playbooks/ask.md +49 -0
  44. package/playbooks/build-and-test.md +155 -0
  45. package/playbooks/explore.md +64 -0
  46. package/playbooks/fix.md +57 -0
  47. package/playbooks/implement-shared.md +68 -0
  48. package/playbooks/implement.md +95 -0
  49. package/playbooks/plan-to-prd.md +104 -0
  50. package/playbooks/plan.md +99 -0
  51. package/playbooks/review.md +68 -0
  52. package/playbooks/test.md +75 -0
  53. package/playbooks/verify.md +190 -0
  54. package/playbooks/work-item.md +74 -0
package/engine/llm.js ADDED
@@ -0,0 +1,116 @@
1
+ /**
2
+ * engine/llm.js — Shared LLM utilities for Minions engine + dashboard
3
+ * Provides callLLM() (with optional session resume) and trackEngineUsage().
4
+ */
5
+
6
+ const path = require('path');
7
+ const shared = require('./shared');
8
+ const { safeRead, safeWrite, safeUnlink, uid, runFile, cleanChildEnv, parseStreamJsonOutput } = shared;
9
+
10
+ const MINIONS_DIR = path.resolve(__dirname, '..');
11
+ const ENGINE_DIR = __dirname;
12
+
13
+ function trackEngineUsage(category, usage) {
14
+ if (!usage) return;
15
+ try {
16
+ const metricsPath = path.join(ENGINE_DIR, 'metrics.json');
17
+ const raw = safeRead(metricsPath);
18
+ const metrics = raw ? JSON.parse(raw) : {};
19
+
20
+ if (!metrics._engine) metrics._engine = {};
21
+ if (!metrics._engine[category]) {
22
+ metrics._engine[category] = { calls: 0, costUsd: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheCreation: 0 };
23
+ }
24
+ const cat = metrics._engine[category];
25
+ cat.calls++;
26
+ cat.costUsd += usage.costUsd || 0;
27
+ cat.inputTokens += usage.inputTokens || 0;
28
+ cat.outputTokens += usage.outputTokens || 0;
29
+ cat.cacheRead += usage.cacheRead || 0;
30
+ cat.cacheCreation = (cat.cacheCreation || 0) + (usage.cacheCreation || 0);
31
+
32
+ const today = new Date().toISOString().slice(0, 10);
33
+ if (!metrics._daily) metrics._daily = {};
34
+ if (!metrics._daily[today]) metrics._daily[today] = { costUsd: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, tasks: 0 };
35
+ const daily = metrics._daily[today];
36
+ daily.costUsd += usage.costUsd || 0;
37
+ daily.inputTokens += usage.inputTokens || 0;
38
+ daily.outputTokens += usage.outputTokens || 0;
39
+ daily.cacheRead += usage.cacheRead || 0;
40
+
41
+ safeWrite(metricsPath, metrics);
42
+ } catch {}
43
+ }
44
+
45
+ // ── Core LLM Call ───────────────────────────────────────────────────────────
46
+
47
+ function callLLM(promptText, sysPromptText, { timeout = 120000, label = 'llm', model = 'sonnet', maxTurns = 1, allowedTools = '', sessionId = null } = {}) {
48
+ return new Promise((resolve) => {
49
+ const id = uid();
50
+ const tmpDir = path.join(ENGINE_DIR, 'tmp');
51
+ if (!require('fs').existsSync(tmpDir)) require('fs').mkdirSync(tmpDir, { recursive: true });
52
+ const promptPath = path.join(tmpDir, `${label}-prompt-${id}.md`);
53
+ const sysPath = path.join(tmpDir, `${label}-sys-${id}.md`);
54
+ safeWrite(promptPath, promptText);
55
+ safeWrite(sysPath, sysPromptText || '');
56
+
57
+ const spawnScript = path.join(ENGINE_DIR, 'spawn-agent.js');
58
+ const args = [
59
+ spawnScript, promptPath, sysPath,
60
+ '--output-format', 'stream-json', '--max-turns', String(maxTurns), '--model', model,
61
+ '--verbose',
62
+ ];
63
+ if (allowedTools) args.push('--allowedTools', allowedTools);
64
+ args.push('--permission-mode', 'bypassPermissions');
65
+
66
+ if (sessionId) args.push('--resume', sessionId);
67
+
68
+ const proc = runFile(process.execPath, args, { cwd: MINIONS_DIR, stdio: ['pipe', 'pipe', 'pipe'], env: cleanChildEnv() });
69
+
70
+ let stdout = '';
71
+ let stderr = '';
72
+ proc.stdout.on('data', d => { stdout += d.toString(); });
73
+ proc.stderr.on('data', d => { stderr += d.toString(); });
74
+
75
+ const timer = setTimeout(() => { try { proc.kill('SIGTERM'); } catch {} }, timeout);
76
+
77
+ proc.on('close', (code) => {
78
+ clearTimeout(timer);
79
+ safeUnlink(promptPath);
80
+ safeUnlink(sysPath);
81
+ const parsed = parseStreamJsonOutput(stdout);
82
+ resolve({ text: parsed.text || '', usage: parsed.usage, sessionId: parsed.sessionId || null, code, stderr, raw: stdout });
83
+ });
84
+
85
+ proc.on('error', (err) => {
86
+ clearTimeout(timer);
87
+ safeUnlink(promptPath);
88
+ safeUnlink(sysPath);
89
+ resolve({ text: '', usage: null, sessionId: null, code: 1, stderr: err.message, raw: '' });
90
+ });
91
+ });
92
+ }
93
+
94
+ /**
95
+ * After a --resume call fails (non-zero exit or empty text), determine whether
96
+ * the underlying session still exists (e.g. a tool timeout mid-turn) vs the
97
+ * session is truly dead (expired, invalid ID, etc.).
98
+ *
99
+ * When the session still exists we should preserve it so the user can retry
100
+ * with "try again" and resume into the same conversation.
101
+ */
102
+ function isResumeSessionStillValid(result) {
103
+ if (!result) return false;
104
+ // If the CLI returned a session_id in the parsed output or raw stream,
105
+ // the session is alive — the call just failed mid-execution.
106
+ if (result.sessionId) return true;
107
+ if (result.raw && result.raw.includes('"session_id"')) return true;
108
+ return false;
109
+ }
110
+
111
+ module.exports = {
112
+ callLLM,
113
+ trackEngineUsage,
114
+ isResumeSessionStillValid,
115
+ };
116
+