@aman_asmuei/aman-agent 0.21.0 → 0.22.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
@@ -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
+ `;
985
998
  }
986
- return token;
987
- } catch {
988
- throw new Error(
989
- "Failed to get GitHub token. Run: gh auth login"
990
- );
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");
991
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;
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
  }
@@ -3184,6 +3293,17 @@ function progressBar(pct) {
3184
3293
  }
3185
3294
 
3186
3295
  // src/commands.ts
3296
+ import {
3297
+ getIdentity as acoreGetIdentity,
3298
+ updateSection as acoreUpdateSection
3299
+ } from "@aman_asmuei/acore-core";
3300
+ import {
3301
+ listRuleCategories as arulesListCategories,
3302
+ addRule as arulesAddRule,
3303
+ removeRule as arulesRemoveRule,
3304
+ toggleRuleAt as arulesToggleRule
3305
+ } from "@aman_asmuei/arules-core";
3306
+ var AGENT_SCOPE = process.env.AMAN_AGENT_SCOPE ?? "dev:agent";
3187
3307
  function readEcosystemFile(filePath, label) {
3188
3308
  if (!fs12.existsSync(filePath)) {
3189
3309
  return pc5.dim(`No ${label} file found at ${filePath}`);
@@ -3209,17 +3329,26 @@ async function mcpWrite(ctx, layer, tool, args) {
3209
3329
  }
3210
3330
  return pc5.green(result);
3211
3331
  }
3212
- async function handleIdentityCommand(action, args, ctx) {
3213
- const home2 = os11.homedir();
3332
+ async function handleIdentityCommand(action, args, _ctx) {
3214
3333
  if (!action) {
3215
- const content = readEcosystemFile(path12.join(home2, ".acore", "core.md"), "identity (acore)");
3216
- return { handled: true, output: content };
3334
+ const identity = await acoreGetIdentity(AGENT_SCOPE);
3335
+ if (!identity) {
3336
+ return {
3337
+ handled: true,
3338
+ output: pc5.dim(
3339
+ `No identity configured for ${AGENT_SCOPE}. Run: npx @aman_asmuei/acore`
3340
+ )
3341
+ };
3342
+ }
3343
+ return { handled: true, output: identity.content.trim() };
3217
3344
  }
3218
3345
  if (action === "update") {
3219
3346
  if (args.length === 0) {
3220
3347
  return {
3221
3348
  handled: true,
3222
- output: pc5.yellow("Usage: /identity update <section>\nTip: describe changes in natural language and the AI will update via MCP.")
3349
+ output: pc5.yellow(
3350
+ "Usage: /identity update <section>\nTip: describe changes in natural language and the AI will update via acore-core."
3351
+ )
3223
3352
  };
3224
3353
  }
3225
3354
  const section = args[0];
@@ -3227,60 +3356,161 @@ async function handleIdentityCommand(action, args, ctx) {
3227
3356
  if (!content) {
3228
3357
  return {
3229
3358
  handled: true,
3230
- output: pc5.yellow("Usage: /identity update <section> <new content...>\nExample: /identity update Personality Warm, curious, and direct.")
3359
+ output: pc5.yellow(
3360
+ "Usage: /identity update <section> <new content...>\nExample: /identity update Personality Warm, curious, and direct."
3361
+ )
3362
+ };
3363
+ }
3364
+ try {
3365
+ await acoreUpdateSection(section, content, AGENT_SCOPE);
3366
+ return { handled: true, output: pc5.green(`Updated section: ${section}`) };
3367
+ } catch (err) {
3368
+ return {
3369
+ handled: true,
3370
+ output: pc5.red(
3371
+ `Failed to update ${section}: ${err instanceof Error ? err.message : String(err)}`
3372
+ )
3231
3373
  };
3232
3374
  }
3233
- const output = await mcpWrite(ctx, "identity", "identity_update_section", { section, content });
3234
- return { handled: true, output };
3235
3375
  }
3236
3376
  if (action === "help") {
3237
- return { handled: true, output: [
3238
- pc5.bold("Identity commands:"),
3239
- ` ${pc5.cyan("/identity")} View current identity`,
3240
- ` ${pc5.cyan("/identity update")} <section> Update a section`
3241
- ].join("\n") };
3377
+ return {
3378
+ handled: true,
3379
+ output: [
3380
+ pc5.bold("Identity commands:"),
3381
+ ` ${pc5.cyan("/identity")} View current identity`,
3382
+ ` ${pc5.cyan("/identity update")} <section> Update a section`
3383
+ ].join("\n")
3384
+ };
3242
3385
  }
3243
- return { handled: true, output: pc5.yellow(`Unknown action: /identity ${action}. Try /identity --help`) };
3386
+ return {
3387
+ handled: true,
3388
+ output: pc5.yellow(
3389
+ `Unknown action: /identity ${action}. Try /identity --help`
3390
+ )
3391
+ };
3244
3392
  }
3245
- async function handleRulesCommand(action, args, ctx) {
3246
- const home2 = os11.homedir();
3393
+ async function handleRulesCommand(action, args, _ctx) {
3247
3394
  if (!action) {
3248
- const content = readEcosystemFile(path12.join(home2, ".arules", "rules.md"), "guardrails (arules)");
3249
- return { handled: true, output: content };
3395
+ const cats = await arulesListCategories(AGENT_SCOPE);
3396
+ if (cats.length === 0) {
3397
+ return {
3398
+ handled: true,
3399
+ output: pc5.dim(
3400
+ `No rules configured for ${AGENT_SCOPE}. Run: npx @aman_asmuei/arules`
3401
+ )
3402
+ };
3403
+ }
3404
+ const lines = [];
3405
+ for (const cat of cats) {
3406
+ lines.push(pc5.bold(`## ${cat.name}`));
3407
+ for (const rule of cat.rules) {
3408
+ lines.push(` - ${rule}`);
3409
+ }
3410
+ lines.push("");
3411
+ }
3412
+ return { handled: true, output: lines.join("\n").trim() };
3250
3413
  }
