@aman_asmuei/aman-agent 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,18 +1,28 @@
1
1
  // src/index.ts
2
2
  import { Command } from "commander";
3
- import * as p from "@clack/prompts";
3
+ import * as p2 from "@clack/prompts";
4
4
  import pc5 from "picocolors";
5
5
 
6
6
  // src/config.ts
7
7
  import fs from "fs";
8
8
  import path from "path";
9
9
  import os from "os";
10
+ var DEFAULT_HOOKS = {
11
+ memoryRecall: true,
12
+ sessionResume: true,
13
+ rulesCheck: true,
14
+ workflowSuggest: true,
15
+ evalPrompt: true,
16
+ autoSessionSave: true
17
+ };
10
18
  var CONFIG_DIR = path.join(os.homedir(), ".aman-agent");
11
19
  var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
12
20
  function loadConfig() {
13
21
  if (!fs.existsSync(CONFIG_PATH)) return null;
14
22
  try {
15
- return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
23
+ const raw = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
24
+ raw.hooks = { ...DEFAULT_HOOKS, ...raw.hooks };
25
+ return raw;
16
26
  } catch {
17
27
  return null;
18
28
  }
@@ -91,10 +101,10 @@ var ECOSYSTEM_FILES = [
91
101
  { name: "skills", dir: ".askill", file: "skills.md" }
92
102
  ];
93
103
  function assembleSystemPrompt(maxTokens) {
94
- const home = os2.homedir();
104
+ const home2 = os2.homedir();
95
105
  const components = [];
96
106
  for (const entry of ECOSYSTEM_FILES) {
97
- const filePath = path2.join(home, entry.dir, entry.file);
107
+ const filePath = path2.join(home2, entry.dir, entry.file);
98
108
  if (fs2.existsSync(filePath)) {
99
109
  const content = fs2.readFileSync(filePath, "utf-8").trim();
100
110
  components.push({
@@ -503,152 +513,425 @@ import * as readline from "readline";
503
513
  import pc3 from "picocolors";
504
514
 
505
515
  // src/commands.ts
516
+ import fs4 from "fs";
517
+ import path4 from "path";
518
+ import os4 from "os";
519
+ import { execFileSync } from "child_process";
520
+ import pc from "picocolors";
521
+
522
+ // src/layers/parsers.ts
506
523
  import fs3 from "fs";
507
524
  import path3 from "path";
508
525
  import os3 from "os";
509
- import { execFileSync } from "child_process";
510
- import pc from "picocolors";
526
+ var home = os3.homedir();
527
+ var LAYER_FILES = [
528
+ { name: "identity", dir: ".acore", file: "core.md" },
529
+ { name: "rules", dir: ".arules", file: "rules.md" },
530
+ { name: "workflows", dir: ".aflow", file: "flow.md" },
531
+ { name: "tools", dir: ".akit", file: "kit.md" },
532
+ { name: "skills", dir: ".askill", file: "skills.md" },
533
+ { name: "eval", dir: ".aeval", file: "eval.md" }
534
+ ];
535
+ function countLines(content, pattern) {
536
+ return (content.match(pattern) || []).length;
537
+ }
538
+ function getLayerSummary(name, content) {
539
+ switch (name) {
540
+ case "identity": {
541
+ const nameMatch = content.match(/^# (.+)/m);
542
+ return nameMatch ? nameMatch[1] : "configured";
543
+ }
544
+ case "rules":
545
+ return `${countLines(content, /^- /gm)} rules`;
546
+ case "workflows":
547
+ return `${countLines(content, /^## /gm)} workflows`;
548
+ case "tools":
549
+ return `${countLines(content, /^- \*\*/gm)} tools`;
550
+ case "skills":
551
+ return `${countLines(content, /^### /gm)} skills`;
552
+ case "eval": {
553
+ const sessions = countLines(content, /^### Session/gm);
554
+ return `${sessions} sessions logged`;
555
+ }
556
+ default:
557
+ return "unknown";
558
+ }
559
+ }
560
+ function getEcosystemStatus(mcpToolCount, amemConnected) {
561
+ const layers = LAYER_FILES.map((entry) => {
562
+ const filePath = path3.join(home, entry.dir, entry.file);
563
+ const exists = fs3.existsSync(filePath);
564
+ let summary = "not configured";
565
+ if (exists) {
566
+ const content = fs3.readFileSync(filePath, "utf-8");
567
+ summary = getLayerSummary(entry.name, content);
568
+ }
569
+ return { name: entry.name, exists, path: filePath, summary };
570
+ });
571
+ return {
572
+ layers,
573
+ mcpConnected: mcpToolCount > 0,
574
+ mcpToolCount,
575
+ amemConnected
576
+ };
577
+ }
578
+
579
+ // src/commands.ts
511
580
  function readEcosystemFile(filePath, label) {
512
- if (!fs3.existsSync(filePath)) {
581
+ if (!fs4.existsSync(filePath)) {
513
582
  return pc.dim(`No ${label} file found at ${filePath}`);
514
583
  }
515
- return fs3.readFileSync(filePath, "utf-8").trim();
584
+ return fs4.readFileSync(filePath, "utf-8").trim();
585
+ }
586
+ function parseCommand(input) {
587
+ const trimmed = input.trim();
588
+ const parts = trimmed.split(/\s+/);
589
+ const base = parts[0].toLowerCase().replace(/^\//, "");
590
+ const action = parts.length > 1 ? parts[1].toLowerCase() : void 0;
591
+ const args = parts.slice(2);
592
+ return { base, action, args };
516
593
  }
517
- function handleCommand(input, model) {
518
- const cmd = input.trim().toLowerCase();
519
- const home = os3.homedir();
520
- if (cmd === "/quit" || cmd === "/exit" || cmd === "/q") {
521
- return { handled: true, quit: true };
594
+ async function mcpWrite(ctx, layer, tool, args) {
595
+ if (!ctx.mcpManager) {
596
+ return pc.red(`Cannot modify ${layer}: aman-mcp not connected. Start it with: npx @aman_asmuei/aman-mcp`);
522
597
  }
523
- if (cmd === "/help") {
524
- return {
525
- handled: true,
526
- output: [
527
- pc.bold("Commands:"),
528
- ` ${pc.cyan("/help")} Show this help`,
529
- ` ${pc.cyan("/identity")} View your AI identity`,
530
- ` ${pc.cyan("/tools")} View installed tools`,
531
- ` ${pc.cyan("/workflows")} View defined workflows`,
532
- ` ${pc.cyan("/rules")} View guardrails`,
533
- ` ${pc.cyan("/skills")} View installed skills`,
534
- ` ${pc.cyan("/remind")} Set a reminder (e.g. /remind 30m Review PR)`,
535
- ` ${pc.cyan("/model")} Show current LLM model`,
536
- ` ${pc.cyan("/update")} Check for updates`,
537
- ` ${pc.cyan("/reconfig")} Reset LLM config (provider, model, API key)`,
538
- ` ${pc.cyan("/clear")} Clear conversation history`,
539
- ` ${pc.cyan("/quit")} Exit`
540
- ].join("\n")
541
- };
598
+ const result = await ctx.mcpManager.callTool(tool, args);
599
+ if (result.startsWith("Error")) {
600
+ return pc.red(result);
542
601
  }
543
- if (cmd === "/identity") {
544
- const content = readEcosystemFile(
545
- path3.join(home, ".acore", "core.md"),
546
- "identity (acore)"
547
- );
602
+ return pc.green(result);
603
+ }
604
+ async function handleIdentityCommand(action, args, ctx) {
605
+ const home2 = os4.homedir();
606
+ if (!action) {
607
+ const content = readEcosystemFile(path4.join(home2, ".acore", "core.md"), "identity (acore)");
548
608
  return { handled: true, output: content };
549
609
  }
550
- if (cmd === "/tools") {
551
- const content = readEcosystemFile(
552
- path3.join(home, ".akit", "kit.md"),
553
- "tools (akit)"
554
- );
610
+ if (action === "update") {
611
+ if (args.length === 0) {
612
+ return {
613
+ handled: true,
614
+ output: pc.yellow("Usage: /identity update <section>\nTip: describe changes in natural language and the AI will update via MCP.")
615
+ };
616
+ }
617
+ const section = args[0];
618
+ const content = args.slice(1).join(" ");
619
+ if (!content) {
620
+ return {
621
+ handled: true,
622
+ output: pc.yellow("Usage: /identity update <section> <new content...>\nExample: /identity update Personality Warm, curious, and direct.")
623
+ };
624
+ }
625
+ const output = await mcpWrite(ctx, "identity", "identity_update_section", { section, content });
626
+ return { handled: true, output };
627
+ }
628
+ return { handled: true, output: pc.yellow(`Unknown action: /identity ${action}. Use /identity or /identity update <section>.`) };
629
+ }
630
+ async function handleRulesCommand(action, args, ctx) {
631
+ const home2 = os4.homedir();
632
+ if (!action) {
633
+ const content = readEcosystemFile(path4.join(home2, ".arules", "rules.md"), "guardrails (arules)");
555
634
  return { handled: true, output: content };
556
635
  }
557
- if (cmd === "/workflows") {
558
- const content = readEcosystemFile(
559
- path3.join(home, ".aflow", "flow.md"),
560
- "workflows (aflow)"
561
- );
636
+ if (action === "add") {
637
+ if (args.length < 2) {
638
+ return { handled: true, output: pc.yellow("Usage: /rules add <category> <rule text...>") };
639
+ }
640
+ const category = args[0];
641
+ const rule = args.slice(1).join(" ");
642
+ const output = await mcpWrite(ctx, "rules", "rules_add", { category, rule });
643
+ return { handled: true, output };
644
+ }
645
+ if (action === "remove") {
646
+ if (args.length < 2) {
647
+ return { handled: true, output: pc.yellow("Usage: /rules remove <category> <index>") };
648
+ }
649
+ const output = await mcpWrite(ctx, "rules", "rules_remove", { category: args[0], index: parseInt(args[1], 10) });
650
+ return { handled: true, output };
651
+ }
652
+ if (action === "toggle") {
653
+ if (args.length < 2) {
654
+ return { handled: true, output: pc.yellow("Usage: /rules toggle <category> <index>") };
655
+ }
656
+ const output = await mcpWrite(ctx, "rules", "rules_toggle", { category: args[0], index: parseInt(args[1], 10) });
657
+ return { handled: true, output };
658
+ }
659
+ return { handled: true, output: pc.yellow(`Unknown action: /rules ${action}. Use /rules [add|remove|toggle].`) };
660
+ }
661
+ async function handleWorkflowsCommand(action, args, ctx) {
662
+ const home2 = os4.homedir();
663
+ if (!action) {
664
+ const content = readEcosystemFile(path4.join(home2, ".aflow", "flow.md"), "workflows (aflow)");
562
665
  return { handled: true, output: content };
563
666
  }
564
- if (cmd === "/rules") {
565
- const content = readEcosystemFile(
566
- path3.join(home, ".arules", "rules.md"),
567
- "guardrails (arules)"
568
- );
667
+ if (action === "add") {
668
+ if (args.length < 1) {
669
+ return { handled: true, output: pc.yellow("Usage: /workflows add <name>") };
670
+ }
671
+ const output = await mcpWrite(ctx, "workflows", "workflow_add", { name: args.join(" ") });
672
+ return { handled: true, output };
673
+ }
674
+ if (action === "remove") {
675
+ if (args.length < 1) {
676
+ return { handled: true, output: pc.yellow("Usage: /workflows remove <name>") };
677
+ }
678
+ const output = await mcpWrite(ctx, "workflows", "workflow_remove", { name: args.join(" ") });
679
+ return { handled: true, output };
680
+ }
681
+ return { handled: true, output: pc.yellow(`Unknown action: /workflows ${action}. Use /workflows [add|remove].`) };
682
+ }
683
+ async function handleToolsCommand(action, args, ctx) {
684
+ const home2 = os4.homedir();
685
+ if (!action) {
686
+ const content = readEcosystemFile(path4.join(home2, ".akit", "kit.md"), "tools (akit)");
569
687
  return { handled: true, output: content };
570
688
  }
571
- if (cmd === "/skills") {
572
- const content = readEcosystemFile(
573
- path3.join(home, ".askill", "skills.md"),
574
- "skills (askill)"
575
- );
689
+ if (action === "add") {
690
+ if (args.length < 3) {
691
+ return { handled: true, output: pc.yellow("Usage: /tools add <name> <type> <description...>") };
692
+ }
693
+ const name = args[0];
694
+ const type = args[1];
695
+ const description = args.slice(2).join(" ");
696
+ const output = await mcpWrite(ctx, "tools", "tools_add", { name, type, description });
697
+ return { handled: true, output };
698
+ }
699
+ if (action === "remove") {
700
+ if (args.length < 1) {
701
+ return { handled: true, output: pc.yellow("Usage: /tools remove <name>") };
702
+ }
703
+ const output = await mcpWrite(ctx, "tools", "tools_remove", { name: args.join(" ") });
704
+ return { handled: true, output };
705
+ }
706
+ return { handled: true, output: pc.yellow(`Unknown action: /tools ${action}. Use /tools [add|remove].`) };
707
+ }
708
+ async function handleSkillsCommand(action, args, ctx) {
709
+ const home2 = os4.homedir();
710
+ if (!action) {
711
+ const content = readEcosystemFile(path4.join(home2, ".askill", "skills.md"), "skills (askill)");
576
712
  return { handled: true, output: content };
577
713
  }
578
- if (cmd === "/model") {
579
- return {
580
- handled: true,
581
- output: model ? `Model: ${pc.bold(model)}` : "Model: unknown"
582
- };
714
+ if (action === "install") {
715
+ if (args.length < 1) {
716
+ return { handled: true, output: pc.yellow("Usage: /skills install <name>") };
717
+ }
718
+ const output = await mcpWrite(ctx, "skills", "skill_install", { name: args.join(" ") });
719
+ return { handled: true, output };
583
720
  }
584
- if (cmd === "/update-config" || cmd === "/reconfig") {
585
- const configPath = path3.join(os3.homedir(), ".aman-agent", "config.json");
586
- if (fs3.existsSync(configPath)) {
587
- fs3.unlinkSync(configPath);
721
+ if (action === "uninstall") {
722
+ if (args.length < 1) {
723
+ return { handled: true, output: pc.yellow("Usage: /skills uninstall <name>") };
588
724
  }
589
- return {
590
- handled: true,
591
- quit: true,
592
- output: [
593
- pc.green("Config reset."),
594
- `Run ${pc.bold("npx @aman_asmuei/aman-agent")} again to reconfigure your LLM provider, model, and API key.`
595
- ].join("\n")
596
- };
725
+ const output = await mcpWrite(ctx, "skills", "skill_uninstall", { name: args.join(" ") });
726
+ return { handled: true, output };
597
727
  }
598
- if (cmd === "/update" || cmd === "/upgrade") {
599
- try {
600
- const current = execFileSync("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
601
- const local = JSON.parse(fs3.readFileSync(path3.join(__dirname, "..", "package.json"), "utf-8")).version;
602
- if (current === local) {
603
- return { handled: true, output: `${pc.green("Up to date")} \u2014 v${local}` };
604
- }
605
- return {
606
- handled: true,
607
- output: [
608
- `${pc.yellow("Update available:")} v${local} \u2192 v${current}`,
609
- "",
610
- `Run this in your terminal:`,
611
- ` ${pc.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
612
- "",
613
- `Or use npx (always latest):`,
614
- ` ${pc.bold("npx @aman_asmuei/aman-agent@latest")}`
615
- ].join("\n")
616
- };
617
- } catch {
728
+ return { handled: true, output: pc.yellow(`Unknown action: /skills ${action}. Use /skills [install|uninstall].`) };
729
+ }
730
+ async function handleEvalCommand(action, args, ctx) {
731
+ const home2 = os4.homedir();
732
+ if (!action) {
733
+ const content = readEcosystemFile(path4.join(home2, ".aeval", "eval.md"), "evaluation (aeval)");
734
+ return { handled: true, output: content };
735
+ }
736
+ if (action === "milestone") {
737
+ if (args.length < 1) {
738
+ return { handled: true, output: pc.yellow("Usage: /eval milestone <text...>") };
739
+ }
740
+ const text2 = args.join(" ");
741
+ const output = await mcpWrite(ctx, "eval", "eval_milestone", { text: text2 });
742
+ return { handled: true, output };
743
+ }
744
+ return { handled: true, output: pc.yellow(`Unknown action: /eval ${action}. Use /eval or /eval milestone <text>.`) };
745
+ }
746
+ async function handleMemoryCommand(action, args, ctx) {
747
+ if (!action) {
748
+ if (!ctx.mcpManager) {
618
749
  return {
619
750
  handled: true,
620
- output: [
621
- `To update, run in your terminal:`,
622
- ` ${pc.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
623
- "",
624
- `Or use npx (always latest):`,
625
- ` ${pc.bold("npx @aman_asmuei/aman-agent@latest")}`
626
- ].join("\n")
751
+ output: pc.red("Memory not available: aman-mcp not connected. Start it with: npx @aman_asmuei/aman-mcp")
627
752
  };
628
753
  }
754
+ const result = await ctx.mcpManager.callTool("memory_context", {});
755
+ if (result.startsWith("Error")) {
756
+ return { handled: true, output: pc.red(result) };
757
+ }
758
+ return { handled: true, output: result };
629
759
  }
630
- if (cmd === "/clear") {
631
- return { handled: true, output: pc.dim("Conversation cleared."), clearHistory: true };
760
+ if (action === "search") {
761
+ if (args.length < 1) {
762
+ return { handled: true, output: pc.yellow("Usage: /memory search <query...>") };
763
+ }
764
+ const query = args.join(" ");
765
+ const output = await mcpWrite(ctx, "memory", "memory_recall", { query });
766
+ return { handled: true, output };
632
767
  }
633
- if (cmd.startsWith("/remind")) {
634
- const parts = input.trim().split(/\s+/);
635
- if (parts.length < 3) {
636
- return {
637
- handled: true,
638
- output: "Usage: /remind <time> <message>\nExamples: /remind 30m Review PR, /remind 2h Deploy, /remind tomorrow Check metrics"
639
- };
768
+ if (action === "clear") {
769
+ if (args.length < 1) {
770
+ return { handled: true, output: pc.yellow("Usage: /memory clear <category>") };
640
771
  }
641
- const timeStr = parts[1];
642
- const message = parts.slice(2).join(" ");
643
- return { handled: true, remind: { timeStr, message } };
772
+ const output = await mcpWrite(ctx, "memory", "memory_forget", { category: args[0] });
773
+ return { handled: true, output };
644
774
  }
645
- if (cmd.startsWith("/")) {
775
+ return { handled: true, output: pc.yellow(`Unknown action: /memory ${action}. Use /memory [search|clear].`) };
776
+ }
777
+ function handleStatusCommand(ctx) {
778
+ const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
779
+ const amemConnected = mcpToolCount > 0;
780
+ const status = getEcosystemStatus(mcpToolCount, amemConnected);
781
+ const lines = [pc.bold("Aman Ecosystem Dashboard"), ""];
782
+ for (const layer of status.layers) {
783
+ const icon = layer.exists ? pc.green("\u25CF") : pc.dim("\u25CB");
784
+ const name = pc.bold(layer.name.padEnd(12));
785
+ const summary = layer.exists ? layer.summary : pc.dim("not configured");
786
+ lines.push(` ${icon} ${name} ${summary}`);
787
+ }
788
+ lines.push("");
789
+ lines.push(` ${status.mcpConnected ? pc.green("\u25CF") : pc.dim("\u25CB")} ${pc.bold("MCP".padEnd(12))} ${status.mcpConnected ? `${status.mcpToolCount} tools available` : pc.dim("not connected")}`);
790
+ lines.push(` ${status.amemConnected ? pc.green("\u25CF") : pc.dim("\u25CB")} ${pc.bold("Memory".padEnd(12))} ${status.amemConnected ? "connected" : pc.dim("not connected")}`);
791
+ return { handled: true, output: lines.join("\n") };
792
+ }
793
+ function handleDoctorCommand(ctx) {
794
+ const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
795
+ const amemConnected = mcpToolCount > 0;
796
+ const status = getEcosystemStatus(mcpToolCount, amemConnected);
797
+ const lines = [pc.bold("Aman Health Check"), ""];
798
+ for (const layer of status.layers) {
799
+ const check = layer.exists ? pc.green("\u2713") : pc.red("\u2717");
800
+ const name = layer.name.padEnd(12);
801
+ const detail = layer.exists ? pc.green(layer.summary) : pc.red("missing");
802
+ lines.push(` ${check} ${name} ${detail}`);
803
+ }
804
+ lines.push("");
805
+ lines.push(` ${status.mcpConnected ? pc.green("\u2713") : pc.red("\u2717")} ${"MCP".padEnd(12)} ${status.mcpConnected ? pc.green(`${status.mcpToolCount} tools`) : pc.red("not connected")}`);
806
+ lines.push(` ${status.amemConnected ? pc.green("\u2713") : pc.red("\u2717")} ${"Memory".padEnd(12)} ${status.amemConnected ? pc.green("connected") : pc.red("not connected")}`);
807
+ return { handled: true, output: lines.join("\n") };
808
+ }
809
+ function handleHelp() {
810
+ return {
811
+ handled: true,
812
+ output: [
813
+ pc.bold("Commands:"),
814
+ ` ${pc.cyan("/help")} Show this help`,
815
+ ` ${pc.cyan("/identity")} View identity [update <section>]`,
816
+ ` ${pc.cyan("/rules")} View rules [add|remove|toggle ...]`,
817
+ ` ${pc.cyan("/workflows")} View workflows [add|remove ...]`,
818
+ ` ${pc.cyan("/tools")} View tools [add|remove ...]`,
819
+ ` ${pc.cyan("/skills")} View skills [install|uninstall ...]`,
820
+ ` ${pc.cyan("/eval")} View evaluation [milestone ...]`,
821
+ ` ${pc.cyan("/memory")} View recent memories [search|clear ...]`,
822
+ ` ${pc.cyan("/status")} Ecosystem dashboard`,
823
+ ` ${pc.cyan("/doctor")} Health check all layers`,
824
+ ` ${pc.cyan("/remind")} Set a reminder`,
825
+ ` ${pc.cyan("/model")} Show current LLM model`,
826
+ ` ${pc.cyan("/update")} Check for updates`,
827
+ ` ${pc.cyan("/reconfig")} Reset LLM config`,
828
+ ` ${pc.cyan("/clear")} Clear conversation history`,
829
+ ` ${pc.cyan("/quit")} Exit`
830
+ ].join("\n")
831
+ };
832
+ }
833
+ function handleRemind(input) {
834
+ const parts = input.trim().split(/\s+/);
835
+ if (parts.length < 3) {
646
836
  return {
647
837
  handled: true,
648
- output: `Unknown command: ${cmd}. Type ${pc.cyan("/help")} for available commands.`
838
+ output: "Usage: /remind <time> <message>\nExamples: /remind 30m Review PR, /remind 2h Deploy, /remind tomorrow Check metrics"
649
839
  };
650
840
  }
651
- return { handled: false };
841
+ const timeStr = parts[1];
842
+ const message = parts.slice(2).join(" ");
843
+ return { handled: true, remind: { timeStr, message } };
844
+ }
845
+ function handleReconfig() {
846
+ const configPath = path4.join(os4.homedir(), ".aman-agent", "config.json");
847
+ if (fs4.existsSync(configPath)) {
848
+ fs4.unlinkSync(configPath);
849
+ }
850
+ return {
851
+ handled: true,
852
+ quit: true,
853
+ output: [
854
+ pc.green("Config reset."),
855
+ `Run ${pc.bold("npx @aman_asmuei/aman-agent")} again to reconfigure your LLM provider, model, and API key.`
856
+ ].join("\n")
857
+ };
858
+ }
859
+ function handleUpdate() {
860
+ try {
861
+ const current = execFileSync("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
862
+ const local = JSON.parse(fs4.readFileSync(path4.join(__dirname, "..", "package.json"), "utf-8")).version;
863
+ if (current === local) {
864
+ return { handled: true, output: `${pc.green("Up to date")} \u2014 v${local}` };
865
+ }
866
+ return {
867
+ handled: true,
868
+ output: [
869
+ `${pc.yellow("Update available:")} v${local} \u2192 v${current}`,
870
+ "",
871
+ `Run this in your terminal:`,
872
+ ` ${pc.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
873
+ "",
874
+ `Or use npx (always latest):`,
875
+ ` ${pc.bold("npx @aman_asmuei/aman-agent@latest")}`
876
+ ].join("\n")
877
+ };
878
+ } catch {
879
+ return {
880
+ handled: true,
881
+ output: [
882
+ `To update, run in your terminal:`,
883
+ ` ${pc.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
884
+ "",
885
+ `Or use npx (always latest):`,
886
+ ` ${pc.bold("npx @aman_asmuei/aman-agent@latest")}`
887
+ ].join("\n")
888
+ };
889
+ }
890
+ }
891
+ async function handleCommand(input, ctx) {
892
+ const trimmed = input.trim();
893
+ if (!trimmed.startsWith("/")) return { handled: false };
894
+ const { base, action, args } = parseCommand(trimmed);
895
+ switch (base) {
896
+ case "quit":
897
+ case "exit":
898
+ case "q":
899
+ return { handled: true, quit: true };
900
+ case "help":
901
+ return handleHelp();
902
+ case "clear":
903
+ return { handled: true, output: pc.dim("Conversation cleared."), clearHistory: true };
904
+ case "model":
905
+ return { handled: true, output: ctx.model ? `Model: ${pc.bold(ctx.model)}` : "Model: unknown" };
906
+ case "identity":
907
+ return handleIdentityCommand(action, args, ctx);
908
+ case "rules":
909
+ return handleRulesCommand(action, args, ctx);
910
+ case "workflows":
911
+ return handleWorkflowsCommand(action, args, ctx);
912
+ case "tools":
913
+ return handleToolsCommand(action, args, ctx);
914
+ case "skills":
915
+ return handleSkillsCommand(action, args, ctx);
916
+ case "eval":
917
+ return handleEvalCommand(action, args, ctx);
918
+ case "memory":
919
+ return handleMemoryCommand(action, args, ctx);
920
+ case "status":
921
+ return handleStatusCommand(ctx);
922
+ case "doctor":
923
+ return handleDoctorCommand(ctx);
924
+ case "remind":
925
+ return handleRemind(trimmed);
926
+ case "update-config":
927
+ case "reconfig":
928
+ return handleReconfig();
929
+ case "update":
930
+ case "upgrade":
931
+ return handleUpdate();
932
+ default:
933
+ return { handled: true, output: `Unknown command: /${base}. Type ${pc.cyan("/help")} for available commands.` };
934
+ }
652
935
  }
653
936
 
654
937
  // src/reminders.ts
@@ -690,14 +973,175 @@ function clearReminders() {
690
973
  activeReminders.length = 0;
691
974
  }
692
975
 
976
+ // src/hooks.ts
977
+ import * as p from "@clack/prompts";
978
+ var isHookCall = false;
979
+ async function onSessionStart(ctx) {
980
+ let greeting = "";
981
+ let contextInjection = "";
982
+ if (ctx.config.memoryRecall) {
983
+ try {
984
+ isHookCall = true;
985
+ const result = await ctx.mcpManager.callTool("memory_context", {});
986
+ if (result && !result.startsWith("Error")) {
987
+ greeting += result;
988
+ }
989
+ } catch {
990
+ } finally {
991
+ isHookCall = false;
992
+ }
993
+ }
994
+ if (ctx.config.sessionResume) {
995
+ try {
996
+ isHookCall = true;
997
+ const result = await ctx.mcpManager.callTool("identity_summary", {});
998
+ if (result && !result.startsWith("Error")) {
999
+ if (greeting) greeting += "\n";
1000
+ greeting += result;
1001
+ }
1002
+ } catch {
1003
+ } finally {
1004
+ isHookCall = false;
1005
+ }
1006
+ }
1007
+ if (greeting) {
1008
+ contextInjection = `<session-context>
1009
+ ${greeting}
1010
+ </session-context>`;
1011
+ }
1012
+ return {
1013
+ greeting: greeting || void 0,
1014
+ contextInjection: contextInjection || void 0
1015
+ };
1016
+ }
1017
+ async function onBeforeToolExec(toolName, toolArgs, ctx) {
1018
+ if (!ctx.config.rulesCheck || isHookCall) {
1019
+ return { allow: true };
1020
+ }
1021
+ if (toolName === "rules_check") {
1022
+ return { allow: true };
1023
+ }
1024
+ try {
1025
+ isHookCall = true;
1026
+ const description = `${toolName}(${JSON.stringify(toolArgs)})`;
1027
+ const result = await ctx.mcpManager.callTool("rules_check", {
1028
+ action: description
1029
+ });
1030
+ try {
1031
+ const parsed = JSON.parse(result);
1032
+ if (parsed.violations && parsed.violations.length > 0) {
1033
+ return {
1034
+ allow: false,
1035
+ reason: parsed.violations.join("; ")
1036
+ };
1037
+ }
1038
+ } catch {
1039
+ }
1040
+ return { allow: true };
1041
+ } catch {
1042
+ return { allow: true };
1043
+ } finally {
1044
+ isHookCall = false;
1045
+ }
1046
+ }
1047
+ async function onWorkflowMatch(userInput, ctx) {
1048
+ if (!ctx.config.workflowSuggest) {
1049
+ return null;
1050
+ }
1051
+ try {
1052
+ isHookCall = true;
1053
+ const result = await ctx.mcpManager.callTool("workflow_list", {});
1054
+ const workflows = JSON.parse(result);
1055
+ const inputLower = userInput.toLowerCase();
1056
+ for (const wf of workflows) {
1057
+ const nameLower = wf.name.toLowerCase();
1058
+ if (inputLower.includes(nameLower)) {
1059
+ const steps = (wf.steps || []).map((s, i) => `${i + 1}. ${s}`).join("\n");
1060
+ return { name: wf.name, steps };
1061
+ }
1062
+ if (wf.description) {
1063
+ const words = wf.description.split(/\s+/).filter((w) => w.length > 4).map((w) => w.toLowerCase());
1064
+ for (const word of words) {
1065
+ if (inputLower.includes(word)) {
1066
+ const steps = (wf.steps || []).map((s, i) => `${i + 1}. ${s}`).join("\n");
1067
+ return { name: wf.name, steps };
1068
+ }
1069
+ }
1070
+ }
1071
+ }
1072
+ return null;
1073
+ } catch {
1074
+ return null;
1075
+ } finally {
1076
+ isHookCall = false;
1077
+ }
1078
+ }
1079
+ async function onSessionEnd(ctx, messages) {
1080
+ try {
1081
+ if (ctx.config.evalPrompt) {
1082
+ const rating = await p.select({
1083
+ message: "Quick rating for this session?",
1084
+ options: [
1085
+ { value: "great", label: "Great" },
1086
+ { value: "good", label: "Good" },
1087
+ { value: "okay", label: "Okay" },
1088
+ { value: "skip", label: "Skip" }
1089
+ ],
1090
+ initialValue: "skip"
1091
+ });
1092
+ if (!p.isCancel(rating) && rating !== "skip") {
1093
+ try {
1094
+ isHookCall = true;
1095
+ await ctx.mcpManager.callTool("eval_log", {
1096
+ rating,
1097
+ highlights: "Quick session rating",
1098
+ improvements: ""
1099
+ });
1100
+ } finally {
1101
+ isHookCall = false;
1102
+ }
1103
+ }
1104
+ }
1105
+ if (ctx.config.autoSessionSave && messages.length > 2) {
1106
+ let lastUserMsg = "";
1107
+ for (let i = messages.length - 1; i >= 0; i--) {
1108
+ if (messages[i].role === "user" && typeof messages[i].content === "string") {
1109
+ lastUserMsg = messages[i].content;
1110
+ break;
1111
+ }
1112
+ }
1113
+ if (lastUserMsg) {
1114
+ try {
1115
+ isHookCall = true;
1116
+ await ctx.mcpManager.callTool("identity_update_session", {
1117
+ resume: lastUserMsg.slice(0, 200),
1118
+ topics: "See conversation history",
1119
+ decisions: "See conversation history"
1120
+ });
1121
+ } finally {
1122
+ isHookCall = false;
1123
+ }
1124
+ }
1125
+ }
1126
+ } catch {
1127
+ }
1128
+ }
1129
+
693
1130
  // src/agent.ts
694
- async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager) {
1131
+ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager, hooksConfig) {
695
1132
  const messages = [];
696
1133
  const rl = readline.createInterface({
697
1134
  input: process.stdin,
698
1135
  output: process.stdout
699
1136
  });
700
- rl.on("SIGINT", () => {
1137
+ rl.on("SIGINT", async () => {
1138
+ if (mcpManager && hooksConfig) {
1139
+ try {
1140
+ const hookCtx = { mcpManager, config: hooksConfig };
1141
+ await onSessionEnd(hookCtx, messages);
1142
+ } catch {
1143
+ }
1144
+ }
701
1145
  console.log(pc3.dim("\nGoodbye.\n"));
702
1146
  rl.close();
703
1147
  process.exit(0);
@@ -714,13 +1158,32 @@ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager)
714
1158
  Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit.
715
1159
  `
716
1160
  );
1161
+ if (mcpManager && hooksConfig) {
1162
+ const hookCtx = { mcpManager, config: hooksConfig };
1163
+ try {
1164
+ const session = await onSessionStart(hookCtx);
1165
+ if (session.greeting) console.log(pc3.dim(session.greeting));
1166
+ if (session.contextInjection) {
1167
+ messages.push({ role: "user", content: session.contextInjection });
1168
+ messages.push({ role: "assistant", content: "I have context from our previous sessions. How can I help?" });
1169
+ }
1170
+ } catch {
1171
+ }
1172
+ }
717
1173
  while (true) {
718
1174
  const input = await prompt();
719
1175
  if (!input.trim()) continue;
720
- const cmdResult = handleCommand(input, model);
1176
+ const cmdResult = await handleCommand(input, { model, mcpManager });
721
1177
  if (cmdResult.handled) {
722
1178
  if (cmdResult.quit) {
723
1179
  clearReminders();
1180
+ if (mcpManager && hooksConfig) {
1181
+ try {
1182
+ const hookCtx = { mcpManager, config: hooksConfig };
1183
+ await onSessionEnd(hookCtx, messages);
1184
+ } catch {
1185
+ }
1186
+ }
724
1187
  console.log(pc3.dim("\nGoodbye.\n"));
725
1188
  rl.close();
726
1189
  return;
@@ -747,12 +1210,33 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
747
1210
  }
748
1211
  continue;
749
1212
  }
1213
+ let activeSystemPrompt = systemPrompt;
1214
+ if (mcpManager && hooksConfig) {
1215
+ try {
1216
+ const hookCtx = { mcpManager, config: hooksConfig };
1217
+ const wfMatch = await onWorkflowMatch(input, hookCtx);
1218
+ if (wfMatch) {
1219
+ const useIt = await new Promise((resolve) => {
1220
+ rl.question(pc3.dim(` Workflow "${wfMatch.name}" matches. Use it? (y/N) `), (answer) => resolve(answer.toLowerCase() === "y"));
1221
+ });
1222
+ if (useIt) {
1223
+ activeSystemPrompt = systemPrompt + `
1224
+
1225
+ <active-workflow>
1226
+ ${wfMatch.steps}
1227
+ </active-workflow>`;
1228
+ console.log(pc3.dim(` Using "${wfMatch.name}" workflow.`));
1229
+ }
1230
+ }
1231
+ } catch {
1232
+ }
1233
+ }
750
1234
  messages.push({ role: "user", content: input });
751
1235
  process.stdout.write(pc3.cyan(`
752
1236
  ${aiName} > `));
753
1237
  try {
754
1238
  let response = await client.chat(
755
- systemPrompt,
1239
+ activeSystemPrompt,
756
1240
  messages,
757
1241
  (chunk) => {
758
1242
  if (chunk.type === "text" && chunk.text) {
@@ -768,6 +1252,21 @@ ${aiName} > `));
768
1252
  while (response.toolUses.length > 0 && mcpManager) {
769
1253
  const toolResults = [];
770
1254
  for (const toolUse of response.toolUses) {
1255
+ if (hooksConfig) {
1256
+ const hookCtx = { mcpManager, config: hooksConfig };
1257
+ const check = await onBeforeToolExec(toolUse.name, toolUse.input, hookCtx);
1258
+ if (!check.allow) {
1259
+ process.stdout.write(pc3.red(` [BLOCKED: ${check.reason}]
1260
+ `));
1261
+ toolResults.push({
1262
+ type: "tool_result",
1263
+ tool_use_id: toolUse.id,
1264
+ content: `BLOCKED by guardrail: ${check.reason}`,
1265
+ is_error: true
1266
+ });
1267
+ continue;
1268
+ }
1269
+ }
771
1270
  process.stdout.write(
772
1271
  pc3.dim(` [using ${toolUse.name}...]
773
1272
  `)
@@ -787,7 +1286,7 @@ ${aiName} > `));
787
1286
  content: toolResults
788
1287
  });
789
1288
  response = await client.chat(
790
- systemPrompt,
1289
+ activeSystemPrompt,
791
1290
  messages,
792
1291
  (chunk) => {
793
1292
  if (chunk.type === "text" && chunk.text) {
@@ -811,22 +1310,22 @@ Error: ${message}`));
811
1310
  }
812
1311
 
813
1312
  // src/scheduler.ts
814
- import fs4 from "fs";
815
- import path4 from "path";
816
- import os4 from "os";
817
- var SCHEDULES_PATH = path4.join(os4.homedir(), ".aman-agent", "schedules.json");
1313
+ import fs5 from "fs";
1314
+ import path5 from "path";
1315
+ import os5 from "os";
1316
+ var SCHEDULES_PATH = path5.join(os5.homedir(), ".aman-agent", "schedules.json");
818
1317
  function loadSchedules() {
819
- if (!fs4.existsSync(SCHEDULES_PATH)) return [];
1318
+ if (!fs5.existsSync(SCHEDULES_PATH)) return [];
820
1319
  try {
821
- return JSON.parse(fs4.readFileSync(SCHEDULES_PATH, "utf-8"));
1320
+ return JSON.parse(fs5.readFileSync(SCHEDULES_PATH, "utf-8"));
822
1321
  } catch {
823
1322
  return [];
824
1323
  }
825
1324
  }
826
1325
  function saveSchedules(tasks) {
827
- const dir = path4.dirname(SCHEDULES_PATH);
828
- fs4.mkdirSync(dir, { recursive: true });
829
- fs4.writeFileSync(
1326
+ const dir = path5.dirname(SCHEDULES_PATH);
1327
+ fs5.mkdirSync(dir, { recursive: true });
1328
+ fs5.writeFileSync(
830
1329
  SCHEDULES_PATH,
831
1330
  JSON.stringify(tasks, null, 2) + "\n",
832
1331
  "utf-8"
@@ -879,9 +1378,9 @@ function isDue(schedule, lastRun, now) {
879
1378
  }
880
1379
 
881
1380
  // src/notifications.ts
882
- import fs5 from "fs";
883
- import path5 from "path";
884
- import os5 from "os";
1381
+ import fs6 from "fs";
1382
+ import path6 from "path";
1383
+ import os6 from "os";
885
1384
  import pc4 from "picocolors";
886
1385
  function checkNotifications() {
887
1386
  const notifications = [];
@@ -892,9 +1391,9 @@ function checkNotifications() {
892
1391
  message: `${task.name} (${task.schedule})`
893
1392
  });
894
1393
  }
895
- const evalPath = path5.join(os5.homedir(), ".aeval", "eval.md");
896
- if (fs5.existsSync(evalPath)) {
897
- const content = fs5.readFileSync(evalPath, "utf-8");
1394
+ const evalPath = path6.join(os6.homedir(), ".aeval", "eval.md");
1395
+ if (fs6.existsSync(evalPath)) {
1396
+ const content = fs6.readFileSync(evalPath, "utf-8");
898
1397
  const dateMatch = content.match(/- Last updated: (.+)$/m);
899
1398
  if (dateMatch) {
900
1399
  const lastDate = new Date(dateMatch[1]);
@@ -924,16 +1423,16 @@ function displayNotifications(notifications) {
924
1423
  }
925
1424
 
926
1425
  // src/index.ts
927
- import fs6 from "fs";
928
- import path6 from "path";
929
- import os6 from "os";
1426
+ import fs7 from "fs";
1427
+ import path7 from "path";
1428
+ import os7 from "os";
930
1429
  var program = new Command();
931
1430
  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) => {
932
- p.intro(pc5.bold("aman agent") + pc5.dim(" \u2014 starting your AI companion"));
1431
+ p2.intro(pc5.bold("aman agent") + pc5.dim(" \u2014 starting your AI companion"));
933
1432
  let config = loadConfig();
934
1433
  if (!config) {
935
- p.log.info("First-time setup \u2014 configure your LLM connection.");
936
- const provider = await p.select({
1434
+ p2.log.info("First-time setup \u2014 configure your LLM connection.");
1435
+ const provider = await p2.select({
937
1436
  message: "LLM provider",
938
1437
  options: [
939
1438
  {
@@ -946,27 +1445,27 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
946
1445
  ],
947
1446
  initialValue: "anthropic"
948
1447
  });
949
- if (p.isCancel(provider)) process.exit(0);
1448
+ if (p2.isCancel(provider)) process.exit(0);
950
1449
  let apiKey = "";
951
1450
  let defaultModel = "";
952
1451
  if (provider === "ollama") {
953
1452
  apiKey = "ollama";
954
- const modelInput = await p.text({
1453
+ const modelInput = await p2.text({
955
1454
  message: "Ollama model name",
956
1455
  placeholder: "llama3.2",
957
1456
  defaultValue: "llama3.2"
958
1457
  });
959
- if (p.isCancel(modelInput)) process.exit(0);
1458
+ if (p2.isCancel(modelInput)) process.exit(0);
960
1459
  defaultModel = modelInput || "llama3.2";
961
1460
  } else if (provider === "anthropic") {
962
- p.log.info("Get your API key from: https://console.anthropic.com/settings/keys");
963
- p.log.info(pc5.dim("Note: API access is separate from Claude Pro subscription. You need API credits."));
964
- apiKey = await p.text({
1461
+ p2.log.info("Get your API key from: https://console.anthropic.com/settings/keys");
1462
+ p2.log.info(pc5.dim("Note: API access is separate from Claude Pro subscription. You need API credits."));
1463
+ apiKey = await p2.text({
965
1464
  message: "API key (starts with sk-ant-)",
966
1465
  validate: (v) => v.length === 0 ? "API key is required" : void 0
967
1466
  });
968
- if (p.isCancel(apiKey)) process.exit(0);
969
- const modelChoice = await p.select({
1467
+ if (p2.isCancel(apiKey)) process.exit(0);
1468
+ const modelChoice = await p2.select({
970
1469
  message: "Claude model",
971
1470
  options: [
972
1471
  { value: "claude-sonnet-4-5-20250514", label: "Claude Sonnet 4.5", hint: "fast, recommended" },
@@ -976,25 +1475,25 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
976
1475
  ],
977
1476
  initialValue: "claude-sonnet-4-5-20250514"
978
1477
  });
979
- if (p.isCancel(modelChoice)) process.exit(0);
1478
+ if (p2.isCancel(modelChoice)) process.exit(0);
980
1479
  if (modelChoice === "custom") {
981
- const customModel = await p.text({
1480
+ const customModel = await p2.text({
982
1481
  message: "Model ID",
983
1482
  placeholder: "claude-sonnet-4-5-20250514",
984
1483
  validate: (v) => v.length === 0 ? "Model ID is required" : void 0
985
1484
  });
986
- if (p.isCancel(customModel)) process.exit(0);
1485
+ if (p2.isCancel(customModel)) process.exit(0);
987
1486
  defaultModel = customModel;
988
1487
  } else {
989
1488
  defaultModel = modelChoice;
990
1489
  }
991
1490
  } else {
992
- apiKey = await p.text({
1491
+ apiKey = await p2.text({
993
1492
  message: "API key",
994
1493
  validate: (v) => v.length === 0 ? "API key is required" : void 0
995
1494
  });
996
- if (p.isCancel(apiKey)) process.exit(0);
997
- const modelChoice = await p.select({
1495
+ if (p2.isCancel(apiKey)) process.exit(0);
1496
+ const modelChoice = await p2.select({
998
1497
  message: "OpenAI model",
999
1498
  options: [
1000
1499
  { value: "gpt-4o", label: "GPT-4o", hint: "recommended" },
@@ -1004,14 +1503,14 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
1004
1503
  ],
1005
1504
  initialValue: "gpt-4o"
1006
1505
  });
1007
- if (p.isCancel(modelChoice)) process.exit(0);
1506
+ if (p2.isCancel(modelChoice)) process.exit(0);
1008
1507
  if (modelChoice === "custom") {
1009
- const customModel = await p.text({
1508
+ const customModel = await p2.text({
1010
1509
  message: "Model ID",
1011
1510
  placeholder: "gpt-4o",
1012
1511
  validate: (v) => v.length === 0 ? "Model ID is required" : void 0
1013
1512
  });
1014
- if (p.isCancel(customModel)) process.exit(0);
1513
+ if (p2.isCancel(customModel)) process.exit(0);
1015
1514
  defaultModel = customModel;
1016
1515
  } else {
1017
1516
  defaultModel = modelChoice;
@@ -1019,44 +1518,44 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
1019
1518
  }
1020
1519
  config = { provider, apiKey, model: defaultModel };
1021
1520
  saveConfig(config);
1022
- p.log.success("Config saved to ~/.aman-agent/config.json");
1521
+ p2.log.success("Config saved to ~/.aman-agent/config.json");
1023
1522
  }
1024
1523
  const model = options.model || config.model;
1025
1524
  const budget = options.budget || void 0;
1026
1525
  const { prompt: systemPrompt, layers, truncated, totalTokens } = assembleSystemPrompt(budget);
1027
1526
  if (layers.length === 0) {
1028
- p.log.warning(
1527
+ p2.log.warning(
1029
1528
  "No ecosystem configured. Run " + pc5.bold("npx @aman_asmuei/aman") + " first."
1030
1529
  );
1031
- p.log.info("Starting with empty system prompt.");
1530
+ p2.log.info("Starting with empty system prompt.");
1032
1531
  } else {
1033
- p.log.success(
1532
+ p2.log.success(
1034
1533
  `Loaded: ${layers.join(", ")} ${pc5.dim(`(${totalTokens.toLocaleString()} tokens)`)}`
1035
1534
  );
1036
1535
  if (truncated.length > 0) {
1037
- p.log.warning(`Truncated: ${truncated.join(", ")} ${pc5.dim("(over budget)")}`);
1536
+ p2.log.warning(`Truncated: ${truncated.join(", ")} ${pc5.dim("(over budget)")}`);
1038
1537
  }
1039
1538
  }
1040
- p.log.info(`Model: ${pc5.dim(model)}`);
1041
- const corePath = path6.join(os6.homedir(), ".acore", "core.md");
1539
+ p2.log.info(`Model: ${pc5.dim(model)}`);
1540
+ const corePath = path7.join(os7.homedir(), ".acore", "core.md");
1042
1541
  let aiName = "Assistant";
1043
- if (fs6.existsSync(corePath)) {
1044
- const content = fs6.readFileSync(corePath, "utf-8");
1542
+ if (fs7.existsSync(corePath)) {
1543
+ const content = fs7.readFileSync(corePath, "utf-8");
1045
1544
  const match = content.match(/^# (.+)$/m);
1046
1545
  if (match) aiName = match[1];
1047
1546
  }
1048
- p.log.success(`${pc5.bold(aiName)} is ready.`);
1547
+ p2.log.success(`${pc5.bold(aiName)} is ready.`);
1049
1548
  const notifications = checkNotifications();
1050
1549
  displayNotifications(notifications);
1051
1550
  const mcpManager = new McpManager();
1052
- p.log.step("Connecting to MCP servers...");
1551
+ p2.log.step("Connecting to MCP servers...");
1053
1552
  await mcpManager.connect("aman", "npx", ["-y", "@aman_asmuei/aman-mcp"]);
1054
1553
  await mcpManager.connect("amem", "npx", ["-y", "@aman_asmuei/amem"]);
1055
1554
  const mcpTools = mcpManager.getTools();
1056
1555
  if (mcpTools.length > 0) {
1057
- p.log.success(`${mcpTools.length} MCP tools available`);
1556
+ p2.log.success(`${mcpTools.length} MCP tools available`);
1058
1557
  } else {
1059
- p.log.info(
1558
+ p2.log.info(
1060
1559
  "No MCP tools connected (install aman-mcp or amem for tool support)"
1061
1560
  );
1062
1561
  }
@@ -1079,7 +1578,8 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
1079
1578
  aiName,
1080
1579
  model,
1081
1580
  toolDefs.length > 0 ? toolDefs : void 0,
1082
- toolDefs.length > 0 ? mcpManager : void 0
1581
+ toolDefs.length > 0 ? mcpManager : void 0,
1582
+ config.hooks
1083
1583
  );
1084
1584
  await mcpManager.disconnect();
1085
1585
  });
@@ -1100,12 +1600,12 @@ program.command("schedule").description("Manage scheduled tasks").argument("[act
1100
1600
  return;
1101
1601
  }
1102
1602
  if (action === "add") {
1103
- const name = await p.text({
1603
+ const name = await p2.text({
1104
1604
  message: "Task name?",
1105
1605
  validate: (v) => v.length === 0 ? "Name is required" : void 0
1106
1606
  });
1107
- if (p.isCancel(name)) return;
1108
- const schedule = await p.select({
1607
+ if (p2.isCancel(name)) return;
1608
+ const schedule = await p2.select({
1109
1609
  message: "Schedule?",
1110
1610
  options: [
1111
1611
  { value: "daily 9am", label: "Daily at 9am" },
@@ -1115,23 +1615,23 @@ program.command("schedule").description("Manage scheduled tasks").argument("[act
1115
1615
  { value: "every 4h", label: "Every 4 hours" }
1116
1616
  ]
1117
1617
  });
1118
- if (p.isCancel(schedule)) return;
1119
- const actionType = await p.select({
1618
+ if (p2.isCancel(schedule)) return;
1619
+ const actionType = await p2.select({
1120
1620
  message: "What should happen?",
1121
1621
  options: [
1122
1622
  { value: "notify", label: "Show notification" },
1123
1623
  { value: "auto-run", label: "Run automatically" }
1124
1624
  ]
1125
1625
  });
1126
- if (p.isCancel(actionType)) return;
1626
+ if (p2.isCancel(actionType)) return;
1127
1627
  let taskAction = "notify";
1128
1628
  if (actionType === "auto-run") {
1129
- const cmd = await p.text({
1629
+ const cmd = await p2.text({
1130
1630
  message: "Command to run?",
1131
1631
  placeholder: "e.g. run:daily-standup",
1132
1632
  validate: (v) => v.length === 0 ? "Command is required" : void 0
1133
1633
  });
1134
- if (p.isCancel(cmd)) return;
1634
+ if (p2.isCancel(cmd)) return;
1135
1635
  taskAction = cmd;
1136
1636
  }
1137
1637
  const task = addSchedule({