@aman_asmuei/aman-agent 0.18.0 → 0.21.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
@@ -726,6 +726,474 @@ function createOllamaClient(model, baseURL) {
726
726
  };
727
727
  }
728
728
 
729
+ // src/llm/claude-code.ts
730
+ import { spawn, execFileSync } from "child_process";
731
+ function isClaudeCliInstalled() {
732
+ try {
733
+ execFileSync("which", ["claude"], { stdio: "ignore" });
734
+ return true;
735
+ } catch {
736
+ return false;
737
+ }
738
+ }
739
+ function extractText(content) {
740
+ if (typeof content === "string") return content;
741
+ return content.map((block) => {
742
+ if (block.type === "text") return block.text;
743
+ if (block.type === "tool_result")
744
+ return `[Tool result for ${block.tool_use_id}]: ${block.content}`;
745
+ if (block.type === "tool_use") return `[Used tool: ${block.name}]`;
746
+ return "";
747
+ }).filter(Boolean).join("\n");
748
+ }
749
+ function formatConversation(systemPrompt, messages, tools) {
750
+ const parts = [];
751
+ let fullSystem = systemPrompt;
752
+ if (tools && tools.length > 0) {
753
+ fullSystem += "\n\n## Available Tools\n";
754
+ fullSystem += "You have access to the following tools. To use a tool, respond with a JSON block in this exact format:\n";
755
+ fullSystem += '```json\n{"tool_use": {"id": "call_1", "name": "tool_name", "input": {\u2026}}}\n```\n\n';
756
+ for (const tool of tools) {
757
+ fullSystem += `### ${tool.name}
758
+ ${tool.description}
759
+ Parameters: ${JSON.stringify(tool.input_schema)}
760
+
761
+ `;
762
+ }
763
+ fullSystem += "You may include multiple tool_use blocks. After each tool use, you will receive the result and can continue.\n";
764
+ }
765
+ if (messages.length > 1) {
766
+ parts.push("<conversation_history>");
767
+ for (let i = 0; i < messages.length - 1; i++) {
768
+ const msg = messages[i];
769
+ const role = msg.role === "user" ? "User" : "Assistant";
770
+ const text3 = extractText(msg.content);
771
+ parts.push(`[${role}]: ${text3}`);
772
+ }
773
+ parts.push("</conversation_history>\n");
774
+ }
775
+ const lastMsg = messages[messages.length - 1];
776
+ if (lastMsg) {
777
+ parts.push(extractText(lastMsg.content));
778
+ }
779
+ return { prompt: parts.join("\n"), systemPrompt: fullSystem };
780
+ }
781
+ function parseToolUses(text3) {
782
+ const toolUses = [];
783
+ const codeBlockRegex = /```json\s*\n?([\s\S]*?)```/g;
784
+ let match;
785
+ while ((match = codeBlockRegex.exec(text3)) !== null) {
786
+ try {
787
+ const parsed = JSON.parse(match[1].trim());
788
+ if (parsed.tool_use) {
789
+ toolUses.push({
790
+ id: parsed.tool_use.id || `call_${toolUses.length + 1}`,
791
+ name: parsed.tool_use.name,
792
+ input: parsed.tool_use.input || {}
793
+ });
794
+ }
795
+ } catch {
796
+ }
797
+ }
798
+ if (toolUses.length === 0) {
799
+ const inlineRegex = /\{"tool_use"\s*:\s*\{[^}]*"name"\s*:\s*"[^"]+?"[^}]*\}\s*\}/g;
800
+ while ((match = inlineRegex.exec(text3)) !== null) {
801
+ try {
802
+ const parsed = JSON.parse(match[0]);
803
+ if (parsed.tool_use) {
804
+ toolUses.push({
805
+ id: parsed.tool_use.id || `call_${toolUses.length + 1}`,
806
+ name: parsed.tool_use.name,
807
+ input: parsed.tool_use.input || {}
808
+ });
809
+ }
810
+ } catch {
811
+ }
812
+ }
813
+ }
814
+ return toolUses;
815
+ }
816
+ function createClaudeCodeClient(model) {
817
+ return {
818
+ async chat(systemPrompt, messages, onChunk, tools, options) {
819
+ const { prompt, systemPrompt: fullSystem } = formatConversation(
820
+ systemPrompt,
821
+ messages,
822
+ tools
823
+ );
824
+ return new Promise((resolve, reject) => {
825
+ const args = [
826
+ "--print",
827
+ "--output-format",
828
+ "stream-json",
829
+ "--system-prompt",
830
+ fullSystem
831
+ ];
832
+ if (model) {
833
+ args.push("--model", model);
834
+ }
835
+ if (options?.maxOutputTokens) {
836
+ args.push("--max-tokens", String(options.maxOutputTokens));
837
+ }
838
+ const proc = spawn("claude", args, {
839
+ stdio: ["pipe", "pipe", "pipe"]
840
+ });
841
+ let fullText = "";
842
+ let buffer = "";
843
+ let stderrOutput = "";
844
+ proc.stdin.write(prompt);
845
+ proc.stdin.end();
846
+ proc.stdout.on("data", (data) => {
847
+ buffer += data.toString();
848
+ const lines = buffer.split("\n");
849
+ buffer = lines.pop() || "";
850
+ for (const line of lines) {
851
+ if (!line.trim()) continue;
852
+ try {
853
+ const event = JSON.parse(line);
854
+ if (event.type === "assistant") {
855
+ if (event.subtype === "text" && event.content) {
856
+ fullText += event.content;
857
+ onChunk({ type: "text", text: event.content });
858
+ }
859
+ } else if (event.type === "content_block_delta") {
860
+ if (event.delta?.type === "text_delta" && event.delta.text) {
861
+ fullText += event.delta.text;
862
+ onChunk({ type: "text", text: event.delta.text });
863
+ }
864
+ } else if (event.type === "message" && event.message?.content) {
865
+ for (const block of event.message.content) {
866
+ if (block.type === "text" && block.text) {
867
+ fullText += block.text;
868
+ onChunk({ type: "text", text: block.text });
869
+ }
870
+ }
871
+ }
872
+ } catch {
873
+ if (line.trim()) {
874
+ fullText += line;
875
+ onChunk({ type: "text", text: line });
876
+ }
877
+ }
878
+ }
879
+ });
880
+ proc.stderr.on("data", (data) => {
881
+ stderrOutput += data.toString();
882
+ });
883
+ proc.on("close", (code) => {
884
+ if (buffer.trim()) {
885
+ try {
886
+ const event = JSON.parse(buffer);
887
+ if (event.type === "assistant" && event.subtype === "text" && event.content) {
888
+ fullText += event.content;
889
+ onChunk({ type: "text", text: event.content });
890
+ }
891
+ } catch {
892
+ if (buffer.trim()) {
893
+ fullText += buffer;
894
+ onChunk({ type: "text", text: buffer });
895
+ }
896
+ }
897
+ }
898
+ onChunk({ type: "done" });
899
+ if (code !== 0 && !fullText) {
900
+ reject(
901
+ new Error(
902
+ `Claude CLI exited with code ${code}${stderrOutput ? `: ${stderrOutput.trim()}` : ""}`
903
+ )
904
+ );
905
+ return;
906
+ }
907
+ const hasTools = tools && tools.length > 0;
908
+ if (hasTools) {
909
+ const toolUses = parseToolUses(fullText);
910
+ if (toolUses.length > 0) {
911
+ let cleanText = fullText;
912
+ const stripRegex = /```json\s*\n?\s*\{"tool_use"[\s\S]*?```/g;
913
+ cleanText = cleanText.replace(stripRegex, "").trim();
914
+ const contentBlocks = [];
915
+ if (cleanText) {
916
+ contentBlocks.push({ type: "text", text: cleanText });
917
+ }
918
+ for (const tu of toolUses) {
919
+ contentBlocks.push({
920
+ type: "tool_use",
921
+ id: tu.id,
922
+ name: tu.name,
923
+ input: tu.input
924
+ });
925
+ }
926
+ resolve({
927
+ message: { role: "assistant", content: contentBlocks },
928
+ toolUses
929
+ });
930
+ return;
931
+ }
932
+ }
933
+ resolve({
934
+ message: { role: "assistant", content: fullText },
935
+ toolUses: []
936
+ });
937
+ });
938
+ proc.on("error", (err) => {
939
+ if (err.code === "ENOENT") {
940
+ reject(
941
+ new Error(
942
+ "Claude CLI not found. Install it with: npm install -g @anthropic-ai/claude-code"
943
+ )
944
+ );
945
+ } else {
946
+ reject(err);
947
+ }
948
+ });
949
+ });
950
+ }
951
+ };
952
+ }
953
+
954
+ // src/llm/copilot.ts
955
+ import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
956
+ function isCopilotCliInstalled() {
957
+ try {
958
+ execFileSync2("which", ["copilot"], { stdio: "ignore" });
959
+ return true;
960
+ } catch {
961
+ return false;
962
+ }
963
+ }
964
+ function isCopilotCliAuthenticated() {
965
+ try {
966
+ const result = execFileSync2("copilot", ["--version"], {
967
+ stdio: ["ignore", "pipe", "ignore"],
968
+ timeout: 5e3
969
+ });
970
+ return result.toString().trim().length > 0;
971
+ } catch {
972
+ return false;
973
+ }
974
+ }
975
+ function extractText2(content) {
976
+ if (typeof content === "string") return content;
977
+ return content.map((block) => {
978
+ if (block.type === "text") return block.text;
979
+ if (block.type === "tool_result")
980
+ return `[Tool result for ${block.tool_use_id}]: ${block.content}`;
981
+ if (block.type === "tool_use") return `[Used tool: ${block.name}]`;
982
+ return "";
983
+ }).filter(Boolean).join("\n");
984
+ }
985
+ function formatConversation2(systemPrompt, messages, tools) {
986
+ const parts = [];
987
+ let fullSystem = systemPrompt;
988
+ if (tools && tools.length > 0) {
989
+ fullSystem += "\n\n## Available Tools\n";
990
+ fullSystem += "You have access to the following tools. To use a tool, respond with a JSON block in this exact format:\n";
991
+ fullSystem += '```json\n{"tool_use": {"id": "call_1", "name": "tool_name", "input": {\u2026}}}\n```\n\n';
992
+ for (const tool of tools) {
993
+ fullSystem += `### ${tool.name}
994
+ ${tool.description}
995
+ Parameters: ${JSON.stringify(tool.input_schema)}
996
+
997
+ `;
998
+ }
999
+ fullSystem += "You may include multiple tool_use blocks. After each tool use, you will receive the result and can continue.\n";
1000
+ }
1001
+ if (messages.length > 1) {
1002
+ parts.push("<conversation_history>");
1003
+ for (let i = 0; i < messages.length - 1; i++) {
1004
+ const msg = messages[i];
1005
+ const role = msg.role === "user" ? "User" : "Assistant";
1006
+ const text3 = extractText2(msg.content);
1007
+ parts.push(`[${role}]: ${text3}`);
1008
+ }
1009
+ parts.push("</conversation_history>\n");
1010
+ }
1011
+ const lastMsg = messages[messages.length - 1];
1012
+ if (lastMsg) {
1013
+ parts.push(extractText2(lastMsg.content));
1014
+ }
1015
+ return { prompt: parts.join("\n"), systemPrompt: fullSystem };
1016
+ }
1017
+ function parseToolUses2(text3) {
1018
+ const toolUses = [];
1019
+ const codeBlockRegex = /```json\s*\n?([\s\S]*?)```/g;
1020
+ let match;
1021
+ while ((match = codeBlockRegex.exec(text3)) !== null) {
1022
+ try {
1023
+ const parsed = JSON.parse(match[1].trim());
1024
+ if (parsed.tool_use) {
1025
+ toolUses.push({
1026
+ id: parsed.tool_use.id || `call_${toolUses.length + 1}`,
1027
+ name: parsed.tool_use.name,
1028
+ input: parsed.tool_use.input || {}
1029
+ });
1030
+ }
1031
+ } catch {
1032
+ }
1033
+ }
1034
+ if (toolUses.length === 0) {
1035
+ const inlineRegex = /\{"tool_use"\s*:\s*\{[^}]*"name"\s*:\s*"[^"]+?"[^}]*\}\s*\}/g;
1036
+ while ((match = inlineRegex.exec(text3)) !== null) {
1037
+ try {
1038
+ const parsed = JSON.parse(match[0]);
1039
+ if (parsed.tool_use) {
1040
+ toolUses.push({
1041
+ id: parsed.tool_use.id || `call_${toolUses.length + 1}`,
1042
+ name: parsed.tool_use.name,
1043
+ input: parsed.tool_use.input || {}
1044
+ });
1045
+ }
1046
+ } catch {
1047
+ }
1048
+ }
1049
+ }
1050
+ return toolUses;
1051
+ }
1052
+ function createCopilotClient(model) {
1053
+ return {
1054
+ async chat(systemPrompt, messages, onChunk, tools, options) {
1055
+ const { prompt, systemPrompt: fullSystem } = formatConversation2(
1056
+ systemPrompt,
1057
+ messages,
1058
+ tools
1059
+ );
1060
+ return new Promise((resolve, reject) => {
1061
+ const args = [
1062
+ "--print",
1063
+ "--output-format",
1064
+ "json",
1065
+ "--silent",
1066
+ "--no-custom-instructions"
1067
+ ];
1068
+ if (model) {
1069
+ args.push("--model", model);
1070
+ }
1071
+ args.push(prompt);
1072
+ const proc = spawn2("copilot", args, {
1073
+ stdio: ["pipe", "pipe", "pipe"],
1074
+ env: {
1075
+ ...process.env,
1076
+ COPILOT_SYSTEM_PROMPT: fullSystem
1077
+ }
1078
+ });
1079
+ let fullText = "";
1080
+ let buffer = "";
1081
+ let stderrOutput = "";
1082
+ proc.stdout.on("data", (data) => {
1083
+ buffer += data.toString();
1084
+ const lines = buffer.split("\n");
1085
+ buffer = lines.pop() || "";
1086
+ for (const line of lines) {
1087
+ if (!line.trim()) continue;
1088
+ try {
1089
+ const event = JSON.parse(line);
1090
+ if (event.type === "assistant" && event.content) {
1091
+ fullText += event.content;
1092
+ onChunk({ type: "text", text: event.content });
1093
+ } else if (event.type === "message" && event.message?.content) {
1094
+ for (const block of event.message.content) {
1095
+ if (block.type === "text" && block.text) {
1096
+ fullText += block.text;
1097
+ onChunk({ type: "text", text: block.text });
1098
+ }
1099
+ }
1100
+ } else if (event.type === "content_block_delta") {
1101
+ if (event.delta?.type === "text_delta" && event.delta.text) {
1102
+ fullText += event.delta.text;
1103
+ onChunk({ type: "text", text: event.delta.text });
1104
+ }
1105
+ } else if (event.role === "assistant" && event.content) {
1106
+ const text3 = typeof event.content === "string" ? event.content : event.content.map((b) => b.text || "").join("");
1107
+ if (text3) {
1108
+ fullText += text3;
1109
+ onChunk({ type: "text", text: text3 });
1110
+ }
1111
+ }
1112
+ } catch {
1113
+ if (line.trim()) {
1114
+ fullText += line;
1115
+ onChunk({ type: "text", text: line });
1116
+ }
1117
+ }
1118
+ }
1119
+ });
1120
+ proc.stderr.on("data", (data) => {
1121
+ stderrOutput += data.toString();
1122
+ });
1123
+ proc.on("close", (code) => {
1124
+ if (buffer.trim()) {
1125
+ try {
1126
+ const event = JSON.parse(buffer);
1127
+ if (event.type === "assistant" && event.content) {
1128
+ fullText += event.content;
1129
+ onChunk({ type: "text", text: event.content });
1130
+ } else if (event.role === "assistant" && typeof event.content === "string") {
1131
+ fullText += event.content;
1132
+ onChunk({ type: "text", text: event.content });
1133
+ }
1134
+ } catch {
1135
+ if (buffer.trim()) {
1136
+ fullText += buffer;
1137
+ onChunk({ type: "text", text: buffer });
1138
+ }
1139
+ }
1140
+ }
1141
+ onChunk({ type: "done" });
1142
+ if (code !== 0 && !fullText) {
1143
+ reject(
1144
+ new Error(
1145
+ `Copilot CLI exited with code ${code}${stderrOutput ? `: ${stderrOutput.trim()}` : ""}`
1146
+ )
1147
+ );
1148
+ return;
1149
+ }
1150
+ const hasTools = tools && tools.length > 0;
1151
+ if (hasTools) {
1152
+ const toolUses = parseToolUses2(fullText);
1153
+ if (toolUses.length > 0) {
1154
+ let cleanText = fullText;
1155
+ const stripRegex = /```json\s*\n?\s*\{"tool_use"[\s\S]*?```/g;
1156
+ cleanText = cleanText.replace(stripRegex, "").trim();
1157
+ const contentBlocks = [];
1158
+ if (cleanText) {
1159
+ contentBlocks.push({ type: "text", text: cleanText });
1160
+ }
1161
+ for (const tu of toolUses) {
1162
+ contentBlocks.push({
1163
+ type: "tool_use",
1164
+ id: tu.id,
1165
+ name: tu.name,
1166
+ input: tu.input
1167
+ });
1168
+ }
1169
+ resolve({
1170
+ message: { role: "assistant", content: contentBlocks },
1171
+ toolUses
1172
+ });
1173
+ return;
1174
+ }
1175
+ }
1176
+ resolve({
1177
+ message: { role: "assistant", content: fullText },
1178
+ toolUses: []
1179
+ });
1180
+ });
1181
+ proc.on("error", (err) => {
1182
+ if (err.code === "ENOENT") {
1183
+ reject(
1184
+ new Error(
1185
+ "Copilot CLI not found. Install it from: https://docs.github.com/copilot/how-tos/copilot-cli"
1186
+ )
1187
+ );
1188
+ } else {
1189
+ reject(err);
1190
+ }
1191
+ });
1192
+ });
1193
+ }
1194
+ };
1195
+ }
1196
+
729
1197
  // src/mcp/client.ts