3251
3414
  if (action === "add") {
3252
3415
  if (args.length < 2) {
3253
- return { handled: true, output: pc5.yellow("Usage: /rules add <category> <rule text...>") };
3416
+ return {
3417
+ handled: true,
3418
+ output: pc5.yellow("Usage: /rules add <category> <rule text...>")
3419
+ };
3254
3420
  }
3255
3421
  const category = args[0];
3256
3422
  const rule = args.slice(1).join(" ");
3257
- const output = await mcpWrite(ctx, "rules", "rules_add", { category, rule });
3258
- return { handled: true, output };
3423
+ try {
3424
+ await arulesAddRule(category, rule, AGENT_SCOPE);
3425
+ return {
3426
+ handled: true,
3427
+ output: pc5.green(`Added rule to "${category}": ${rule}`)
3428
+ };
3429
+ } catch (err) {
3430
+ return {
3431
+ handled: true,
3432
+ output: pc5.red(
3433
+ `Failed: ${err instanceof Error ? err.message : String(err)}`
3434
+ )
3435
+ };
3436
+ }
3259
3437
  }
3260
3438
  if (action === "remove") {
3261
3439
  if (args.length < 2) {
3262
- return { handled: true, output: pc5.yellow("Usage: /rules remove <category> <index>") };
3440
+ return {
3441
+ handled: true,
3442
+ output: pc5.yellow("Usage: /rules remove <category> <index>")
3443
+ };
3444
+ }
3445
+ const category = args[0];
3446
+ const idx = parseInt(args[1], 10);
3447
+ if (isNaN(idx) || idx < 1) {
3448
+ return {
3449
+ handled: true,
3450
+ output: pc5.yellow("Index must be a positive integer.")
3451
+ };
3452
+ }
3453
+ try {
3454
+ await arulesRemoveRule(category, idx, AGENT_SCOPE);
3455
+ return {
3456
+ handled: true,
3457
+ output: pc5.green(`Removed rule ${idx} from "${category}"`)
3458
+ };
3459
+ } catch (err) {
3460
+ return {
3461
+ handled: true,
3462
+ output: pc5.red(
3463
+ `Failed: ${err instanceof Error ? err.message : String(err)}`
3464
+ )
3465
+ };
3263
3466
  }
3264
- const output = await mcpWrite(ctx, "rules", "rules_remove", { category: args[0], index: parseInt(args[1], 10) });
3265
- return { handled: true, output };
3266
3467
  }
