agdi 3.3.2 → 3.3.5

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,45 @@
1
+ // src/core/event-bus.ts
2
+ import { EventEmitter } from "events";
3
+ var agentEventBus = new EventEmitter();
4
+ agentEventBus.setMaxListeners(50);
5
+ var thoughtFlushTimeout = null;
6
+ var pendingThought = null;
7
+ function emitAgentEvent(event) {
8
+ const fullEvent = {
9
+ ...event,
10
+ timestamp: Date.now()
11
+ };
12
+ if (event.type === "thought") {
13
+ pendingThought = fullEvent;
14
+ if (!thoughtFlushTimeout) {
15
+ thoughtFlushTimeout = setTimeout(() => {
16
+ if (pendingThought) {
17
+ agentEventBus.emit("agent_event", pendingThought);
18
+ pendingThought = null;
19
+ }
20
+ thoughtFlushTimeout = null;
21
+ }, 100);
22
+ }
23
+ } else {
24
+ agentEventBus.emit("agent_event", fullEvent);
25
+ }
26
+ }
27
+ function cleanupEventBus() {
28
+ agentEventBus.removeAllListeners();
29
+ if (thoughtFlushTimeout) {
30
+ clearTimeout(thoughtFlushTimeout);
31
+ }
32
+ }
33
+ process.on("exit", cleanupEventBus);
34
+ process.on("SIGINT", cleanupEventBus);
35
+ process.on("uncaughtException", (err) => {
36
+ console.error("Uncaught exception:", err);
37
+ cleanupEventBus();
38
+ process.exit(1);
39
+ });
40
+
41
+ export {
42
+ agentEventBus,
43
+ emitAgentEvent,
44
+ cleanupEventBus
45
+ };
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  agentEventBus,
3
+ cleanupEventBus,
3
4
  emitAgentEvent
4
- } from "./chunk-AEWEBMJY.js";
5
+ } from "./chunk-A5UTYKKM.js";
5
6
  export {
6
7
  agentEventBus,
8
+ cleanupEventBus,
7
9
  emitAgentEvent
8
10
  };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  agentEventBus,
4
4
  emitAgentEvent
