@aman_asmuei/aman-agent 0.2.3 → 0.3.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
@@ -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 };
774
+ }
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) {
836
+ return {
837
+ handled: true,
838
+ output: "Usage: /remind <time> <message>\nExamples: /remind 30m Review PR, /remind 2h Deploy, /remind tomorrow Check metrics"
839
+ };
644
840
  }
645
- if (cmd.startsWith("/")) {
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 {
646
879
  return {
647
880
  handled: true,
648
- output: `Unknown command: ${cmd}. Type ${pc.cyan("/help")} for available commands.`
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")
649
888
  };
650
889
  }
651
- return { handled: false };
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,40 +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
- const authMethod = await p.select({
963
- message: "Authentication method",
964
- options: [
965
- { value: "api-key", label: "API key", hint: "from console.anthropic.com" },
966
- { value: "subscription", label: "Claude Pro/Team subscription", hint: "uses claude.ai session" }
967
- ],
968
- initialValue: "api-key"
969
- });
970
- if (p.isCancel(authMethod)) process.exit(0);
971
- if (authMethod === "subscription") {
972
- p.log.info("To use your Claude subscription:");
973
- p.log.info("1. Go to https://console.anthropic.com/settings/keys");
974
- p.log.info("2. Create an API key (included with Pro/Team plan)");
975
- p.log.info("3. Paste it below");
976
- }
977
- apiKey = await p.text({
978
- message: "API key",
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({
1464
+ message: "API key (starts with sk-ant-)",
979
1465
  validate: (v) => v.length === 0 ? "API key is required" : void 0
980
1466
  });
981
- if (p.isCancel(apiKey)) process.exit(0);
982
- const modelChoice = await p.select({
1467
+ if (p2.isCancel(apiKey)) process.exit(0);
1468
+ const modelChoice = await p2.select({
983
1469
  message: "Claude model",
984
1470
  options: [
985
1471
  { value: "claude-sonnet-4-5-20250514", label: "Claude Sonnet 4.5", hint: "fast, recommended" },
@@ -989,25 +1475,25 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
989
1475
  ],
990
1476
  initialValue: "claude-sonnet-4-5-20250514"
991
1477
  });
992
- if (p.isCancel(modelChoice)) process.exit(0);
1478
+ if (p2.isCancel(modelChoice)) process.exit(0);
993
1479
  if (modelChoice === "custom") {
994
- const customModel = await p.text({
1480
+ const customModel = await p2.text({
995
1481
  message: "Model ID",
996
1482
  placeholder: "claude-sonnet-4-5-20250514",
997
1483
  validate: (v) => v.length === 0 ? "Model ID is required" : void 0
998
1484
  });
999
- if (p.isCancel(customModel)) process.exit(0);
1485
+ if (p2.isCancel(customModel)) process.exit(0);
1000
1486
  defaultModel = customModel;
1001
1487
  } else {
1002
1488
  defaultModel = modelChoice;
1003
1489
  }
1004
1490
  } else {
1005
- apiKey = await p.text({
1491
+ apiKey = await p2.text({
1006
1492
  message: "API key",
1007
1493
  validate: (v) => v.length === 0 ? "API key is required" : void 0
1008
1494
  });
1009
- if (p.isCancel(apiKey)) process.exit(0);
1010
- const modelChoice = await p.select({
1495
+ if (p2.isCancel(apiKey)) process.exit(0);
1496
+ const modelChoice = await p2.select({
1011
1497
  message: "OpenAI model",
1012
1498
  options: [
1013
1499
  { value: "gpt-4o", label: "GPT-4o", hint: "recommended" },
@@ -1017,14 +1503,14 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
1017
1503
  ],
1018
1504
  initialValue: "gpt-4o"
1019
1505
  });
1020
- if (p.isCancel(modelChoice)) process.exit(0);
1506
+ if (p2.isCancel(modelChoice)) process.exit(0);
1021
1507
  if (modelChoice === "custom") {
1022
- const customModel = await p.text({
1508
+ const customModel = await p2.text({
1023
1509
  message: "Model ID",
1024
1510
  placeholder: "gpt-4o",
1025
1511
  validate: (v) => v.length === 0 ? "Model ID is required" : void 0
1026
1512
  });
1027
- if (p.isCancel(customModel)) process.exit(0);
1513
+ if (p2.isCancel(customModel)) process.exit(0);
1028
1514
  defaultModel = customModel;
1029
1515
  } else {
1030
1516
  defaultModel = modelChoice;
@@ -1032,44 +1518,44 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
1032
1518
  }
1033
1519
  config = { provider, apiKey, model: defaultModel };
1034
1520
  saveConfig(config);
1035
- p.log.success("Config saved to ~/.aman-agent/config.json");
1521
+ p2.log.success("Config saved to ~/.aman-agent/config.json");
1036
1522
  }
1037
1523
  const model = options.model || config.model;
1038
1524
  const budget = options.budget || void 0;
1039
1525
  const { prompt: systemPrompt, layers, truncated, totalTokens } = assembleSystemPrompt(budget);
1040
1526
  if (layers.length === 0) {
1041
- p.log.warning(
1527
+ p2.log.warning(
1042
1528
  "No ecosystem configured. Run " + pc5.bold("npx @aman_asmuei/aman") + " first."
1043
1529
  );
1044
- p.log.info("Starting with empty system prompt.");
1530
+ p2.log.info("Starting with empty system prompt.");
1045
1531
  } else {
1046
- p.log.success(
1532
+ p2.log.success(
1047
1533
  `Loaded: ${layers.join(", ")} ${pc5.dim(`(${totalTokens.toLocaleString()} tokens)`)}`
1048
1534
  );
1049
1535
  if (truncated.length > 0) {
1050
- p.log.warning(`Truncated: ${truncated.join(", ")} ${pc5.dim("(over budget)")}`);
1536
+ p2.log.warning(`Truncated: ${truncated.join(", ")} ${pc5.dim("(over budget)")}`);
1051
1537
  }
1052
1538
  }
1053
- p.log.info(`Model: ${pc5.dim(model)}`);
1054
- 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");
1055
1541
  let aiName = "Assistant";
1056
- if (fs6.existsSync(corePath)) {
1057
- const content = fs6.readFileSync(corePath, "utf-8");
1542
+ if (fs7.existsSync(corePath)) {
1543
+ const content = fs7.readFileSync(corePath, "utf-8");
1058
1544
  const match = content.match(/^# (.+)$/m);
1059
1545
  if (match) aiName = match[1];
1060
1546
  }
1061
- p.log.success(`${pc5.bold(aiName)} is ready.`);
1547
+ p2.log.success(`${pc5.bold(aiName)} is ready.`);
1062
1548
  const notifications = checkNotifications();
1063
1549
  displayNotifications(notifications);
1064
1550
  const mcpManager = new McpManager();
1065
- p.log.step("Connecting to MCP servers...");
1551
+ p2.log.step("Connecting to MCP servers...");
1066
1552
  await mcpManager.connect("aman", "npx", ["-y", "@aman_asmuei/aman-mcp"]);
1067
1553
  await mcpManager.connect("amem", "npx", ["-y", "@aman_asmuei/amem"]);
1068
1554
  const mcpTools = mcpManager.getTools();
1069
1555
  if (mcpTools.length > 0) {
1070
- p.log.success(`${mcpTools.length} MCP tools available`);
1556
+ p2.log.success(`${mcpTools.length} MCP tools available`);
1071
1557
  } else {
1072
- p.log.info(
1558
+ p2.log.info(
1073
1559
  "No MCP tools connected (install aman-mcp or amem for tool support)"
1074
1560
  );
1075
1561
  }
@@ -1092,7 +1578,8 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
1092
1578
  aiName,
1093
1579
  model,
1094
1580
  toolDefs.length > 0 ? toolDefs : void 0,
1095
- toolDefs.length > 0 ? mcpManager : void 0
1581
+ toolDefs.length > 0 ? mcpManager : void 0,
1582
+ config.hooks
1096
1583
  );
1097
1584
  await mcpManager.disconnect();
1098
1585
  });
@@ -1113,12 +1600,12 @@ program.command("schedule").description("Manage scheduled tasks").argument("[act
1113
1600
  return;
1114
1601
  }
1115
1602
  if (action === "add") {
1116
- const name = await p.text({
1603
+ const name = await p2.text({
1117
1604
  message: "Task name?",
1118
1605
  validate: (v) => v.length === 0 ? "Name is required" : void 0
1119
1606
  });
1120
- if (p.isCancel(name)) return;
1121
- const schedule = await p.select({
1607
+ if (p2.isCancel(name)) return;
1608
+ const schedule = await p2.select({
1122
1609
  message: "Schedule?",
1123
1610
  options: [
1124
1611
  { value: "daily 9am", label: "Daily at 9am" },
@@ -1128,23 +1615,23 @@ program.command("schedule").description("Manage scheduled tasks").argument("[act
1128
1615
  { value: "every 4h", label: "Every 4 hours" }
1129
1616
  ]
1130
1617
  });
1131
- if (p.isCancel(schedule)) return;
1132
- const actionType = await p.select({
1618
+ if (p2.isCancel(schedule)) return;
1619
+ const actionType = await p2.select({
1133
1620
  message: "What should happen?",
1134
1621
  options: [
1135
1622
  { value: "notify", label: "Show notification" },
1136
1623
  { value: "auto-run", label: "Run automatically" }
1137
1624
  ]
1138
1625
  });
1139
- if (p.isCancel(actionType)) return;
1626
+ if (p2.isCancel(actionType)) return;
1140
1627
  let taskAction = "notify";
1141
1628
  if (actionType === "auto-run") {
1142
- const cmd = await p.text({
1629
+ const cmd = await p2.text({
1143
1630
  message: "Command to run?",
1144
1631
  placeholder: "e.g. run:daily-standup",
1145
1632
  validate: (v) => v.length === 0 ? "Command is required" : void 0
1146
1633
  });
1147
- if (p.isCancel(cmd)) return;
1634
+ if (p2.isCancel(cmd)) return;
1148
1635
  taskAction = cmd;
1149
1636
  }
1150
1637
  const task = addSchedule({