@aman_asmuei/aman-agent 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -14,7 +14,8 @@ var DEFAULT_HOOKS = {
14
14
  workflowSuggest: true,
15
15
  evalPrompt: true,
16
16
  autoSessionSave: true,
17
- extractMemories: true
17
+ extractMemories: true,
18
+ featureHints: true
18
19
  };
19
20
  var CONFIG_DIR = path.join(os.homedir(), ".aman-agent");
20
21
  var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
@@ -593,10 +594,13 @@ var McpManager = class {
593
594
 
594
595
  // src/agent.ts
595
596
  import * as readline from "readline";
596
- import fs6 from "fs";
597
- import path6 from "path";
598
- import os6 from "os";
597
+ import fs7 from "fs";
598
+ import path7 from "path";
599
+ import os7 from "os";
599
600
  import pc3 from "picocolors";
601
+ import { marked } from "marked";
602
+ import TerminalRenderer from "marked-terminal";
603
+ import logUpdate from "log-update";
600
604
 
601
605
  // src/commands.ts
602
606
  import fs5 from "fs";
@@ -858,7 +862,56 @@ async function handleMemoryCommand(action, args, ctx) {
858
862
  const output = await mcpWrite(ctx, "memory", "memory_forget", { category: args[0] });
859
863
  return { handled: true, output };
860
864
  }
861
- return { handled: true, output: pc.yellow(`Unknown action: /memory ${action}. Use /memory [search|clear].`) };
865
+ if (action === "timeline") {
866
+ if (!ctx.mcpManager) {
867
+ return { handled: true, output: pc.red("Memory not available: MCP not connected.") };
868
+ }
869
+ try {
870
+ const result = await ctx.mcpManager.callTool("memory_recall", { query: "*", limit: 500 });
871
+ if (result.startsWith("Error") || result.includes("No memories found")) {
872
+ return { handled: true, output: pc.dim("No memories yet. Start chatting and I'll remember what matters.") };
873
+ }
874
+ try {
875
+ const memories = JSON.parse(result);
876
+ if (Array.isArray(memories) && memories.length > 0) {
877
+ const byDate = /* @__PURE__ */ new Map();
878
+ for (const mem of memories) {
879
+ const date = mem.created_at ? new Date(mem.created_at).toLocaleDateString("en-US", { month: "short", day: "numeric" }) : "Unknown";
880
+ byDate.set(date, (byDate.get(date) || 0) + 1);
881
+ }
882
+ const maxCount = Math.max(...byDate.values());
883
+ const barWidth = 10;
884
+ const lines = [pc.bold("Memory Timeline:"), ""];
885
+ for (const [date, count] of byDate) {
886
+ const filled = Math.round(count / maxCount * barWidth);
887
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(barWidth - filled);
888
+ lines.push(` ${date.padEnd(8)} ${bar} ${count} memories`);
889
+ }
890
+ const tags = /* @__PURE__ */ new Map();
891
+ for (const mem of memories) {
892
+ if (Array.isArray(mem.tags)) {
893
+ for (const tag of mem.tags) {
894
+ tags.set(tag, (tags.get(tag) || 0) + 1);
895
+ }
896
+ }
897
+ }
898
+ lines.push("");
899
+ lines.push(` Total: ${memories.length} memories`);
900
+ if (tags.size > 0) {
901
+ const topTags = [...tags.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([tag, count]) => `#${tag} (${count})`).join(", ");
902
+ lines.push(` Top tags: ${topTags}`);
903
+ }
904
+ return { handled: true, output: lines.join("\n") };
905
+ }
906
+ } catch {
907
+ }
908
+ const lineCount = result.split("\n").filter((l) => l.trim()).length;
909
+ return { handled: true, output: `Total memories: ~${lineCount} entries.` };
910
+ } catch {
911
+ return { handled: true, output: pc.red("Failed to retrieve memory timeline.") };
912
+ }
913
+ }
914
+ return { handled: true, output: pc.yellow(`Unknown action: /memory ${action}. Use /memory [search|clear|timeline].`) };
862
915
  }
