@aman_asmuei/aman-agent 0.21.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
@@ -952,135 +952,244 @@ function createClaudeCodeClient(model) {
952
952
  }
953
953
 
954
954
  // src/llm/copilot.ts
955
- import OpenAI3 from "openai";
956
- import { execFileSync as execFileSync2 } from "child_process";
957
- var GITHUB_MODELS_BASE_URL = "https://models.inference.ai.azure.com";
958
- function isGhCliInstalled() {
955
+ import { spawn as spawn2, execFileSync as execFileSync2 } from "child_process";
956
+ function isCopilotCliInstalled() {
959
957
  try {
960
- execFileSync2("which", ["gh"], { stdio: "ignore" });
958
+ execFileSync2("which", ["copilot"], { stdio: "ignore" });
961
959
  return true;
962
960
  } catch {
963
961
  return false;
964
962
  }
965
963
  }
966
- function isGhAuthenticated() {
964
+ function isCopilotCliAuthenticated() {
967
965
  try {
968
- const result = execFileSync2("gh", ["auth", "status"], {
969
- stdio: ["ignore", "pipe", "pipe"],
966
+ const result = execFileSync2("copilot", ["--version"], {
967
+ stdio: ["ignore", "pipe", "ignore"],
970
968
  timeout: 5e3
971
969
  });
972
- return true;
970
+ return result.toString().trim().length > 0;
973
971
  } catch {
974
972
  return false;
975
973
  }
976
974
  }
977
- function getGhToken() {
978
- try {
979
- const token = execFileSync2("gh", ["auth", "token"], {
980
- stdio: ["ignore", "pipe", "ignore"],
981
- timeout: 5e3
982
- }).toString().trim();
983
- if (!token) {
984
- throw new Error("No token returned from gh auth token");
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
+ }
985
1048
  }
986
- return token;
987
- } catch {
988
- throw new Error(
989
- "Failed to get GitHub token. Run: gh auth login"
990
- );
991
1049
  }
1050
+ return toolUses;
992
1051
  }
993
1052
  function createCopilotClient(model) {
994
1053
  return {
995
1054
  async chat(systemPrompt, messages, onChunk, tools, options) {
996
- const token = getGhToken();
997
- const client = new OpenAI3({
998
- baseURL: GITHUB_MODELS_BASE_URL,
999
- apiKey: token
1000
- });
1001
- const openaiMessages = toOpenAICompatibleMessages(systemPrompt, messages);
1002
- const hasTools = tools && tools.length > 0;
1003
- try {
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
+ });
1004
1079
  let fullText = "";
1005
- const toolCallAccumulators = /* @__PURE__ */ new Map();
1006
- const createParams = {
1007
- model,
1008
- max_tokens: options?.maxOutputTokens ?? 8192,
1009
- messages: openaiMessages,
1010
- stream: true
1011
- };
1012
- if (hasTools) {
1013
- createParams.tools = tools.map((t) => ({
1014
- type: "function",
1015
- function: {
1016
- name: t.name,
1017
- description: t.description,
1018
- parameters: t.input_schema
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
+ }
1019
1117
  }
1020
- }));
1021
- }
1022
- const stream = await client.chat.completions.create(
1023
- createParams
1024
- );
1025
- for await (const chunk of stream) {
1026
- const delta = chunk.choices[0]?.delta;
1027
- if (!delta) continue;
1028
- if (delta.content) {
1029
- fullText += delta.content;
1030
- onChunk({ type: "text", text: delta.content });
1031
1118
  }
1032
- if (delta.tool_calls) {
1033
- for (const tc of delta.tool_calls) {
1034
- const idx = tc.index;
1035
- let acc = toolCallAccumulators.get(idx);
1036
- if (!acc) {
1037
- acc = { id: "", name: "", arguments: "" };
1038
- toolCallAccumulators.set(idx, acc);
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 });
1039
1138
  }
1040
- if (tc.id) acc.id = tc.id;
1041
- if (tc.function?.name) acc.name = tc.function.name;
1042
- if (tc.function?.arguments) acc.arguments += tc.function.arguments;
1043
1139
  }
1044
1140
  }
1045
- }
1046
- const toolUses = Array.from(toolCallAccumulators.entries()).sort(([a], [b]) => a - b).map(([, acc]) => ({
1047
- id: acc.id,
1048
- name: acc.name,
1049
- input: JSON.parse(acc.arguments || "{}")
1050
- }));
1051
- onChunk({ type: "done" });
1052
- if (toolUses.length > 0) {
1053
- const contentBlocks = [
1054
- ...fullText ? [{ type: "text", text: fullText }] : [],
1055
- ...toolUses.map((tu) => ({
1056
- type: "tool_use",
1057
- id: tu.id,
1058
- name: tu.name,
1059
- input: tu.input
1060
- }))
1061
- ];
1062
- return {
1063
- message: { role: "assistant", content: contentBlocks },
1064
- toolUses
1065
- };
1066
- }
1067
- return {
1068
- message: { role: "assistant", content: fullText },
1069
- toolUses: []
1070
- };
1071
- } catch (error) {
1072
- if (error instanceof OpenAI3.AuthenticationError) {
1073
- throw new Error(
1074
- "GitHub authentication failed. Run: gh auth login"
1075
- );
1076
- }
1077
- if (error instanceof OpenAI3.RateLimitError) {
1078
- throw new Error(
1079
- "Rate limited by GitHub Models. Copilot subscribers get higher limits."
1080
- );
1081
- }
1082
- throw error;
1083
- }
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
+ });
1084
1193
  }
1085
1194
  };
1086
1195
  }