3267
3468
  if (action === "toggle") {
3268
3469
  if (args.length < 2) {
3269
- return { handled: true, output: pc5.yellow("Usage: /rules toggle <category> <index>") };
3470
+ return {
3471
+ handled: true,
3472
+ output: pc5.yellow("Usage: /rules toggle <category> <index>")
3473
+ };
3474
+ }
3475
+ const category = args[0];
3476
+ const idx = parseInt(args[1], 10);
3477
+ if (isNaN(idx) || idx < 1) {
3478
+ return {
3479
+ handled: true,
3480
+ output: pc5.yellow("Index must be a positive integer.")
3481
+ };
3482
+ }
3483
+ try {
3484
+ await arulesToggleRule(category, idx, AGENT_SCOPE);
3485
+ return {
3486
+ handled: true,
3487
+ output: pc5.green(`Toggled rule ${idx} in "${category}"`)
3488
+ };
3489
+ } catch (err) {
3490
+ return {
3491
+ handled: true,
3492
+ output: pc5.red(
3493
+ `Failed: ${err instanceof Error ? err.message : String(err)}`
3494
+ )
3495
+ };
3270
3496
  }
3271
- const output = await mcpWrite(ctx, "rules", "rules_toggle", { category: args[0], index: parseInt(args[1], 10) });
3272
- return { handled: true, output };
3273
3497
  }
3274
3498
  if (action === "help") {
3275
- return { handled: true, output: [
3276
- pc5.bold("Rules commands:"),
3277
- ` ${pc5.cyan("/rules")} View current rules`,
3278
- ` ${pc5.cyan("/rules add")} <category> <text> Add a rule`,
3279
- ` ${pc5.cyan("/rules remove")} <category> <idx> Remove a rule`,
3280
- ` ${pc5.cyan("/rules toggle")} <category> <idx> Toggle a rule`
3281
- ].join("\n") };
3499
+ return {
3500
+ handled: true,
3501
+ output: [
3502
+ pc5.bold("Rules commands:"),
3503
+ ` ${pc5.cyan("/rules")} View current rules`,
3504
+ ` ${pc5.cyan("/rules add")} <category> <text> Add a rule`,
3505
+ ` ${pc5.cyan("/rules remove")} <category> <idx> Remove a rule`,
3506
+ ` ${pc5.cyan("/rules toggle")} <category> <idx> Toggle a rule`
3507
+ ].join("\n")
3508
+ };
3282
3509
  }
3283
- return { handled: true, output: pc5.yellow(`Unknown action: /rules ${action}. Try /rules --help`) };
3510
+ return {
3511
+ handled: true,
3512
+ output: pc5.yellow(`Unknown action: /rules ${action}. Try /rules --help`)
3513
+ };
3284
3514
  }
3285
3515
  async function handleWorkflowsCommand(action, args, ctx) {
3286
3516
  const home2 = os11.homedir();
@@ -3312,205 +3542,29 @@ async function handleWorkflowsCommand(action, args, ctx) {
3312
3542
  }
3313
3543
  return { handled: true, output: pc5.yellow(`Unknown action: /workflows ${action}. Try /workflows --help`) };
3314
3544
  }