730
1198
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
731
1199
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
@@ -931,7 +1399,7 @@ import logUpdate from "log-update";
931
1399
  import fs12 from "fs";
932
1400
  import path12 from "path";
933
1401
  import os11 from "os";
934
- import { execFileSync } from "child_process";
1402
+ import { execFileSync as execFileSync3 } from "child_process";
935
1403
  import pc5 from "picocolors";
936
1404
 
937
1405
  // src/layers/parsers.ts
@@ -3487,6 +3955,7 @@ function handleHelp() {
3487
3955
  ` ${pc5.cyan("/profile me")} View your profile`,
3488
3956
  ` ${pc5.cyan("/profile edit")} Edit your profile`,
3489
3957
  ` ${pc5.cyan("/profile")} List agent profiles`,
3958
+ ` ${pc5.cyan("/showcase")} Browse & switch companion templates`,
3490
3959
  ` ${pc5.cyan("/delegate")} Delegate tasks to sub-agents`,
3491
3960
  ` ${pc5.cyan("/team")} Manage agent teams`,
3492
3961
  ` ${pc5.cyan("/update")} Check for updates`,
@@ -3553,8 +4022,8 @@ function handleReset(action) {
3553
4022
  }
3554
4023
  function handleUpdate() {
3555
4024
  try {
3556
- const current = execFileSync("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
3557
- const local = true ? "0.18.0" : "unknown";
4025
+ const current = execFileSync3("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
4026
+ const local = true ? "0.21.1" : "unknown";
3558
4027
  if (current === local) {
3559
4028
  return { handled: true, output: `${pc5.green("Up to date")} \u2014 v${local}` };
3560
4029
  }
@@ -4123,6 +4592,103 @@ async function handleReminderCommand(action, args) {
4123
4592
  }
4124
4593
  return { handled: true, output: pc5.yellow(`Unknown action: /reminder ${action}. Try /reminder --help`) };
4125
4594
  }
4595
+ function handleShowcaseCommand(action, args) {
4596
+ const showcases = loadShowcaseManifest();
4597
+ if (showcases.length === 0) {
4598
+ return {
4599
+ handled: true,
4600
+ output: pc5.dim("No showcase templates found.") + `
4601
+
4602
+ Install aman-showcase to get 13 pre-built companion personalities:
4603
+ ${pc5.bold("npm install -g @aman_asmuei/aman-showcase")}
4604
+ Or place it as a sibling directory to aman-agent.`
4605
+ };
4606
+ }
4607
+ const corePath = path12.join(os11.homedir(), ".acore", "core.md");
4608
+ let currentShowcase = null;
4609
+ if (fs12.existsSync(corePath)) {
4610
+ const content = fs12.readFileSync(corePath, "utf-8");
4611
+ const nameMatch = content.match(/^# (.+)/m);
4612
+ if (nameMatch) {
4613
+ const coreName = nameMatch[1].trim().toLowerCase();
4614
+ const match = showcases.find((s) => coreName.includes(s.name) || coreName.includes(s.title.split("\u2014")[0].trim().toLowerCase()));
4615
+ if (match) currentShowcase = match.name;
4616
+ }
4617
+ }
4618
+ if (!action || action === "list") {
4619
+ const lines = showcases.map((s) => {
4620
+ const active = s.name === currentShowcase ? pc5.green(" \u2190 active") : "";
4621
+ const langBadge = s.language === "ms" ? " [BM]" : s.language === "en+ms" ? " [EN/BM]" : "";
4622
+ return ` ${pc5.bold(s.name.padEnd(12))} ${s.title}${langBadge}${active}`;
4623
+ });
4624
+ const currentLine = currentShowcase ? `
4625
+ Current: ${pc5.bold(currentShowcase)}
4626
+ ` : `
4627
+ No showcase active (using default personality)
4628
+ `;
4629
+ return {
4630
+ handled: true,
4631
+ output: `Showcase templates (${showcases.length}):
4632
+
4633
+ ${lines.join("\n")}
4634
+ ${currentLine}
4635
+ ${pc5.dim("Switch with: /showcase install <name>")}`
4636
+ };
4637
+ }
4638
+ if (action === "install" || action === "switch" || action === "use") {
4639
+ const name = args[0];
4640
+ if (!name) {
4641
+ return { handled: true, output: pc5.yellow("Usage: /showcase install <name>\n\nRun /showcase list to see available templates.") };
4642
+ }
4643
+ const entry = showcases.find((s) => s.name === name);
4644
+ if (!entry) {
4645
+ return { handled: true, output: pc5.red(`Showcase not found: ${name}`) + `
4646
+
4647
+ Available: ${showcases.map((s) => s.name).join(", ")}` };
4648
+ }
4649
+ if (name === currentShowcase) {
4650
+ return { handled: true, output: pc5.dim(`${entry.title} is already active.`) };
4651
+ }
4652
+ try {
4653
+ const result = installShowcaseTemplate(name);
4654
+ const lines = [pc5.green(`Installed ${pc5.bold(entry.title)}`)];
4655
+ for (const f of result.installed) {
4656
+ lines.push(pc5.dim(` ${f}`));
4657
+ }
4658
+ if (result.backed_up.length > 0) {
4659
+ lines.push(pc5.dim(`
4660
+ Backed up ${result.backed_up.length} existing file(s) (.bak)`));
4661
+ }
4662
+ lines.push("");
4663
+ lines.push(pc5.yellow("Restart aman-agent to use the new personality."));
4664
+ lines.push(pc5.dim("Your user profile (/profile me) is unchanged \u2014 only the AI personality switched."));
4665
+ return { handled: true, output: lines.join("\n") };
4666
+ } catch (err) {
4667
+ return { handled: true, output: pc5.red(`Failed to install: ${err instanceof Error ? err.message : String(err)}`) };
4668
+ }
4669
+ }
4670
+ if (action === "current") {
4671
+ if (currentShowcase) {
4672
+ const entry = showcases.find((s) => s.name === currentShowcase);
4673
+ return { handled: true, output: `Active showcase: ${pc5.bold(entry?.title || currentShowcase)}
4674
+ ${pc5.dim(entry?.description || "")}` };
4675
+ }
4676
+ return { handled: true, output: pc5.dim("No showcase active \u2014 using default personality.") + `
4677
+ ${pc5.dim("Install one with: /showcase install <name>")}` };
4678
+ }
4679
+ if (action === "help") {
4680
+ return { handled: true, output: `Showcase commands:
4681
+
4682
+ /showcase List all available templates
4683
+ /showcase install <n> Install/switch to a template
4684
+ /showcase current Show active template
4685
+
4686
+ ${pc5.dim("Showcase templates replace your AI's personality, workflows, rules, and skills.")}
4687
+ ${pc5.dim("Your user profile (/profile me) stays unchanged \u2014 only the AI personality switches.")}
4688
+ ${pc5.dim("Existing files are backed up (.bak) before overwriting.")}` };
4689
+ }
4690
+ return { handled: true, output: pc5.yellow(`Unknown action: /showcase ${action}. Try /showcase help`) };
4691
+ }
4126
4692
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
4127
4693
  "quit",
4128
4694
  "exit",
@@ -4151,7 +4717,8 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
4151
4717
  "plan",
4152
4718
  "profile",
4153
4719
  "delegate",
4154
- "team"
4720
+ "team",
4721
+ "showcase"
4155
4722
  ]);
4156
4723
  async function handleCommand(input, ctx) {
4157
4724
  const trimmed = input.trim();
@@ -4208,6 +4775,8 @@ async function handleCommand(input, ctx) {
4208
4775
  return handleTeamCommand(action, args, ctx);
4209
4776
  case "reminder":
4210
4777
  return handleReminderCommand(action, args);
4778
+ case "showcase":
4779
+ return handleShowcaseCommand(action, args);
4211
4780
  case "update":
4212
4781
  case "upgrade":
4213
4782
  return handleUpdate();
@@ -5909,7 +6478,7 @@ function bootstrapEcosystem() {
5909
6478
  return true;
5910
6479
  }
5911
6480
  var program = new Command();
5912
- program.name("aman-agent").description("Your AI companion, running locally").version("0.18.0").option("--model <model>", "Override LLM model").option("--budget <tokens>", "Token budget for system prompt (default: 8000)", parseInt).option("--profile <name>", "Use a specific agent profile (e.g., coder, writer, researcher)").action(async (options) => {
6481
+ program.name("aman-agent").description("Your AI companion, running locally").version("0.21.1").option("--model <model>", "Override LLM model").option("--budget <tokens>", "Token budget for system prompt (default: 8000)", parseInt).option("--profile <name>", "Use a specific agent profile (e.g., coder, writer, researcher)").action(async (options) => {
5913
6482
  p3.intro(pc8.bold("aman agent") + pc8.dim(" \u2014 your AI companion"));
5914
6483
  let config = loadConfig();
5915
6484
  if (!config) {
@@ -5926,14 +6495,19 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
5926
6495
  message: "LLM provider",
5927
6496
  options: [
5928
6497
  {
5929
- value: "anthropic",
6498
+ value: "claude-code",
5930
6499
  label: "Claude (Anthropic)",
5931
6500
  hint: "recommended"
5932
6501
  },
6502
+ {
6503
+ value: "copilot",
6504
+ label: "GitHub Copilot",
6505
+ hint: "uses GitHub Models"
6506
+ },
5933
6507
  { value: "openai", label: "GPT (OpenAI)" },
5934
6508
  { value: "ollama", label: "Ollama (local)", hint: "free, runs offline" }
5935
6509
  ],
5936
- initialValue: "anthropic"
6510
+ initialValue: "claude-code"
5937
6511
  });
5938
6512
  if (p3.isCancel(provider)) process.exit(0);
5939
6513
  let apiKey = "";
@@ -5947,20 +6521,58 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
5947
6521
  });
5948
6522
  if (p3.isCancel(modelInput)) process.exit(0);
5949
6523
  defaultModel = modelInput || "llama3.2";
5950
- } else if (provider === "anthropic") {
5951
- p3.log.info("Get your API key from: https://console.anthropic.com/settings/keys");
5952
- p3.log.info(pc8.dim("Note: API access is separate from Claude Pro subscription. You need API credits."));
5953
- apiKey = await p3.text({
5954
- message: "API key (starts with sk-ant-)",
5955
- validate: (v) => v.length === 0 ? "API key is required" : void 0
6524
+ } else if (provider === "claude-code") {
6525
+ p3.note(
6526
+ [
6527
+ `${pc8.bold("Claude Plans:")}`,
6528
+ "",
6529
+ ` ${pc8.cyan("Free")} $0/mo Basic access`,
6530
+ ` ${pc8.cyan("Pro")} $20/mo Full models + Claude Code`,
6531
+ ` ${pc8.cyan("Max 5x")} $100/mo 5\xD7 usage + Claude Code`,
6532
+ ` ${pc8.cyan("Max 20x")} $200/mo 20\xD7 usage + Opus 4.6 + 1M context`,
6533
+ ` ${pc8.cyan("Team")} $25+/seat Collaborative workspace`,
6534
+ ` ${pc8.cyan("Enterprise")} Custom SSO, admin, dedicated support`,
6535
+ "",
6536
+ `${pc8.dim("Authentication is handled by Claude Code CLI.")}`,
6537
+ `${pc8.dim("Supports: subscription, API billing, Bedrock, Vertex AI.")}`
6538
+ ].join("\n"),
6539
+ "Claude Plans"
6540
+ );
6541
+ if (!isClaudeCliInstalled()) {
6542
+ p3.log.error("Claude Code CLI is not installed.");
6543
+ p3.log.info("Install it with:");
6544
+ p3.log.step(pc8.bold("npm install -g @anthropic-ai/claude-code"));
6545
+ p3.log.info(pc8.dim("Then re-run aman-agent to continue setup."));
6546
+ process.exit(1);
6547
+ }
6548
+ p3.log.success("Claude Code CLI detected.");
6549
+ const authAction = await p3.select({
6550
+ message: "Authentication",
6551
+ options: [
6552
+ { value: "logged-in", label: "Already logged in to Claude Code" },
6553
+ { value: "login", label: "Log in now", hint: "runs: claude login" }
6554
+ ]
5956
6555
  });
5957
- if (p3.isCancel(apiKey)) process.exit(0);
6556
+ if (p3.isCancel(authAction)) process.exit(0);
6557
+ if (authAction === "login") {
6558
+ p3.log.step("Launching Claude Code login...");
6559
+ const { spawnSync } = await import("child_process");
6560
+ const loginResult = spawnSync("claude", ["login"], {
6561
+ stdio: "inherit"
6562
+ });
6563
+ if (loginResult.status !== 0) {
6564
+ p3.log.error("Login failed or was cancelled. Please try again.");
6565
+ process.exit(1);
6566
+ }
6567
+ p3.log.success("Login successful.");
6568
+ }
6569
+ apiKey = "claude-code";
5958
6570
  const modelChoice = await p3.select({
5959
6571
  message: "Claude model",
5960
6572
  options: [
5961
6573
  { value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", hint: "fast, recommended" },
5962
6574
  { value: "claude-opus-4-6", label: "Claude Opus 4.6", hint: "most capable" },
5963
- { value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", hint: "fastest, cheapest" },
6575
+ { value: "claude-haiku-4-5-20251001", label: "Claude Haiku 4.5", hint: "fastest" },
5964
6576
  { value: "custom", label: "Custom model ID" }
5965
6577
  ],
5966
6578
  initialValue: "claude-sonnet-4-6"
@@ -5977,6 +6589,82 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
5977
6589
  } else {
5978
6590
  defaultModel = modelChoice;
5979
6591
  }
6592
+ } else if (provider === "copilot") {
6593
+ p3.note(
6594
+ [
6595
+ `${pc8.bold("GitHub Copilot Plans:")}`,
6596
+ "",
6597
+ ` ${pc8.cyan("Free")} $0/mo 2,000 code completions + 50 chat msgs`,
6598
+ ` ${pc8.cyan("Pro")} $10/mo Unlimited completions + chat`,
6599
+ ` ${pc8.cyan("Pro+")} $39/mo Unlimited + Opus/o1 + agent mode`,
6600
+ ` ${pc8.cyan("Business")} $19/user/mo Team admin + policy controls`,
6601
+ ` ${pc8.cyan("Enterprise")} $39/user/mo SSO, audit logs, IP indemnity`,
6602
+ "",
6603
+ `${pc8.dim("Authentication is handled by the Copilot CLI.")}`,
6604
+ `${pc8.dim("Subscribe at: https://github.com/features/copilot")}`
6605
+ ].join("\n"),
6606
+ "Copilot Plans"
6607
+ );
6608
+ if (!isCopilotCliInstalled()) {
6609
+ p3.log.error("Copilot CLI is not installed.");
6610
+ p3.log.info("Install it from:");
6611
+ p3.log.step(pc8.bold("https://docs.github.com/copilot/how-tos/copilot-cli"));
6612
+ p3.log.info(pc8.dim("Then re-run aman-agent to continue setup."));
6613
+ process.exit(1);
6614
+ }
6615
+ p3.log.success("Copilot CLI detected.");
6616
+ const copilotAuth = isCopilotCliAuthenticated();
6617
+ if (copilotAuth) {
6618
+ p3.log.success("Copilot authentication found.");
6619
+ } else {
6620
+ p3.log.warn("Not logged in to Copilot.");
6621
+ const authAction = await p3.select({
6622
+ message: "Authentication",
6623
+ options: [
6624
+ { value: "login", label: "Log in now", hint: "runs: copilot login" },
6625
+ { value: "skip", label: "Skip (I'll log in later)" }
6626
+ ]
6627
+ });
6628
+ if (p3.isCancel(authAction)) process.exit(0);
6629
+ if (authAction === "login") {
6630
+ p3.log.step("Launching Copilot login...");
6631
+ const { spawnSync } = await import("child_process");
6632
+ const loginResult = spawnSync("copilot", ["login"], {
6633
+ stdio: "inherit"
6634
+ });
6635
+ if (loginResult.status !== 0) {
6636
+ p3.log.error("Login failed or was cancelled.");
6637
+ process.exit(1);
6638
+ }
6639
+ p3.log.success("Copilot login successful.");
6640
+ }
6641
+ }
6642
+ apiKey = "copilot";
6643
+ const modelChoice = await p3.select({
6644
+ message: "Model",
6645
+ options: [
6646
+ { value: "default", label: "Default", hint: "Copilot's default model" },
6647
+ { value: "gpt-4o", label: "GPT-4o", hint: "fast" },
6648
+ { value: "gpt-5.2", label: "GPT-5.2", hint: "most capable" },
6649
+ { value: "o3-mini", label: "o3-mini", hint: "reasoning" },
6650
+ { value: "custom", label: "Custom model ID" }
6651
+ ],
6652
+ initialValue: "default"
6653
+ });
6654
+ if (p3.isCancel(modelChoice)) process.exit(0);
6655
+ if (modelChoice === "custom") {
6656
+ const customModel = await p3.text({
6657
+ message: "Model ID (run copilot --help for available models)",
6658
+ placeholder: "gpt-4o",
6659
+ validate: (v) => v.length === 0 ? "Model ID is required" : void 0
6660
+ });
6661
+ if (p3.isCancel(customModel)) process.exit(0);
6662
+ defaultModel = customModel;
6663
+ } else if (modelChoice === "default") {
6664
+ defaultModel = "";
6665
+ } else {
6666
+ defaultModel = modelChoice;
6667
+ }
5980
6668
  } else {
5981
6669
  apiKey = await p3.text({
5982
6670
  message: "API key",
@@ -6093,7 +6781,11 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
6093
6781
  input_schema: t.input_schema
6094
6782
  }));
6095
6783
  let client;
6096
- if (config.provider === "anthropic") {
6784
+ if (config.provider === "claude-code") {
6785
+ client = createClaudeCodeClient(model);
6786
+ } else if (config.provider === "copilot") {
6787
+ client = createCopilotClient(model);
6788
+ } else if (config.provider === "anthropic") {
6097
6789
  client = createAnthropicClient(config.apiKey, model);
6098
6790
  } else if (config.provider === "ollama") {
6099
6791
  client = createOllamaClient(model);