863
916
  function handleStatusCommand(ctx) {
864
917
  const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
@@ -881,15 +934,45 @@ function handleDoctorCommand(ctx) {
881
934
  const amemConnected = mcpToolCount > 0;
882
935
  const status = getEcosystemStatus(mcpToolCount, amemConnected);
883
936
  const lines = [pc.bold("Aman Health Check"), ""];
937
+ let healthy = 0;
938
+ let fixes = 0;
939
+ let suggestions = 0;
884
940
  for (const layer of status.layers) {
885
- const check = layer.exists ? pc.green("\u2713") : pc.red("\u2717");
886
- const name = layer.name.padEnd(12);
887
- const detail = layer.exists ? pc.green(layer.summary) : pc.red("missing");
888
- lines.push(` ${check} ${name} ${detail}`);
941
+ if (layer.exists) {
942
+ lines.push(` ${pc.green("\u2713")} ${layer.name.padEnd(12)} ${pc.green(layer.summary)}`);
943
+ healthy++;
944
+ } else {
945
+ const isRequired = ["identity", "rules"].includes(layer.name.toLowerCase());
946
+ if (isRequired) {
947
+ lines.push(` ${pc.red("\u2717")} ${layer.name.padEnd(12)} ${pc.red("missing")}`);
948
+ lines.push(` ${pc.dim("\u2192 Fix: aman-agent init")}`);
949
+ fixes++;
950
+ } else {
951
+ lines.push(` ${pc.yellow("\u26A0")} ${layer.name.padEnd(12)} ${pc.yellow("empty")}`);
952
+ const cmd = layer.name.toLowerCase() === "workflows" ? "/workflows add <name>" : layer.name.toLowerCase() === "tools" ? "/tools add <name> <type> <desc>" : layer.name.toLowerCase() === "skills" ? "/skills install <name>" : "";
953
+ if (cmd) lines.push(` ${pc.dim(`\u2192 Add with ${cmd}`)}`);
954
+ suggestions++;
955
+ }
956
+ }
889
957
  }
890
958
  lines.push("");
891
959
  lines.push(` ${status.mcpConnected ? pc.green("\u2713") : pc.red("\u2717")} ${"MCP".padEnd(12)} ${status.mcpConnected ? pc.green(`${status.mcpToolCount} tools`) : pc.red("not connected")}`);
960
+ if (!status.mcpConnected) {
961
+ lines.push(` ${pc.dim("\u2192 Fix: ensure npx is available and network is connected")}`);
962
+ fixes++;
963
+ } else {
964
+ healthy++;
965
+ }
892
966
  lines.push(` ${status.amemConnected ? pc.green("\u2713") : pc.red("\u2717")} ${"Memory".padEnd(12)} ${status.amemConnected ? pc.green("connected") : pc.red("not connected")}`);
967
+ if (!status.amemConnected) {
968
+ lines.push(` ${pc.dim("\u2192 Fix: npx @aman_asmuei/amem")}`);
969
+ fixes++;
970
+ } else {
971
+ healthy++;
972
+ }
973
+ const total = healthy + fixes + suggestions;
974
+ lines.push("");
975
+ lines.push(` Overall: ${healthy}/${total} healthy.${fixes > 0 ? ` ${fixes} fix${fixes > 1 ? "es" : ""} needed.` : ""}${suggestions > 0 ? ` ${suggestions} suggestion${suggestions > 1 ? "s" : ""}.` : ""}`);
893
976
  return { handled: true, output: lines.join("\n") };
894
977
  }
895
978
  function handleHelp() {
@@ -904,7 +987,7 @@ function handleHelp() {
904
987
  ` ${pc.cyan("/tools")} View tools [add|remove ...]`,
905
988
  ` ${pc.cyan("/skills")} View skills [install|uninstall ...]`,
906
989
  ` ${pc.cyan("/eval")} View evaluation [milestone ...]`,
907
- ` ${pc.cyan("/memory")} View recent memories [search|clear ...]`,
990
+ ` ${pc.cyan("/memory")} View recent memories [search|clear|timeline]`,
908
991
  ` ${pc.cyan("/status")} Ecosystem dashboard`,
909
992
  ` ${pc.cyan("/doctor")} Health check all layers`,
910
993
  ` ${pc.cyan("/decisions")} View decision log [<project>]`,
@@ -1074,6 +1157,41 @@ var isHookCall = false;
1074
1157
  async function onSessionStart(ctx) {
1075
1158
  let greeting = "";
1076
1159
  let contextInjection = "";
1160
+ let firstRun = false;
1161
+ let resumeTopic;
1162
+ const visibleReminders = [];
1163
+ try {
1164
+ isHookCall = true;
1165
+ const recallResult = await ctx.mcpManager.callTool("memory_recall", { query: "*", limit: 1 });
1166
+ if (!recallResult || recallResult.startsWith("Error") || recallResult.includes("No memories found")) {
1167
+ firstRun = true;
1168
+ }
1169
+ } catch {
1170
+ firstRun = true;
1171
+ } finally {
1172
+ isHookCall = false;
1173
+ }
1174
+ if (firstRun) {
1175
+ contextInjection = `<first-session>
1176
+ This is your FIRST conversation with this user. Introduce yourself warmly:
1177
+ - Share your name and that you're their personal AI companion
1178
+ - Mention you'll remember what matters across conversations
1179
+ - Ask what they'd like to be called
1180
+ - Keep it to 3-4 sentences, natural tone
1181
+ </first-session>`;
1182
+ const timeContext2 = getTimeContext();
1183
+ contextInjection = `<session-context>
1184
+ ${timeContext2}
1185
+ </session-context>
1186
+ ${contextInjection}`;
1187
+ return {
1188
+ greeting: void 0,
1189
+ contextInjection,
1190
+ firstRun,
1191
+ visibleReminders,
1192
+ resumeTopic: void 0
1193
+ };
1194
+ }
1077
1195
  if (ctx.config.memoryRecall) {
1078
1196
  try {
1079
1197
  isHookCall = true;
@@ -1094,6 +1212,10 @@ async function onSessionStart(ctx) {
1094
1212
  if (result && !result.startsWith("Error")) {
1095
1213
  if (greeting) greeting += "\n";
1096
1214
  greeting += result;
1215
+ const topicMatch = result.match(/(?:resume|last|topic)[:\s]*(.+?)(?:\n|$)/i);
1216
+ if (topicMatch) {
1217
+ resumeTopic = topicMatch[1].trim();
1218
+ }
1097
1219
  }
1098
1220
  } catch (err) {
1099
1221
  log.warn("hooks", "identity_summary failed", err);
@@ -1109,6 +1231,10 @@ async function onSessionStart(ctx) {
1109
1231
  const reminderResult = await ctx.mcpManager.callTool("reminder_check", {});
1110
1232
  if (reminderResult && !reminderResult.startsWith("Error") && !reminderResult.includes("No pending")) {
1111
1233
  greeting += "\n\n<pending-reminders>\n" + reminderResult + "\n</pending-reminders>";
1234
+ const lines = reminderResult.split("\n").filter((l) => l.trim().length > 0);
1235
+ for (const line of lines) {
1236
+ visibleReminders.push(line.trim());
1237
+ }
1112
1238
  }
1113
1239
  } catch (err) {
1114
1240
  log.debug("hooks", "reminder_check failed", err);
@@ -1122,7 +1248,10 @@ ${greeting}
1122
1248
  }
1123
1249
  return {
1124
1250
  greeting: greeting || void 0,
1125
- contextInjection: contextInjection || void 0
1251
+ contextInjection: contextInjection || void 0,
1252
+ firstRun,
1253
+ visibleReminders,
1254
+ resumeTopic
1126
1255
  };
1127
1256
  }
1128
1257
  async function onBeforeToolExec(toolName, toolArgs, ctx) {
@@ -1331,9 +1460,7 @@ ${summaryParts.slice(0, 20).join("\n")}
1331
1460
  }
1332
1461
 
1333
1462
  // src/memory-extractor.ts
1334
- var AUTO_STORE_TYPES = /* @__PURE__ */ new Set(["preference", "fact", "pattern", "topology"]);
1335
- var CONFIRM_TYPES = /* @__PURE__ */ new Set(["decision", "correction"]);
1336
- var VALID_TYPES = /* @__PURE__ */ new Set([...AUTO_STORE_TYPES, ...CONFIRM_TYPES]);
1463
+ var VALID_TYPES = /* @__PURE__ */ new Set(["preference", "fact", "pattern", "topology", "decision", "correction"]);
1337
1464
  var MIN_RESPONSE_LENGTH = 50;
1338
1465
  var MIN_TURNS_BETWEEN_EMPTY = 3;
1339
1466
  var EXTRACTION_PROMPT = `Analyze this conversation turn. Extract any information worth remembering long-term.
@@ -1352,8 +1479,8 @@ Type guide:
1352
1479
  - "fact" = objective information about systems, people, projects
1353
1480
  - "pattern" = recurring behavior, coding style, approach
1354
1481
  - "topology" = how systems/components connect to each other
1355
- - "decision" = explicit choice between alternatives (requires confirmation)
1356
- - "correction" = user correcting a prior wrong assumption (requires confirmation)
1482
+ - "decision" = explicit choice between alternatives
1483
+ - "correction" = user correcting a prior wrong assumption
1357
1484
 
1358
1485
  Rules:
1359
1486
  - Only extract genuinely useful LONG-TERM information
@@ -1382,7 +1509,7 @@ function parseExtractionResult(raw) {
1382
1509
  return [];
1383
1510
  }
1384
1511
  }
1385
- async function extractMemories(userMessage, assistantResponse, client, mcpManager, state, confirmFn) {
1512
+ async function extractMemories(userMessage, assistantResponse, client, mcpManager, state) {
1386
1513
  if (!shouldExtract(assistantResponse, state.turnsSinceLastExtraction, state.lastExtractionCount)) {
1387
1514
  state.turnsSinceLastExtraction++;
1388
1515
  return 0;
@@ -1422,10 +1549,6 @@ Assistant: ${assistantResponse.slice(0, 2e3)}`;
1422
1549
  }
1423
1550
  } catch {
1424
1551
  }
1425
- if (CONFIRM_TYPES.has(candidate.type)) {
1426
- const confirmed = await confirmFn(candidate.content);
1427
- if (!confirmed) continue;
1428
- }
1429
1552
  try {
1430
1553
  await mcpManager.callTool("memory_store", {
1431
1554
  content: candidate.content,
@@ -1450,7 +1573,89 @@ Assistant: ${assistantResponse.slice(0, 2e3)}`;
1450
1573
  }
1451
1574
  }
1452
1575
 
1576
+ // src/errors.ts
1577
+ var ERROR_MAPPINGS = [
1578
+ { pattern: /rate.?limit|429/i, message: "Rate limited. I'll retry automatically." },
1579
+ { pattern: /401|unauthorized/i, message: "API key invalid. Run /reconfig to fix." },
1580
+ { pattern: /403|forbidden/i, message: "API key doesn't have access to this model. Try a different model with --model." },
1581
+ { pattern: /fetch failed|network/i, message: "Network error. Check your internet connection." },
1582
+ { pattern: /ECONNREFUSED/i, message: "Can't reach the API. Are you behind a proxy or firewall?" },
1583
+ { pattern: /context.?length/i, message: "Conversation too long. Use /clear to start fresh or I'll auto-trim." },
1584
+ { pattern: /overloaded/i, message: "API is overloaded. Retrying in a moment..." },
1585
+ { pattern: /ETIMEDOUT/i, message: "Request timed out. Retrying..." }
1586
+ ];
1587
+ function humanizeError(message) {
1588
+ for (const mapping of ERROR_MAPPINGS) {
1589
+ if (mapping.pattern.test(message)) {
1590
+ return mapping.message;
1591
+ }
1592
+ }
1593
+ return message;
1594
+ }
1595
+
1596
+ // src/hints.ts
1597
+ import fs6 from "fs";
1598
+ import path6 from "path";
1599
+ import os6 from "os";
1600
+ var HINTS = [
1601
+ {
1602
+ id: "eval",
1603
+ minTurn: 15,
1604
+ condition: () => true,
1605
+ text: "Tip: See how our relationship has evolved with /eval"
1606
+ },
1607
+ {
1608
+ id: "memory-search",
1609
+ minTurn: 3,
1610
+ condition: (ctx) => ctx.memoryCount >= 10,
1611
+ text: "Tip: Search everything I remember with /memory search <query>"
1612
+ },
1613
+ {
1614
+ id: "workflows",
1615
+ minTurn: 5,
1616
+ condition: (ctx) => !ctx.hasWorkflows,
1617
+ text: "Tip: Teach me multi-step processes with /workflows add"
1618
+ },
1619
+ {
1620
+ id: "rules",
1621
+ minTurn: 8,
1622
+ condition: () => true,
1623
+ text: "Tip: Set guardrails for what I should/shouldn't do with /rules"
1624
+ }
1625
+ ];
1626
+ function getHint(state, ctx) {
1627
+ if (state.hintShownThisSession) return null;
1628
+ for (const hint of HINTS) {
1629
+ if (state.turnCount >= hint.minTurn && !state.shownHints.has(hint.id) && hint.condition(ctx)) {
1630
+ state.shownHints.add(hint.id);
1631
+ state.hintShownThisSession = true;
1632
+ return hint.text;
1633
+ }
1634
+ }
1635
+ return null;
1636
+ }
1637
+ var HINTS_FILE = path6.join(os6.homedir(), ".aman-agent", "hints-seen.json");
1638
+ function loadShownHints() {
1639
+ try {
1640
+ if (fs6.existsSync(HINTS_FILE)) {
1641
+ const data = JSON.parse(fs6.readFileSync(HINTS_FILE, "utf-8"));
1642
+ return new Set(Array.isArray(data) ? data : []);
1643
+ }
1644
+ } catch {
1645
+ }
1646
+ return /* @__PURE__ */ new Set();
1647
+ }
1648
+ function saveShownHints(shown) {
1649
+ try {
1650
+ const dir = path6.dirname(HINTS_FILE);
1651
+ fs6.mkdirSync(dir, { recursive: true });
1652
+ fs6.writeFileSync(HINTS_FILE, JSON.stringify([...shown]), "utf-8");
1653
+ } catch {
1654
+ }
1655
+ }
1656
+
1453
1657
  // src/agent.ts
1658
+ marked.use({ renderer: new TerminalRenderer() });
1454
1659
  async function recallForMessage(input, mcpManager) {
1455
1660
  try {
1456
1661
  const result = await mcpManager.callTool("memory_recall", {
@@ -1484,13 +1689,35 @@ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager,
1484
1689
  const messages = [];
1485
1690
  const sessionId = generateSessionId();
1486
1691
  const extractorState = { turnsSinceLastExtraction: 0, lastExtractionCount: 0 };
1692
+ const hintState = {
1693
+ turnCount: 0,
1694
+ shownHints: loadShownHints(),
1695
+ hintShownThisSession: false
1696
+ };
1487
1697
  const isRetryable = (err) => err.message.includes("Rate limit") || err.message.includes("rate limit") || err.message.includes("ECONNRESET") || err.message.includes("ETIMEDOUT") || err.message.includes("fetch failed");
1698
+ let responseBuffer = "";
1488
1699
  const onChunkHandler = (chunk) => {
1489
1700
  if (chunk.type === "text" && chunk.text) {
1490
- process.stdout.write(chunk.text);
1701
+ responseBuffer += chunk.text;
1702
+ if (process.stdout.isTTY) {
1703
+ logUpdate(responseBuffer);
1704
+ } else {
1705
+ process.stdout.write(chunk.text);
1706
+ }
1491
1707
  }
1492
1708
  if (chunk.type === "done") {
1493
- process.stdout.write("\n");
1709
+ if (process.stdout.isTTY && responseBuffer.trim()) {
1710
+ try {
1711
+ const rendered = marked(responseBuffer.trim());
1712
+ logUpdate(rendered);
1713
+ logUpdate.done();
1714
+ } catch {
1715
+ logUpdate.done();
1716
+ }
1717
+ } else {
1718
+ process.stdout.write("\n");
1719
+ }
1720
+ responseBuffer = "";
1494
1721
  }
1495
1722
  };
1496
1723
  const rl = readline.createInterface({
@@ -1526,10 +1753,25 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
1526
1753
  const hookCtx = { mcpManager, config: hooksConfig };
1527
1754
  try {
1528
1755
  const session = await onSessionStart(hookCtx);
1529
- if (session.greeting) console.log(pc3.dim(session.greeting));
1756
+ if (!session.firstRun) {
1757
+ if (session.resumeTopic) {
1758
+ console.log(pc3.dim(` Welcome back. Last time we talked about ${session.resumeTopic}`));
1759
+ } else {
1760
+ console.log(pc3.dim(" Welcome back."));
1761
+ }
1762
+ }
1763
+ if (session.visibleReminders && session.visibleReminders.length > 0) {
1764
+ for (const reminder of session.visibleReminders) {
1765
+ console.log(pc3.yellow(` Reminder: ${reminder}`));
1766
+ }
1767
+ }
1530
1768
  if (session.contextInjection) {
1531
1769
  messages.push({ role: "user", content: session.contextInjection });
1532
- messages.push({ role: "assistant", content: "I have context from our previous sessions. How can I help?" });
1770
+ if (session.firstRun) {
1771
+ messages.push({ role: "assistant", content: "acknowledged" });
1772
+ } else {
1773
+ messages.push({ role: "assistant", content: "I have context from our previous sessions. How can I help?" });
1774
+ }
1533
1775
  }
1534
1776
  } catch (err) {
1535
1777
  log.warn("agent", "session start hook failed", err);
@@ -1555,9 +1797,9 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
1555
1797
  }
1556
1798
  if (cmdResult.exportConversation) {
1557
1799
  try {
1558
- const exportDir = path6.join(os6.homedir(), ".aman-agent", "exports");
1559
- fs6.mkdirSync(exportDir, { recursive: true });
1560
- const exportPath = path6.join(exportDir, `${sessionId}.md`);
1800
+ const exportDir = path7.join(os7.homedir(), ".aman-agent", "exports");
1801
+ fs7.mkdirSync(exportDir, { recursive: true });
1802
+ const exportPath = path7.join(exportDir, `${sessionId}.md`);
1561
1803
  const lines = [
1562
1804
  `# Conversation \u2014 ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
1563
1805
  `**Model:** ${model}`,
@@ -1571,7 +1813,7 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
1571
1813
  lines.push(`${label} ${msg.content}`, "");
1572
1814
  }
1573
1815
  }
1574
- fs6.writeFileSync(exportPath, lines.join("\n"), "utf-8");
1816
+ fs7.writeFileSync(exportPath, lines.join("\n"), "utf-8");
1575
1817
  console.log(pc3.green(`Exported to ${exportPath}`));
1576
1818
  } catch {
1577
1819
  console.log(pc3.red("Failed to export conversation."));
@@ -1620,16 +1862,19 @@ ${wfMatch.steps}
1620
1862
  await trimConversation(messages, client);
1621
1863
  messages.push({ role: "user", content: input });
1622
1864
  let augmentedSystemPrompt = activeSystemPrompt;
1865
+ let memoryTokens = 0;
1623
1866
  if (mcpManager) {
1624
1867
  const recall = await recallForMessage(input, mcpManager);
1625
1868
  if (recall) {
1626
1869
  augmentedSystemPrompt = activeSystemPrompt + recall.text;
1627
- process.stdout.write(pc3.dim(` [memories: ~${recall.tokenEstimate} tokens]
1628
- `));
1870
+ memoryTokens = recall.tokenEstimate;
1629
1871
  }
1630
1872
  }
1631
- process.stdout.write(pc3.cyan(`
1632
- ${aiName} > `));
1873
+ const divider = "\u2500".repeat(Math.min(process.stdout.columns || 60, 60) - aiName.length - 2);
1874
+ process.stdout.write(`
1875
+ ${pc3.cyan(pc3.bold(aiName))} ${pc3.dim(divider)}
1876
+
1877
+ `);
1633
1878
  try {
1634
1879
  let response = await withRetry(
1635
1880
  () => client.chat(augmentedSystemPrompt, messages, onChunkHandler, tools),
@@ -1682,24 +1927,21 @@ ${aiName} > `));
1682
1927
  );
1683
1928
  messages.push(response.message);
1684
1929
  }
1930
+ const footerParts = [];
1931
+ if (memoryTokens > 0) footerParts.push(`memories: ~${memoryTokens} tokens`);
1932
+ const footer = footerParts.length > 0 ? ` ${footerParts.join(" | ")}` : "";
1933
+ const footerDivider = "\u2500".repeat(Math.min(process.stdout.columns || 60, 60) - footer.length - 1);
1934
+ process.stdout.write(pc3.dim(` ${footerDivider}${footer}
1935
+ `));
1685
1936
  if (mcpManager && hooksConfig?.extractMemories) {
1686
1937
  const assistantText = typeof response.message.content === "string" ? response.message.content : response.message.content.filter((b) => b.type === "text").map((b) => "text" in b ? b.text : "").join("");
1687
1938
  if (assistantText) {
1688
- const confirmFn = async (content) => {
1689
- return new Promise((resolve) => {
1690
- rl.question(
1691
- pc3.dim(` Remember: "${content}"? (y/N) `),
1692
- (answer) => resolve(answer.toLowerCase() === "y")
1693
- );
1694
- });
1695
- };
1696
1939
  const count = await extractMemories(
1697
1940
  input,
1698
1941
  assistantText,
1699
1942
  client,
1700
1943
  mcpManager,
1701
- extractorState,
1702
- confirmFn
1944
+ extractorState
1703
1945
  );
1704
1946
  if (count > 0) {
1705
1947
  process.stdout.write(pc3.dim(` [${count} memory${count > 1 ? "ies" : ""} stored]
@@ -1709,11 +1951,22 @@ ${aiName} > `));
1709
1951
  } else {
1710
1952
  extractorState.turnsSinceLastExtraction++;
1711
1953
  }
1954
+ if (hooksConfig?.featureHints) {
1955
+ hintState.turnCount++;
1956
+ const hasWorkflows = fs7.existsSync(path7.join(os7.homedir(), ".aflow", "flow.md"));
1957
+ const memoryCount = memoryTokens > 0 ? Math.floor(memoryTokens / 5) : 0;
1958
+ const hint = getHint(hintState, { hasWorkflows, memoryCount });
1959
+ if (hint) {
1960
+ process.stdout.write(pc3.dim(` ${hint}
1961
+ `));
1962
+ saveShownHints(hintState.shownHints);
1963
+ }
1964
+ }
1712
1965
  } catch (error) {
1713
- const message = error instanceof Error ? error.message : "Unknown error occurred";
1966
+ const rawMessage = error instanceof Error ? error.message : "Unknown error occurred";
1967
+ const friendly = humanizeError(rawMessage);
1714
1968
  console.error(pc3.red(`
1715
- Error: ${message}`));
1716
- messages.pop();
1969
+ ${friendly}`));
1717
1970
  }
1718
1971
  }
1719
1972
  }
@@ -1734,106 +1987,281 @@ async function saveConversationToMemory(mcpManager, messages, sessionId) {
1734
1987
  }
1735
1988
 
1736
1989
  // src/index.ts
1737
- import fs7 from "fs";
1738
- import path7 from "path";
1739
- import os7 from "os";
1990
+ import fs8 from "fs";
1991
+ import path8 from "path";
1992
+ import os8 from "os";
1993
+
1994
+ // src/presets.ts
1995
+ var PRESETS = {
1996
+ coding: {
1997
+ identity: {
1998
+ personality: "Direct, technical, concise. Shows code over explanation.",
1999
+ style: "Use short answers. Lead with the solution, explain after."
2000
+ },
2001
+ rules: [
2002
+ { category: "response", rule: "Always show code examples, not just descriptions" },
2003
+ { category: "safety", rule: "Never execute destructive commands without confirmation" },
2004
+ { category: "quality", rule: "Follow project conventions over personal preference" }
2005
+ ],
2006
+ workflows: [
2007
+ { name: "debug", description: "Systematic debugging process", steps: ["Reproduce the issue", "Identify root cause", "Propose fix", "Verify fix"] }
2008
+ ]
2009
+ },
2010
+ creative: {
2011
+ identity: {
2012
+ personality: "Warm, imaginative, encouraging. Explores multiple angles.",
2013
+ style: "Use metaphors and vivid language. Ask 'what if' questions."
2014
+ },
2015
+ rules: [
2016
+ { category: "response", rule: "Always offer 2-3 alternative approaches" },
2017
+ { category: "tone", rule: "Encourage experimentation, never dismiss ideas" }
2018
+ ],
2019
+ workflows: [
2020
+ { name: "brainstorm", description: "Creative brainstorming process", steps: ["Explore the problem space", "Generate 5+ ideas", "Evaluate trade-offs", "Refine top 2"] }
2021
+ ]
2022
+ },
2023
+ assistant: {
2024
+ identity: {
2025
+ personality: "Organized, proactive, action-oriented.",
2026
+ style: "Use bullet points and checklists. Summarize key takeaways."
2027
+ },
2028
+ rules: [
2029
+ { category: "response", rule: "End responses with clear next steps when applicable" },
2030
+ { category: "memory", rule: "Always track deadlines and commitments mentioned" }
2031
+ ],
2032
+ workflows: [
2033
+ { name: "plan", description: "Task planning process", steps: ["Clarify the goal", "Break into tasks", "Prioritize", "Set deadlines"] }
2034
+ ]
2035
+ },
2036
+ learning: {
2037
+ identity: {
2038
+ personality: "Patient, curious, Socratic. Builds understanding layer by layer.",
2039
+ style: "Use analogies. Check understanding before moving on."
2040
+ },
2041
+ rules: [
2042
+ { category: "response", rule: "Explain concepts before showing solutions" },
2043
+ { category: "teaching", rule: "Ask a follow-up question to reinforce learning" }
2044
+ ],
2045
+ workflows: []
2046
+ },
2047
+ minimal: {
2048
+ identity: {
2049
+ personality: "Helpful and adaptive. Matches the user's tone and needs.",
2050
+ style: "Clear and concise. Prioritizes usefulness over verbosity."
2051
+ },
2052
+ rules: [],
2053
+ workflows: []
2054
+ }
2055
+ };
2056
+ function applyPreset(name, companionName) {
2057
+ const preset = PRESETS[name];
2058
+ const coreMd = [
2059
+ `# ${companionName}`,
2060
+ "",
2061
+ "## Personality",
2062
+ preset.identity.personality,
2063
+ "",
2064
+ "## Style",
2065
+ preset.identity.style,
2066
+ "",
2067
+ "## Session",
2068
+ "_New companion \u2014 no prior sessions._"
2069
+ ].join("\n");
2070
+ let rulesMd = null;
2071
+ if (preset.rules.length > 0) {
2072
+ const grouped = /* @__PURE__ */ new Map();
2073
+ for (const r of preset.rules) {
2074
+ if (!grouped.has(r.category)) grouped.set(r.category, []);
2075
+ grouped.get(r.category).push(r.rule);
2076
+ }
2077
+ const sections = [...grouped.entries()].map(([cat, rules]) => `## ${cat}
2078
+ ${rules.map((r) => `- ${r}`).join("\n")}`).join("\n\n");
2079
+ rulesMd = `# Guardrails
2080
+
2081
+ ${sections}`;
2082
+ }
2083
+ let flowMd = null;
2084
+ if (preset.workflows.length > 0) {
2085
+ const wfSections = preset.workflows.map((wf) => {
2086
+ const steps = wf.steps.map((s, i) => `${i + 1}. ${s}`).join("\n");
2087
+ return `## ${wf.name}
2088
+ ${wf.description}
2089
+
2090
+ ${steps}`;
2091
+ }).join("\n\n");
2092
+ flowMd = `# Workflows
2093
+
2094
+ ${wfSections}`;
2095
+ }
2096
+ return { coreMd, rulesMd, flowMd };
2097
+ }
2098
+
2099
+ // src/index.ts
2100
+ async function autoDetectConfig() {
2101
+ const anthropicKey = process.env.ANTHROPIC_API_KEY;
2102
+ if (anthropicKey) {
2103
+ return { provider: "anthropic", apiKey: anthropicKey, model: "claude-sonnet-4-6" };
2104
+ }
2105
+ const openaiKey = process.env.OPENAI_API_KEY;
2106
+ if (openaiKey) {
2107
+ return { provider: "openai", apiKey: openaiKey, model: "gpt-4o" };
2108
+ }
2109
+ try {
2110
+ const controller = new AbortController();
2111
+ const timeout = setTimeout(() => controller.abort(), 1e3);
2112
+ const res = await fetch("http://localhost:11434/", { signal: controller.signal });
2113
+ clearTimeout(timeout);
2114
+ if (res.ok) {
2115
+ return { provider: "ollama", apiKey: "ollama", model: "llama3.2" };
2116
+ }
2117
+ } catch {
2118
+ }
2119
+ return null;
2120
+ }
2121
+ function bootstrapEcosystem() {
2122
+ const home2 = os8.homedir();
2123
+ const corePath = path8.join(home2, ".acore", "core.md");
2124
+ if (fs8.existsSync(corePath)) return false;
2125
+ fs8.mkdirSync(path8.join(home2, ".acore"), { recursive: true });
2126
+ fs8.writeFileSync(corePath, [
2127
+ "# Aman",
2128
+ "",
2129
+ "## Personality",
2130
+ "Helpful, adaptive, and thoughtful. Matches the user's tone and needs.",
2131
+ "",
2132
+ "## Style",
2133
+ "Clear and concise. Prioritizes usefulness over verbosity.",
2134
+ "",
2135
+ "## Session",
2136
+ "_New companion \u2014 no prior sessions._"
2137
+ ].join("\n"), "utf-8");
2138
+ const rulesDir = path8.join(home2, ".arules");
2139
+ const rulesPath = path8.join(rulesDir, "rules.md");
2140
+ if (!fs8.existsSync(rulesPath)) {
2141
+ fs8.mkdirSync(rulesDir, { recursive: true });
2142
+ fs8.writeFileSync(rulesPath, [
2143
+ "# Guardrails",
2144
+ "",
2145
+ "## safety",
2146
+ "- Never execute destructive commands without explicit confirmation",
2147
+ "- Never expose API keys, passwords, or secrets in responses",
2148
+ "",
2149
+ "## behavior",
2150
+ `- Be honest when uncertain \u2014 say "I'm not sure" rather than guessing`,
2151
+ "- Respect the user's preferences stored in memory"
2152
+ ].join("\n"), "utf-8");
2153
+ }
2154
+ return true;
2155
+ }
1740
2156
  var program = new Command();
1741
2157
  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).action(async (options) => {
1742
- p2.intro(pc4.bold("aman agent") + pc4.dim(" \u2014 starting your AI companion"));
2158
+ p2.intro(pc4.bold("aman agent") + pc4.dim(" \u2014 your AI companion"));
1743
2159
  let config = loadConfig();
1744
2160
  if (!config) {
1745
- p2.log.info("First-time setup \u2014 configure your LLM connection.");
1746
- const provider = await p2.select({
1747
- message: "LLM provider",
1748
- options: [
1749
- {
1750
- value: "anthropic",
1751
- label: "Claude (Anthropic)",
1752
- hint: "recommended"
1753
- },
1754
- { value: "openai", label: "GPT (OpenAI)" },
1755
- { value: "ollama", label: "Ollama (local)", hint: "free, runs offline" }
1756
- ],
1757
- initialValue: "anthropic"
1758
- });
1759
- if (p2.isCancel(provider)) process.exit(0);
1760
- let apiKey = "";
1761
- let defaultModel = "";
1762
- if (provider === "ollama") {
1763
- apiKey = "ollama";
1764
- const modelInput = await p2.text({
1765
- message: "Ollama model name",
1766
- placeholder: "llama3.2",
1767
- defaultValue: "llama3.2"
1768
- });
1769
- if (p2.isCancel(modelInput)) process.exit(0);
1770
- defaultModel = modelInput || "llama3.2";
1771
- } else if (provider === "anthropic") {
1772
- p2.log.info("Get your API key from: https://console.anthropic.com/settings/keys");
1773
- p2.log.info(pc4.dim("Note: API access is separate from Claude Pro subscription. You need API credits."));
1774
- apiKey = await p2.text({
1775
- message: "API key (starts with sk-ant-)",
1776
- validate: (v) => v.length === 0 ? "API key is required" : void 0
1777
- });
1778
- if (p2.isCancel(apiKey)) process.exit(0);
1779
- const modelChoice = await p2.select({
1780
- message: "Claude model",
1781
- options: [
1782
- { value: "claude-sonnet-4-5-20250514", label: "Claude Sonnet 4.5", hint: "fast, recommended" },
1783
- { value: "claude-opus-4-6", label: "Claude Opus 4.6", hint: "most capable" },
1784
- { value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", hint: "fastest, cheapest" },
1785
- { value: "custom", label: "Custom model ID" }
1786
- ],
1787
- initialValue: "claude-sonnet-4-5-20250514"
1788
- });
1789
- if (p2.isCancel(modelChoice)) process.exit(0);
1790
- if (modelChoice === "custom") {
1791
- const customModel = await p2.text({
1792
- message: "Model ID",
1793
- placeholder: "claude-sonnet-4-5-20250514",
1794
- validate: (v) => v.length === 0 ? "Model ID is required" : void 0
1795
- });
1796
- if (p2.isCancel(customModel)) process.exit(0);
1797
- defaultModel = customModel;
1798
- } else {
1799
- defaultModel = modelChoice;
1800
- }
2161
+ const detected = await autoDetectConfig();
2162
+ if (detected) {
2163
+ config = detected;
2164
+ const providerLabel = detected.provider === "anthropic" ? "Anthropic API key" : detected.provider === "openai" ? "OpenAI API key" : "Ollama";
2165
+ const modelLabel = detected.provider === "anthropic" ? "Claude Sonnet 4.6" : detected.provider === "openai" ? "GPT-4o" : "Llama 3.2";
2166
+ p2.log.success(`Auto-detected ${providerLabel}. Using ${modelLabel}.`);
1801
2167
  } else {
1802
- apiKey = await p2.text({
1803
- message: "API key",
1804
- validate: (v) => v.length === 0 ? "API key is required" : void 0
1805
- });
1806
- if (p2.isCancel(apiKey)) process.exit(0);
1807
- const modelChoice = await p2.select({
1808
- message: "OpenAI model",
2168
+ p2.log.info("First-time setup \u2014 configure your LLM connection.");
2169
+ const provider = await p2.select({
2170
+ message: "LLM provider",
1809
2171
  options: [
1810
- { value: "gpt-4o", label: "GPT-4o", hint: "recommended" },
1811
- { value: "gpt-4o-mini", label: "GPT-4o Mini", hint: "faster, cheaper" },
1812
- { value: "o3", label: "o3", hint: "reasoning model" },
1813
- { value: "custom", label: "Custom model ID" }
2172
+ {
2173
+ value: "anthropic",
2174
+ label: "Claude (Anthropic)",
2175
+ hint: "recommended"
2176
+ },
2177
+ { value: "openai", label: "GPT (OpenAI)" },
2178
+ { value: "ollama", label: "Ollama (local)", hint: "free, runs offline" }
1814
2179
  ],
1815
- initialValue: "gpt-4o"
2180
+ initialValue: "anthropic"
1816
2181
  });
1817
- if (p2.isCancel(modelChoice)) process.exit(0);
1818
- if (modelChoice === "custom") {
1819
- const customModel = await p2.text({
1820
- message: "Model ID",
1821
- placeholder: "gpt-4o",
1822
- validate: (v) => v.length === 0 ? "Model ID is required" : void 0
2182
+ if (p2.isCancel(provider)) process.exit(0);
2183
+ let apiKey = "";
2184
+ let defaultModel = "";
2185
+ if (provider === "ollama") {
2186
+ apiKey = "ollama";
2187
+ const modelInput = await p2.text({
2188
+ message: "Ollama model name",
2189
+ placeholder: "llama3.2",
2190
+ defaultValue: "llama3.2"
2191
+ });
2192
+ if (p2.isCancel(modelInput)) process.exit(0);
2193
+ defaultModel = modelInput || "llama3.2";
2194
+ } else if (provider === "anthropic") {
2195
+ p2.log.info("Get your API key from: https://console.anthropic.com/settings/keys");
2196
+ p2.log.info(pc4.dim("Note: API access is separate from Claude Pro subscription. You need API credits."));
2197
+ apiKey = await p2.text({
2198
+ message: "API key (starts with sk-ant-)",
2199
+ validate: (v) => v.length === 0 ? "API key is required" : void 0
1823
2200
  });
1824
- if (p2.isCancel(customModel)) process.exit(0);
1825
- defaultModel = customModel;
2201
+ if (p2.isCancel(apiKey)) process.exit(0);
2202
+ const modelChoice = await p2.select({
2203
+ message: "Claude model",
2204
+ options: [
2205
+ { value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", hint: "fast, recommended" },
2206
+ { value: "claude-opus-4-6", label: "Claude Opus 4.6", hint: "most capable" },
2207
+ { value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", hint: "fastest, cheapest" },
2208
+ { value: "custom", label: "Custom model ID" }
2209
+ ],
2210
+ initialValue: "claude-sonnet-4-6"
2211
+ });
2212
+ if (p2.isCancel(modelChoice)) process.exit(0);
2213
+ if (modelChoice === "custom") {
2214
+ const customModel = await p2.text({
2215
+ message: "Model ID",
2216
+ placeholder: "claude-sonnet-4-6",
2217
+ validate: (v) => v.length === 0 ? "Model ID is required" : void 0
2218
+ });
2219
+ if (p2.isCancel(customModel)) process.exit(0);
2220
+ defaultModel = customModel;
2221
+ } else {
2222
+ defaultModel = modelChoice;
2223
+ }
1826
2224
  } else {
1827
- defaultModel = modelChoice;
2225
+ apiKey = await p2.text({
2226
+ message: "API key",
2227
+ validate: (v) => v.length === 0 ? "API key is required" : void 0
2228
+ });
2229
+ if (p2.isCancel(apiKey)) process.exit(0);
2230
+ const modelChoice = await p2.select({
2231
+ message: "OpenAI model",
2232
+ options: [
2233
+ { value: "gpt-4o", label: "GPT-4o", hint: "recommended" },
2234
+ { value: "gpt-4o-mini", label: "GPT-4o Mini", hint: "faster, cheaper" },
2235
+ { value: "o3", label: "o3", hint: "reasoning model" },
2236
+ { value: "custom", label: "Custom model ID" }
2237
+ ],
2238
+ initialValue: "gpt-4o"
2239
+ });
2240
+ if (p2.isCancel(modelChoice)) process.exit(0);
2241
+ if (modelChoice === "custom") {
2242
+ const customModel = await p2.text({
2243
+ message: "Model ID",
2244
+ placeholder: "gpt-4o",
2245
+ validate: (v) => v.length === 0 ? "Model ID is required" : void 0
2246
+ });
2247
+ if (p2.isCancel(customModel)) process.exit(0);
2248
+ defaultModel = customModel;
2249
+ } else {
2250
+ defaultModel = modelChoice;
2251
+ }
1828
2252
  }
2253
+ config = { provider, apiKey, model: defaultModel };
2254
+ saveConfig(config);
2255
+ p2.log.success("Config saved to ~/.aman-agent/config.json");
1829
2256
  }
1830
- config = { provider, apiKey, model: defaultModel };
1831
- saveConfig(config);
1832
- p2.log.success("Config saved to ~/.aman-agent/config.json");
1833
2257
  }
1834
2258
  const model = options.model || config.model;
2259
+ const s = p2.spinner();
2260
+ s.start("Loading ecosystem");
2261
+ bootstrapEcosystem();
1835
2262
  const budget = options.budget || void 0;
1836
2263
  const { prompt: systemPrompt, layers, truncated, totalTokens } = assembleSystemPrompt(budget);
2264
+ s.stop("Ecosystem loaded");
1837
2265
  if (layers.length === 0) {
1838
2266
  p2.log.warning(
1839
2267
  "No ecosystem configured. Run " + pc4.bold("npx @aman_asmuei/aman") + " first."
@@ -1847,37 +2275,44 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
1847
2275
  p2.log.warning(`Truncated: ${truncated.join(", ")} ${pc4.dim("(over budget)")}`);
1848
2276
  }
1849
2277
  }
1850
- p2.log.info(`Model: ${pc4.dim(model)}`);
1851
- const corePath = path7.join(os7.homedir(), ".acore", "core.md");
2278
+ const corePath = path8.join(os8.homedir(), ".acore", "core.md");
1852
2279
  let aiName = "Assistant";
1853
- if (fs7.existsSync(corePath)) {
1854
- const content = fs7.readFileSync(corePath, "utf-8");
2280
+ if (fs8.existsSync(corePath)) {
2281
+ const content = fs8.readFileSync(corePath, "utf-8");
1855
2282
  const match = content.match(/^# (.+)$/m);
1856
2283
  if (match) aiName = match[1];
1857
2284
  }
1858
- p2.log.success(`${pc4.bold(aiName)} is ready.`);
1859
2285
  const mcpManager = new McpManager();
1860
- p2.log.step("Connecting to MCP servers...");
2286
+ const mcpSpinner = p2.spinner();
2287
+ mcpSpinner.start("Connecting to MCP servers");
1861
2288
  await mcpManager.connect("aman", "npx", ["-y", "@aman_asmuei/aman-mcp"]);
1862
2289
  await mcpManager.connect("amem", "npx", ["-y", "@aman_asmuei/amem"]);
1863
2290
  const mcpTools = mcpManager.getTools();
2291
+ mcpSpinner.stop("MCP connected");
1864
2292
  if (mcpTools.length > 0) {
1865
2293
  p2.log.success(`${mcpTools.length} MCP tools available`);
1866
2294
  if (mcpTools.some((t) => t.name === "memory_consolidate")) {
2295
+ const memSpinner = p2.spinner();
2296
+ memSpinner.start("Consolidating memory");
1867
2297
  try {
1868
2298
  const consolidateResult = await mcpManager.callTool("memory_consolidate", { dry_run: false });
1869
2299
  if (consolidateResult && !consolidateResult.startsWith("Error")) {
1870
2300
  try {
1871
2301
  const report = JSON.parse(consolidateResult);
2302
+ memSpinner.stop("Memory consolidated");
1872
2303
  if (report.merged > 0 || report.pruned > 0 || report.promoted > 0) {
1873
2304
  p2.log.info(
1874
2305
  `Memory health: ${report.healthScore ?? "?"}% ` + pc4.dim(`(merged ${report.merged}, pruned ${report.pruned}, promoted ${report.promoted})`)
1875
2306
  );
1876
2307
  }
1877
2308
  } catch {
2309
+ memSpinner.stop("Memory consolidated");
1878
2310
  }
2311
+ } else {
2312
+ memSpinner.stop("Memory consolidated");
1879
2313
  }
1880
2314
  } catch {
2315
+ memSpinner.stop("Memory consolidation skipped");
1881
2316
  }
1882
2317
  }
1883
2318
  } else {
@@ -1898,6 +2333,7 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
1898
2333
  } else {
1899
2334
  client = createOpenAIClient(config.apiKey, model);
1900
2335
  }
2336
+ p2.log.success(`${pc4.bold(aiName)} is ready. Model: ${pc4.dim(model)}`);
1901
2337
  await runAgent(
1902
2338
  client,
1903
2339
  systemPrompt,
@@ -1909,5 +2345,44 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
1909
2345
  );
1910
2346
  await mcpManager.disconnect();
1911
2347
  });
2348
+ program.command("init").description("Set up your AI companion with a guided wizard").action(async () => {
2349
+ p2.intro(pc4.bold("aman agent init") + pc4.dim(" \u2014 set up your companion"));
2350
+ const name = await p2.text({
2351
+ message: "What should your companion be called?",
2352
+ placeholder: "Aman",
2353
+ defaultValue: "Aman"
2354
+ });
2355
+ if (p2.isCancel(name)) process.exit(0);
2356
+ const preset = await p2.select({
2357
+ message: "What kind of companion do you need?",
2358
+ options: [
2359
+ { value: "coding", label: "Coding Partner", hint: "direct, technical, concise" },
2360
+ { value: "creative", label: "Creative Collaborator", hint: "warm, imaginative" },
2361
+ { value: "assistant", label: "Personal Assistant", hint: "organized, action-oriented" },
2362
+ { value: "learning", label: "Learning Buddy", hint: "patient, Socratic" },
2363
+ { value: "minimal", label: "Minimal", hint: "just chat, I'll customize later" }
2364
+ ],
2365
+ initialValue: "coding"
2366
+ });
2367
+ if (p2.isCancel(preset)) process.exit(0);
2368
+ const result = applyPreset(preset, name || "Aman");
2369
+ const home2 = os8.homedir();
2370
+ fs8.mkdirSync(path8.join(home2, ".acore"), { recursive: true });
2371
+ fs8.writeFileSync(path8.join(home2, ".acore", "core.md"), result.coreMd, "utf-8");
2372
+ p2.log.success(`Identity created \u2014 ${PRESETS[preset].identity.personality.split(".")[0].toLowerCase()}`);
2373
+ if (result.rulesMd) {
2374
+ fs8.mkdirSync(path8.join(home2, ".arules"), { recursive: true });
2375
+ fs8.writeFileSync(path8.join(home2, ".arules", "rules.md"), result.rulesMd, "utf-8");
2376
+ const ruleCount = (result.rulesMd.match(/^- /gm) || []).length;
2377
+ p2.log.success(`${ruleCount} rules set`);
2378
+ }
2379
+ if (result.flowMd) {
2380
+ fs8.mkdirSync(path8.join(home2, ".aflow"), { recursive: true });
2381
+ fs8.writeFileSync(path8.join(home2, ".aflow", "flow.md"), result.flowMd, "utf-8");
2382
+ const wfCount = (result.flowMd.match(/^## /gm) || []).length;
2383
+ p2.log.success(`${wfCount} workflow${wfCount > 1 ? "s" : ""} added`);
2384
+ }
2385
+ p2.outro(`Your companion is ready. Run: ${pc4.bold("aman-agent")}`);
2386
+ });
1912
2387
  program.parse();
1913
2388
  //# sourceMappingURL=index.js.map