stella-coder 5.0.1 → 5.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.
@@ -0,0 +1,58 @@
1
+ name: Deploy to GitHub Pages
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+ pages: write
11
+ id-token: write
12
+
13
+ concurrency:
14
+ group: "pages"
15
+ cancel-in-progress: false
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - name: Checkout
22
+ uses: actions/checkout@v4
23
+
24
+ - name: Setup Node.js
25
+ uses: actions/setup-node@v4
26
+ with:
27
+ node-version: "20"
28
+
29
+ - name: Install pnpm
30
+ uses: pnpm/action-setup@v4
31
+ with:
32
+ version: 9
33
+
34
+ - name: Setup Pages
35
+ id: pages
36
+ uses: actions/configure-pages@v5
37
+
38
+ - name: Install dependencies
39
+ run: pnpm install
40
+
41
+ - name: Build with Next.js
42
+ run: pnpm run build
43
+
44
+ - name: Upload artifact
45
+ uses: actions/upload-pages-artifact@v3
46
+ with:
47
+ path: ./out
48
+
49
+ deploy:
50
+ environment:
51
+ name: github-pages
52
+ url: ${{ steps.deployment.outputs.page_url }}
53
+ runs-on: ubuntu-latest
54
+ needs: build
55
+ steps:
56
+ - name: Deploy to GitHub Pages
57
+ id: deployment
58
+ uses: actions/deploy-pages@v4
package/next.config.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  /** @type {import('next').NextConfig} */
2
2
  const nextConfig = {
3
+ output: "export",
3
4
  typescript: {
4
5
  ignoreBuildErrors: true,
5
6
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stella-coder",
3
- "version": "5.0.1",
3
+ "version": "5.1.1",
4
4
  "private": false,
5
5
  "description": "Stella Coder 5.0 — AI coding agent with Telegram bot, huge context, TDD, Git ecosystem, presentations, computer control, smart home, Office automation, and antivirus",
6
6
  "main": "stella-cli/index.mjs",
@@ -0,0 +1,550 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, appendFileSync } from "fs"
2
+ import { join } from "path"
3
+ import { homedir } from "os"
4
+ import { execSync } from "child_process"
5
+
6
+ const STELLA_DIR = join(homedir(), ".stella")
7
+ const AUTO_DIR = join(STELLA_DIR, "autonomous")
8
+ const MEMORY_FILE = join(AUTO_DIR, "memory.json")
9
+ const TASKS_FILE = join(AUTO_DIR, "tasks.json")
10
+ const LOG_FILE = join(AUTO_DIR, "activity.log")
11
+ const DASHBOARD_FILE = join(AUTO_DIR, "dashboard.html")
12
+ const STATE_FILE = join(AUTO_DIR, "state.json")
13
+
14
+ function ensureDir() {
15
+ if (!existsSync(AUTO_DIR)) mkdirSync(AUTO_DIR, { recursive: true })
16
+ }
17
+
18
+ function loadJSON(file, fallback) {
19
+ try { return JSON.parse(readFileSync(file, "utf-8")) } catch { return fallback }
20
+ }
21
+
22
+ function saveJSON(file, data) {
23
+ ensureDir()
24
+ writeFileSync(file, JSON.stringify(data, null, 2))
25
+ }
26
+
27
+ function logEntry(action, details) {
28
+ ensureDir()
29
+ const entry = `[${new Date().toISOString()}] ${action}: ${details}\n`
30
+ appendFileSync(LOG_FILE, entry)
31
+ }
32
+
33
+ function shellRun(cmd) {
34
+ try {
35
+ execSync(cmd, { timeout: 60000, stdio: "pipe", shell: true })
36
+ return true
37
+ } catch { return false }
38
+ }
39
+
40
+ export class AutonomousAgent {
41
+ constructor() {
42
+ ensureDir()
43
+ this.memory = loadJSON(MEMORY_FILE, { projects: [], accounts: [], videos: [], earnings: 0, lessons: [] })
44
+ this.tasks = loadJSON(TASKS_FILE, { queue: [], completed: [], failed: [] })
45
+ this.state = loadJSON(STATE_FILE, { running: false, startedAt: null, goal: "", pid: null, iterations: 0 })
46
+ }
47
+
48
+ save() {
49
+ saveJSON(MEMORY_FILE, this.memory)
50
+ saveJSON(TASKS_FILE, this.tasks)
51
+ saveJSON(STATE_FILE, this.state)
52
+ }
53
+
54
+ planGoal(goal) {
55
+ const plan = {
56
+ id: Date.now().toString(36),
57
+ goal,
58
+ createdAt: new Date().toISOString(),
59
+ steps: [],
60
+ status: "planning"
61
+ }
62
+
63
+ const g = goal.toLowerCase()
64
+
65
+ if (g.includes("видео") || g.includes("video")) {
66
+ plan.steps = [
67
+ { id: "concept", name: "Create video concept and script", status: "pending", subtasks: [
68
+ "Write a compelling script/storyboard for the video",
69
+ "Create HTML presentation with animations showing the product",
70
+ "Add text overlays, transitions, and effects"
71
+ ], results: [] },
72
+ { id: "build_video", name: "Build HTML video/animation", status: "pending", subtasks: [
73
+ "Create self-contained HTML file with CSS animations",
74
+ "Add GSAP or CSS keyframe animations",
75
+ "Create cinematic transitions between scenes",
76
+ "Add background music placeholder"
77
+ ], results: [] },
78
+ { id: "export", name: "Export and place on desktop", status: "pending", subtasks: [
79
+ "Copy the HTML video file to Desktop",
80
+ "Create a .bat launcher to open it in browser",
81
+ "Create a poster/thumbnail image"
82
+ ], results: [] },
83
+ { id: "promote", name: "Promote the content", status: "pending", subtasks: [
84
+ "Create social media post templates",
85
+ "Write promotional text for sharing"
86
+ ], results: [] }
87
+ ]
88
+ } else if (g.includes("приложен") || g.includes("app") || g.includes("заработ") || g.includes("$") || g.includes("монетиз") || g.includes("donate") || g.includes("заработ")) {
89
+ plan.steps = [
90
+ { id: "research", name: "Research market & competitors", status: "pending", subtasks: [
91
+ "Search for trending app ideas and niches",
92
+ "Analyze top competitors in the space",
93
+ "Define monetization strategy (ads, subscriptions, donations)"
94
+ ], results: [] },
95
+ { id: "architect", name: "Architecture & design", status: "pending", subtasks: [
96
+ "Choose tech stack (Next.js + Tailwind + Supabase)",
97
+ "Design database schema",
98
+ "Create API structure"
99
+ ], results: [] },
100
+ { id: "build", name: "Build the application", status: "pending", subtasks: [
101
+ "Create project with package.json, config files",
102
+ "Build frontend with UI components",
103
+ "Build backend API routes",
104
+ "Add Stripe/Paddle payment integration",
105
+ "Add donation button (Buy Me a Coffee / Ko-fi)",
106
+ "Add user authentication"
107
+ ], results: [] },
108
+ { id: "deploy", name: "Deploy to production", status: "pending", subtasks: [
109
+ "Initialize git repo, commit all files",
110
+ "Push to GitHub",
111
+ "Deploy frontend to Vercel",
112
+ "Deploy backend to Railway/Render",
113
+ "Configure custom domain if needed"
114
+ ], results: [] },
115
+ { id: "accounts", name: "Create promo accounts", status: "pending", subtasks: [
116
+ "Create YouTube channel for the project",
117
+ "Create TikTok account",
118
+ "Create Twitter/X account",
119
+ "Create Telegram channel",
120
+ "Create Product Hunt listing"
121
+ ], results: [] },
122
+ { id: "content", name: "Create promotional content", status: "pending", subtasks: [
123
+ "Write launch blog post",
124
+ "Create demo video script",
125
+ "Write Reddit posts for r/SideProject, r/webdev",
126
+ "Create Hacker News Show HN post",
127
+ "Write Product Hunt description"
128
+ ], results: [] },
129
+ { id: "dashboard", name: "Build management dashboard", status: "pending", subtasks: [
130
+ "Create HTML dashboard page",
131
+ "Add analytics tracking",
132
+ "Add financial metrics view",
133
+ "Add user management"
134
+ ], results: [] },
135
+ { id: "optimize", name: "Growth & optimization", status: "pending", subtasks: [
136
+ "SEO optimization",
137
+ "Performance audit",
138
+ "Add analytics (Google Analytics / Plausible)",
139
+ "Set up email list"
140
+ ], results: [] }
141
+ ]
142
+ } else if (g.includes("сайт") || g.includes("website") || g.includes("лендинг")) {
143
+ plan.steps = [
144
+ { id: "design", name: "Design the website", status: "pending", subtasks: [
145
+ "Create wireframe and layout",
146
+ "Design UI with modern dark theme",
147
+ "Write compelling copy"
148
+ ], results: [] },
149
+ { id: "build", name: "Build the website", status: "pending", subtasks: [
150
+ "Create HTML/CSS/JS files",
151
+ "Add responsive design",
152
+ "Add animations and interactions"
153
+ ], results: [] },
154
+ { id: "seo", name: "SEO & performance", status: "pending", subtasks: [
155
+ "Add meta tags and Open Graph",
156
+ "Optimize images and loading",
157
+ "Add structured data"
158
+ ], results: [] },
159
+ { id: "deploy", name: "Deploy the website", status: "pending", subtasks: [
160
+ "Push to GitHub",
161
+ "Enable GitHub Pages or deploy to Vercel",
162
+ "Configure domain"
163
+ ], results: [] },
164
+ { id: "promo", name: "Promote the website", status: "pending", subtasks: [
165
+ "Share on social media",
166
+ "Submit to directories",
167
+ "Run ad campaigns"
168
+ ], results: [] }
169
+ ]
170
+ } else {
171
+ plan.steps = [
172
+ { id: "analyze", name: "Analyze the task", status: "pending", subtasks: [
173
+ "Break down requirements",
174
+ "Identify needed resources",
175
+ "Create execution plan"
176
+ ], results: [] },
177
+ { id: "execute", name: "Execute the plan", status: "pending", subtasks: [
178
+ "Implement step by step",
179
+ "Test at each stage",
180
+ "Handle errors and retry"
181
+ ], results: [] },
182
+ { id: "deliver", name: "Deliver results", status: "pending", subtasks: [
183
+ "Document everything",
184
+ "Create summary report",
185
+ "Generate dashboard"
186
+ ], results: [] }
187
+ ]
188
+ }
189
+
190
+ this.tasks.queue.push(plan)
191
+ this.state.goal = goal
192
+ this.save()
193
+ logEntry("PLAN_CREATED", goal)
194
+ return plan
195
+ }
196
+
197
+ async executeSubtask(subtask, goal, apiCall) {
198
+ const prompt = `You are an autonomous coding agent. Complete this task: "${subtask}"
199
+ Context: Goal is "${goal}"
200
+
201
+ You MUST return a JSON object with actual executable commands and file contents.
202
+ DO NOT just describe — ACTUALLY PLAN THE EXECUTION.
203
+
204
+ Return this exact JSON structure:
205
+ {
206
+ "action": "what will be done",
207
+ "filesToCreate": [
208
+ {"path": "relative/path/file.ext", "content": "FULL file content with all code"}
209
+ ],
210
+ "commandsToRun": [
211
+ "shell command 1",
212
+ "shell command 2"
213
+ ],
214
+ "result": "description of what will happen",
215
+ "nextSteps": ["what to do next"]
216
+ }
217
+
218
+ IMPORTANT:
219
+ - filesToCreate must contain REAL, COMPLETE, WORKING code (not placeholders)
220
+ - commandsToRun must be real shell commands (npm init, mkdir, etc.)
221
+ - Return ONLY the JSON, no markdown, no explanations`
222
+
223
+ const response = await apiCall(prompt)
224
+ let parsed
225
+ try {
226
+ // Strip markdown code fences if present
227
+ let clean = response.trim()
228
+ if (clean.startsWith("```")) {
229
+ clean = clean.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "")
230
+ }
231
+ parsed = JSON.parse(clean)
232
+ } catch {
233
+ parsed = { action: "completed", result: response, filesToCreate: [], commandsToRun: [] }
234
+ }
235
+
236
+ const created = []
237
+ const executed = []
238
+
239
+ // Create actual files
240
+ if (parsed.filesToCreate && Array.isArray(parsed.filesToCreate)) {
241
+ for (const file of parsed.filesToCreate) {
242
+ if (file.path && file.content) {
243
+ try {
244
+ const fullPath = join(process.cwd(), file.path)
245
+ const dir = dirname(fullPath)
246
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
247
+ writeFileSync(fullPath, file.content, "utf-8")
248
+ created.push(file.path)
249
+ this.memory.projects.push({ file: file.path, created: new Date().toISOString(), goal, subtask })
250
+ logEntry("FILE_CREATED", file.path)
251
+ } catch (err) {
252
+ logEntry("FILE_ERROR", `${file.path}: ${err.message}`)
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ // Execute actual commands
259
+ if (parsed.commandsToRun && Array.isArray(parsed.commandsToRun)) {
260
+ for (const cmd of parsed.commandsToRun) {
261
+ try {
262
+ execSync(cmd, { timeout: 30000, stdio: "pipe", shell: true, cwd: process.cwd() })
263
+ executed.push(cmd)
264
+ logEntry("CMD_RUN", cmd)
265
+ } catch (err) {
266
+ logEntry("CMD_FAIL", `${cmd}: ${err.message?.slice(0, 100)}`)
267
+ }
268
+ }
269
+ }
270
+
271
+ logEntry("SUBTASK_DONE", `${subtask}: ${created.length} files, ${executed.length} cmds`)
272
+ this.save()
273
+ return { ...parsed, filesCreated: created, commandsRun: executed }
274
+ }
275
+
276
+ async runIteration(apiCall, onProgress) {
277
+ const plan = this.tasks.queue.find(p => p.status !== "completed" && p.status !== "failed")
278
+ if (!plan) return null
279
+
280
+ plan.status = "running"
281
+ const step = plan.steps.find(s => s.status === "pending")
282
+ if (!step) {
283
+ const hasFailed = plan.steps.some(s => s.status === "failed")
284
+ plan.status = hasFailed ? "failed" : "completed"
285
+ this.save()
286
+ logEntry("PLAN_DONE", `${plan.goal}: ${plan.status}`)
287
+ return { type: "plan_done", plan }
288
+ }
289
+
290
+ step.status = "running"
291
+ this.save()
292
+ if (onProgress) onProgress(`⚡ Executing: ${step.name}`)
293
+ logEntry("STEP_START", step.name)
294
+
295
+ const stepResults = []
296
+ let failed = false
297
+
298
+ for (const subtask of step.subtasks) {
299
+ try {
300
+ if (onProgress) onProgress(` → ${subtask}`)
301
+ const result = await this.executeSubtask(subtask, plan.goal, apiCall)
302
+ stepResults.push(result)
303
+
304
+ // Report created files
305
+ if (result.filesCreated && result.filesCreated.length > 0) {
306
+ for (const f of result.filesCreated) {
307
+ if (onProgress) onProgress(` 📄 Created: ${f}`)
308
+ }
309
+ }
310
+ if (result.commandsRun && result.commandsRun.length > 0) {
311
+ for (const c of result.commandsRun) {
312
+ if (onProgress) onProgress(` ⚙️ Ran: ${c}`)
313
+ }
314
+ }
315
+ } catch (err) {
316
+ logEntry("SUBTASK_FAIL", `${subtask}: ${err.message}`)
317
+ stepResults.push({ error: err.message })
318
+ failed = true
319
+ }
320
+ }
321
+
322
+ step.status = failed ? "failed" : "completed"
323
+ step.results = stepResults
324
+ this.save()
325
+ logEntry("STEP_DONE", `${step.name}: ${step.status}`)
326
+ return { type: "step_done", step, plan }
327
+ }
328
+
329
+ async start(goal, apiCall, onProgress) {
330
+ this.state.running = true
331
+ this.state.startedAt = new Date().toISOString()
332
+ this.state.goal = goal
333
+ this.state.iterations = 0
334
+ this.save()
335
+
336
+ const plan = this.planGoal(goal)
337
+ logEntry("AGENT_START", goal)
338
+
339
+ while (this.state.running) {
340
+ this.state.iterations++
341
+ const result = await this.runIteration(apiCall, onProgress)
342
+
343
+ if (!result) {
344
+ this.state.running = false
345
+ this.save()
346
+ logEntry("AGENT_DONE", "All plans completed!")
347
+ if (onProgress) onProgress("ALL_DONE")
348
+ break
349
+ }
350
+
351
+ if (result.type === "plan_done") {
352
+ if (onProgress) onProgress(`Plan completed: ${result.plan.goal}`)
353
+ } else if (result.type === "step_done") {
354
+ if (onProgress) onProgress(`Step done: ${result.step.name}`)
355
+ }
356
+
357
+ await new Promise(r => setTimeout(r, 500))
358
+ }
359
+
360
+ this.state.running = false
361
+ this.save()
362
+ }
363
+
364
+ stop() {
365
+ this.state.running = false
366
+ this.save()
367
+ logEntry("AGENT_STOPPED", "Manually stopped")
368
+ }
369
+
370
+ getStatus() {
371
+ const plan = this.tasks.queue.find(p => p.status === "running" || p.status === "planning")
372
+ return {
373
+ running: this.state.running,
374
+ goal: this.state.goal,
375
+ iterations: this.state.iterations,
376
+ startedAt: this.state.startedAt,
377
+ currentPlan: plan ? {
378
+ goal: plan.goal,
379
+ status: plan.status,
380
+ steps: plan.steps.map(s => ({
381
+ name: s.name,
382
+ status: s.status,
383
+ total: s.subtasks.length,
384
+ done: (s.results || []).filter(r => !r.error).length
385
+ }))
386
+ } : null,
387
+ completedPlans: this.tasks.queue.filter(p => p.status === "completed").length,
388
+ failedPlans: this.tasks.queue.filter(p => p.status === "failed").length,
389
+ totalFiles: this.memory.projects.length,
390
+ totalEarnings: this.memory.earnings,
391
+ accounts: this.memory.accounts.length
392
+ }
393
+ }
394
+
395
+ generateDashboard() {
396
+ const completedPlans = this.tasks.queue.filter(p => p.status === "completed")
397
+ const failedPlans = this.tasks.queue.filter(p => p.status === "failed")
398
+ const activePlans = this.tasks.queue.filter(p => p.status === "running" || p.status === "planning")
399
+ const allFiles = [...new Set(this.memory.projects.map(p => p.file))]
400
+ const logs = existsSync(LOG_FILE) ? readFileSync(LOG_FILE, "utf-8").split("\n").filter(Boolean).slice(-100) : []
401
+
402
+ const html = `<!DOCTYPE html>
403
+ <html lang="ru">
404
+ <head>
405
+ <meta charset="UTF-8">
406
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
407
+ <title>Stella Autonomous Dashboard</title>
408
+ <style>
409
+ *{margin:0;padding:0;box-sizing:border-box}
410
+ body{background:#0a0a0f;color:#f8fafc;font-family:'Courier New',monospace;min-height:100vh}
411
+ .hdr{background:linear-gradient(135deg,#0f0f1a,#1a0a2e);padding:2rem 3rem;border-bottom:2px solid #22c55e;display:flex;justify-content:space-between;align-items:center}
412
+ .hdr h1{font-size:2rem;color:#22c55e}
413
+ .hdr .status{display:flex;align-items:center;gap:8px}
414
+ .hdr .dot{width:12px;height:12px;border-radius:50%;background:${this.state.running ? "#22c55e" : "#ef4444"};animation:pulse 1.5s infinite}
415
+ @keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
416
+ .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1rem;padding:2rem 3rem}
417
+ .card{background:#111118;border:1px solid #22c55e33;border-radius:12px;padding:1.5rem;text-align:center;transition:border-color .3s}
418
+ .card:hover{border-color:#22c55e}
419
+ .card .num{font-size:2.5rem;color:#22c55e;font-weight:bold}
420
+ .card .lbl{color:#94a3b8;margin-top:.3rem;font-size:.85rem}
421
+ .sec{padding:1.5rem 3rem}
422
+ .sec h2{color:#22c55e;font-size:1.3rem;margin-bottom:1rem;display:flex;align-items:center;gap:8px}
423
+ .plan-card{background:#111118;border:1px solid #333;border-radius:12px;padding:1.5rem;margin-bottom:1rem}
424
+ .plan-card h3{color:#e2e8f0;font-size:1.1rem}
425
+ .plan-card .meta{color:#64748b;font-size:.8rem;margin:.5rem 0}
426
+ .step-row{display:flex;align-items:center;gap:10px;padding:.6rem 0;border-bottom:1px solid #1a1a2e}
427
+ .step-row:last-child{border:none}
428
+ .badge{padding:3px 10px;border-radius:6px;font-size:.7rem;font-weight:bold;text-transform:uppercase}
429
+ .badge.completed{background:#22c55e22;color:#22c55e}
430
+ .badge.failed{background:#ef444422;color:#ef4444}
431
+ .badge.running{background:#eab30822;color:#eab308}
432
+ .badge.pending{background:#64748b22;color:#64748b}
433
+ .badge.planning{background:#3b82f622;color:#3b82f6}
434
+ .files-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:.5rem}
435
+ .file-item{background:#16161f;padding:.5rem 1rem;border-radius:6px;font-size:.85rem;color:#22c55e;display:flex;align-items:center;gap:6px}
436
+ .log-box{background:#111118;border-radius:12px;padding:1rem;max-height:400px;overflow-y:auto;font-size:.78rem;color:#94a3b8;line-height:1.6}
437
+ .log-box div{padding:2px 0;border-bottom:1px solid #111}
438
+ .log-box .ts{color:#64748b}
439
+ .footer{text-align:center;padding:2rem;color:#475569;border-top:1px solid #1a1a2e;margin-top:2rem}
440
+ .empty{text-align:center;padding:3rem;color:#475569;font-size:1.1rem}
441
+ </style>
442
+ </head>
443
+ <body>
444
+ <div class="hdr">
445
+ <div>
446
+ <h1>🤖 Stella Autonomous Agent</h1>
447
+ <p style="color:#94a3b8;margin-top:.3rem">Self-driving coding machine — powered by codex alex</p>
448
+ </div>
449
+ <div class="status">
450
+ <div class="dot"></div>
451
+ <span style="color:${this.state.running ? "#22c55e" : "#ef4444"};font-weight:bold">${this.state.running ? "RUNNING" : "IDLE"}</span>
452
+ </div>
453
+ </div>
454
+
455
+ <div class="grid">
456
+ <div class="card"><div class="num">${completedPlans.length}</div><div class="lbl">Plans Done</div></div>
457
+ <div class="card"><div class="num">${activePlans.length}</div><div class="lbl">Active Plans</div></div>
458
+ <div class="card"><div class="num">${failedPlans.length}</div><div class="lbl">Failed</div></div>
459
+ <div class="card"><div class="num">${allFiles.length}</div><div class="lbl">Files Created</div></div>
460
+ <div class="card"><div class="num">$${this.memory.earnings.toFixed(2)}</div><div class="lbl">Earnings</div></div>
461
+ <div class="card"><div class="num">${this.memory.accounts.length}</div><div class="lbl">Accounts</div></div>
462
+ <div class="card"><div class="num">${this.state.iterations}</div><div class="lbl">Iterations</div></div>
463
+ <div class="card"><div class="num">${this.memory.videos.length}</div><div class="lbl">Videos</div></div>
464
+ </div>
465
+
466
+ ${activePlans.length > 0 ? `
467
+ <div class="sec">
468
+ <h2>⚡ Active Plans</h2>
469
+ ${activePlans.map(p => `
470
+ <div class="plan-card">
471
+ <h3>${p.goal}</h3>
472
+ <div class="meta">Started: ${new Date(p.createdAt).toLocaleString()}</div>
473
+ ${p.steps.map(s => `
474
+ <div class="step-row">
475
+ <span class="badge ${s.status}">${s.status}</span>
476
+ <span>${s.name}</span>
477
+ <span style="margin-left:auto;color:#64748b">${(s.results||[]).filter(r=>!r.error).length}/${s.subtasks.length}</span>
478
+ </div>
479
+ `).join("")}
480
+ </div>
481
+ `).join("")}
482
+ </div>` : ""}
483
+
484
+ ${completedPlans.length > 0 ? `
485
+ <div class="sec">
486
+ <h2>✅ Completed Plans</h2>
487
+ ${completedPlans.map(p => `
488
+ <div class="plan-card">
489
+ <h3>${p.goal}</h3>
490
+ <div class="meta">Completed: ${new Date(p.createdAt).toLocaleString()}</div>
491
+ ${p.steps.map(s => `
492
+ <div class="step-row">
493
+ <span class="badge completed">done</span>
494
+ <span>${s.name}</span>
495
+ </div>
496
+ `).join("")}
497
+ </div>
498
+ `).join("")}
499
+ </div>` : ""}
500
+
501
+ ${failedPlans.length > 0 ? `
502
+ <div class="sec">
503
+ <h2>❌ Failed Plans</h2>
504
+ ${failedPlans.map(p => `
505
+ <div class="plan-card">
506
+ <h3>${p.goal}</h3>
507
+ ${p.steps.filter(s=>s.status==="failed").map(s => `
508
+ <div class="step-row">
509
+ <span class="badge failed">failed</span>
510
+ <span>${s.name}</span>
511
+ </div>
512
+ `).join("")}
513
+ </div>
514
+ `).join("")}
515
+ </div>` : ""}
516
+
517
+ <div class="sec">
518
+ <h2>📁 All Created Files</h2>
519
+ ${allFiles.length > 0 ? `
520
+ <div class="files-grid">
521
+ ${allFiles.map(f => `<div class="file-item">📄 ${f}</div>`).join("")}
522
+ </div>
523
+ ` : '<div class="empty">No files created yet</div>'}
524
+ </div>
525
+
526
+ <div class="sec">
527
+ <h2>📝 Activity Log (last 100 entries)</h2>
528
+ <div class="log-box">
529
+ ${logs.length > 0 ? logs.map(l => {
530
+ const parts = l.match(/\[(.*?)\] (\w+): (.*)/)
531
+ if (parts) return `<div><span class="ts">${parts[1]}</span> <span style="color:#22c55e">${parts[2]}</span> ${parts[3]}</div>`
532
+ return `<div>${l}</div>`
533
+ }).join("") : '<div>No activity yet</div>'}
534
+ </div>
535
+ </div>
536
+
537
+ <div class="footer">
538
+ <p>Stella Coder 5.1 — Autonomous Agent Dashboard</p>
539
+ <p>Generated: ${new Date().toLocaleString()} · Goal: ${this.state.goal || "None"}</p>
540
+ </div>
541
+ </body>
542
+ </html>`
543
+
544
+ ensureDir()
545
+ writeFileSync(DASHBOARD_FILE, html)
546
+ return DASHBOARD_FILE
547
+ }
548
+ }
549
+
550
+ export default AutonomousAgent
@@ -30,7 +30,7 @@ export function printBanner({ model, cwd, version }) {
30
30
  console.log(
31
31
  box(
32
32
  [
33
- violet("✻") + " Добро пожаловать в " + bold(violet("Stella Coder 5.0")) + "!",
33
+ violet("✻") + " Добро пожаловать в " + bold(violet("Stella Coder 5.1")) + "!",
34
34
  "",
35
35
  dim(" модель: ") + blue(model),
36
36
  dim(" папка: ") + gray(cwd),
@@ -21,6 +21,7 @@ import {
21
21
  import { runSubagent, listSubagents, parseAgentCommand, SUBAGENTS } from "./subagents.mjs"
22
22
  import { mcp, MCP_COMMANDS } from "./mcp.mjs"
23
23
  import { generatePresentation, createPresentationFromTopic, AVAILABLE_THEMES, exportToPDF } from "./presentations.mjs"
24
+ import { AutonomousAgent } from "./autonomous-agent.mjs"
24
25
  import {
25
26
  buildRepoMap, buildProjectContext, compressContext,
26
27
  loadSpec, generateSpecTemplate,
@@ -40,7 +41,7 @@ import {
40
41
  generateAdminCode, listAuthorizedUsers,
41
42
  } from "./telegram-bot.mjs"
42
43
 
43
- const VERSION = "5.0.1"
44
+ const VERSION = "5.1.1"
44
45
  const CONFIG_DIR = path.join(os.homedir(), ".stella")
45
46
  const CONFIG_PATH = path.join(CONFIG_DIR, "config.json")
46
47
  const HISTORY_PATH = path.join(CONFIG_DIR, "history.json")
@@ -642,6 +643,13 @@ const COMMANDS = [
642
643
  ["/tg-code", "сгенерировать код для продажи доступа"],
643
644
  ["/tg-users", "показать кто подключён"],
644
645
 
646
+ // Autonomous Agent
647
+ ["/auto", "запустить автономный режим (цель)"],
648
+ ["/auto-stop", "остановить автономный агента"],
649
+ ["/auto-status", "статус автономного агента"],
650
+ ["/auto-dashboard", "создать dashboard с результатами"],
651
+ ["/auto-history", "история выполненных задач"],
652
+
645
653
  // Управление компьютером
646
654
  ["/open", "открыть приложение/файл/URL"],
647
655
  ["/app", "запустить приложение"],
@@ -766,6 +774,7 @@ async function handleCommand(line) {
766
774
  ["🧠 Coding Brain", ["/brain", "/brain-map", "/brain-compress", "/spec", "/tdd", "/lint-auto", "/format-auto", "/typecheck-auto", "/test-auto", "/fix-all"]],
767
775
  ["📱 Telegram", ["/tg", "/tg-stop", "/tg-notify", "/tg-sessions", "/tg-verify", "/tg-code", "/tg-users"]],
768
776
  ["🔀 Git Экосистема", ["/git-eco", "/git-pr", "/git-merge-auto"]],
777
+ ["🤖 Autonomous Agent", ["/auto", "/auto-stop", "/auto-status", "/auto-dashboard", "/auto-history"]],
769
778
  ["⚙️ Настройки", ["/help", "/model", "/clear", "/compact", "/cost", "/context", "/config", "/version", "/login", "/newkey", "/doctor", "/sessions", "/color", "/lang", "/shortcut"]],
770
779
  ]
771
780
  for (const [cat, cmds] of categories) {
@@ -3387,6 +3396,204 @@ async function handleCommand(line) {
3387
3396
  return
3388
3397
  }
3389
3398
 
3399
+ // ═══════════════════════════════════════════════════
3400
+ // AUTONOMOUS AGENT
3401
+ // ═══════════════════════════════════════════════════
3402
+ case "/auto": {
3403
+ if (!arg) {
3404
+ console.log(dim("\n Использование: /auto <цель>\n"))
3405
+ console.log(dim(" Пример: /auto создай приложение и заработай $10\n"))
3406
+ console.log(dim(" Агент будет работать автономно пока не выполнит цель.\n"))
3407
+ return
3408
+ }
3409
+
3410
+ const goal = arg.trim()
3411
+ const agent = new AutonomousAgent()
3412
+
3413
+ console.log()
3414
+ console.log(box([
3415
+ violet("🤖 AUTONOMOUS AGENT"),
3416
+ "",
3417
+ white("Цель: ") + cyan(goal),
3418
+ "",
3419
+ dim("Агент создаст план и начнёт автономное выполнение."),
3420
+ dim("Каждый шаг будет выполняться автоматически."),
3421
+ dim("Используй /auto-stop для остановки."),
3422
+ dim("Используй /auto-status для проверки прогресса."),
3423
+ ], { color: violet, padding: 2 }))
3424
+ console.log()
3425
+
3426
+ const confirm = await question(" " + green("Начать автономное выполнение? (y/n) › "))
3427
+ if (confirm.trim().toLowerCase() !== "y" && confirm.trim().toLowerCase() !== "д") {
3428
+ console.log(dim(" Отменено\n"))
3429
+ return
3430
+ }
3431
+
3432
+ console.log()
3433
+ startSpinner("Составляю план выполнения")
3434
+ const plan = agent.planGoal(goal)
3435
+ stopSpinner()
3436
+
3437
+ console.log(box([
3438
+ green("✓ План создан!"),
3439
+ "",
3440
+ ...plan.steps.map((s, i) => dim(` ${i + 1}. `) + white(s.name)),
3441
+ "",
3442
+ dim(`Всего шагов: ${plan.steps.length}`),
3443
+ ], { title: "План", color: green, padding: 2 }))
3444
+ console.log()
3445
+
3446
+ const apiCall = async (prompt) => {
3447
+ const response = await queryAI(prompt, apiKey, state.model)
3448
+ return response
3449
+ }
3450
+
3451
+ console.log(dim(" Запускаю автономный режим...\n"))
3452
+
3453
+ agent.state.running = true
3454
+ agent.state.startedAt = new Date().toISOString()
3455
+ agent.state.goal = goal
3456
+ agent.save()
3457
+
3458
+ let stepCount = 0
3459
+ const runAuto = async () => {
3460
+ while (agent.state.running) {
3461
+ const result = await agent.runIteration(apiCall, (msg) => {
3462
+ if (msg === "ALL_DONE") {
3463
+ console.log(green("\n ✅ ВСЕ ЗАДАЧИ ВЫПОЛНЕНЫ!\n"))
3464
+ agent.generateDashboard()
3465
+ console.log(dim(" Dashboard: ~/.stella/autonomous/dashboard.html\n"))
3466
+ return
3467
+ }
3468
+ console.log(dim(" ") + violet("→") + " " + white(msg))
3469
+ })
3470
+
3471
+ if (!result) {
3472
+ agent.state.running = false
3473
+ agent.save()
3474
+ console.log(green("\n ✅ Все планы выполнены!\n"))
3475
+ agent.generateDashboard()
3476
+ console.log(dim(" Dashboard: ~/.stella/autonomous/dashboard.html\n"))
3477
+ break
3478
+ }
3479
+
3480
+ stepCount++
3481
+ if (stepCount % 5 === 0) {
3482
+ console.log(dim(` [${stepCount} шагов выполнено]`))
3483
+ }
3484
+
3485
+ await new Promise(r => setTimeout(r, 1000))
3486
+ }
3487
+ }
3488
+
3489
+ runAuto().catch(err => {
3490
+ console.log(red(`\n ✗ Ошибка: ${err.message}\n`))
3491
+ agent.state.running = false
3492
+ agent.save()
3493
+ })
3494
+
3495
+ return
3496
+ }
3497
+
3498
+ case "/auto-stop": {
3499
+ console.log()
3500
+ const agent = new AutonomousAgent()
3501
+ if (!agent.state.running) {
3502
+ console.log(dim(" Агент не запущен\n"))
3503
+ return
3504
+ }
3505
+ agent.stop()
3506
+ console.log(green(" ✓ Автономный агент остановлен\n"))
3507
+ console.log(dim(` Выполнено шагов: ${agent.state.iterations}`))
3508
+ console.log(dim(` Создано файлов: ${agent.memory.projects.length}`))
3509
+ console.log()
3510
+ return
3511
+ }
3512
+
3513
+ case "/auto-status": {
3514
+ console.log()
3515
+ const agent = new AutonomousAgent()
3516
+ const status = agent.getStatus()
3517
+
3518
+ if (!status.running && !status.currentPlan) {
3519
+ console.log(dim(" Агент не запущен. Используй /auto <цель> для запуска.\n"))
3520
+ return
3521
+ }
3522
+
3523
+ const items = [
3524
+ dim("Статус: ") + (status.running ? green("● RUNNINg") : red("○ STOPPED")),
3525
+ dim("Цель: ") + white(status.goal || "Нет"),
3526
+ dim("Итераций: ") + cyan(String(status.iterations)),
3527
+ dim("Начато: ") + white(status.startedAt ? new Date(status.startedAt).toLocaleString() : "—"),
3528
+ "",
3529
+ dim("Планов выполнено: ") + green(String(status.completedPlans)),
3530
+ dim("Планов.failed: ") + red(String(status.failedPlans)),
3531
+ dim("Файлов создано: ") + cyan(String(status.totalFiles)),
3532
+ dim("Заработано: ") + green("$" + status.totalEarnings.toFixed(2)),
3533
+ dim("Аккаунтов: ") + cyan(String(status.accounts)),
3534
+ ]
3535
+
3536
+ if (status.currentPlan) {
3537
+ items.push("", violet("Текущий план: ") + white(status.currentPlan.goal))
3538
+ items.push(dim("Прогресс:"))
3539
+ for (const step of status.currentPlan.steps) {
3540
+ const icon = step.status === "completed" ? green("✓") : step.status === "running" ? yellow("●") : step.status === "failed" ? red("✗") : dim("○")
3541
+ items.push(` ${icon} ${step.name} ${dim(`(${step.done}/${step.total})`)}`)
3542
+ }
3543
+ }
3544
+
3545
+ console.log(box(items, { title: "🤖 Autonomous Agent Status", color: violet, padding: 2 }))
3546
+ console.log()
3547
+ return
3548
+ }
3549
+
3550
+ case "/auto-dashboard": {
3551
+ console.log()
3552
+ const agent = new AutonomousAgent()
3553
+ startSpinner("Создаю dashboard")
3554
+ const path = agent.generateDashboard()
3555
+ stopSpinner()
3556
+ console.log(green(` ✓ Dashboard создан: ${path}`))
3557
+ console.log(dim(" Откройте в браузере для просмотра результатов\n"))
3558
+
3559
+ const openIt = await question(" " + green("Открыть dashboard? (y/n) › "))
3560
+ if (openIt.trim().toLowerCase() === "y" || openIt.trim().toLowerCase() === "д") {
3561
+ try {
3562
+ execSync(`start "" "${path}"`, { shell: "cmd.exe", stdio: "pipe" })
3563
+ } catch (e) {
3564
+ console.log(yellow(" ⚠ Не удалось открыть автоматически\n"))
3565
+ }
3566
+ }
3567
+ console.log()
3568
+ return
3569
+ }
3570
+
3571
+ case "/auto-history": {
3572
+ console.log()
3573
+ const agent = new AutonomousAgent()
3574
+ const allPlans = agent.tasks.queue
3575
+
3576
+ if (allPlans.length === 0) {
3577
+ console.log(dim(" Нет истории. Используй /auto <цель> для запуска.\n"))
3578
+ return
3579
+ }
3580
+
3581
+ console.log(box([
3582
+ ...allPlans.map(p => {
3583
+ const icon = p.status === "completed" ? green("✓") : p.status === "failed" ? red("✗") : yellow("●")
3584
+ return `${icon} ${white(p.goal)} ${dim(`(${p.status})`)}`
3585
+ }),
3586
+ ], { title: "История автономных задач", color: violet, padding: 2 }))
3587
+ console.log()
3588
+
3589
+ // Show memory stats
3590
+ console.log(dim(` Всего проектов: ${agent.memory.projects.length}`))
3591
+ console.log(dim(` Аккаунтов: ${agent.memory.accounts.length}`))
3592
+ console.log(dim(` Уроков: ${agent.memory.lessons.length}`))
3593
+ console.log()
3594
+ return
3595
+ }
3596
+
3390
3597
  default:
3391
3598
  console.log(dim(`\n Неизвестная команда: ${cmd}. Смотри /help\n`))
3392
3599
  }
@@ -2,6 +2,7 @@ import { tool } from "ai"
2
2
  import { z } from "zod"
3
3
  import fs from "node:fs"
4
4
  import path from "node:path"
5
+ import os from "node:os"
5
6
  import { execSync } from "node:child_process"
6
7
 
7
8
  const MAX_OUTPUT = 30000
@@ -949,12 +950,12 @@ export function createTools(permissions) {
949
950
  platform: process.platform,
950
951
  arch: process.arch,
951
952
  nodeVersion: process.version,
952
- hostname: require("os").hostname(),
953
- username: require("os").userInfo().username,
954
- cpus: require("os").cpus().length,
955
- totalMemory: `${(require("os").totalmem() / 1e9).toFixed(1)} GB`,
956
- freeMemory: `${(require("os").freemem() / 1e9).toFixed(1)} GB`,
957
- uptime: `${Math.floor(require("os").uptime() / 3600)}h ${Math.floor((require("os").uptime() % 3600) / 60)}m`,
953
+ hostname: os.hostname(),
954
+ username: os.userInfo().username,
955
+ cpus: os.cpus().length,
956
+ totalMemory: `${(os.totalmem() / 1e9).toFixed(1)} GB`,
957
+ freeMemory: `${(os.freemem() / 1e9).toFixed(1)} GB`,
958
+ uptime: `${Math.floor(os.uptime() / 3600)}h ${Math.floor((os.uptime() % 3600) / 60)}m`,
958
959
  }
959
960
  return info
960
961
  } catch (e) {