5
- } from "./chunk-AEWEBMJY.js";
5
+ } from "./chunk-A5UTYKKM.js";
6
6
  import {
7
7
  loadConfig,
8
8
  saveConfig
@@ -343,9 +343,9 @@ var errorGradient = gradient([THEME.red, "#b91c1c"]);
343
343
  var goldGradient = gradient([THEME.yellow, "#fbbf24"]);
344
344
  async function renderBanner(version = "v2.6.0") {
345
345
  console.clear();
346
- const text = await new Promise((resolve) => {
346
+ const text = await new Promise((resolve2) => {
347
347
  figlet("AGDI", { font: "Slant" }, (err, data) => {
348
- resolve(data || "AGDI");
348
+ resolve2(data || "AGDI");
349
349
  });
350
350
  });
351
351
  console.log(brandGradient.multiline(text));
@@ -1576,7 +1576,7 @@ async function runProject(targetDir) {
1576
1576
  console.log(chalk6.yellow("\u{1F4E6} Installing dependencies...\n"));
1577
1577
  const installSpinner = ora3("Running npm install...").start();
1578
1578
  try {
1579
- await new Promise((resolve, reject) => {
1579
+ await new Promise((resolve2, reject) => {
1580
1580
  const install = spawn("npm", ["install"], {
1581
1581
  cwd: absoluteDir,
1582
1582
  stdio: "inherit",
@@ -1585,7 +1585,7 @@ async function runProject(targetDir) {
1585
1585
  install.on("close", (code) => {
1586
1586
  if (code === 0) {
1587
1587
  installSpinner.succeed("Dependencies installed!");
1588
- resolve();
1588
+ resolve2();
1589
1589
  } else {
1590
1590
  installSpinner.fail("npm install failed");
1591
1591
  reject(new Error(`npm install exited with code ${code}`));
@@ -2101,8 +2101,9 @@ var BaseAgent = class {
2101
2101
  }
2102
2102
  /**
2103
2103
  * Generate a response from the LLM
2104
+ * Includes timeout protection to prevent indefinite hanging
2104
2105
  */
2105
- async think(prompt) {
2106
+ async think(prompt, timeoutMs = 12e4) {
2106
2107
  this.log("Thinking...", "info");
2107
2108
  emitAgentEvent({
2108
2109
  type: "thought",
@@ -2110,7 +2111,12 @@ var BaseAgent = class {
2110
2111
  role: this.role,
2111
2112
  message: `Analyzing task...`
2112
2113
  });
2113
- const response = await this.llm.generate(prompt, this.getSystemPrompt());
2114
+ const response = await Promise.race([
2115
+ this.llm.generate(prompt, this.getSystemPrompt()),
2116
+ new Promise(
2117
+ (_, reject) => setTimeout(() => reject(new Error(`LLM request timed out after ${timeoutMs / 1e3}s`)), timeoutMs)
2118
+ )
2119
+ ]);
2114
2120
  const summary = response.text.split("\n")[0].substring(0, 80) + "...";
2115
2121
  emitAgentEvent({
2116
2122
  type: "thought",
@@ -3148,12 +3154,17 @@ Requirements:
3148
3154
  }
3149
3155
  try {
3150
3156
  const prodFlag = production ? "--prod" : "";
3151
- const command = `npx vercel ${prodFlag} --yes --token ${this.vercelToken}`;
3152
- this.log(`Running: npx vercel ${prodFlag} --yes --token ***`, "info");
3157
+ const command = `npx vercel ${prodFlag} --yes`;
3158
+ this.log(`Running: npx vercel ${prodFlag} --yes`, "info");
3153
3159
  const { stdout, stderr } = await execAsync2(command, {
3154
3160
  cwd,
3155
- timeout: 3e5
3161
+ timeout: 3e5,
3156
3162
  // 5 minutes
3163
+ env: {
3164
+ ...process.env,
3165
+ VERCEL_TOKEN: this.vercelToken
3166
+ // Pass via env, not CLI (security)
3167
+ }
3157
3168
  });
3158
3169
  const output = stdout + stderr;
3159
3170
  const urlMatch = output.match(/https:\/\/[^\s]+\.vercel\.app/);
@@ -3203,10 +3214,15 @@ Requirements:
3203
3214
  try {
3204
3215
  const prodFlag = production ? "--prod" : "";
3205
3216
  const outputDir = await detectBuildOutputDir(cwd);
3206
- const command = `npx netlify deploy ${prodFlag} --auth ${this.netlifyToken} --dir=${outputDir}`;
3217
+ const command = `npx netlify deploy ${prodFlag} --dir=${outputDir}`;
3207
3218
  const { stdout, stderr } = await execAsync2(command, {
3208
3219
  cwd,
3209
- timeout: 3e5
3220
+ timeout: 3e5,
3221
+ env: {
3222
+ ...process.env,
3223
+ NETLIFY_AUTH_TOKEN: this.netlifyToken
3224
+ // Pass via env, not CLI (security)
3225
+ }
3210
3226
  });
3211
3227
  const output = stdout + (stderr || "");
3212
3228
  const urlMatch = output.match(/https:\/\/[^\s]+\.netlify\.app/);
@@ -3592,7 +3608,7 @@ ${qaResult.errors?.join("\n")}`,
3592
3608
  */
3593
3609
  async executeTask(task) {
3594
3610
  this.log(`[${task.assignee}] ${task.title}`, "task");
3595
- const { emitAgentEvent: emitAgentEvent2 } = await import("./event-bus-EDC4P3T5.js");
3611
+ const { emitAgentEvent: emitAgentEvent2 } = await import("./event-bus-Q3WCETQQ.js");
3596
3612
  emitAgentEvent2({
3597
3613
  type: "handoff",
3598
3614
  agentName: task.assignee,
@@ -3647,10 +3663,21 @@ ${qaResult.errors?.join("\n")}`,
3647
3663
  }
3648
3664
  /**
3649
3665
  * Write generated files to disk
3666
+ * SECURITY: All paths are validated to prevent directory traversal attacks
3650
3667
  */
3651
3668
  async writeFiles(files, filesCreated, filesModified) {
3669
+ const workspaceRoot = path5.resolve(this.config.workspaceRoot);
3652
3670
  for (const file of files) {
3653
- const fullPath = path5.join(this.config.workspaceRoot, file.path);
3671
+ const validationResult = this.validateFilePath(file.path, workspaceRoot);
3672
+ if (!validationResult.valid) {
3673
+ this.log(`\u{1F6E1}\uFE0F Blocked unsafe path: ${file.path} - ${validationResult.error}`, "error");
3674
+ await this.appendTrace("file_blocked", {
3675
+ path: file.path,
3676
+ reason: validationResult.error
3677
+ });
3678
+ continue;
3679
+ }
3680
+ const fullPath = validationResult.resolvedPath;
3654
3681
  const dir = path5.dirname(fullPath);
3655
3682
  try {
3656
3683
  await fs5.mkdir(dir, { recursive: true });
@@ -3665,7 +3692,7 @@ ${qaResult.errors?.join("\n")}`,
3665
3692
  }
3666
3693
  await fs5.writeFile(fullPath, file.content, "utf-8");
3667
3694
  const afterContent = file.content;
3668
- const outputPath = path5.join(this.runDir, "outputs", file.path);
3695
+ const outputPath = path5.join(this.runDir, "outputs", validationResult.normalizedPath);
3669
3696
  await fs5.mkdir(path5.dirname(outputPath), { recursive: true });
3670
3697
  await fs5.writeFile(outputPath, file.content, "utf-8");
3671
3698
  const beforeHash = beforeContent ? this.hashContent(beforeContent) : null;
@@ -3692,6 +3719,54 @@ ${qaResult.errors?.join("\n")}`,
3692
3719
  }
3693
3720
  }
3694
3721
  }
3722
+ /**
3723
+ * Validate a file path to prevent directory traversal attacks
3724
+ * SECURITY: Critical function - blocks paths that escape workspace root
3725
+ */
3726
+ validateFilePath(filePath, workspaceRoot) {
3727
+ if (path5.isAbsolute(filePath)) {
3728
+ return {
3729
+ valid: false,
3730
+ error: "Absolute paths not allowed"
3731
+ };
3732
+ }
3733
+ const normalizedPath = path5.normalize(filePath).replace(/^(\.\.[\\/])+/, "");
3734
+ const resolvedPath = path5.resolve(workspaceRoot, filePath);
3735
+ if (filePath.includes("..")) {
3736
+ if (!resolvedPath.startsWith(workspaceRoot + path5.sep) && resolvedPath !== workspaceRoot) {
3737
+ return {
3738
+ valid: false,
3739
+ error: "Path traversal blocked: cannot escape workspace root"
3740
+ };
3741
+ }
3742
+ }
3743
+ if (!resolvedPath.startsWith(workspaceRoot)) {
3744
+ return {
3745
+ valid: false,
3746
+ error: "Path traversal blocked: resolved path outside workspace"
3747
+ };
3748
+ }
3749
+ const sensitivePatterns = [
3750
+ /^\.git\//,
3751
+ /^node_modules\//,
3752
+ /^\.env$/,
3753
+ /^\.env\./,
3754
+ /\/\.git\//
3755
+ ];
3756
+ for (const pattern of sensitivePatterns) {
3757
+ if (pattern.test(normalizedPath)) {
3758
+ return {
3759
+ valid: false,
3760
+ error: `Blocked write to sensitive path: ${normalizedPath}`
3761
+ };
3762
+ }
3763
+ }
3764
+ return {
3765
+ valid: true,
3766
+ resolvedPath,
3767
+ normalizedPath
3768
+ };
3769
+ }
3695
3770
  async initializeRun(userPrompt) {
3696
3771
  this.runId = uuidv42();
3697
3772
  this.runDir = path5.join(this.config.workspaceRoot, "runs", this.runId);
@@ -4185,146 +4260,186 @@ import { render } from "ink";
4185
4260
 
4186
4261
  // src/ui/tui.tsx
4187
4262
  import { useState, useEffect } from "react";
4188
- import { Box, Text, useApp } from "ink";
4189
- import BigText from "ink-big-text";
4190
- import Spinner from "ink-spinner";
4263
+ import { Box, Text, useApp, Spacer } from "ink";
4191
4264
  import TextInput from "ink-text-input";
4192
4265
  import fs7 from "fs";
4193
4266
  import path7 from "path";
4194
4267
  import os from "os";
4195
4268
  import { jsx, jsxs } from "react/jsx-runtime";
4269
+ var THEME2 = {
4270
+ bg: "black",
4271
+ // Main background
4272
+ text: "#E5E5E5",
4273
+ // Primary text
4274
+ dim: "#6B7280",
4275
+ // Secondary/Dim text
4276
+ accent: "#3B82F6",
4277
+ // Brand Blue
4278
+ border: "#374151",
4279
+ // Dark gray border
4280
+ success: "#10B981",
4281
+ warning: "#F59E0B",
4282
+ error: "#EF4444",
4283
+ cardBg: "#111827",
4284
+ // Slightly lighter bg for cards
4285
+ highlight: "#1F2937"
4286
+ // Hover/Selection state
4287
+ };
4196
4288
  var BootScreen = ({ onComplete }) => {
4197
4289
  const [progress, setProgress] = useState(0);
4198
- const [log, setLog] = useState("");
4199
- const logs = [
4200
- "Initializing core systems...",
4201
- "Loading neural modules...",
4202
- "Connecting to Agdi Cloud...",
4203
- "Verifying agent credentials...",
4204
- "Mounting virtual filesystem...",
4205
- "Establishing secure uplink...",
4206
- "Syncing global state...",
4207
- "Boot sequence complete."
4208
- ];
4209
4290
  useEffect(() => {
4210
4291
  const timer = setInterval(() => {
4211
4292
  setProgress((prev) => {
4212
- const next = prev + 5;
4293
+ const next = prev + 10;
4213
4294
  if (next >= 100) {
4214
4295
  clearInterval(timer);
4215
- setTimeout(onComplete, 200);
4296
+ setTimeout(onComplete, 100);
4216
4297
  return 100;
4217
4298
  }
4218
4299
  return next;
4219
4300
  });
4220
- const index = Math.floor(progress / 100 * logs.length);
4221
- setLog(logs[index] || logs[logs.length - 1]);
4222
- }, 30);
4301
+ }, 20);
4223
4302
  return () => clearInterval(timer);
4224
- }, [progress]);
4225
- const width = 60;
4226
- const filled = Math.floor(width * progress / 100);
4227
- const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
4228
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 20, children: [
4229
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(BigText, { text: "AGDI OS", font: "block", align: "center", colors: ["cyan", "blue"] }) }),
4230
- /* @__PURE__ */ jsx(Box, { marginY: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
4231
- bar,
4232
- " ",
4233
- progress,
4234
- "%"
4235
- ] }) }),
4236
- /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
4237
- "\u279C ",
4238
- log
4303
+ }, []);
4304
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 15, children: [
4305
+ /* @__PURE__ */ jsx(Text, { color: THEME2.accent, bold: true, children: "Initializing Agdi Workspace..." }),
4306
+ /* @__PURE__ */ jsxs(Text, { color: THEME2.dim, children: [
4307
+ "\u2588",
4308
+ "\u2588".repeat(progress / 5)
4239
4309
  ] })
4240
4310
  ] });
4241
4311
  };
4242
- var FileExplorer = ({ cwd, lastChange }) => {
4243
- const [files, setFiles] = useState([]);
4244
- useEffect(() => {
4245
- const refresh = () => {
4246
- try {
4247
- const list = fs7.readdirSync(cwd).filter((f) => !f.startsWith(".")).slice(0, 15);
4248
- setFiles(list);
4249
- } catch {
4250
- setFiles(["<Error reading dir>"]);
4251
- }
4252
- };
4253
- refresh();
4254
- const timer = setInterval(refresh, 2e3);
4255
- return () => clearInterval(timer);
4256
- }, [cwd, lastChange]);
4257
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", title: " FILESYSTEM ", width: "30%", children: [
4258
- /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
4259
- " ",
4260
- cwd.length > 30 ? "..." + cwd.slice(-30) : cwd
4312
+ var Header = () => {
4313
+ return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: THEME2.border, paddingX: 1, flexDirection: "row", justifyContent: "space-between", children: [
4314
+ /* @__PURE__ */ jsxs(Box, { children: [
4315
+ /* @__PURE__ */ jsx(Text, { color: THEME2.warning, children: "\u26A1 " }),
4316
+ /* @__PURE__ */ jsx(Text, { bold: true, color: THEME2.text, children: "Starter " }),
4317
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: os.homedir() })
4261
4318
  ] }),
4262
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
4263
- files.map((f, i) => /* @__PURE__ */ jsxs(Text, { color: "white", children: [
4264
- fs7.statSync(path7.join(cwd, f)).isDirectory() ? "\u{1F4C1} " : "\u{1F4C4} ",
4265
- f
4266
- ] }, i)),
4267
- files.length === 0 && /* @__PURE__ */ jsx(Text, { color: "gray", children: " (empty)" })
4268
- ] })
4319
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "Agdi v3.3.4" }) })
4269
4320
  ] });
4270
4321
  };
4271
- var LogPanel = ({ logs }) => {
4272
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", title: " AGENT LOGS ", width: "70%", marginLeft: 1, children: [
4273
- logs.slice(-12).map((log, i) => /* @__PURE__ */ jsxs(Text, { color: "green", children: [
4274
- /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
4275
- "[",
4276
- (/* @__PURE__ */ new Date()).toLocaleTimeString(),
4277
- "]"
4278
- ] }),
4279
- " ",
4280
- log
4281
- ] }, i)),
4282
- logs.length === 0 && /* @__PURE__ */ jsx(Text, { color: "gray", children: "Waiting for agent activity..." })
4322
+ var Footer = ({ status }) => {
4323
+ return /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: THEME2.border, borderTop: false, paddingX: 1, flexDirection: "row", justifyContent: "space-between", children: [
4324
+ /* @__PURE__ */ jsxs(Box, { children: [
4325
+ /* @__PURE__ */ jsx(Text, { color: status === "IDLE" ? THEME2.success : THEME2.warning, children: "\u25CF " }),
4326
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: status })
4327
+ ] }),
4328
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "CONNECT TELEGRAM / WHATSAPP (ALPHA)" }) }),
4329
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: THEME2.error, children: "Stop & Disconnect" }) })
4330
+ ] });
4331
+ };
4332
+ var Sidebar = ({ history }) => {
4333
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: 32, borderStyle: "single", borderColor: THEME2.border, borderRight: false, borderTop: false, borderBottom: false, paddingX: 1, children: [
4334
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, borderStyle: "round", borderColor: THEME2.text, justifyContent: "center", paddingX: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, color: THEME2.text, children: "+ New task" }) }),
4335
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, bold: true, children: "RECENTS" }),
4336
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, children: history.length === 0 ? /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "No recent sessions" }) : history.map((h, i) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", justifyContent: "space-between", marginBottom: 0, children: [
4337
+ /* @__PURE__ */ jsx(Text, { color: THEME2.text, children: h.length > 18 ? h.substring(0, 15) + "..." : h }),
4338
+ /* @__PURE__ */ jsx(Box, { borderStyle: "single", borderColor: THEME2.dim, paddingX: 1, marginLeft: 1, children: /* @__PURE__ */ jsx(Text, { color: THEME2.dim, dimColor: true, children: "STARTER" }) })
4339
+ ] }, i)) })
4340
+ ] });
4341
+ };
4342
+ var ContextPanel = ({ files, agentStatus }) => {
4343
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: 35, borderStyle: "single", borderColor: THEME2.border, borderLeft: false, borderTop: false, borderBottom: false, paddingX: 1, children: [
4344
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: THEME2.border, padding: 0, children: [
4345
+ /* @__PURE__ */ jsx(Text, { bold: true, color: THEME2.text, children: " Context" }),
4346
+ /* @__PURE__ */ jsxs(Box, { marginLeft: 1, flexDirection: "column", children: [
4347
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, bold: true, children: "WORKING FILES" }),
4348
+ files.length === 0 ? /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "None yet." }) : files.slice(0, 5).map((f, i) => (
4349
+ // @ts-expect-error: Ink type definition mismatch for key prop
4350
+ /* @__PURE__ */ jsxs(Text, { color: THEME2.accent, children: [
4351
+ "\u{1F4C4} ",
4352
+ f
4353
+ ] }, i)
4354
+ ))
4355
+ ] })
4356
+ ] }),
4357
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: THEME2.border, padding: 0, children: [
4358
+ /* @__PURE__ */ jsx(Text, { bold: true, color: THEME2.text, children: " Plugins" }),
4359
+ /* @__PURE__ */ jsxs(Box, { marginLeft: 1, flexDirection: "column", children: [
4360
+ /* @__PURE__ */ jsx(Text, { color: THEME2.success, children: "\u2022 Scheduler" }),
4361
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: " Run scheduled jobs" })
4362
+ ] })
4363
+ ] }),
4364
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: THEME2.border, padding: 0, children: [
4365
+ /* @__PURE__ */ jsx(Text, { bold: true, color: THEME2.text, children: " MCP" }),
4366
+ /* @__PURE__ */ jsxs(Box, { marginLeft: 1, flexDirection: "column", children: [
4367
+ /* @__PURE__ */ jsx(Text, { color: THEME2.success, children: "\u2022 chrome-devtools" }),
4368
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: " Connected - npx -y chrome..." })
4369
+ ] })
4370
+ ] }),
4371
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: THEME2.border, padding: 0, children: [
4372
+ /* @__PURE__ */ jsx(Text, { bold: true, color: THEME2.text, children: " Skills" }),
4373
+ /* @__PURE__ */ jsxs(Box, { marginLeft: 1, flexDirection: "column", children: [
4374
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "\u2394 Agent Creator" }),
4375
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, dimColor: true, children: " bash" }),
4376
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "\u2394 Command Creator" }),
4377
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, dimColor: true, children: " bash" })
4378
+ ] })
4379
+ ] })
4283
4380
  ] });