@@ -3914,7 +4023,7 @@ function handleReset(action) {
3914
4023
  function handleUpdate() {
3915
4024
  try {
3916
4025
  const current = execFileSync3("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
3917
- const local = true ? "0.21.0" : "unknown";
4026
+ const local = true ? "0.21.1" : "unknown";
3918
4027
  if (current === local) {
3919
4028
  return { handled: true, output: `${pc5.green("Up to date")} \u2014 v${local}` };
3920
4029
  }
@@ -6369,7 +6478,7 @@ function bootstrapEcosystem() {
6369
6478
  return true;
6370
6479
  }
6371
6480
  var program = new Command();
6372
- program.name("aman-agent").description("Your AI companion, running locally").version("0.21.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) => {
6373
6482
  p3.intro(pc8.bold("aman agent") + pc8.dim(" \u2014 your AI companion"));
6374
6483
  let config = loadConfig();
6375
6484
  if (!config) {
@@ -6491,68 +6600,68 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
6491
6600
  ` ${pc8.cyan("Business")} $19/user/mo Team admin + policy controls`,
6492
6601
  ` ${pc8.cyan("Enterprise")} $39/user/mo SSO, audit logs, IP indemnity`,
6493
6602
  "",
6494
- `${pc8.dim("Authentication is handled by the GitHub CLI (gh).")}`,
6603
+ `${pc8.dim("Authentication is handled by the Copilot CLI.")}`,
6495
6604
  `${pc8.dim("Subscribe at: https://github.com/features/copilot")}`
6496
6605
  ].join("\n"),
6497
6606
  "Copilot Plans"
6498
6607
  );
6499
- if (!isGhCliInstalled()) {
6500
- p3.log.error("GitHub CLI (gh) is not installed.");
6608
+ if (!isCopilotCliInstalled()) {
6609
+ p3.log.error("Copilot CLI is not installed.");
6501
6610
  p3.log.info("Install it from:");
6502
- p3.log.step(pc8.bold("https://cli.github.com"));
6611
+ p3.log.step(pc8.bold("https://docs.github.com/copilot/how-tos/copilot-cli"));
6503
6612
  p3.log.info(pc8.dim("Then re-run aman-agent to continue setup."));
6504
6613
  process.exit(1);
6505
6614
  }
6506
- p3.log.success("GitHub CLI detected.");
6507
- const ghAuth = isGhAuthenticated();
6508
- if (ghAuth) {
6509
- p3.log.success("GitHub authentication found.");
6615
+ p3.log.success("Copilot CLI detected.");
6616
+ const copilotAuth = isCopilotCliAuthenticated();
6617
+ if (copilotAuth) {
6618
+ p3.log.success("Copilot authentication found.");
6510
6619
  } else {
6511
- p3.log.warn("Not logged in to GitHub.");
6620
+ p3.log.warn("Not logged in to Copilot.");
6512
6621
  const authAction = await p3.select({
6513
6622
  message: "Authentication",
6514
6623
  options: [
6515
- { value: "login", label: "Log in now", hint: "runs: gh auth login" },
6624
+ { value: "login", label: "Log in now", hint: "runs: copilot login" },
6516
6625
  { value: "skip", label: "Skip (I'll log in later)" }
6517
6626
  ]
6518
6627
  });
6519
6628
  if (p3.isCancel(authAction)) process.exit(0);
6520
6629
  if (authAction === "login") {
6521
- p3.log.step("Launching GitHub login...");
6630
+ p3.log.step("Launching Copilot login...");
6522
6631
  const { spawnSync } = await import("child_process");
6523
- const loginResult = spawnSync("gh", ["auth", "login"], {
6632
+ const loginResult = spawnSync("copilot", ["login"], {
6524
6633
  stdio: "inherit"
6525
6634
  });
6526
6635
  if (loginResult.status !== 0) {
6527
6636
  p3.log.error("Login failed or was cancelled.");
6528
6637
  process.exit(1);
6529
6638
  }
6530
- p3.log.success("GitHub login successful.");
6639
+ p3.log.success("Copilot login successful.");
6531
6640
  }
6532
6641
  }
6533
6642
  apiKey = "copilot";
6534
6643
  const modelChoice = await p3.select({
6535
6644
  message: "Model",
6536
6645
  options: [
6537
- { value: "gpt-4o", label: "GPT-4o", hint: "fast, recommended" },
6538
- { value: "gpt-4o-mini", label: "GPT-4o Mini", hint: "fastest" },
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" },
6539
6649
  { value: "o3-mini", label: "o3-mini", hint: "reasoning" },
6540
- { value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6", hint: "via GitHub Models" },
6541
- { value: "meta-llama-3.1-405b-instruct", label: "Llama 3.1 405B", hint: "open source" },
6542
- { value: "mistral-large-2411", label: "Mistral Large", hint: "open source" },
6543
6650
  { value: "custom", label: "Custom model ID" }
6544
6651
  ],
6545
- initialValue: "gpt-4o"
6652
+ initialValue: "default"
6546
6653
  });
6547
6654
  if (p3.isCancel(modelChoice)) process.exit(0);
6548
6655
  if (modelChoice === "custom") {
6549
6656
  const customModel = await p3.text({
6550
- message: "Model ID",
6657
+ message: "Model ID (run copilot --help for available models)",
6551
6658
  placeholder: "gpt-4o",
6552
6659
  validate: (v) => v.length === 0 ? "Model ID is required" : void 0
6553
6660
  });
6554
6661
  if (p3.isCancel(customModel)) process.exit(0);
6555
6662
  defaultModel = customModel;
6663
+ } else if (modelChoice === "default") {
6664
+ defaultModel = "";
6556
6665
  } else {
6557
6666
  defaultModel = modelChoice;
6558
6667
  }