3315
- var AKIT_REGISTRY = [
3316
- { name: "web-search", description: "Search the web for current information", category: "search", mcp: { package: "@anthropic/web-search", command: "npx", args: ["-y", "@anthropic/web-search"] } },
3317
- { name: "brave-search", description: "Private web search via Brave", category: "search", mcp: { package: "@modelcontextprotocol/server-brave-search", command: "npx", args: ["-y", "@modelcontextprotocol/server-brave-search"], env: { BRAVE_API_KEY: "" } }, envHint: "Set BRAVE_API_KEY from https://brave.com/search/api/" },
3318
- { name: "github", description: "Manage GitHub repos, PRs, issues", category: "development", mcp: { package: "@modelcontextprotocol/server-github", command: "npx", args: ["-y", "@modelcontextprotocol/server-github"], env: { GITHUB_TOKEN: "" } }, envHint: "Set GITHUB_TOKEN from https://github.com/settings/tokens" },
3319
- { name: "git", description: "Git operations \u2014 log, diff, blame, branch", category: "development", mcp: { package: "@modelcontextprotocol/server-git", command: "npx", args: ["-y", "@modelcontextprotocol/server-git"] } },
3320
- { name: "filesystem", description: "Read, write, and search files", category: "development", mcp: { package: "@modelcontextprotocol/server-filesystem", command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "."] } },
3321
- { name: "linear", description: "Manage Linear issues and projects", category: "development", mcp: { package: "@linear/mcp-server", command: "npx", args: ["-y", "@linear/mcp-server"], env: { LINEAR_API_KEY: "" } }, envHint: "Set LINEAR_API_KEY from Linear settings \u2192 API" },
3322
- { name: "sentry", description: "Monitor and triage app errors", category: "development", mcp: { package: "@sentry/mcp-server", command: "npx", args: ["-y", "@sentry/mcp-server"], env: { SENTRY_AUTH_TOKEN: "" } }, envHint: "Set SENTRY_AUTH_TOKEN from Sentry settings \u2192 API keys" },
3323
- { name: "postgres", description: "Query PostgreSQL databases", category: "data", mcp: { package: "@modelcontextprotocol/server-postgres", command: "npx", args: ["-y", "@modelcontextprotocol/server-postgres"], env: { DATABASE_URL: "" } }, envHint: "Set DATABASE_URL (e.g., postgresql://user:pass@localhost/db)" },
3324
- { name: "sqlite", description: "Query local SQLite databases", category: "data", mcp: { package: "@modelcontextprotocol/server-sqlite", command: "npx", args: ["-y", "@modelcontextprotocol/server-sqlite"] } },
3325
- { name: "fetch", description: "HTTP requests to APIs", category: "automation", mcp: { package: "@modelcontextprotocol/server-fetch", command: "npx", args: ["-y", "@modelcontextprotocol/server-fetch"] } },
3326
- { name: "puppeteer", description: "Browser automation and scraping", category: "automation", mcp: { package: "@modelcontextprotocol/server-puppeteer", command: "npx", args: ["-y", "@modelcontextprotocol/server-puppeteer"] } },
3327
- { name: "docker", description: "Manage Docker containers", category: "automation", mcp: { package: "@modelcontextprotocol/server-docker", command: "npx", args: ["-y", "@modelcontextprotocol/server-docker"] } },
3328
- { name: "slack", description: "Send and read Slack messages", category: "communication", mcp: { package: "@modelcontextprotocol/server-slack", command: "npx", args: ["-y", "@modelcontextprotocol/server-slack"], env: { SLACK_BOT_TOKEN: "" } }, envHint: "Set SLACK_BOT_TOKEN from your Slack app settings" },
3329
- { name: "notion", description: "Read and write Notion pages", category: "communication", mcp: { package: "@notionhq/notion-mcp-server", command: "npx", args: ["-y", "@notionhq/notion-mcp-server"], env: { NOTION_API_KEY: "" } }, envHint: "Set NOTION_API_KEY from https://notion.so/my-integrations" },
3330
- { name: "social", description: "Post to Bluesky, X/Twitter, Threads", category: "communication", mcp: { package: "@aman_asmuei/aman-social", command: "npx", args: ["-y", "@aman_asmuei/aman-social"] }, envHint: "Set BLUESKY_HANDLE + BLUESKY_APP_PASSWORD, TWITTER_API_KEY + secrets, or THREADS_ACCESS_TOKEN" },
3331
- { name: "memory", description: "Persistent AI memory via amem", category: "memory", mcp: { package: "@aman_asmuei/amem", command: "npx", args: ["-y", "@aman_asmuei/amem"] } },
3332
- { name: "docling", description: "Convert PDF, DOCX, PPTX, XLSX to markdown", category: "documents", mcp: { package: "docling-mcp", command: "uvx", args: ["docling-mcp"] }, envHint: "Requires Python 3.10+. Install: pip install docling" }
3333
- ];
3334
- function loadAkitInstalled() {
3335
- const filePath = path12.join(os11.homedir(), ".akit", "installed.json");
3336
- if (!fs12.existsSync(filePath)) return [];
3337
- try {
3338
- return JSON.parse(fs12.readFileSync(filePath, "utf-8"));
3339
- } catch {
3340
- return [];
3341
- }
3342
- }
3343
- function saveAkitInstalled(tools) {
3344
- const dir = path12.join(os11.homedir(), ".akit");
3345
- fs12.mkdirSync(dir, { recursive: true });
3346
- fs12.writeFileSync(path12.join(dir, "installed.json"), JSON.stringify(tools, null, 2) + "\n", "utf-8");
3347
- }
3348
- function addToAmanAgentConfig(name, mcpConfig) {
3349
- const configPath = path12.join(os11.homedir(), ".aman-agent", "config.json");
3350
- if (!fs12.existsSync(configPath)) return;
3351
- try {
3352
- const config = JSON.parse(fs12.readFileSync(configPath, "utf-8"));
3353
- if (!config.mcpServers) config.mcpServers = {};
3354
- config.mcpServers[name] = mcpConfig;
3355
- fs12.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
3356
- } catch {
3357
- }
3358
- }
3359
- function removeFromAmanAgentConfig(name) {
3360
- const configPath = path12.join(os11.homedir(), ".aman-agent", "config.json");
3361
- if (!fs12.existsSync(configPath)) return;
3362
- try {
3363
- const config = JSON.parse(fs12.readFileSync(configPath, "utf-8"));
3364
- if (config.mcpServers) {
3365
- delete config.mcpServers[name];
3366
- fs12.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
3367
- }
3368
- } catch {
3369
- }
3370
- }
3371
- function handleAkitCommand(action, args) {
3372
- const installed = loadAkitInstalled();
3373
- const installedNames = new Set(installed.map((t) => t.name));
3374
- if (action === "add") {
3375
- const available2 = AKIT_REGISTRY.filter((t) => !installedNames.has(t.name));
3376
- if (args.length < 1) {
3377
- if (available2.length === 0) {
3378
- return { handled: true, output: pc5.green("All tools are installed!") };
3379
- }
3380
- const lines3 = [pc5.bold("Select a tool to install:"), ""];
3381
- available2.forEach((tool2, i) => {
3382
- const num2 = pc5.cyan(String(i + 1).padStart(2));
3383
- lines3.push(` ${num2} ${tool2.name.padEnd(16)} ${pc5.dim(tool2.description)}`);
3384
- });
3385
- lines3.push("");
3386
- lines3.push(` Type: ${pc5.cyan("/akit add <number>")} or ${pc5.cyan("/akit add <name>")}`);
3387
- lines3.push(` Custom: ${pc5.cyan("/akit add custom <name> <command> <args...>")}`);
3388
- return { handled: true, output: lines3.join("\n") };
3389
- }
3390
- if (args[0].toLowerCase() === "custom") {
3391
- if (args.length < 3) {
3392
- return { handled: true, output: pc5.yellow("Usage: /akit add custom <name> <command> <args...>\nExample: /akit add custom my-tool npx -y @org/my-mcp-server") };
3393
- }
3394
- const customName = args[1];
3395
- const customCommand = args[2];
3396
- const customArgs = args.slice(3);
3397
- if (installedNames.has(customName)) {
3398
- return { handled: true, output: pc5.yellow(`${customName} is already installed.`) };
3399
- }
3400
- installed.push({
3401
- name: customName,
3402
- installedAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
3403
- mcpConfigured: true
3404
- });
3405
- saveAkitInstalled(installed);
3406
- addToAmanAgentConfig(customName, { command: customCommand, args: customArgs });
3407
- return {
3408
- handled: true,
3409
- output: [
3410
- pc5.green(`\u2713 Added ${pc5.bold(customName)}`) + pc5.dim(` (custom MCP: ${customCommand} ${customArgs.join(" ")})`),
3411
- pc5.dim(" Restart aman-agent to load the new tool.")
3412
- ].join("\n")
3413
- };
3414
- }
3415
- const input = args[0].toLowerCase();
3416
- let tool;
3417
- const num = parseInt(input, 10);
3418
- if (!isNaN(num) && num >= 1 && num <= available2.length) {
3419
- tool = available2[num - 1];
3420
- } else {
3421
- tool = AKIT_REGISTRY.find((t) => t.name === input);
3422
- }
3423
- if (!tool) {
3424
- return {
3425
- handled: true,
3426
- output: [
3427
- pc5.red(`Tool "${input}" not found.`),
3428
- `Type ${pc5.cyan("/akit add")} to see available tools.`
3429
- ].join("\n")
3430
- };
3431
- }
3432
- if (installedNames.has(tool.name)) {
3433
- return { handled: true, output: pc5.yellow(`${tool.name} is already installed.`) };
3434
- }
3435
- installed.push({
3436
- name: tool.name,
3437
- installedAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
3438
- mcpConfigured: tool.mcp !== null
3439
- });
3440
- saveAkitInstalled(installed);
3441
- if (tool.mcp) {
3442
- addToAmanAgentConfig(tool.name, {
3443
- command: tool.mcp.command,
3444
- args: tool.mcp.args
3445
- });
3446
- }
3447
- const lines2 = [
3448
- pc5.green(`\u2713 Added ${pc5.bold(tool.name)}`) + (tool.mcp ? pc5.dim(` (MCP: ${tool.mcp.package})`) : "")
3449
- ];
3450
- if (tool.envHint) {
3451
- lines2.push(pc5.yellow(` \u26A0 ${tool.envHint}`));
3452
- }
3453
- if (tool.mcp) {
3454
- lines2.push(pc5.dim(" Restart aman-agent to load the new tool."));
3455
- }
3456
- return { handled: true, output: lines2.join("\n") };
3457
- }
3458
- if (action === "remove") {
3459
- if (args.length < 1) {
3460
- return { handled: true, output: pc5.yellow("Usage: /akit remove <tool>") };
3461
- }
3462
- const toolName = args[0].toLowerCase();
3463
- if (!installedNames.has(toolName)) {
3464
- return { handled: true, output: pc5.red(`${toolName} is not installed.`) };
3465
- }
3466
- const updated = installed.filter((t) => t.name !== toolName);
3467
- saveAkitInstalled(updated);
3468
- removeFromAmanAgentConfig(toolName);
3469
- return {
3470
- handled: true,
3471
- output: pc5.green(`\u2713 Removed ${pc5.bold(toolName)}`) + pc5.dim(" (restart aman-agent to apply)")
3472
- };
3473
- }
3474
- if (action === "help") {
3475
- return {
3476
- handled: true,
3477
- output: [
3478
- pc5.bold("akit \u2014 Tool Management"),
3479
- "",
3480
- ` ${pc5.cyan("/akit")} List installed & available tools`,
3481
- ` ${pc5.cyan("/akit add <tool>")} Install a tool`,
3482
- ` ${pc5.cyan("/akit remove <tool>")} Uninstall a tool`
3483
- ].join("\n")
3484
- };
3485
- }
3486
- const available = AKIT_REGISTRY.filter((t) => !installedNames.has(t.name));
3487
- const lines = [pc5.bold("akit \u2014 AI Tool Manager"), ""];
3488
- if (installed.length > 0) {
3489
- lines.push(` ${pc5.bold(`Installed (${installed.length})`)}`);
3490
- for (const tool of installed) {
3491
- const mcp = tool.mcpConfigured ? pc5.green("MCP") : pc5.dim("manual");
3492
- lines.push(` ${pc5.green("\u25CF")} ${pc5.bold(tool.name.padEnd(16))} ${mcp} ${pc5.dim(tool.installedAt)}`);
3493
- }
3494
- lines.push("");
3495
- }
3496
- if (available.length > 0) {
3497
- lines.push(` ${pc5.bold(`Available (${available.length})`)}`);
3498
- const byCategory = /* @__PURE__ */ new Map();
3499
- for (const tool of available) {
3500
- if (!byCategory.has(tool.category)) byCategory.set(tool.category, []);
3501
- byCategory.get(tool.category).push(tool);
3502
- }
3503
- for (const [category, tools] of byCategory) {
3504
- lines.push(` ${pc5.dim(category)}`);
3505
- for (const tool of tools) {
3506
- lines.push(` ${pc5.dim("\u25CB")} ${tool.name.padEnd(16)} ${pc5.dim(tool.description)}`);
3507
- }
3508
- }
3509
- lines.push("");
3510
- }
3511
- lines.push(` ${pc5.cyan("/akit add <tool>")} Install a tool`);
3512
- lines.push(` ${pc5.cyan("/akit remove <tool>")} Uninstall a tool`);
3513
- return { handled: true, output: lines.join("\n") };
3545
+ function handleAkitCommand(_action, _args) {
3546
+ return {
3547
+ handled: true,
3548
+ output: [
3549
+ pc5.bold("akit \u2014 Tool Management"),
3550
+ "",
3551
+ pc5.dim(
3552
+ "Tool management is now handled by the standalone akit CLI rather than"
3553
+ ),
3554
+ pc5.dim(
3555
+ "duplicated inside aman-agent. The akit slash command is informational only."
3556
+ ),
3557
+ "",
3558
+ ` ${pc5.cyan("npx @aman_asmuei/akit list")} List installed tools`,
3559
+ ` ${pc5.cyan("npx @aman_asmuei/akit search <query>")} Search the tool registry`,
3560
+ ` ${pc5.cyan("npx @aman_asmuei/akit add <tool>")} Install a tool`,
3561
+ ` ${pc5.cyan("npx @aman_asmuei/akit remove <tool>")} Uninstall a tool`,
3562
+ "",
3563
+ pc5.dim(
3564
+ "Restart aman-agent after installing/removing tools to pick up changes."
3565
+ )
3566
+ ].join("\n")
3567
+ };
3514
3568
  }
3515
3569
  async function handleSkillsCommand(action, args, ctx) {
3516
3570
  const home2 = os11.homedir();
@@ -3914,7 +3968,7 @@ function handleReset(action) {
3914
3968
  function handleUpdate() {
3915
3969
  try {
3916
3970
  const current = execFileSync3("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
3917
- const local = true ? "0.21.0" : "unknown";
3971
+ const local = true ? "0.22.0" : "unknown";
3918
3972
  if (current === local) {
3919
3973
  return { handled: true, output: `${pc5.green("Up to date")} \u2014 v${local}` };
3920
3974
  }
@@ -6369,7 +6423,7 @@ function bootstrapEcosystem() {
6369
6423
  return true;
6370
6424
  }
6371
6425
  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) => {
6426
+ program.name("aman-agent").description("Your AI companion, running locally").version("0.22.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) => {
6373
6427
  p3.intro(pc8.bold("aman agent") + pc8.dim(" \u2014 your AI companion"));
6374
6428
  let config = loadConfig();
6375
6429
  if (!config) {
@@ -6491,68 +6545,68 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
6491
6545
  ` ${pc8.cyan("Business")} $19/user/mo Team admin + policy controls`,
6492
6546
  ` ${pc8.cyan("Enterprise")} $39/user/mo SSO, audit logs, IP indemnity`,
6493
6547
  "",
6494
- `${pc8.dim("Authentication is handled by the GitHub CLI (gh).")}`,
6548
+ `${pc8.dim("Authentication is handled by the Copilot CLI.")}`,
6495
6549
  `${pc8.dim("Subscribe at: https://github.com/features/copilot")}`
6496
6550
  ].join("\n"),
6497
6551
  "Copilot Plans"
6498
6552
  );
6499
- if (!isGhCliInstalled()) {
6500
- p3.log.error("GitHub CLI (gh) is not installed.");
6553
+ if (!isCopilotCliInstalled()) {
6554
+ p3.log.error("Copilot CLI is not installed.");
6501
6555
  p3.log.info("Install it from:");
6502
- p3.log.step(pc8.bold("https://cli.github.com"));
6556
+ p3.log.step(pc8.bold("https://docs.github.com/copilot/how-tos/copilot-cli"));
6503
6557
  p3.log.info(pc8.dim("Then re-run aman-agent to continue setup."));
6504
6558
  process.exit(1);
6505
6559
  }
6506
- p3.log.success("GitHub CLI detected.");
6507
- const ghAuth = isGhAuthenticated();
6508
- if (ghAuth) {
6509
- p3.log.success("GitHub authentication found.");
6560
+ p3.log.success("Copilot CLI detected.");
6561
+ const copilotAuth = isCopilotCliAuthenticated();
6562
+ if (copilotAuth) {
6563
+ p3.log.success("Copilot authentication found.");
6510
6564
  } else {
6511
- p3.log.warn("Not logged in to GitHub.");
6565
+ p3.log.warn("Not logged in to Copilot.");
6512
6566
  const authAction = await p3.select({
6513
6567
  message: "Authentication",
6514
6568
  options: [
6515
- { value: "login", label: "Log in now", hint: "runs: gh auth login" },
6569
+ { value: "login", label: "Log in now", hint: "runs: copilot login" },
6516
6570
  { value: "skip", label: "Skip (I'll log in later)" }
6517
6571
  ]
6518
6572
  });
6519
6573
  if (p3.isCancel(authAction)) process.exit(0);
6520
6574
  if (authAction === "login") {
6521
- p3.log.step("Launching GitHub login...");
6575
+ p3.log.step("Launching Copilot login...");
6522
6576
  const { spawnSync } = await import("child_process");
6523
- const loginResult = spawnSync("gh", ["auth", "login"], {
6577
+ const loginResult = spawnSync("copilot", ["login"], {
6524
6578
  stdio: "inherit"
6525
6579
  });
6526
6580
  if (loginResult.status !== 0) {
6527
6581
  p3.log.error("Login failed or was cancelled.");
6528
6582
  process.exit(1);
6529
6583
  }
6530
- p3.log.success("GitHub login successful.");
6584
+ p3.log.success("Copilot login successful.");
6531
6585
  }
6532
6586
  }
6533
6587
  apiKey = "copilot";
6534
6588
  const modelChoice = await p3.select({
6535
6589
  message: "Model",
6536
6590
  options: [
6537
- { value: "gpt-4o", label: "GPT-4o", hint: "fast, recommended" },
6538
- { value: "gpt-4o-mini", label: "GPT-4o Mini", hint: "fastest" },
6591
+ { value: "default", label: "Default", hint: "Copilot's default model" },
6592
+ { value: "gpt-4o", label: "GPT-4o", hint: "fast" },
6593
+ { value: "gpt-5.2", label: "GPT-5.2", hint: "most capable" },
6539
6594
  { 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
6595
  { value: "custom", label: "Custom model ID" }
6544
6596
  ],
6545
- initialValue: "gpt-4o"
6597
+ initialValue: "default"
6546
6598
  });
6547
6599
  if (p3.isCancel(modelChoice)) process.exit(0);
6548
6600
  if (modelChoice === "custom") {
6549
6601
  const customModel = await p3.text({
6550
- message: "Model ID",
6602
+ message: "Model ID (run copilot --help for available models)",
6551
6603
  placeholder: "gpt-4o",
6552
6604
  validate: (v) => v.length === 0 ? "Model ID is required" : void 0
6553
6605
  });
6554
6606
  if (p3.isCancel(customModel)) process.exit(0);
6555
6607
  defaultModel = customModel;
6608
+ } else if (modelChoice === "default") {
6609
+ defaultModel = "";
6556
6610
  } else {
6557
6611
  defaultModel = modelChoice;
6558
6612
  }