4284
4381
  };
4285
- var ChatPanel = ({ history, onSend, placeholder = "" }) => {
4382
+ var ChatArea = ({ history, onSend, placeholder }) => {
4286
4383
  const [query, setQuery] = useState("");
4287
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", title: " MISSION CONTROL ", height: 12, children: [
4288
- /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, justifyContent: "flex-end", children: history.slice(-5).map((msg, i) => /* @__PURE__ */ jsxs(Box, { marginY: 0, children: [
4289
- /* @__PURE__ */ jsx(Text, { color: msg.role === "user" ? "cyan" : msg.role === "system" ? "yellow" : "magenta", bold: true, children: msg.role === "user" ? "YOU \u203A " : msg.role === "system" ? "SYS \u203A " : "AGDI \u203A " }),
4290
- /* @__PURE__ */ jsx(Text, { children: msg.text })
4384
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "single", borderColor: THEME2.border, borderTop: false, borderBottom: false, marginLeft: 0, marginRight: 0, children: [
4385
+ /* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, padding: 1, justifyContent: "flex-end", children: history.length === 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 10, children: [
4386
+ /* @__PURE__ */ jsx(Box, { borderStyle: "round", borderColor: THEME2.border, padding: 1, marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "\u26A1" }) }),
4387
+ /* @__PURE__ */ jsx(Text, { bold: true, color: THEME2.text, children: "Start a conversation" }),
4388
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "Describe what you want to do, and Agdi will take it" }),
4389
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "from there." })
4390
+ ] }) : history.slice(-10).map((msg, i) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: THEME2.border, padding: 1, children: [
4391
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [
4392
+ /* @__PURE__ */ jsx(Text, { bold: true, color: msg.role === "user" ? THEME2.text : THEME2.accent, children: msg.role === "user" ? "You" : "Agdi" }),
4393
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: msg.role === "system" ? "System" : "Unknown Model" })
4394
+ ] }),
4395
+ /* @__PURE__ */ jsx(Text, { color: THEME2.text, children: msg.text })
4291
4396
  ] }, i)) }),
