@aman_asmuei/aman-agent 0.17.0 → 0.17.2

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/dist/index.js CHANGED
@@ -232,7 +232,7 @@ function toAnthropicMessages(messages) {
232
232
  function createAnthropicClient(apiKey, model) {
233
233
  const client = new Anthropic({ apiKey });
234
234
  return {
235
- async chat(systemPrompt, messages, onChunk, tools) {
235
+ async chat(systemPrompt, messages, onChunk, tools, options) {
236
236
  const anthropicMessages = toAnthropicMessages(messages);
237
237
  const hasTools = tools && tools.length > 0;
238
238
  try {
@@ -242,7 +242,7 @@ function createAnthropicClient(apiKey, model) {
242
242
  let currentBlockIndex = -1;
243
243
  const createParams = {
244
244
  model,
245
- max_tokens: 8192,
245
+ max_tokens: options?.maxOutputTokens ?? 8192,
246
246
  system: systemPrompt,
247
247
  messages: anthropicMessages,
248
248
  stream: true
@@ -330,7 +330,9 @@ function createAnthropicClient(apiKey, model) {
330
330
 
331
331
  // src/llm/openai.ts
332
332
  import OpenAI from "openai";
333
- function toOpenAIMessages(systemPrompt, messages) {
333
+
334
+ // src/llm/openai-compat.ts
335
+ function toOpenAICompatibleMessages(systemPrompt, messages) {
334
336
  const result = [
335
337
  { role: "system", content: systemPrompt }
336
338
  ];
@@ -398,18 +400,20 @@ function toOpenAIMessages(systemPrompt, messages) {
398
400
  }
399
401
  return result;
400
402
  }
403
+
404
+ // src/llm/openai.ts
401
405
  function createOpenAIClient(apiKey, model) {
402
406
  const client = new OpenAI({ apiKey });
403
407
  return {
404
- async chat(systemPrompt, messages, onChunk, tools) {
405
- const openaiMessages = toOpenAIMessages(systemPrompt, messages);
408
+ async chat(systemPrompt, messages, onChunk, tools, options) {
409
+ const openaiMessages = toOpenAICompatibleMessages(systemPrompt, messages);
406
410
  const hasTools = tools && tools.length > 0;
407
411
  try {
408
412
  let fullText = "";
409
413
  const toolCallAccumulators = /* @__PURE__ */ new Map();
410
414
  const createParams = {
411
415
  model,
412
- max_tokens: 8192,
416
+ max_tokens: options?.maxOutputTokens ?? 8192,
413
417
  messages: openaiMessages,
414
418
  stream: true
415
419
  };
@@ -495,74 +499,6 @@ function createOpenAIClient(apiKey, model) {
495
499
 
496
500
  // src/llm/ollama.ts
497
501
  import OpenAI2 from "openai";
498
- function toOllamaMessages(systemPrompt, messages) {
499
- const result = [
500
- { role: "system", content: systemPrompt }
501
- ];
502
- for (const m of messages) {
503
- if (typeof m.content === "string") {
504
- result.push({
505
- role: m.role,
506
- content: m.content
507
- });
508
- } else if (m.role === "assistant") {
509
- const textParts = m.content.filter((b) => b.type === "text");
510
- const toolUseParts = m.content.filter((b) => b.type === "tool_use");
511
- const text2 = textParts.map((b) => "text" in b ? b.text : "").join("");
512
- if (toolUseParts.length > 0) {
513
- result.push({
514
- role: "assistant",
515
- content: text2 || null,
516
- tool_calls: toolUseParts.map((b) => ({
517
- id: "id" in b ? b.id : "",
518
- type: "function",
519
- function: {
520
- name: "name" in b ? b.name : "",
521
- arguments: JSON.stringify("input" in b ? b.input : {})
522
- }
523
- }))
524
- });
525
- } else {
526
- result.push({ role: "assistant", content: text2 });
527
- }
528
- } else if (m.role === "user") {
529
- const toolResults = m.content.filter((b) => b.type === "tool_result");
530
- if (toolResults.length > 0) {
531
- for (const tr of toolResults) {
532
- if (tr.type === "tool_result") {
533
- result.push({
534
- role: "tool",
535
- tool_call_id: tr.tool_use_id,
536
- content: tr.content
537
- });
538
- }
539
- }
540
- } else {
541
- const hasImages = m.content.some((b) => b.type === "image");
542
- if (hasImages) {
543
- const parts = [];
544
- for (const b of m.content) {
545
- if (b.type === "text") {
546
- parts.push({ type: "text", text: b.text });
547
- } else if (b.type === "image") {
548
- parts.push({
549
- type: "image_url",
550
- image_url: {
551
- url: `data:${b.source.media_type};base64,${b.source.data}`
552
- }
553
- });
554
- }
555
- }
556
- result.push({ role: "user", content: parts });
557
- } else {
558
- const text2 = m.content.map((b) => "text" in b ? b.text : "").join("");
559
- result.push({ role: "user", content: text2 });
560
- }
561
- }
562
- }
563
- }
564
- return result;
565
- }
566
502
  function createOllamaClient(model, baseURL) {
567
503
  const client = new OpenAI2({
568
504
  baseURL: baseURL || "http://localhost:11434/v1",
@@ -570,15 +506,15 @@ function createOllamaClient(model, baseURL) {
570
506
  // Ollama doesn't require a real key
571
507
  });
572
508
  return {
573
- async chat(systemPrompt, messages, onChunk, tools) {
574
- const ollamaMessages = toOllamaMessages(systemPrompt, messages);
509
+ async chat(systemPrompt, messages, onChunk, tools, options) {
510
+ const ollamaMessages = toOpenAICompatibleMessages(systemPrompt, messages);
575
511
  const hasTools = tools && tools.length > 0;
576
512
  try {
577
513
  let fullText = "";
578
514
  const toolCallAccumulators = /* @__PURE__ */ new Map();
579
515
  const createParams = {
580
516
  model,
581
- max_tokens: 8192,
517
+ max_tokens: options?.maxOutputTokens ?? 4096,
582
518
  messages: ollamaMessages,
583
519
  stream: true
584
520
  };
@@ -730,12 +666,18 @@ async function withRetry(fn, options) {
730
666
  }
731
667
 
732
668
  // src/mcp/client.ts
669
+ var TOOL_CALL_TIMEOUT_MS = 3e4;
733
670
  var McpManager = class {
734
671
  connections = [];
735
672
  tools = [];
736
- async connect(name, command, args) {
673
+ async connect(name, command, args, env) {
737
674
  try {
738
- const transport = new StdioClientTransport({ command, args, stderr: "pipe" });
675
+ const transport = new StdioClientTransport({
676
+ command,
677
+ args,
678
+ stderr: "pipe",
679
+ env: env ? env : void 0
680
+ });
739
681
  const client = new Client({
740
682
  name: `aman-agent-${name}`,
741
683
  version: "0.1.0"
@@ -746,9 +688,16 @@ var McpManager = class {
746
688
  log.debug("mcp", `[${name} stderr] ${chunk.toString().trim()}`);
747
689
  });
748
690
  }
749
- this.connections.push({ name, client, transport });
691
+ this.connections.push({ name, client, transport, connectParams: { command, args, env } });
750
692
  const toolsResult = await client.listTools();
751
693
  for (const tool of toolsResult.tools) {
694
+ const existing = this.tools.find((t) => t.name === tool.name);
695
+ if (existing) {
696
+ log.warn(
697
+ "mcp",
698
+ `Warning: tool "${tool.name}" from server "${name}" shadows existing tool from "${existing.serverName}"`
699
+ );
700
+ }
752
701
  this.tools.push({
753
702
  name: tool.name,
754
703
  description: tool.description || "",
@@ -771,7 +720,15 @@ var McpManager = class {
771
720
  if (!conn) return `Error: server ${tool.serverName} not connected`;
772
721
  try {
773
722
  const result = await withRetry(
774
- () => conn.client.callTool({ name: toolName, arguments: args }),
723
+ () => Promise.race([
724
+ conn.client.callTool({ name: toolName, arguments: args }),
725
+ new Promise(
726
+ (_, reject) => setTimeout(
727
+ () => reject(new Error(`Tool ${toolName} timed out after 30s`)),
728
+ TOOL_CALL_TIMEOUT_MS
729
+ )
730
+ )
731
+ ]),
775
732
  { maxAttempts: 2, baseDelay: 500, retryable: (err) => err.message.includes("ETIMEDOUT") || err.message.includes("timeout") }
776
733
  );
777
734
  if (result.content && Array.isArray(result.content)) {
@@ -782,6 +739,23 @@ var McpManager = class {
782
739
  return `Error calling ${toolName}: ${error instanceof Error ? error.message : String(error)}`;
783
740
  }
784
741
  }
742
+ async reconnect(name) {
743
+ const connIndex = this.connections.findIndex((c) => c.name === name);
744
+ if (connIndex === -1) {
745
+ log.error("mcp", `Cannot reconnect: no connection found for "${name}"`);
746
+ return;
747
+ }
748
+ const conn = this.connections[connIndex];
749
+ const { command, args, env } = conn.connectParams;
750
+ try {
751
+ await conn.client.close();
752
+ } catch (err) {
753
+ log.debug("mcp", `Error closing old connection for ${name}`, err);
754
+ }
755
+ this.connections.splice(connIndex, 1);
756
+ this.tools = this.tools.filter((t) => t.serverName !== name);
757
+ await this.connect(name, command, args, env);
758
+ }
785
759
  async disconnect() {
786
760
  for (const conn of this.connections) {
787
761
  try {
@@ -878,7 +852,10 @@ import {
878
852
  consolidateMemories,
879
853
  cosineSimilarity,
880
854
  preloadEmbeddings,
881
- buildVectorIndex
855
+ buildVectorIndex,
856
+ recallMemories,
857
+ generateEmbedding,
858
+ getVectorIndex
882
859
  } from "@aman_asmuei/amem-core";
883
860
  import path5 from "path";
884
861
  import os5 from "os";
@@ -890,7 +867,27 @@ async function initMemory(project) {
890
867
  const amemDir = process.env.AMEM_DIR ?? path5.join(os5.homedir(), ".amem");
891
868
  if (!fs5.existsSync(amemDir)) fs5.mkdirSync(amemDir, { recursive: true });
892
869
  const dbPath = process.env.AMEM_DB ?? path5.join(amemDir, "memory.db");
893
- db = createDatabase(dbPath);
870
+ try {
871
+ db = createDatabase(dbPath);
872
+ } catch (err) {
873
+ const backupPath = `${dbPath}.corrupt.${Date.now()}`;
874
+ try {
875
+ if (fs5.existsSync(dbPath)) {
876
+ fs5.renameSync(dbPath, backupPath);
877
+ if (fs5.existsSync(`${dbPath}-wal`)) fs5.unlinkSync(`${dbPath}-wal`);
878
+ if (fs5.existsSync(`${dbPath}-shm`)) fs5.unlinkSync(`${dbPath}-shm`);
879
+ console.error(`[amem] Database corrupted \u2014 backed up to ${backupPath}`);
880
+ console.error("[amem] Creating fresh database. Previous memories are in the backup file.");
881
+ db = createDatabase(dbPath);
882
+ } else {
883
+ throw err;
884
+ }
885
+ } catch {
886
+ console.error(`[amem] Failed to initialize memory: ${err instanceof Error ? err.message : String(err)}`);
887
+ console.error(`[amem] Try deleting ${amemDir} to reset: rm -rf ${amemDir}`);
888
+ throw err;
889
+ }
890
+ }
894
891
  currentProject = project ?? "global";
895
892
  preloadEmbeddings();
896
893
  setTimeout(() => {
@@ -933,16 +930,86 @@ function memoryLog(sessionId, role, content) {
933
930
  });
934
931
  }
935
932
  function reminderCheck() {
936
- return getDb().checkReminders();
933
+ const all = getDb().checkReminders();
934
+ return all.filter((r) => r.scope === "global" || r.scope === currentProject);
935
+ }
936
+ async function memoryForget(opts) {
937
+ const db2 = getDb();
938
+ if (opts.id) {
939
+ const fullId = db2.resolveId(opts.id) ?? opts.id;
940
+ const memory = db2.getById(fullId);
941
+ if (!memory) return { deleted: 0, message: `Memory ${opts.id} not found.` };
942
+ db2.deleteMemory(fullId);
943
+ const vecIdx = getVectorIndex();
944
+ if (vecIdx) vecIdx.remove(fullId);
945
+ return { deleted: 1, message: `Deleted: "${memory.content}" (${memory.type})` };
946
+ }
947
+ if (opts.type) {
948
+ const all = db2.getAllForProject(currentProject);
949
+ const matches = all.filter((m) => m.type === opts.type);
950
+ if (matches.length === 0) return { deleted: 0, message: `No memories of type "${opts.type}" found.` };
951
+ const vecIdx = getVectorIndex();
952
+ for (const m of matches) {
953
+ db2.deleteMemory(m.id);
954
+ if (vecIdx) vecIdx.remove(m.id);
955
+ }
956
+ return { deleted: matches.length, message: `Deleted ${matches.length} "${opts.type}" memories.` };
957
+ }
958
+ if (opts.query) {
959
+ const queryEmbedding = await generateEmbedding(opts.query);
960
+ const matches = recallMemories(db2, { query: opts.query, queryEmbedding, limit: 50, minConfidence: 0, scope: currentProject });
961
+ if (matches.length === 0) return { deleted: 0, message: `No memories found matching "${opts.query}".` };
962
+ const vecIdx = getVectorIndex();
963
+ for (const m of matches) {
964
+ db2.deleteMemory(m.id);
965
+ if (vecIdx) vecIdx.remove(m.id);
966
+ }
967
+ return { deleted: matches.length, message: `Deleted ${matches.length} memories matching "${opts.query}".` };
968
+ }
969
+ return { deleted: 0, message: "Provide an id, type, or query to forget." };
970
+ }
971
+ var memoryConfig = {};
972
+ function setMemoryConfig(config) {
973
+ memoryConfig = config;
974
+ }
975
+ function getMaxRecallTokens() {
976
+ return memoryConfig.maxRecallTokens ?? 1500;
937
977
  }
938
978
  function memoryConsolidate(dryRun = false) {
939
979
  return consolidateMemories(getDb(), cosineSimilarity, {
940
980
  dryRun,
941
- maxStaleDays: 90,
942
- minConfidence: 0.3,
943
- minAccessCount: 0
981
+ maxStaleDays: memoryConfig.maxStaleDays ?? 90,
982
+ minConfidence: memoryConfig.minConfidence ?? 0.3,
983
+ minAccessCount: memoryConfig.minAccessCount ?? 0
944
984
  });
945
985
  }
986
+ function isMemoryInitialized() {
987
+ return db !== null;
988
+ }
989
+ function memoryStats() {
990
+ return getDb().getStats();
991
+ }
992
+ function memoryExport() {
993
+ return getDb().getAllForProject(currentProject);
994
+ }
995
+ function memorySince(hours) {
996
+ const since = Date.now() - hours * 60 * 60 * 1e3;
997
+ const all = getDb().getMemoriesSince(since);
998
+ return all.filter((m) => m.scope === "global" || m.scope === currentProject);
999
+ }
1000
+ function memorySearch(query, limit) {
1001
+ return getDb().fullTextSearch(query, limit, currentProject);
1002
+ }
1003
+ function reminderSet(content, dueAt) {
1004
+ return getDb().insertReminder(content, dueAt ?? null, currentProject);
1005
+ }
1006
+ function reminderList(includeCompleted) {
1007
+ return getDb().listReminders(includeCompleted, currentProject);
1008
+ }
1009
+ function reminderComplete(id) {
1010
+ const fullId = getDb().resolveReminderId(id) ?? id;
1011
+ return getDb().completeReminder(fullId);
1012
+ }
946
1013
 
947
1014
  // src/profile-templates.ts
948
1015
  import fs6 from "fs";
@@ -1664,7 +1731,8 @@ function parseCommand(input) {
1664
1731
  const trimmed = input.trim();
1665
1732
  const parts = trimmed.split(/\s+/);
1666
1733
  const base = parts[0].toLowerCase().replace(/^\//, "");
1667
- const action = parts.length > 1 ? parts[1].toLowerCase() : void 0;
1734
+ let action = parts.length > 1 ? parts[1].toLowerCase() : void 0;
1735
+ if (action === "--help" || action === "-h") action = "help";
1668
1736
  const args = parts.slice(2);
1669
1737
  return { base, action, args };
1670
1738
  }
@@ -1702,7 +1770,14 @@ async function handleIdentityCommand(action, args, ctx) {
1702
1770
  const output = await mcpWrite(ctx, "identity", "identity_update_section", { section, content });
1703
1771
  return { handled: true, output };
1704
1772
  }
1705
- return { handled: true, output: pc3.yellow(`Unknown action: /identity ${action}. Use /identity or /identity update <section>.`) };
1773
+ if (action === "help") {
1774
+ return { handled: true, output: [
1775
+ pc3.bold("Identity commands:"),
1776
+ ` ${pc3.cyan("/identity")} View current identity`,
1777
+ ` ${pc3.cyan("/identity update")} <section> Update a section`
1778
+ ].join("\n") };
1779
+ }
1780
+ return { handled: true, output: pc3.yellow(`Unknown action: /identity ${action}. Try /identity --help`) };
1706
1781
  }
1707
1782
  async function handleRulesCommand(action, args, ctx) {
1708
1783
  const home2 = os9.homedir();
@@ -1733,7 +1808,16 @@ async function handleRulesCommand(action, args, ctx) {
1733
1808
  const output = await mcpWrite(ctx, "rules", "rules_toggle", { category: args[0], index: parseInt(args[1], 10) });
1734
1809
  return { handled: true, output };
1735
1810
  }
1736
- return { handled: true, output: pc3.yellow(`Unknown action: /rules ${action}. Use /rules [add|remove|toggle].`) };
1811
+ if (action === "help") {
1812
+ return { handled: true, output: [
1813
+ pc3.bold("Rules commands:"),
1814
+ ` ${pc3.cyan("/rules")} View current rules`,
1815
+ ` ${pc3.cyan("/rules add")} <category> <text> Add a rule`,
1816
+ ` ${pc3.cyan("/rules remove")} <category> <idx> Remove a rule`,
1817
+ ` ${pc3.cyan("/rules toggle")} <category> <idx> Toggle a rule`
1818
+ ].join("\n") };
1819
+ }
1820
+ return { handled: true, output: pc3.yellow(`Unknown action: /rules ${action}. Try /rules --help`) };
1737
1821
  }
1738
1822
  async function handleWorkflowsCommand(action, args, ctx) {
1739
1823
  const home2 = os9.homedir();
@@ -1755,7 +1839,15 @@ async function handleWorkflowsCommand(action, args, ctx) {
1755
1839
  const output = await mcpWrite(ctx, "workflows", "workflow_remove", { name: args.join(" ") });
1756
1840
  return { handled: true, output };
1757
1841
  }
1758
- return { handled: true, output: pc3.yellow(`Unknown action: /workflows ${action}. Use /workflows [add|remove].`) };
1842
+ if (action === "help") {
1843
+ return { handled: true, output: [
1844
+ pc3.bold("Workflow commands:"),
1845
+ ` ${pc3.cyan("/workflows")} View current workflows`,
1846
+ ` ${pc3.cyan("/workflows add")} <name> Add a workflow`,
1847
+ ` ${pc3.cyan("/workflows remove")} <name> Remove a workflow`
1848
+ ].join("\n") };
1849
+ }
1850
+ return { handled: true, output: pc3.yellow(`Unknown action: /workflows ${action}. Try /workflows --help`) };
1759
1851
  }
1760
1852
  var AKIT_REGISTRY = [
1761
1853
  { name: "web-search", description: "Search the web for current information", category: "search", mcp: { package: "@anthropic/web-search", command: "npx", args: ["-y", "@anthropic/web-search"] } },
@@ -1977,7 +2069,15 @@ async function handleSkillsCommand(action, args, ctx) {
1977
2069
  const output = await mcpWrite(ctx, "skills", "skill_uninstall", { name: args.join(" ") });
1978
2070
  return { handled: true, output };
1979
2071
  }
1980
- return { handled: true, output: pc3.yellow(`Unknown action: /skills ${action}. Use /skills [install|uninstall].`) };
2072
+ if (action === "help") {
2073
+ return { handled: true, output: [
2074
+ pc3.bold("Skills commands:"),
2075
+ ` ${pc3.cyan("/skills")} View installed skills`,
2076
+ ` ${pc3.cyan("/skills install")} <name> Install a skill`,
2077
+ ` ${pc3.cyan("/skills uninstall")} <name> Uninstall a skill`
2078
+ ].join("\n") };
2079
+ }
2080
+ return { handled: true, output: pc3.yellow(`Unknown action: /skills ${action}. Try /skills --help`) };
1981
2081
  }
1982
2082
  async function handleEvalCommand(action, args, ctx) {
1983
2083
  const home2 = os9.homedir();
@@ -2007,7 +2107,7 @@ async function handleMemoryCommand(action, args, ctx) {
2007
2107
  return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
2008
2108
  }
2009
2109
  }
2010
- if (action && !["search", "clear", "timeline"].includes(action)) {
2110
+ if (action && !["search", "clear", "timeline", "stats", "export", "since", "fts", "help"].includes(action)) {
2011
2111
  try {
2012
2112
  const topic = [action, ...args].join(" ");
2013
2113
  const result = await memoryContext(topic);
@@ -2033,10 +2133,18 @@ async function handleMemoryCommand(action, args, ctx) {
2033
2133
  }
2034
2134
  if (action === "clear") {
2035
2135
  if (args.length < 1) {
2036
- return { handled: true, output: pc3.yellow("Usage: /memory clear <category>") };
2136
+ return { handled: true, output: pc3.yellow("Usage: /memory clear <query> \u2014 delete memories matching a search query\n /memory clear --type <type> \u2014 delete all memories of a type (correction|decision|pattern|preference|topology|fact)") };
2137
+ }
2138
+ try {
2139
+ if (args[0] === "--type" && args[1]) {
2140
+ const result2 = await memoryForget({ type: args[1] });
2141
+ return { handled: true, output: result2.deleted > 0 ? pc3.green(result2.message) : pc3.dim(result2.message) };
2142
+ }
2143
+ const result = await memoryForget({ query: args.join(" ") });
2144
+ return { handled: true, output: result.deleted > 0 ? pc3.green(result.message) : pc3.dim(result.message) };
2145
+ } catch (err) {
2146
+ return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
2037
2147
  }
2038
- const output = await mcpWrite(ctx, "memory", "memory_forget", { category: args[0] });
2039
- return { handled: true, output };
2040
2148
  }
2041
2149
  if (action === "timeline") {
2042
2150
  try {
@@ -2082,11 +2190,115 @@ async function handleMemoryCommand(action, args, ctx) {
2082
2190
  return { handled: true, output: pc3.red("Failed to retrieve memory timeline.") };
2083
2191
  }
2084
2192
  }
2085
- return { handled: true, output: pc3.yellow(`Unknown action: /memory ${action}. Use /memory [search|clear|timeline].`) };
2193
+ if (action === "stats") {
2194
+ try {
2195
+ const stats = memoryStats();
2196
+ const lines = [pc3.bold("Memory Statistics:"), ""];
2197
+ lines.push(` Total memories: ${pc3.bold(String(stats.total))}`);
2198
+ if (Object.keys(stats.byType).length > 0) {
2199
+ lines.push("");
2200
+ lines.push(` ${pc3.dim("By type:")}`);
2201
+ for (const [type, count] of Object.entries(stats.byType)) {
2202
+ lines.push(` ${type.padEnd(16)} ${count}`);
2203
+ }
2204
+ }
2205
+ return { handled: true, output: lines.join("\n") };
2206
+ } catch (err) {
2207
+ return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
2208
+ }
2209
+ }
2210
+ if (action === "export") {
2211
+ try {
2212
+ const format = args[0] === "json" ? "json" : "markdown";
2213
+ const memories = memoryExport();
2214
+ if (memories.length === 0) {
2215
+ return { handled: true, output: pc3.dim("No memories to export.") };
2216
+ }
2217
+ if (format === "json") {
2218
+ const jsonOut = memories.map((m) => ({ id: m.id, type: m.type, content: m.content, tags: m.tags, confidence: m.confidence, createdAt: m.createdAt, tier: m.tier }));
2219
+ return { handled: true, output: JSON.stringify(jsonOut, null, 2) };
2220
+ }
2221
+ const lines = [`# Memory Export (${memories.length} memories)`, ""];
2222
+ for (const m of memories) {
2223
+ const date = new Date(m.createdAt).toLocaleDateString();
2224
+ const tags = m.tags.length > 0 ? ` [${m.tags.map((t) => `#${t}`).join(", ")}]` : "";
2225
+ lines.push(`- **[${m.type}]** ${m.content}${tags} ${pc3.dim(`(${date}, ${Math.round(m.confidence * 100)}%)`)}`);
2226
+ }
2227
+ return { handled: true, output: lines.join("\n") };
2228
+ } catch (err) {
2229
+ return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
2230
+ }
2231
+ }
2232
+ if (action === "since") {
2233
+ try {
2234
+ let hours = 24;
2235
+ if (args[0]) {
2236
+ const match = args[0].match(/^(\d+)(h|d|w)$/);
2237
+ if (match) {
2238
+ const value = parseInt(match[1], 10);
2239
+ const unit = match[2];
2240
+ if (unit === "h") hours = value;
2241
+ else if (unit === "d") hours = value * 24;
2242
+ else if (unit === "w") hours = value * 24 * 7;
2243
+ } else {
2244
+ return { handled: true, output: pc3.yellow("Usage: /memory since <Nh|Nd|Nw> (e.g., 24h, 7d, 1w)") };
2245
+ }
2246
+ }
2247
+ const memories = memorySince(hours);
2248
+ if (memories.length === 0) {
2249
+ return { handled: true, output: pc3.dim(`No memories in the last ${args[0] || "24h"}.`) };
2250
+ }
2251
+ const lines = [pc3.bold(`Memories since ${args[0] || "24h"} (${memories.length}):`), ""];
2252
+ for (const m of memories) {
2253
+ const age = Math.round((Date.now() - m.createdAt) / 36e5);
2254
+ const ageStr = age < 1 ? "<1h ago" : `${age}h ago`;
2255
+ lines.push(` ${pc3.dim(ageStr.padEnd(10))} [${m.type}] ${m.content}`);
2256
+ }
2257
+ return { handled: true, output: lines.join("\n") };
2258
+ } catch (err) {
2259
+ return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
2260
+ }
2261
+ }
2262
+ if (action === "fts") {
2263
+ if (args.length < 1) {
2264
+ return { handled: true, output: pc3.yellow("Usage: /memory fts <query...> \u2014 full-text search") };
2265
+ }
2266
+ try {
2267
+ const query = args.join(" ");
2268
+ const results = memorySearch(query, 20);
2269
+ if (results.length === 0) {
2270
+ return { handled: true, output: pc3.dim(`No results for full-text search: "${query}".`) };
2271
+ }
2272
+ const lines = [pc3.bold(`FTS results for "${query}" (${results.length}):`), ""];
2273
+ for (const m of results) {
2274
+ const tags = m.tags.length > 0 ? ` ${pc3.dim(m.tags.map((t) => `#${t}`).join(" "))}` : "";
2275
+ lines.push(` [${m.type}] ${m.content}${tags}`);
2276
+ }
2277
+ return { handled: true, output: lines.join("\n") };
2278
+ } catch (err) {
2279
+ return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
2280
+ }
2281
+ }
2282
+ if (action === "help") {
2283
+ return { handled: true, output: [
2284
+ pc3.bold("Memory commands:"),
2285
+ ` ${pc3.cyan("/memory")} View recent context`,
2286
+ ` ${pc3.cyan("/memory")} <topic> Context for a topic`,
2287
+ ` ${pc3.cyan("/memory search")} <query> Search memories (semantic)`,
2288
+ ` ${pc3.cyan("/memory fts")} <query> Full-text search (FTS5)`,
2289
+ ` ${pc3.cyan("/memory since")} <Nh|Nd|Nw> Memories from time window`,
2290
+ ` ${pc3.cyan("/memory stats")} Show memory statistics`,
2291
+ ` ${pc3.cyan("/memory export")} [json] Export all memories`,
2292
+ ` ${pc3.cyan("/memory timeline")} View memory timeline`,
2293
+ ` ${pc3.cyan("/memory clear")} <query> Delete matching memories`,
2294
+ ` ${pc3.cyan("/memory clear --type")} <type> Delete all of a type`
2295
+ ].join("\n") };
2296
+ }
2297
+ return { handled: true, output: pc3.yellow(`Unknown action: /memory ${action}. Try /memory --help`) };
2086
2298
  }
2087
2299
  function handleStatusCommand(ctx) {
2088
2300
  const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
2089
- const amemConnected = mcpToolCount > 0;
2301
+ const amemConnected = isMemoryInitialized();
2090
2302
  const status = getEcosystemStatus(mcpToolCount, amemConnected);
2091
2303
  const lines = [pc3.bold("Aman Ecosystem Dashboard"), ""];
2092
2304
  for (const layer of status.layers) {
@@ -2102,7 +2314,7 @@ function handleStatusCommand(ctx) {
2102
2314
  }
2103
2315
  function handleDoctorCommand(ctx) {
2104
2316
  const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
2105
- const amemConnected = mcpToolCount > 0;
2317
+ const amemConnected = isMemoryInitialized();
2106
2318
  const status = getEcosystemStatus(mcpToolCount, amemConnected);
2107
2319
  const lines = [pc3.bold("Aman Health Check"), ""];
2108
2320
  let healthy = 0;
@@ -2136,7 +2348,7 @@ function handleDoctorCommand(ctx) {
2136
2348
  }
2137
2349
  lines.push(` ${status.amemConnected ? pc3.green("\u2713") : pc3.red("\u2717")} ${"Memory".padEnd(12)} ${status.amemConnected ? pc3.green("connected") : pc3.red("not connected")}`);
2138
2350
  if (!status.amemConnected) {
2139
- lines.push(` ${pc3.dim("\u2192 Fix: npx @aman_asmuei/amem")}`);
2351
+ lines.push(` ${pc3.dim("\u2192 Fix: restart aman-agent (memory initializes automatically)")}`);
2140
2352
  fixes++;
2141
2353
  } else {
2142
2354
  healthy++;
@@ -2158,7 +2370,8 @@ function handleHelp() {
2158
2370
  ` ${pc3.cyan("/akit")} Manage tools [add|remove <tool>]`,
2159
2371
  ` ${pc3.cyan("/skills")} View skills [install|uninstall ...]`,
2160
2372
  ` ${pc3.cyan("/eval")} View evaluation [milestone ...]`,
2161
- ` ${pc3.cyan("/memory")} View recent memories [search|clear|timeline]`,
2373
+ ` ${pc3.cyan("/memory")} View recent memories [search|fts|since|stats|export|clear|timeline]`,
2374
+ ` ${pc3.cyan("/reminder")} Manage reminders [set|check|done]`,
2162
2375
  ` ${pc3.cyan("/status")} Ecosystem dashboard`,
2163
2376
  ` ${pc3.cyan("/doctor")} Health check all layers`,
2164
2377
  ` ${pc3.cyan("/decisions")} View decision log [<project>]`,
@@ -2166,8 +2379,12 @@ function handleHelp() {
2166
2379
  ` ${pc3.cyan("/debug")} Show debug log`,
2167
2380
  ` ${pc3.cyan("/save")} Save conversation to memory`,
2168
2381
  ` ${pc3.cyan("/model")} Show current LLM model`,
2382
+ ` ${pc3.cyan("/plan")} Manage multi-step plans`,
2383
+ ` ${pc3.cyan("/profile")} Switch agent profiles`,
2384
+ ` ${pc3.cyan("/delegate")} Delegate tasks to sub-agents`,
2385
+ ` ${pc3.cyan("/team")} Manage agent teams`,
2169
2386
  ` ${pc3.cyan("/update")} Check for updates`,
2170
- ` ${pc3.cyan("/reconfig")} Reset LLM config`,
2387
+ ` ${pc3.cyan("/reset")} Full reset [all|memory|config|identity|rules]`,
2171
2388
  ` ${pc3.cyan("/clear")} Clear conversation history`,
2172
2389
  ` ${pc3.cyan("/quit")} Exit`
2173
2390
  ].join("\n")
@@ -2176,27 +2393,62 @@ function handleHelp() {
2176
2393
  function handleSave() {
2177
2394
  return { handled: true, saveConversation: true };
2178
2395
  }
2179
- function handleReconfig() {
2180
- const configDir = path9.join(os9.homedir(), ".aman-agent");
2181
- const configPath = path9.join(configDir, "config.json");
2182
- if (fs9.existsSync(configPath)) {
2183
- fs9.unlinkSync(configPath);
2396
+ function handleReset(action) {
2397
+ const dirs = {
2398
+ config: path9.join(os9.homedir(), ".aman-agent"),
2399
+ memory: path9.join(os9.homedir(), ".amem"),
2400
+ identity: path9.join(os9.homedir(), ".acore"),
2401
+ rules: path9.join(os9.homedir(), ".arules")
2402
+ };
2403
+ if (action === "help" || !action) {
2404
+ return {
2405
+ handled: true,
2406
+ output: [
2407
+ pc3.bold("Reset options:"),
2408
+ ` ${pc3.cyan("/reset all")} Full reset \u2014 config, memory, identity, rules`,
2409
+ ` ${pc3.cyan("/reset memory")} Clear all memories only`,
2410
+ ` ${pc3.cyan("/reset config")} Reset LLM config only`,
2411
+ ` ${pc3.cyan("/reset identity")} Reset persona/identity only`,
2412
+ ` ${pc3.cyan("/reset rules")} Reset guardrails only`,
2413
+ "",
2414
+ pc3.dim("Directories:"),
2415
+ ...Object.entries(dirs).map(([k, v]) => ` ${k}: ${pc3.dim(v)}`)
2416
+ ].join("\n")
2417
+ };
2418
+ }
2419
+ const targets = action === "all" ? ["config", "memory", "identity", "rules"] : [action];
2420
+ if (!targets.every((t) => t in dirs)) {
2421
+ return { handled: true, output: pc3.red(`Unknown target: ${action}. Use /reset help`) };
2422
+ }
2423
+ const removed = [];
2424
+ for (const target of targets) {
2425
+ const dir = dirs[target];
2426
+ if (fs9.existsSync(dir)) {
2427
+ fs9.rmSync(dir, { recursive: true, force: true });
2428
+ removed.push(target);
2429
+ }
2430
+ }
2431
+ if (targets.includes("config")) {
2432
+ const configDir = dirs.config;
2433
+ fs9.mkdirSync(configDir, { recursive: true });
2434
+ fs9.writeFileSync(path9.join(configDir, ".reconfig"), "", "utf-8");
2435
+ }
2436
+ if (removed.length === 0) {
2437
+ return { handled: true, output: pc3.dim("Nothing to reset \u2014 directories don't exist.") };
2184
2438
  }
2185
- fs9.mkdirSync(configDir, { recursive: true });
2186
- fs9.writeFileSync(path9.join(configDir, ".reconfig"), "", "utf-8");
2187
2439
  return {
2188
2440
  handled: true,
2189
2441
  quit: true,
2190
2442
  output: [
2191
- pc3.green("Config reset."),
2192
- "Next run will prompt you to choose your LLM provider."
2443
+ pc3.green(`Reset complete: ${removed.join(", ")}`),
2444
+ "Restart aman-agent to begin fresh."
2193
2445
  ].join("\n")
2194
2446
  };
2195
2447
  }
2196
2448
  function handleUpdate() {
2197
2449
  try {
2198
2450
  const current = execFileSync("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
2199
- const local = JSON.parse(fs9.readFileSync(path9.join(__dirname, "..", "package.json"), "utf-8")).version;
2451
+ const local = true ? "0.17.2" : "unknown";
2200
2452
  if (current === local) {
2201
2453
  return { handled: true, output: `${pc3.green("Up to date")} \u2014 v${local}` };
2202
2454
  }
@@ -2634,6 +2886,86 @@ function handlePlanCommand(action, args) {
2634
2886
  return { handled: true, output: pc3.yellow(`Unknown plan action: ${action}. Try /plan help`) };
2635
2887
  }
2636
2888
  }
2889
+ async function handleReminderCommand(action, args) {
2890
+ if (!action || action === "list") {
2891
+ try {
2892
+ const reminders = reminderList();
2893
+ if (reminders.length === 0) return { handled: true, output: pc3.dim("No reminders.") };
2894
+ const lines = [pc3.bold(`Reminders (${reminders.length}):`), ""];
2895
+ for (const r of reminders) {
2896
+ const status = r.completed ? pc3.green("[done]") : pc3.yellow("[todo]");
2897
+ const due = r.dueAt ? ` ${pc3.dim(`due: ${new Date(r.dueAt).toLocaleString()}`)}` : "";
2898
+ lines.push(` ${status} ${r.content}${due} ${pc3.dim(`(${r.id.slice(0, 8)})`)}`);
2899
+ }
2900
+ return { handled: true, output: lines.join("\n") };
2901
+ } catch (err) {
2902
+ return { handled: true, output: pc3.red(`Reminder error: ${err instanceof Error ? err.message : String(err)}`) };
2903
+ }
2904
+ }
2905
+ if (action === "set" || action === "add") {
2906
+ if (args.length === 0) return { handled: true, output: pc3.yellow("Usage: /reminder set <text> [--due <time>]\n Time formats: 1h, 2d, 1w, or ISO date (2026-04-10)") };
2907
+ let dueAt;
2908
+ const dueIdx = args.indexOf("--due");
2909
+ let contentArgs = args;
2910
+ if (dueIdx >= 0 && args[dueIdx + 1]) {
2911
+ const dueStr = args[dueIdx + 1];
2912
+ contentArgs = [...args.slice(0, dueIdx), ...args.slice(dueIdx + 2)];
2913
+ const relMatch = dueStr.match(/^(\d+)(h|d|w)$/);
2914
+ if (relMatch) {
2915
+ const num = parseInt(relMatch[1], 10);
2916
+ const unit = relMatch[2];
2917
+ const ms = unit === "h" ? num * 36e5 : unit === "d" ? num * 864e5 : num * 6048e5;
2918
+ dueAt = Date.now() + ms;
2919
+ } else {
2920
+ const parsed = Date.parse(dueStr);
2921
+ if (!isNaN(parsed)) dueAt = parsed;
2922
+ }
2923
+ }
2924
+ const content = contentArgs.join(" ");
2925
+ if (!content) return { handled: true, output: pc3.yellow("Usage: /reminder set <text> [--due <time>]") };
2926
+ try {
2927
+ const id = reminderSet(content, dueAt);
2928
+ const dueInfo = dueAt ? ` (due: ${new Date(dueAt).toLocaleDateString()})` : "";
2929
+ return { handled: true, output: pc3.green(`Reminder set: "${content}"${dueInfo} (ID: ${id.slice(0, 8)})`) };
2930
+ } catch (err) {
2931
+ return { handled: true, output: pc3.red(`Reminder error: ${err instanceof Error ? err.message : String(err)}`) };
2932
+ }
2933
+ }
2934
+ if (action === "done" || action === "complete") {
2935
+ if (!args[0]) return { handled: true, output: pc3.yellow("Usage: /reminder done <id>") };
2936
+ try {
2937
+ const result = reminderComplete(args[0]);
2938
+ return { handled: true, output: result ? pc3.green("Reminder completed.") : pc3.yellow("Reminder not found.") };
2939
+ } catch (err) {
2940
+ return { handled: true, output: pc3.red(`Reminder error: ${err instanceof Error ? err.message : String(err)}`) };
2941
+ }
2942
+ }
2943
+ if (action === "check") {
2944
+ try {
2945
+ const reminders = reminderCheck();
2946
+ if (reminders.length === 0) return { handled: true, output: pc3.dim("No pending reminders.") };
2947
+ const lines = [pc3.bold("Pending Reminders:"), ""];
2948
+ for (const r of reminders) {
2949
+ const icon = r.status === "overdue" ? pc3.red("!!!") : r.status === "today" ? pc3.yellow("(!)") : pc3.dim("( )");
2950
+ const due = r.dueAt ? ` ${pc3.dim(`due: ${new Date(r.dueAt).toLocaleString()}`)}` : "";
2951
+ lines.push(` ${icon} ${r.content}${due} ${pc3.dim(`[${r.status}]`)}`);
2952
+ }
2953
+ return { handled: true, output: lines.join("\n") };
2954
+ } catch (err) {
2955
+ return { handled: true, output: pc3.red(`Reminder error: ${err instanceof Error ? err.message : String(err)}`) };
2956
+ }
2957
+ }
2958
+ if (action === "help") {
2959
+ return { handled: true, output: [
2960
+ pc3.bold("Reminder commands:"),
2961
+ ` ${pc3.cyan("/reminder")} List all reminders`,
2962
+ ` ${pc3.cyan("/reminder set")} <text> Create a reminder [--due 1h|2d|1w|date]`,
2963
+ ` ${pc3.cyan("/reminder check")} Show overdue/upcoming`,
2964
+ ` ${pc3.cyan("/reminder done")} <id> Mark as completed`
2965
+ ].join("\n") };
2966
+ }
2967
+ return { handled: true, output: pc3.yellow(`Unknown action: /reminder ${action}. Try /reminder --help`) };
2968
+ }
2637
2969
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
2638
2970
  "quit",
2639
2971
  "exit",
@@ -2655,8 +2987,8 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
2655
2987
  "decisions",
2656
2988
  "export",
2657
2989
  "debug",
2658
- "update-config",
2659
- "reconfig",
2990
+ "reset",
2991
+ "reminder",
2660
2992
  "update",
2661
2993
  "upgrade",
2662
2994
  "plan",
@@ -2707,9 +3039,8 @@ async function handleCommand(input, ctx) {
2707
3039
  return handleExportCommand();
2708
3040
  case "debug":
2709
3041
  return handleDebugCommand();
2710
- case "update-config":
2711
- case "reconfig":
2712
- return handleReconfig();
3042
+ case "reset":
3043
+ return handleReset(action);
2713
3044
  case "plan":
2714
3045
  return handlePlanCommand(action, args);
2715
3046
  case "profile":
@@ -2718,6 +3049,8 @@ async function handleCommand(input, ctx) {
2718
3049
  return handleDelegateCommand(action, args, ctx);
2719
3050
  case "team":
2720
3051
  return handleTeamCommand(action, args, ctx);
3052
+ case "reminder":
3053
+ return handleReminderCommand(action, args);
2721
3054
  case "update":
2722
3055
  case "upgrade":
2723
3056
  return handleUpdate();
@@ -2933,14 +3266,18 @@ async function onSessionStart(ctx) {
2933
3266
  let firstRun = false;
2934
3267
  let resumeTopic;
2935
3268
  const visibleReminders = [];
2936
- try {
2937
- isHookCall = true;
2938
- const recallResult = await memoryRecall("*", { limit: 1 });
2939
- firstRun = recallResult.total === 0;
2940
- } catch {
2941
- firstRun = true;
2942
- } finally {
2943
- isHookCall = false;
3269
+ if (!isMemoryInitialized()) {
3270
+ firstRun = false;
3271
+ } else {
3272
+ try {
3273
+ isHookCall = true;
3274
+ const recallResult = await memoryRecall("*", { limit: 1 });
3275
+ firstRun = recallResult.total === 0;
3276
+ } catch {
3277
+ firstRun = true;
3278
+ } finally {
3279
+ isHookCall = false;
3280
+ }
2944
3281
  }
2945
3282
  if (firstRun) {
2946
3283
  contextInjection = `<first-session>
@@ -3685,8 +4022,8 @@ Rules:
3685
4022
  - Be conservative \u2014 90% of turns produce nothing worth storing
3686
4023
  - Return ONLY the JSON array, no other text`;
3687
4024
  function shouldExtract(assistantResponse, turnsSinceLastExtraction, lastExtractionCount) {
3688
- if (lastExtractionCount > 0) return true;
3689
4025
  if (assistantResponse.length < MIN_RESPONSE_LENGTH) return false;
4026
+ if (lastExtractionCount > 0 && turnsSinceLastExtraction >= 1) return true;
3690
4027
  if (turnsSinceLastExtraction < MIN_TURNS_BETWEEN_EMPTY) return false;
3691
4028
  return true;
3692
4029
  }
@@ -3914,7 +4251,7 @@ var BackgroundTaskManager = class {
3914
4251
  // src/errors.ts
3915
4252
  var ERROR_MAPPINGS = [
3916
4253
  { pattern: /rate.?limit|429/i, message: "Rate limited. I'll retry automatically." },
3917
- { pattern: /401|unauthorized/i, message: "API key invalid. Run /reconfig to fix." },
4254
+ { pattern: /401|unauthorized/i, message: "API key invalid. Run /reset config to fix." },
3918
4255
  { pattern: /403|forbidden/i, message: "API key doesn't have access to this model. Try a different model with --model." },
3919
4256
  { pattern: /fetch failed|network/i, message: "Network error. Check your internet connection." },
3920
4257
  { pattern: /ECONNREFUSED/i, message: "Can't reach the API. Are you behind a proxy or firewall?" },
@@ -4001,13 +4338,20 @@ async function recallForMessage(input) {
4001
4338
  return null;
4002
4339
  }
4003
4340
  const tokenEstimate = result.tokenEstimate ?? Math.round(result.text.split(/\s+/).filter(Boolean).length * 1.3);
4341
+ const MAX_MEMORY_TOKENS = getMaxRecallTokens();
4342
+ let memoryText = result.text;
4343
+ if (tokenEstimate > MAX_MEMORY_TOKENS) {
4344
+ const maxChars = MAX_MEMORY_TOKENS * 4;
4345
+ memoryText = memoryText.slice(0, maxChars) + "\n[... memory truncated to fit token budget]";
4346
+ log.debug("agent", `memory recall truncated from ~${tokenEstimate} to ~${MAX_MEMORY_TOKENS} tokens`);
4347
+ }
4004
4348
  return {
4005
4349
  text: `
4006
4350
 
4007
4351
  <relevant-memories>
4008
- ${result.text}
4352
+ ${memoryText}
4009
4353
  </relevant-memories>`,
4010
- tokenEstimate
4354
+ tokenEstimate: Math.min(tokenEstimate, MAX_MEMORY_TOKENS)
4011
4355
  };
4012
4356
  } catch (err) {
4013
4357
  log.debug("agent", "memory recall failed", err);
@@ -4063,7 +4407,7 @@ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager,
4063
4407
  shownHints: loadShownHints(),
4064
4408
  hintShownThisSession: false
4065
4409
  };
4066
- const isRetryable2 = (err) => err.message.includes("Rate limit") || err.message.includes("rate limit") || err.message.includes("ECONNRESET") || err.message.includes("ETIMEDOUT") || err.message.includes("fetch failed");
4410
+ const isRetryable2 = (err) => err.message.includes("Rate limit") || err.message.includes("rate limit") || err.message.includes("ECONNRESET") || err.message.includes("ETIMEDOUT") || err.message.includes("fetch failed") || err.message.includes("socket hang up") || err.message.includes("network socket disconnected") || err.message.includes("ENOTFOUND") || err.message.includes("EAI_AGAIN");
4067
4411
  let responseBuffer = "";
4068
4412
  const onChunkHandler = (chunk) => {
4069
4413
  if (chunk.type === "text" && chunk.text) {
@@ -4481,7 +4825,18 @@ ${converted}
4481
4825
  { maxAttempts: 3, baseDelay: 1e3, retryable: isRetryable2 }
4482
4826
  );
4483
4827
  messages.push(response.message);
4828
+ const MAX_TOOL_TURNS = 20;
4829
+ let toolTurnCount = 0;
4484
4830
  while (response.toolUses.length > 0 && mcpManager) {
4831
+ toolTurnCount++;
4832
+ if (toolTurnCount > MAX_TOOL_TURNS) {
4833
+ messages.push({
4834
+ role: "assistant",
4835
+ content: "Tool execution limit reached (20). Breaking to prevent infinite loop."
4836
+ });
4837
+ console.log(pc6.yellow("\n Tool execution limit reached (20). Breaking to prevent infinite loop."));
4838
+ break;
4839
+ }
4485
4840
  const toolResults = await Promise.all(
4486
4841
  response.toolUses.map(async (toolUse) => {
4487
4842
  if (hooksConfig) {
@@ -4796,10 +5151,22 @@ function bootstrapEcosystem() {
4796
5151
  "- Respect the user's preferences stored in memory"
4797
5152
  ].join("\n"), "utf-8");
4798
5153
  }
5154
+ const flowDir = path14.join(home2, ".aflow");
5155
+ const flowPath = path14.join(flowDir, "flow.md");
5156
+ if (!fs14.existsSync(flowPath)) {
5157
+ fs14.mkdirSync(flowDir, { recursive: true });
5158
+ fs14.writeFileSync(flowPath, "# Workflows\n\n_No workflows defined yet. Use /workflows add to create one._\n", "utf-8");
5159
+ }
5160
+ const skillDir = path14.join(home2, ".askill");
5161
+ const skillPath = path14.join(skillDir, "skills.md");
5162
+ if (!fs14.existsSync(skillPath)) {
5163
+ fs14.mkdirSync(skillDir, { recursive: true });
5164
+ fs14.writeFileSync(skillPath, "# Skills\n\n_No skills installed yet. Use /skills install to add domain expertise._\n", "utf-8");
5165
+ }
4799
5166
  return true;
4800
5167
  }
4801
5168
  var program = new Command();
4802
- program.name("aman-agent").description("Your AI companion, running locally").version("0.1.0").option("--model <model>", "Override LLM model").option("--budget <tokens>", "Token budget for system prompt (default: 8000)", parseInt).option("--profile <name>", "Use a specific agent profile (e.g., coder, writer, researcher)").action(async (options) => {
5169
+ program.name("aman-agent").description("Your AI companion, running locally").version("0.17.2").option("--model <model>", "Override LLM model").option("--budget <tokens>", "Token budget for system prompt (default: 8000)", parseInt).option("--profile <name>", "Use a specific agent profile (e.g., coder, writer, researcher)").action(async (options) => {
4803
5170
  p2.intro(pc7.bold("aman agent") + pc7.dim(" \u2014 your AI companion"));
4804
5171
  let config = loadConfig();
4805
5172
  if (!config) {
@@ -4808,7 +5175,7 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
4808
5175
  config = detected;
4809
5176
  const providerLabel = detected.provider === "anthropic" ? "Anthropic API key" : detected.provider === "openai" ? "OpenAI API key" : "Ollama";
4810
5177
  p2.log.success(`Auto-detected ${providerLabel}. Using ${pc7.bold(detected.model)}.`);
4811
- p2.log.info(pc7.dim("Change anytime with /reconfig"));
5178
+ p2.log.info(pc7.dim("Change anytime with /reset config"));
4812
5179
  saveConfig(config);
4813
5180
  } else {
4814
5181
  p2.log.info("First-time setup \u2014 configure your LLM connection.");
@@ -4926,39 +5293,47 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
4926
5293
  }
4927
5294
  }
4928
5295
  const aiName = getProfileAiName(profile);
4929
- await initMemory();
5296
+ if (config.memory) setMemoryConfig(config.memory);
5297
+ try {
5298
+ await initMemory();
5299
+ } catch (err) {
5300
+ p2.log.warning(`Memory initialization failed: ${err instanceof Error ? err.message : String(err)}`);
5301
+ }
5302
+ if (isMemoryInitialized()) {
5303
+ const memSpinner = p2.spinner();
5304
+ memSpinner.start("Consolidating memory");
5305
+ try {
5306
+ const report = memoryConsolidate();
5307
+ memSpinner.stop("Memory consolidated");
5308
+ if (report.merged > 0 || report.pruned > 0 || report.promoted > 0) {
5309
+ p2.log.info(
5310
+ `Memory health: ${report.healthScore ?? "?"}% ` + pc7.dim(`(merged ${report.merged}, pruned ${report.pruned}, promoted ${report.promoted})`)
5311
+ );
5312
+ }
5313
+ } catch {
5314
+ memSpinner.stop("Memory consolidation skipped");
5315
+ }
5316
+ }
4930
5317
  const mcpManager = new McpManager();
4931
5318
  const mcpSpinner = p2.spinner();
4932
5319
  mcpSpinner.start("Connecting to MCP servers");
4933
- await mcpManager.connect("aman", "npx", ["-y", "@aman_asmuei/aman-mcp"]);
5320
+ const connections = [
5321
+ mcpManager.connect("aman", "npx", ["-y", "@aman_asmuei/aman-mcp"])
5322
+ ];
4934
5323
  if (config.mcpServers) {
4935
5324
  for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
4936
5325
  if (name === "aman" || name === "amem") continue;
4937
- await mcpManager.connect(name, serverConfig.command, serverConfig.args);
5326
+ connections.push(mcpManager.connect(name, serverConfig.command, serverConfig.args, serverConfig.env));
4938
5327
  }
4939
5328
  }
5329
+ await Promise.all(connections);
4940
5330
  const mcpTools = mcpManager.getTools();
4941
5331
  mcpSpinner.stop("MCP connected");
4942
5332
  if (mcpTools.length > 0) {
4943
5333
  p2.log.success(`${mcpTools.length} MCP tools available`);
4944
- {
4945
- const memSpinner = p2.spinner();
4946
- memSpinner.start("Consolidating memory");
4947
- try {
4948
- const report = memoryConsolidate();
4949
- memSpinner.stop("Memory consolidated");
4950
- if (report.merged > 0 || report.pruned > 0 || report.promoted > 0) {
4951
- p2.log.info(
4952
- `Memory health: ${report.healthScore ?? "?"}% ` + pc7.dim(`(merged ${report.merged}, pruned ${report.pruned}, promoted ${report.promoted})`)
4953
- );
4954
- }
4955
- } catch {
4956
- memSpinner.stop("Memory consolidation skipped");
4957
- }
4958
- }
4959
5334
  } else {
4960
5335
  p2.log.info(
4961
- "No MCP tools connected (install aman-mcp or amem for tool support)"
5336
+ "No MCP tools connected (install aman-mcp for tool support)"
4962
5337
  );
4963
5338
  }
4964
5339
  const toolDefs = mcpTools.map((t) => ({