4292
- /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "gray", marginTop: 1, children: [
4293
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u279C " }),
4294
- /* @__PURE__ */ jsx(
4295
- TextInput,
4296
- {
4297
- value: query,
4298
- onChange: setQuery,
4299
- onSubmit: (val) => {
4300
- onSend(val);
4301
- setQuery("");
4302
- },
4303
- placeholder
4304
- }
4305
- )
4397
+ /* @__PURE__ */ jsxs(Box, { borderStyle: "round", borderColor: THEME2.dim, paddingX: 1, marginX: 2, marginBottom: 1, flexDirection: "column", children: [
4398
+ /* @__PURE__ */ jsx(Box, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: THEME2.dim, italic: true, children: "OpenCode Zen \u2022 Big Pickle \u2022 Variant X-High" }) }),
4399
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
4400
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "> " }),
4401
+ /* @__PURE__ */ jsx(
4402
+ TextInput,
4403
+ {
4404
+ value: query,
4405
+ onChange: setQuery,
4406
+ onSubmit: (val) => {
4407
+ onSend(val);
4408
+ setQuery("");
4409
+ },
4410
+ placeholder
4411
+ }
4412
+ )
4413
+ ] }),
4414
+ /* @__PURE__ */ jsxs(Box, { marginTop: 0, justifyContent: "flex-end", flexDirection: "row", children: [
4415
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "@ Default agent" }),
4416
+ /* @__PURE__ */ jsx(Spacer, {}),
4417
+ /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "\u{1F4CE} \u2794" })
4418
+ ] })
4306
4419
  ] })
4307
4420
  ] });
4308
4421
  };
4309
4422
  var Dashboard = () => {
4310
4423
  const { exit } = useApp();
4311
4424
  const [cwd, setCwd] = useState(process.cwd());
4312
- const [logs, setLogs] = useState(["System ready.", "Initializing Neural Link..."]);
4313
4425
  const [chatHistory, setChatHistory] = useState([]);
4314
4426
  const [step, setStep] = useState("safety");
4315
4427
  const [pendingAction, setPendingAction] = useState(null);
4316
- const [lastFileChange, setLastFileChange] = useState(0);
4317
- const [activeAgent, setActiveAgent] = useState("IDLE");
4428
+ const [activeFiles, setActiveFiles] = useState([]);
4429
+ const [agentStatus, setAgentStatus] = useState("IDLE");
4318
4430
  useEffect(() => {
4319
4431
  const handleEvent = (event) => {
4320
4432
  if (event.type === "handoff") {
4321
- setActiveAgent(event.role.toUpperCase());
4433
+ setAgentStatus(event.role.toUpperCase());
4322
4434
  }
4323
- if (event.type === "log" || event.type === "thought") {
4324
- setLogs((prev) => [...prev, `[${event.agentName}] ${event.message}`]);
4435
+ if (event.type === "thought") {
4436
+ if (!event.message.startsWith("Analyzing")) {
4437
+ setChatHistory((prev) => [...prev, { role: "system", text: `[${event.agentName}] ${event.message}` }]);
4438
+ }
4325
4439
  }
4326
- if (event.message.includes("Created:") || event.message.includes("Modified:")) {
4327
- setLastFileChange(Date.now());
4440
+ if (event.message.includes("Created:")) {
4441
+ const filename = event.message.split("Created:")[1].trim();
4442
+ setActiveFiles((prev) => [filename, ...prev].slice(0, 5));
4328
4443
  }
4329
4444
  };
4330
4445
  agentEventBus.on("agent_event", handleEvent);
@@ -4339,13 +4454,12 @@ var Dashboard = () => {
4339
4454
  const isUnsafe = cwd === home || cwd === root;
4340
4455
  if (isUnsafe) {
4341
4456
  setChatHistory([
4342
- { role: "system", text: `\u26A0\uFE0F SAFETY WARNING: You are running in ${cwd}` },
4343
- { role: "ai", text: "It is unsafe to run here. Should I create a new project folder for you? (yes/no)" }
4457
+ { role: "system", text: `\u26A0\uFE0F Safety Check: Running in ${cwd}` },
4458
+ { role: "ai", text: "This directory is too broad. Should I create a new project folder? (yes/no)" }
4344
4459
  ]);
4345
4460
  setPendingAction("safety_confirm");
4346
4461
  } else {
4347
4462
  setStep("prompt");
4348
- setChatHistory([{ role: "ai", text: "Safety check passed. What are we building today?" }]);
4349
4463
  }
4350
4464
  }
4351
4465
  }, []);
@@ -4354,13 +4468,12 @@ var Dashboard = () => {
4354
4468
  setChatHistory((prev) => [...prev, { role: "user", text: cmd }]);
4355
4469
  if (step === "safety" && pendingAction === "safety_confirm") {
4356
4470
  if (cmd.toLowerCase().startsWith("y")) {
4357
- setChatHistory((prev) => [...prev, { role: "ai", text: "Great. Enter a name for your project folder:" }]);
4471
+ setChatHistory((prev) => [...prev, { role: "ai", text: "Name your project:" }]);
4358
4472
  setPendingAction("create_folder");
4359
4473
  } else {
4360
- setChatHistory((prev) => [...prev, { role: "ai", text: "Okay, proceeding in current directory (AT YOUR OWN RISK)." }]);
4474
+ setChatHistory((prev) => [...prev, { role: "ai", text: "Proceeding in current directory." }]);
4361
4475
  setStep("prompt");
4362
4476
  setPendingAction(null);
4363
- setTimeout(() => setChatHistory((prev) => [...prev, { role: "ai", text: "What are we building today?" }]), 500);
4364
4477
  }
4365
4478
  return;
4366
4479
  }
@@ -4370,20 +4483,17 @@ var Dashboard = () => {
4370
4483
  if (!fs7.existsSync(newPath)) fs7.mkdirSync(newPath);
4371
4484
  process.chdir(newPath);
4372
4485
  setCwd(newPath);
4373
- setLogs((prev) => [...prev, `Switched directory to ${newPath}`]);
4374
4486
  setChatHistory((prev) => [...prev, { role: "system", text: `\u{1F4C2} Switched to ${newPath}` }]);
4375
4487
  setStep("prompt");
4376
4488
  setPendingAction(null);
4377
- setTimeout(() => setChatHistory((prev) => [...prev, { role: "ai", text: "What are we building today?" }]), 500);
4378
4489
  } catch (e) {
4379
- setChatHistory((prev) => [...prev, { role: "system", text: `Error creating folder: ${e}` }]);
4490
+ setChatHistory((prev) => [...prev, { role: "system", text: `Error: ${e}` }]);
4380
4491
  }
4381
4492
  return;
4382
4493
  }
4383
4494
  if (step === "prompt") {
4384
- setChatHistory((prev) => [...prev, { role: "ai", text: "Analyzing requirements..." }]);
4385
4495
  setStep("active");
4386
- setActiveAgent("MANAGER");
4496
+ setAgentStatus("MANAGER");
4387
4497
  const activeConfig = getActiveProvider();
4388
4498
  if (!activeConfig) {
4389
4499
  setChatHistory((prev) => [...prev, { role: "system", text: '\u274C No API key found. Run "agdi auth" first.' }]);
@@ -4396,51 +4506,31 @@ var Dashboard = () => {
4396
4506
  runSquadCommand(cmd, llm, {
4397
4507
  output: cwd,
4398
4508
  verbose: false,
4399
- // We handle logs via event bus now
4400
4509
  deploy: false
4401
4510
  }).then(() => {
4402
- setActiveAgent("IDLE");
4403
- setChatHistory((prev) => [...prev, { role: "ai", text: "\u{1F680} Mission Complete! Check the files." }]);
4511
+ setAgentStatus("IDLE");
4512
+ setChatHistory((prev) => [...prev, { role: "ai", text: "Task completed." }]);
4404
4513
  }).catch((err) => {
4405
- setActiveAgent("ERROR");
4406
- setChatHistory((prev) => [...prev, { role: "system", text: `\u274C Error: ${err.message}` }]);
4514
+ setAgentStatus("ERROR");
4515
+ setChatHistory((prev) => [...prev, { role: "system", text: `Error: ${err.message}` }]);
4407
4516
  });
4408
4517
  }
4409
4518
  };
4410
- const getStatusColor = () => {
4411
- switch (activeAgent) {
4412
- case "MANAGER":
4413
- return "magenta";
4414
- case "FRONTEND":
4415
- return "cyan";
4416
- case "BACKEND":
4417
- return "blue";
4418
- case "QA":
4419
- return "yellow";
4420
- case "DEVOPS":
4421
- return "green";
4422
- case "ERROR":
4423
- return "red";
4424
- default:
4425
- return "gray";
4426
- }
4427
- };
4428
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", padding: 1, height: "100%", children: [
4429
- /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", marginBottom: 1, children: [
4430
- /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "AGDI v3.3.2 [ONLINE]" }),
4431
- /* @__PURE__ */ jsxs(Text, { color: getStatusColor(), children: [
4432
- "AGENT: ",
4433
- activeAgent,
4434
- " ",
4435
- activeAgent !== "IDLE" && /* @__PURE__ */ jsx(Spinner, { type: "dots" })
4436
- ] }),
4437
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "NET: CONNECTED" })
4438
- ] }),
4519
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", height: 35, width: 120, children: [
4520
+ /* @__PURE__ */ jsx(Header, {}),
4439
4521
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [
4440
- /* @__PURE__ */ jsx(FileExplorer, { cwd, lastChange: lastFileChange }),
4441
- /* @__PURE__ */ jsx(LogPanel, { logs })
4522
+ /* @__PURE__ */ jsx(Sidebar, { history: ["New session - " + (/* @__PURE__ */ new Date()).toLocaleDateString()] }),
4523
+ /* @__PURE__ */ jsx(
4524
+ ChatArea,
4525
+ {
4526
+ history: chatHistory,
4527
+ onSend: handleCommand,
4528
+ placeholder: step === "prompt" ? "Ask Agdi..." : "Reply..."
4529
+ }
4530
+ ),
4531
+ /* @__PURE__ */ jsx(ContextPanel, { files: activeFiles, agentStatus })
4442
4532
  ] }),
4443
- /* @__PURE__ */ jsx(Box, { marginTop: 0, children: /* @__PURE__ */ jsx(ChatPanel, { history: chatHistory, onSend: handleCommand, placeholder: step === "prompt" ? "Describe your app..." : "" }) })
4533
+ /* @__PURE__ */ jsx(Footer, { status: agentStatus })
4444
4534
  ] });
4445
4535
  };
4446
4536
  var App = () => {
@@ -4465,7 +4555,7 @@ ${chalk14.cyan(`/_/ |_|\\_, /\\__,_//_/ `)}
4465
4555
  ${chalk14.cyan(` /____/ `)}
4466
4556
  `;
4467
4557
  var program = new Command();
4468
- program.name("agdi").description(chalk14.cyan("\u{1F9B8} The Autonomous AI Employee")).version("3.3.2").option("-y, --yes", "Auto-approve all prompts (headless/CI mode)").option("-m, --minimal", "Generate only the requested file(s), not a full app").option("-d, --dry-run", "Show what would be created without writing files").option("--saas", "Generate a production SaaS blueprint (Next.js + Prisma + Postgres + Stripe)");
4558
+ program.name("agdi").description(chalk14.cyan("\u{1F9B8} The Autonomous AI Employee")).version("3.3.4").option("-y, --yes", "Auto-approve all prompts (headless/CI mode)").option("-m, --minimal", "Generate only the requested file(s), not a full app").option("-d, --dry-run", "Show what would be created without writing files").option("--saas", "Generate a production SaaS blueprint (Next.js + Prisma + Postgres + Stripe)");
4469
4559
  program.hook("preAction", (thisCommand) => {
4470
4560
  const opts = thisCommand.opts();
4471
4561
  if (opts.yes) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agdi",
3
- "version": "3.3.2",
3
+ "version": "3.3.5",
4
4
  "description": "AI-powered app generator - build full-stack apps from natural language in your terminal",
5
5
  "type": "module",
6
6
  "bin": {
@@ -90,4 +90,4 @@
90
90
  "ts-node": "^10.9.2",
91
91
  "uuid": "^13.0.0"
92
92
  }
93
- }
93
+ }
@@ -1,14 +0,0 @@
1
- // src/core/event-bus.ts
2
- import { EventEmitter } from "events";
3
- var agentEventBus = new EventEmitter();
4
- function emitAgentEvent(event) {
5
- agentEventBus.emit("agent_event", {
6
- ...event,
7
- timestamp: Date.now()
8
- });
9
- }
10
-
11
- export {
12
- agentEventBus,
13
- emitAgentEvent
14
- };