@aman_asmuei/aman-agent 0.7.7 → 0.16.2

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,7 +1,7 @@
1
1
  // src/index.ts
2
2
  import { Command } from "commander";
3
3
  import * as p2 from "@clack/prompts";
4
- import pc4 from "picocolors";
4
+ import pc7 from "picocolors";
5
5
 
6
6
  // src/config.ts
7
7
  import fs from "fs";
@@ -15,7 +15,8 @@ var DEFAULT_HOOKS = {
15
15
  evalPrompt: true,
16
16
  autoSessionSave: true,
17
17
  extractMemories: true,
18
- featureHints: true
18
+ featureHints: true,
19
+ personalityAdapt: true
19
20
  };
20
21
  var CONFIG_DIR = path.join(os.homedir(), ".aman-agent");
21
22
  var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
@@ -96,18 +97,35 @@ function buildBudgetedPrompt(components, maxTokens = 8e3) {
96
97
 
97
98
  // src/prompt.ts
98
99
  var ECOSYSTEM_FILES = [
99
- { name: "identity", dir: ".acore", file: "core.md" },
100
+ { name: "identity", dir: ".acore", file: "core.md", profileOverridable: true },
100
101
  { name: "tools", dir: ".akit", file: "kit.md" },
101
102
  { name: "workflows", dir: ".aflow", file: "flow.md" },
102
- { name: "guardrails", dir: ".arules", file: "rules.md" },
103
- { name: "skills", dir: ".askill", file: "skills.md" }
103
+ { name: "guardrails", dir: ".arules", file: "rules.md", profileOverridable: true },
104
+ { name: "skills", dir: ".askill", file: "skills.md", profileOverridable: true }
104
105
  ];
105
- function assembleSystemPrompt(maxTokens) {
106
+ function resolveLayerPath(entry, home2, profile) {
107
+ if (profile && entry.profileOverridable) {
108
+ const profilePath = path2.join(home2, ".acore", "profiles", profile, entry.file);
109
+ if (fs2.existsSync(profilePath)) return profilePath;
110
+ if (entry.name === "guardrails") {
111
+ const altPath = path2.join(home2, ".acore", "profiles", profile, "rules.md");
112
+ if (fs2.existsSync(altPath)) return altPath;
113
+ }
114
+ if (entry.name === "skills") {
115
+ const altPath = path2.join(home2, ".acore", "profiles", profile, "skills.md");
116
+ if (fs2.existsSync(altPath)) return altPath;
117
+ }
118
+ }
119
+ const globalPath = path2.join(home2, entry.dir, entry.file);
120
+ if (fs2.existsSync(globalPath)) return globalPath;
121
+ return null;
122
+ }
123
+ function assembleSystemPrompt(maxTokens, profile) {
106
124
  const home2 = os2.homedir();
107
125
  const components = [];
108
126
  for (const entry of ECOSYSTEM_FILES) {
109
- const filePath = path2.join(home2, entry.dir, entry.file);
110
- if (fs2.existsSync(filePath)) {
127
+ const filePath = resolveLayerPath(entry, home2, profile);
128
+ if (filePath) {
111
129
  const content = fs2.readFileSync(filePath, "utf-8").trim();
112
130
  components.push({
113
131
  name: entry.name,
@@ -130,9 +148,43 @@ function assembleSystemPrompt(maxTokens) {
130
148
  prompt: budgeted.prompt,
131
149
  layers: budgeted.included,
132
150
  truncated: budgeted.truncated,
133
- totalTokens: budgeted.totalTokens
151
+ totalTokens: budgeted.totalTokens,
152
+ profile
134
153
  };
135
154
  }
155
+ function listProfiles() {
156
+ const profilesDir = path2.join(os2.homedir(), ".acore", "profiles");
157
+ if (!fs2.existsSync(profilesDir)) return [];
158
+ const profiles = [];
159
+ for (const entry of fs2.readdirSync(profilesDir, { withFileTypes: true })) {
160
+ if (!entry.isDirectory()) continue;
161
+ const corePath = path2.join(profilesDir, entry.name, "core.md");
162
+ if (!fs2.existsSync(corePath)) continue;
163
+ const content = fs2.readFileSync(corePath, "utf-8");
164
+ const nameMatch = content.match(/^# (.+)/m);
165
+ const personalityMatch = content.match(/- Personality:\s*(.+)/);
166
+ profiles.push({
167
+ name: entry.name,
168
+ aiName: nameMatch?.[1]?.trim() || entry.name,
169
+ personality: personalityMatch?.[1]?.trim() || "default"
170
+ });
171
+ }
172
+ return profiles;
173
+ }
174
+ function getProfileAiName(profile) {
175
+ const home2 = os2.homedir();
176
+ let corePath;
177
+ if (profile) {
178
+ const profileCorePath = path2.join(home2, ".acore", "profiles", profile, "core.md");
179
+ corePath = fs2.existsSync(profileCorePath) ? profileCorePath : path2.join(home2, ".acore", "core.md");
180
+ } else {
181
+ corePath = path2.join(home2, ".acore", "core.md");
182
+ }
183
+ if (!fs2.existsSync(corePath)) return "Assistant";
184
+ const content = fs2.readFileSync(corePath, "utf-8");
185
+ const match = content.match(/^# (.+)$/m);
186
+ return match?.[1]?.trim() || "Assistant";
187
+ }
136
188
 
137
189
  // src/llm/anthropic.ts
138
190
  import Anthropic from "@anthropic-ai/sdk";
@@ -147,6 +199,16 @@ function toAnthropicMessages(messages) {
147
199
  if (block.type === "text") {
148
200
  return { type: "text", text: block.text };
149
201
  }
202
+ if (block.type === "image") {
203
+ return {
204
+ type: "image",
205
+ source: {
206
+ type: "base64",
207
+ media_type: block.source.media_type,
208
+ data: block.source.data
209
+ }
210
+ };
211
+ }
150
212
  if (block.type === "tool_use") {
151
213
  return {
152
214
  type: "tool_use",
@@ -311,8 +373,26 @@ function toOpenAIMessages(systemPrompt, messages) {
311
373
  }
312
374
  }
313
375
  } else {
314
- const text2 = m.content.map((b) => "text" in b ? b.text : "").join("");
315
- result.push({ role: "user", content: text2 });
376
+ const hasImages = m.content.some((b) => b.type === "image");
377
+ if (hasImages) {
378
+ const parts = [];
379
+ for (const b of m.content) {
380
+ if (b.type === "text") {
381
+ parts.push({ type: "text", text: b.text });
382
+ } else if (b.type === "image") {
383
+ parts.push({
384
+ type: "image_url",
385
+ image_url: {
386
+ url: `data:${b.source.media_type};base64,${b.source.data}`
387
+ }
388
+ });
389
+ }
390
+ }
391
+ result.push({ role: "user", content: parts });
392
+ } else {
393
+ const text2 = m.content.map((b) => "text" in b ? b.text : "").join("");
394
+ result.push({ role: "user", content: text2 });
395
+ }
316
396
  }
317
397
  }
318
398
  }
@@ -415,6 +495,74 @@ function createOpenAIClient(apiKey, model) {
415
495
 
416
496
  // src/llm/ollama.ts
417
497
  import OpenAI2 from "openai";
498
+ function toOllamaMessages(systemPrompt, messages) {
499
+ const result = [
500
+ { role: "system", content: systemPrompt }
501
+ ];
502
+ for (const m of messages) {
503
+ if (typeof m.content === "string") {
504
+ result.push({
505
+ role: m.role,
506
+ content: m.content
507
+ });
508
+ } else if (m.role === "assistant") {
509
+ const textParts = m.content.filter((b) => b.type === "text");
510
+ const toolUseParts = m.content.filter((b) => b.type === "tool_use");
511
+ const text2 = textParts.map((b) => "text" in b ? b.text : "").join("");
512
+ if (toolUseParts.length > 0) {
513
+ result.push({
514
+ role: "assistant",
515
+ content: text2 || null,
516
+ tool_calls: toolUseParts.map((b) => ({
517
+ id: "id" in b ? b.id : "",
518
+ type: "function",
519
+ function: {
520
+ name: "name" in b ? b.name : "",
521
+ arguments: JSON.stringify("input" in b ? b.input : {})
522
+ }
523
+ }))
524
+ });
525
+ } else {
526
+ result.push({ role: "assistant", content: text2 });
527
+ }
528
+ } else if (m.role === "user") {
529
+ const toolResults = m.content.filter((b) => b.type === "tool_result");
530
+ if (toolResults.length > 0) {
531
+ for (const tr of toolResults) {
532
+ if (tr.type === "tool_result") {
533
+ result.push({
534
+ role: "tool",
535
+ tool_call_id: tr.tool_use_id,
536
+ content: tr.content
537
+ });
538
+ }
539
+ }
540
+ } else {
541
+ const hasImages = m.content.some((b) => b.type === "image");
542
+ if (hasImages) {
543
+ const parts = [];
544
+ for (const b of m.content) {
545
+ if (b.type === "text") {
546
+ parts.push({ type: "text", text: b.text });
547
+ } else if (b.type === "image") {
548
+ parts.push({
549
+ type: "image_url",
550
+ image_url: {
551
+ url: `data:${b.source.media_type};base64,${b.source.data}`
552
+ }
553
+ });
554
+ }
555
+ }
556
+ result.push({ role: "user", content: parts });
557
+ } else {
558
+ const text2 = m.content.map((b) => "text" in b ? b.text : "").join("");
559
+ result.push({ role: "user", content: text2 });
560
+ }
561
+ }
562
+ }
563
+ }
564
+ return result;
565
+ }
418
566
  function createOllamaClient(model, baseURL) {
419
567
  const client = new OpenAI2({
420
568
  baseURL: baseURL || "http://localhost:11434/v1",
@@ -422,28 +570,83 @@ function createOllamaClient(model, baseURL) {
422
570
  // Ollama doesn't require a real key
423
571
  });
424
572
  return {
425
- async chat(systemPrompt, messages, onChunk, _tools) {
426
- let fullText = "";
573
+ async chat(systemPrompt, messages, onChunk, tools) {
574
+ const ollamaMessages = toOllamaMessages(systemPrompt, messages);
575
+ const hasTools = tools && tools.length > 0;
427
576
  try {
428
- const stream = await client.chat.completions.create({
577
+ let fullText = "";
578
+ const toolCallAccumulators = /* @__PURE__ */ new Map();
579
+ const createParams = {
429
580
  model,
430
581
  max_tokens: 8192,
431
- messages: [
432
- { role: "system", content: systemPrompt },
433
- ...messages.map((m) => ({
434
- role: m.role,
435
- content: typeof m.content === "string" ? m.content : m.content.filter((b) => b.type === "text").map((b) => "text" in b ? b.text : "").join("")
436
- }))
437
- ],
582
+ messages: ollamaMessages,
438
583
  stream: true
439
- });
584
+ };
585
+ if (hasTools) {
586
+ createParams.tools = tools.map((t) => ({
587
+ type: "function",
588
+ function: {
589
+ name: t.name,
590
+ description: t.description,
591
+ parameters: t.input_schema
592
+ }
593
+ }));
594
+ }
595
+ const stream = await client.chat.completions.create(
596
+ createParams
597
+ );
440
598
  for await (const chunk of stream) {
441
- const text2 = chunk.choices[0]?.delta?.content || "";
442
- if (text2) {
443
- fullText += text2;
444
- onChunk({ type: "text", text: text2 });
599
+ const delta = chunk.choices[0]?.delta;
600
+ if (!delta) continue;
601
+ if (delta.content) {
602
+ fullText += delta.content;
603
+ onChunk({ type: "text", text: delta.content });
445
604
  }
605
+ if (delta.tool_calls) {
606
+ for (const tc of delta.tool_calls) {
607
+ const idx = tc.index;
608
+ let acc = toolCallAccumulators.get(idx);
609
+ if (!acc) {
610
+ acc = { id: "", name: "", arguments: "" };
611
+ toolCallAccumulators.set(idx, acc);
612
+ }
613
+ if (tc.id) {
614
+ acc.id = tc.id;
615
+ }
616
+ if (tc.function?.name) {
617
+ acc.name = tc.function.name;
618
+ }
619
+ if (tc.function?.arguments) {
620
+ acc.arguments += tc.function.arguments;
621
+ }
622
+ }
623
+ }
624
+ }
625
+ const toolUses = Array.from(toolCallAccumulators.entries()).sort(([a], [b]) => a - b).map(([, acc]) => ({
626
+ id: acc.id,
627
+ name: acc.name,
628
+ input: JSON.parse(acc.arguments || "{}")
629
+ }));
630
+ onChunk({ type: "done" });
631
+ if (toolUses.length > 0) {
632
+ const contentBlocks = [
633
+ ...fullText ? [{ type: "text", text: fullText }] : [],
634
+ ...toolUses.map((tu) => ({
635
+ type: "tool_use",
636
+ id: tu.id,
637
+ name: tu.name,
638
+ input: tu.input
639
+ }))
640
+ ];
641
+ return {
642
+ message: { role: "assistant", content: contentBlocks },
643
+ toolUses
644
+ };
446
645
  }
646
+ return {
647
+ message: { role: "assistant", content: fullText },
648
+ toolUses: []
649
+ };
447
650
  } catch (error) {
448
651
  if (error instanceof Error && error.message.includes("ECONNREFUSED")) {
449
652
  throw new Error(
@@ -452,11 +655,6 @@ function createOllamaClient(model, baseURL) {
452
655
  }
453
656
  throw error;
454
657
  }
455
- onChunk({ type: "done" });
456
- return {
457
- message: { role: "assistant", content: fullText },
458
- toolUses: []
459
- };
460
658
  }
461
659
  };
462
660
  }
@@ -599,20 +797,20 @@ var McpManager = class {
599
797
 
600
798
  // src/agent.ts
601
799
  import * as readline from "readline";
602
- import fs7 from "fs";
603
- import path7 from "path";
604
- import os7 from "os";
605
- import pc3 from "picocolors";
800
+ import fs12 from "fs";
801
+ import path12 from "path";
802
+ import os11 from "os";
803
+ import pc6 from "picocolors";
606
804
  import { marked } from "marked";
607
805
  import { markedTerminal } from "marked-terminal";
608
806
  import logUpdate from "log-update";
609
807
 
610
808
  // src/commands.ts
611
- import fs5 from "fs";
612
- import path5 from "path";
613
- import os5 from "os";
809
+ import fs8 from "fs";
810
+ import path8 from "path";
811
+ import os8 from "os";
614
812
  import { execFileSync } from "child_process";
615
- import pc from "picocolors";
813
+ import pc3 from "picocolors";
616
814
 
617
815
  // src/layers/parsers.ts
618
816
  import fs4 from "fs";
@@ -671,12 +869,721 @@ function getEcosystemStatus(mcpToolCount, amemConnected) {
671
869
  };
672
870
  }
673
871
 
872
+ // src/profile-templates.ts
873
+ import fs5 from "fs";
874
+ import path5 from "path";
875
+ import os5 from "os";
876
+ var BUILT_IN_PROFILES = [
877
+ {
878
+ name: "coder",
879
+ label: "Coder",
880
+ description: "Direct, technical, code-first. Skips pleasantries, shows code.",
881
+ core: `# Coder
882
+
883
+ ## Identity
884
+ - Role: Coder is your technical pair programmer
885
+ - Personality: direct, precise, efficient \u2014 code speaks louder than words
886
+ - Communication: lead with code, explain after. No fluff.
887
+ - Values: simplicity over cleverness, working code over perfect code, tests over trust
888
+ - Boundaries: won't pretend to be human, flags when out of depth
889
+
890
+ ### Appearance
891
+ - Base: focused developer, dark hoodie, terminal glow
892
+ - Style: minimal
893
+ - Palette: green on black`,
894
+ rules: `# Coder Rules
895
+
896
+ ## Always
897
+ - Show code before explaining
898
+ - Include error handling
899
+ - Suggest tests for new code
900
+
901
+ ## Never
902
+ - Write code without understanding the requirement
903
+ - Push to main without tests
904
+ - Ignore security implications`
905
+ },
906
+ {
907
+ name: "writer",
908
+ label: "Writer",
909
+ description: "Creative, eloquent, story-driven. Focuses on narrative and engagement.",
910
+ core: `# Muse
911
+
912
+ ## Identity
913
+ - Role: Muse is your creative writing partner
914
+ - Personality: eloquent, imaginative, encouraging \u2014 finds the story in everything
915
+ - Communication: explore ideas together, offer alternatives, celebrate good writing
916
+ - Values: authenticity over formulas, voice over grammar, emotion over information
917
+ - Boundaries: won't write without understanding the audience, flags when content is sensitive
918
+
919
+ ### Appearance
920
+ - Base: warm expression, creative energy, pen in hand
921
+ - Style: illustrated
922
+ - Palette: warm amber and cream`,
923
+ rules: `# Writer Rules
924
+
925
+ ## Always
926
+ - Ask about the target audience
927
+ - Offer 2-3 angle options before drafting
928
+ - Read drafts aloud mentally for rhythm
929
+
930
+ ## Never
931
+ - Use cliches without subverting them
932
+ - Write without a clear hook
933
+ - Ignore tone consistency`
934
+ },
935
+ {
936
+ name: "researcher",
937
+ label: "Researcher",
938
+ description: "Analytical, thorough, citation-focused. Digs deep, verifies claims.",
939
+ core: `# Scholar
940
+
941
+ ## Identity
942
+ - Role: Scholar is your research analyst
943
+ - Personality: analytical, thorough, intellectually curious \u2014 never takes claims at face value
944
+ - Communication: present findings with evidence, flag uncertainty, compare perspectives
945
+ - Values: accuracy over speed, nuance over simplification, primary sources over summaries
946
+ - Boundaries: clearly marks speculation vs fact, flags when evidence is insufficient
947
+
948
+ ### Appearance
949
+ - Base: thoughtful expression, glasses, surrounded by notes
950
+ - Style: minimal
951
+ - Palette: navy and white`,
952
+ rules: `# Researcher Rules
953
+
954
+ ## Always
955
+ - Cite sources when making factual claims
956
+ - Flag confidence level (high/medium/low)
957
+ - Present multiple perspectives on contested topics
958
+
959
+ ## Never
960
+ - Present speculation as fact
961
+ - Ignore contradicting evidence
962
+ - Oversimplify complex topics`
963
+ }
964
+ ];
965
+ function installProfileTemplate(templateName, userName) {
966
+ const template = BUILT_IN_PROFILES.find((t) => t.name === templateName);
967
+ if (!template) return null;
968
+ const profileDir = path5.join(os5.homedir(), ".acore", "profiles", template.name);
969
+ if (fs5.existsSync(profileDir)) return `Profile already exists: ${template.name}`;
970
+ fs5.mkdirSync(profileDir, { recursive: true });
971
+ let core = template.core;
972
+ if (userName) {
973
+ core += `
974
+
975
+ ---
976
+
977
+ ## Relationship
978
+ - Name: ${userName}
979
+ - Nicknames: []
980
+ - Communication: [updated over time]
981
+ - Detail level: balanced
982
+ `;
983
+ }
984
+ fs5.writeFileSync(path5.join(profileDir, "core.md"), core, "utf-8");
985
+ if (template.rules) {
986
+ fs5.writeFileSync(path5.join(profileDir, "rules.md"), template.rules, "utf-8");
987
+ }
988
+ if (template.skills) {
989
+ fs5.writeFileSync(path5.join(profileDir, "skills.md"), template.skills, "utf-8");
990
+ }
991
+ return null;
992
+ }
993
+
994
+ // src/delegate.ts
995
+ import pc from "picocolors";
996
+ var isRetryable = (err) => {
997
+ if (err instanceof Error) {
998
+ const msg = err.message.toLowerCase();
999
+ return msg.includes("rate") || msg.includes("timeout") || msg.includes("econnreset");
1000
+ }
1001
+ return false;
1002
+ };
1003
+ async function delegateTask(task, profile, client, mcpManager, options = {}) {
1004
+ const maxTurns = options.maxTurns ?? 10;
1005
+ const silent = options.silent ?? false;
1006
+ const tools = options.tools;
1007
+ try {
1008
+ const { prompt: systemPrompt } = assembleSystemPrompt(void 0, profile);
1009
+ const delegationPrompt = `${systemPrompt}
1010
+
1011
+ <delegation>
1012
+ You are being delegated a specific task by the primary agent. Complete this task thoroughly and return your result. You have access to tools if needed. Focus on the task \u2014 do not ask follow-up questions, just do your best with what you have.
1013
+ </delegation>`;
1014
+ const messages = [
1015
+ { role: "user", content: task }
1016
+ ];
1017
+ const toolsUsed = [];
1018
+ let turns = 0;
1019
+ const onChunk = silent ? () => {
1020
+ } : (chunk) => {
1021
+ if (chunk.type === "text" && chunk.text) {
1022
+ process.stdout.write(chunk.text);
1023
+ }
1024
+ };
1025
+ let response = await withRetry(
1026
+ () => client.chat(delegationPrompt, messages, onChunk, tools),
1027
+ { maxAttempts: 2, baseDelay: 1e3, retryable: isRetryable }
1028
+ );
1029
+ messages.push(response.message);
1030
+ while (response.toolUses.length > 0 && turns < maxTurns) {
1031
+ turns++;
1032
+ const toolResults = await Promise.all(
1033
+ response.toolUses.map(async (toolUse) => {
1034
+ if (!silent) {
1035
+ process.stdout.write(pc.dim(` [${profile}:${toolUse.name}...]
1036
+ `));
1037
+ }
1038
+ toolsUsed.push(toolUse.name);
1039
+ try {
1040
+ const result = await mcpManager.callTool(toolUse.name, toolUse.input);
1041
+ return {
1042
+ type: "tool_result",
1043
+ tool_use_id: toolUse.id,
1044
+ content: result
1045
+ };
1046
+ } catch (err) {
1047
+ return {
1048
+ type: "tool_result",
1049
+ tool_use_id: toolUse.id,
1050
+ content: `Error: ${err instanceof Error ? err.message : String(err)}`,
1051
+ is_error: true
1052
+ };
1053
+ }
1054
+ })
1055
+ );
1056
+ messages.push({ role: "user", content: toolResults });
1057
+ response = await withRetry(
1058
+ () => client.chat(delegationPrompt, messages, onChunk, tools),
1059
+ { maxAttempts: 2, baseDelay: 1e3, retryable: isRetryable }
1060
+ );
1061
+ messages.push(response.message);
1062
+ }
1063
+ const finalMessage = response.message;
1064
+ const responseText = typeof finalMessage.content === "string" ? finalMessage.content : finalMessage.content.filter((b) => b.type === "text").map((b) => "text" in b ? b.text : "").join("");
1065
+ return {
1066
+ profile,
1067
+ task,
1068
+ response: responseText,
1069
+ toolsUsed: [...new Set(toolsUsed)],
1070
+ turns,
1071
+ success: true
1072
+ };
1073
+ } catch (err) {
1074
+ const error = err instanceof Error ? err.message : String(err);
1075
+ log.warn("delegate", `Delegation to ${profile} failed: ${error}`);
1076
+ return {
1077
+ profile,
1078
+ task,
1079
+ response: "",
1080
+ toolsUsed: [],
1081
+ turns: 0,
1082
+ success: false,
1083
+ error
1084
+ };
1085
+ }
1086
+ }
1087
+ async function delegateParallel(tasks, client, mcpManager, options = {}) {
1088
+ return Promise.all(
1089
+ tasks.map(
1090
+ ({ task, profile }) => delegateTask(task, profile, client, mcpManager, { ...options, silent: true })
1091
+ )
1092
+ );
1093
+ }
1094
+ async function delegatePipeline(steps, initialInput, client, mcpManager, options = {}) {
1095
+ const results = [];
1096
+ let previousResult = initialInput;
1097
+ for (const step of steps) {
1098
+ const task = step.taskTemplate.replace("{{input}}", previousResult);
1099
+ if (!options.silent) {
1100
+ process.stdout.write(pc.dim(`
1101
+ [delegating to ${step.profile}...]
1102
+ `));
1103
+ }
1104
+ const result = await delegateTask(task, step.profile, client, mcpManager, {
1105
+ ...options,
1106
+ silent: true
1107
+ });
1108
+ results.push(result);
1109
+ if (!result.success) break;
1110
+ previousResult = result.response;
1111
+ }
1112
+ return results;
1113
+ }
1114
+
1115
+ // src/teams.ts
1116
+ import fs6 from "fs";
1117
+ import path6 from "path";
1118
+ import os6 from "os";
1119
+ import pc2 from "picocolors";
1120
+ function getTeamsDir() {
1121
+ return path6.join(os6.homedir(), ".acore", "teams");
1122
+ }
1123
+ function ensureTeamsDir() {
1124
+ const dir = getTeamsDir();
1125
+ if (!fs6.existsSync(dir)) fs6.mkdirSync(dir, { recursive: true });
1126
+ return dir;
1127
+ }
1128
+ function teamPath(name) {
1129
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
1130
+ return path6.join(ensureTeamsDir(), `${slug}.json`);
1131
+ }
1132
+ function createTeam(team) {
1133
+ const fp = teamPath(team.name);
1134
+ fs6.writeFileSync(fp, JSON.stringify(team, null, 2), "utf-8");
1135
+ }
1136
+ function loadTeam(name) {
1137
+ const fp = teamPath(name);
1138
+ if (!fs6.existsSync(fp)) return null;
1139
+ try {
1140
+ return JSON.parse(fs6.readFileSync(fp, "utf-8"));
1141
+ } catch {
1142
+ return null;
1143
+ }
1144
+ }
1145
+ function listTeams() {
1146
+ const dir = getTeamsDir();
1147
+ if (!fs6.existsSync(dir)) return [];
1148
+ const teams = [];
1149
+ for (const file of fs6.readdirSync(dir)) {
1150
+ if (!file.endsWith(".json")) continue;
1151
+ try {
1152
+ const content = fs6.readFileSync(path6.join(dir, file), "utf-8");
1153
+ teams.push(JSON.parse(content));
1154
+ } catch {
1155
+ }
1156
+ }
1157
+ return teams;
1158
+ }
1159
+ function deleteTeam(name) {
1160
+ const fp = teamPath(name);
1161
+ if (!fs6.existsSync(fp)) return false;
1162
+ fs6.unlinkSync(fp);
1163
+ return true;
1164
+ }
1165
+ async function runTeam(team, task, client, mcpManager, tools) {
1166
+ process.stdout.write(pc2.dim(`
1167
+ Team: ${team.name} (${team.workflow} mode)
1168
+ `));
1169
+ process.stdout.write(pc2.dim(` Members: ${team.members.map((m) => m.profile).join(", ")}
1170
+
1171
+ `));
1172
+ switch (team.workflow) {
1173
+ case "pipeline":
1174
+ return runPipeline(team, task, client, mcpManager, tools);
1175
+ case "parallel":
1176
+ return runParallel(team, task, client, mcpManager, tools);
1177
+ case "coordinator":
1178
+ return runCoordinator(team, task, client, mcpManager, tools);
1179
+ default:
1180
+ return {
1181
+ team: team.name,
1182
+ task,
1183
+ workflow: team.workflow,
1184
+ results: [],
1185
+ finalOutput: `Unknown workflow mode: ${team.workflow}`,
1186
+ success: false
1187
+ };
1188
+ }
1189
+ }
1190
+ async function runPipeline(team, task, client, mcpManager, tools) {
1191
+ const steps = team.members.map((m, i) => ({
1192
+ profile: m.profile,
1193
+ taskTemplate: i === 0 ? `${task}
1194
+
1195
+ Your role: ${m.role}` : `${m.role}. Here is the previous agent's work:
1196
+
1197
+ {{input}}`
1198
+ }));
1199
+ for (const step of steps) {
1200
+ process.stdout.write(pc2.dim(` [${step.profile}: ${team.members.find((m) => m.profile === step.profile)?.role}...]
1201
+ `));
1202
+ }
1203
+ const results = await delegatePipeline(steps, task, client, mcpManager, {
1204
+ tools,
1205
+ silent: true
1206
+ });
1207
+ const lastResult = results[results.length - 1];
1208
+ const success = results.every((r) => r.success);
1209
+ return {
1210
+ team: team.name,
1211
+ task,
1212
+ workflow: "pipeline",
1213
+ results,
1214
+ finalOutput: lastResult?.response || "",
1215
+ success
1216
+ };
1217
+ }
1218
+ async function runParallel(team, task, client, mcpManager, tools) {
1219
+ const tasks = team.members.map((m) => ({
1220
+ profile: m.profile,
1221
+ task: `${task}
1222
+
1223
+ Your specific role: ${m.role}. Focus only on your role.`
1224
+ }));
1225
+ for (const m of team.members) {
1226
+ process.stdout.write(pc2.dim(` [${m.profile}: ${m.role} (parallel)...]
1227
+ `));
1228
+ }
1229
+ const results = await delegateParallel(tasks, client, mcpManager, { tools });
1230
+ const mergeInput = results.filter((r) => r.success).map((r) => `[${r.profile} \u2014 ${team.members.find((m) => m.profile === r.profile)?.role}]:
1231
+ ${r.response}`).join("\n\n---\n\n");
1232
+ process.stdout.write(pc2.dim(` [merging results...]
1233
+ `));
1234
+ const mergeResult = await delegateTask(
1235
+ `You are the team coordinator. Multiple agents worked on this task in parallel. Merge their outputs into a single cohesive result. Keep the best parts from each.
1236
+
1237
+ Original task: ${task}
1238
+
1239
+ ${mergeInput}`,
1240
+ team.coordinator === "default" ? team.members[0]?.profile || "default" : team.coordinator,
1241
+ client,
1242
+ mcpManager,
1243
+ { tools, silent: true }
1244
+ );
1245
+ return {
1246
+ team: team.name,
1247
+ task,
1248
+ workflow: "parallel",
1249
+ results: [...results, mergeResult],
1250
+ finalOutput: mergeResult.response,
1251
+ success: results.some((r) => r.success)
1252
+ };
1253
+ }
1254
+ async function runCoordinator(team, task, client, mcpManager, tools) {
1255
+ const memberDescriptions = team.members.map((m) => `- ${m.profile}: ${m.role}`).join("\n");
1256
+ process.stdout.write(pc2.dim(` [coordinator planning...]
1257
+ `));
1258
+ const planResult = await delegateTask(
1259
+ `You are the coordinator of a team. Your job is to break down this task and decide which team members should handle each part.
1260
+
1261
+ Team members:
1262
+ ${memberDescriptions}
1263
+
1264
+ Task: ${task}
1265
+
1266
+ Respond with a JSON array of assignments:
1267
+ [{"profile": "member-name", "subtask": "what they should do"}]
1268
+
1269
+ Only use the JSON array, no other text.`,
1270
+ team.coordinator === "default" ? team.members[0]?.profile || "default" : team.coordinator,
1271
+ client,
1272
+ mcpManager,
1273
+ { tools: void 0, silent: true, maxTurns: 0 }
1274
+ );
1275
+ if (!planResult.success) {
1276
+ return {
1277
+ team: team.name,
1278
+ task,
1279
+ workflow: "coordinator",
1280
+ results: [planResult],
1281
+ finalOutput: `Coordinator failed to plan: ${planResult.error}`,
1282
+ success: false
1283
+ };
1284
+ }
1285
+ let assignments;
1286
+ try {
1287
+ let cleaned = planResult.response.trim();
1288
+ const codeBlockMatch = cleaned.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
1289
+ if (codeBlockMatch) cleaned = codeBlockMatch[1].trim();
1290
+ assignments = JSON.parse(cleaned);
1291
+ } catch {
1292
+ assignments = team.members.map((m) => ({ profile: m.profile, subtask: `${m.role}: ${task}` }));
1293
+ }
1294
+ for (const a of assignments) {
1295
+ process.stdout.write(pc2.dim(` [${a.profile}: ${a.subtask.slice(0, 60)}...]
1296
+ `));
1297
+ }
1298
+ const results = await delegateParallel(
1299
+ assignments.map((a) => ({ profile: a.profile, task: a.subtask })),
1300
+ client,
1301
+ mcpManager,
1302
+ { tools }
1303
+ );
1304
+ const mergeInput = results.filter((r) => r.success).map((r, i) => `[${assignments[i]?.profile} \u2014 ${assignments[i]?.subtask}]:
1305
+ ${r.response}`).join("\n\n---\n\n");
1306
+ process.stdout.write(pc2.dim(` [coordinator merging...]
1307
+ `));
1308
+ const mergeResult = await delegateTask(
1309
+ `You are the team coordinator. Your team members completed their assigned work. Combine their outputs into a single cohesive, polished result.
1310
+
1311
+ Original task: ${task}
1312
+
1313
+ ${mergeInput}`,
1314
+ team.coordinator === "default" ? team.members[0]?.profile || "default" : team.coordinator,
1315
+ client,
1316
+ mcpManager,
1317
+ { tools, silent: true }
1318
+ );
1319
+ return {
1320
+ team: team.name,
1321
+ task,
1322
+ workflow: "coordinator",
1323
+ results: [...results, mergeResult],
1324
+ finalOutput: mergeResult.response,
1325
+ success: results.some((r) => r.success)
1326
+ };
1327
+ }
1328
+ function formatTeam(team) {
1329
+ const lines = [];
1330
+ lines.push(`Team: ${pc2.bold(team.name)}`);
1331
+ lines.push(`Goal: ${team.goal}`);
1332
+ lines.push(`Mode: ${team.workflow}`);
1333
+ lines.push(`Coordinator: ${team.coordinator}`);
1334
+ lines.push("");
1335
+ lines.push("Members:");
1336
+ for (const m of team.members) {
1337
+ lines.push(` ${pc2.bold(m.profile)} \u2014 ${m.role}`);
1338
+ }
1339
+ return lines.join("\n");
1340
+ }
1341
+ function formatTeamResult(result) {
1342
+ const lines = [];
1343
+ lines.push(`
1344
+ ${pc2.bold(`Team: ${result.team}`)} (${result.workflow})`);
1345
+ for (const r of result.results) {
1346
+ const status = r.success ? pc2.green("\u2713") : pc2.red("\u2717");
1347
+ const tools = r.toolsUsed.length > 0 ? pc2.dim(` (${r.toolsUsed.join(", ")})`) : "";
1348
+ lines.push(` ${status} ${pc2.bold(r.profile)}${tools}`);
1349
+ }
1350
+ lines.push("");
1351
+ lines.push(result.finalOutput);
1352
+ return lines.join("\n");
1353
+ }
1354
+ var BUILT_IN_TEAMS = [
1355
+ {
1356
+ name: "content-team",
1357
+ goal: "Create and publish high-quality content",
1358
+ coordinator: "default",
1359
+ members: [
1360
+ { profile: "writer", role: "Draft compelling content with engaging narrative" },
1361
+ { profile: "researcher", role: "Fact-check claims and add citations" }
1362
+ ],
1363
+ workflow: "pipeline"
1364
+ },
1365
+ {
1366
+ name: "dev-team",
1367
+ goal: "Build and review code with quality assurance",
1368
+ coordinator: "default",
1369
+ members: [
1370
+ { profile: "coder", role: "Write clean, tested implementation code" },
1371
+ { profile: "researcher", role: "Review for security, performance, and best practices" }
1372
+ ],
1373
+ workflow: "pipeline"
1374
+ },
1375
+ {
1376
+ name: "research-team",
1377
+ goal: "Deep research with multiple perspectives",
1378
+ coordinator: "default",
1379
+ members: [
1380
+ { profile: "researcher", role: "Research the topic thoroughly with citations" },
1381
+ { profile: "writer", role: "Synthesize findings into clear, readable format" }
1382
+ ],
1383
+ workflow: "pipeline"
1384
+ }
1385
+ ];
1386
+
1387
+ // src/plans.ts
1388
+ import fs7 from "fs";
1389
+ import path7 from "path";
1390
+ import os7 from "os";
1391
+ function getPlansDir() {
1392
+ const localDir = path7.join(process.cwd(), ".acore", "plans");
1393
+ const localAcore = path7.join(process.cwd(), ".acore");
1394
+ if (fs7.existsSync(localAcore)) return localDir;
1395
+ return path7.join(os7.homedir(), ".acore", "plans");
1396
+ }
1397
+ function ensurePlansDir() {
1398
+ const dir = getPlansDir();
1399
+ if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
1400
+ return dir;
1401
+ }
1402
+ function planPath(name) {
1403
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
1404
+ return path7.join(ensurePlansDir(), `${slug}.md`);
1405
+ }
1406
+ function serializePlan(plan) {
1407
+ const lines = [];
1408
+ lines.push(`# ${plan.name}`);
1409
+ lines.push("");
1410
+ lines.push(`**Goal:** ${plan.goal}`);
1411
+ lines.push(`**Created:** ${plan.createdAt}`);
1412
+ lines.push(`**Updated:** ${plan.updatedAt}`);
1413
+ lines.push(`**Active:** ${plan.active}`);
1414
+ lines.push("");
1415
+ lines.push("## Steps");
1416
+ lines.push("");
1417
+ for (const step of plan.steps) {
1418
+ lines.push(`- [${step.done ? "x" : " "}] ${step.text}`);
1419
+ }
1420
+ lines.push("");
1421
+ return lines.join("\n");
1422
+ }
1423
+ function parsePlan(content, filePath) {
1424
+ try {
1425
+ const nameMatch = content.match(/^# (.+)/m);
1426
+ const goalMatch = content.match(/\*\*Goal:\*\*\s*(.+)/);
1427
+ const createdMatch = content.match(/\*\*Created:\*\*\s*(.+)/);
1428
+ const updatedMatch = content.match(/\*\*Updated:\*\*\s*(.+)/);
1429
+ const activeMatch = content.match(/\*\*Active:\*\*\s*(.+)/);
1430
+ const name = nameMatch?.[1]?.trim() || path7.basename(filePath, ".md");
1431
+ const goal = goalMatch?.[1]?.trim() || "";
1432
+ const createdAt = createdMatch?.[1]?.trim() || "";
1433
+ const updatedAt = updatedMatch?.[1]?.trim() || "";
1434
+ const active = activeMatch?.[1]?.trim() === "true";
1435
+ const steps = [];
1436
+ const stepMatches = content.matchAll(/- \[([ x])\] (.+)/g);
1437
+ for (const match of stepMatches) {
1438
+ steps.push({
1439
+ done: match[1] === "x",
1440
+ text: match[2].trim()
1441
+ });
1442
+ }
1443
+ return { name, goal, steps, createdAt, updatedAt, active };
1444
+ } catch (err) {
1445
+ log.debug("plans", "Failed to parse plan: " + filePath, err);
1446
+ return null;
1447
+ }
1448
+ }
1449
+ function createPlan(name, goal, steps) {
1450
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1451
+ const plan = {
1452
+ name,
1453
+ goal,
1454
+ steps: steps.map((text2) => ({ text: text2, done: false })),
1455
+ createdAt: now,
1456
+ updatedAt: now,
1457
+ active: true
1458
+ };
1459
+ const existing = listPlans();
1460
+ for (const p3 of existing) {
1461
+ if (p3.active) {
1462
+ p3.active = false;
1463
+ p3.updatedAt = now;
1464
+ savePlan(p3);
1465
+ }
1466
+ }
1467
+ savePlan(plan);
1468
+ return plan;
1469
+ }
1470
+ function savePlan(plan) {
1471
+ const fp = planPath(plan.name);
1472
+ fs7.writeFileSync(fp, serializePlan(plan), "utf-8");
1473
+ }
1474
+ function loadPlan(name) {
1475
+ const fp = planPath(name);
1476
+ if (!fs7.existsSync(fp)) return null;
1477
+ const content = fs7.readFileSync(fp, "utf-8");
1478
+ return parsePlan(content, fp);
1479
+ }
1480
+ function listPlans() {
1481
+ const dir = getPlansDir();
1482
+ if (!fs7.existsSync(dir)) return [];
1483
+ const plans = [];
1484
+ for (const file of fs7.readdirSync(dir)) {
1485
+ if (!file.endsWith(".md")) continue;
1486
+ const fp = path7.join(dir, file);
1487
+ const content = fs7.readFileSync(fp, "utf-8");
1488
+ const plan = parsePlan(content, fp);
1489
+ if (plan) plans.push(plan);
1490
+ }
1491
+ return plans;
1492
+ }
1493
+ function getActivePlan() {
1494
+ const plans = listPlans();
1495
+ return plans.find((p3) => p3.active) || null;
1496
+ }
1497
+ function markStepDone(plan, stepIndex) {
1498
+ if (stepIndex < 0 || stepIndex >= plan.steps.length) return false;
1499
+ plan.steps[stepIndex].done = true;
1500
+ plan.updatedAt = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1501
+ savePlan(plan);
1502
+ return true;
1503
+ }
1504
+ function markStepUndone(plan, stepIndex) {
1505
+ if (stepIndex < 0 || stepIndex >= plan.steps.length) return false;
1506
+ plan.steps[stepIndex].done = false;
1507
+ plan.updatedAt = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1508
+ savePlan(plan);
1509
+ return true;
1510
+ }
1511
+ function setActivePlan(name) {
1512
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1513
+ const plans = listPlans();
1514
+ for (const p3 of plans) {
1515
+ if (p3.active) {
1516
+ p3.active = false;
1517
+ p3.updatedAt = now;
1518
+ savePlan(p3);
1519
+ }
1520
+ }
1521
+ const target = loadPlan(name);
1522
+ if (!target) return null;
1523
+ target.active = true;
1524
+ target.updatedAt = now;
1525
+ savePlan(target);
1526
+ return target;
1527
+ }
1528
+ function formatPlan(plan) {
1529
+ const total = plan.steps.length;
1530
+ const done = plan.steps.filter((s) => s.done).length;
1531
+ const pct = total > 0 ? Math.round(done / total * 100) : 0;
1532
+ const bar = progressBar(pct);
1533
+ const lines = [];
1534
+ lines.push(`Plan: ${plan.name} ${plan.active ? "(active)" : "(inactive)"}`);
1535
+ lines.push(`Goal: ${plan.goal}`);
1536
+ lines.push(`Progress: ${bar} ${done}/${total} (${pct}%)`);
1537
+ lines.push("");
1538
+ for (let i = 0; i < plan.steps.length; i++) {
1539
+ const step = plan.steps[i];
1540
+ const marker = step.done ? "\u2713" : " ";
1541
+ const num = String(i + 1).padStart(2, " ");
1542
+ lines.push(` ${num}. [${marker}] ${step.text}`);
1543
+ }
1544
+ if (done === total && total > 0) {
1545
+ lines.push("\n All steps complete!");
1546
+ } else {
1547
+ const next = plan.steps.findIndex((s) => !s.done);
1548
+ if (next >= 0) {
1549
+ lines.push(`
1550
+ Next: Step ${next + 1} \u2014 ${plan.steps[next].text}`);
1551
+ }
1552
+ }
1553
+ return lines.join("\n");
1554
+ }
1555
+ function formatPlanForPrompt(plan) {
1556
+ const total = plan.steps.length;
1557
+ const done = plan.steps.filter((s) => s.done).length;
1558
+ const lines = [];
1559
+ lines.push(`<active-plan name="${plan.name}" progress="${done}/${total}">`);
1560
+ lines.push(`Goal: ${plan.goal}`);
1561
+ lines.push("");
1562
+ for (let i = 0; i < plan.steps.length; i++) {
1563
+ const step = plan.steps[i];
1564
+ lines.push(`- [${step.done ? "x" : " "}] Step ${i + 1}: ${step.text}`);
1565
+ }
1566
+ const next = plan.steps.findIndex((s) => !s.done);
1567
+ if (next >= 0) {
1568
+ lines.push("");
1569
+ lines.push(`Current focus: Step ${next + 1} \u2014 ${plan.steps[next].text}`);
1570
+ lines.push("After completing the current step, remind the user to mark it done with /plan done and suggest committing their work.");
1571
+ }
1572
+ lines.push("</active-plan>");
1573
+ return lines.join("\n");
1574
+ }
1575
+ function progressBar(pct) {
1576
+ const filled = Math.round(pct / 5);
1577
+ const empty = 20 - filled;
1578
+ return `[${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}]`;
1579
+ }
1580
+
674
1581
  // src/commands.ts
675
1582
  function readEcosystemFile(filePath, label) {
676
- if (!fs5.existsSync(filePath)) {
677
- return pc.dim(`No ${label} file found at ${filePath}`);
1583
+ if (!fs8.existsSync(filePath)) {
1584
+ return pc3.dim(`No ${label} file found at ${filePath}`);
678
1585
  }
679
- return fs5.readFileSync(filePath, "utf-8").trim();
1586
+ return fs8.readFileSync(filePath, "utf-8").trim();
680
1587
  }
681
1588
  function parseCommand(input) {
682
1589
  const trimmed = input.trim();
@@ -688,25 +1595,25 @@ function parseCommand(input) {
688
1595
  }
689
1596
  async function mcpWrite(ctx, layer, tool, args) {
690
1597
  if (!ctx.mcpManager) {
691
- return pc.red(`Cannot modify ${layer}: aman-mcp not connected. Start it with: npx @aman_asmuei/aman-mcp`);
1598
+ return pc3.red(`Cannot modify ${layer}: aman-mcp not connected. Start it with: npx @aman_asmuei/aman-mcp`);
692
1599
  }
693
1600
  const result = await ctx.mcpManager.callTool(tool, args);
694
1601
  if (result.startsWith("Error")) {
695
- return pc.red(result);
1602
+ return pc3.red(result);
696
1603
  }
697
- return pc.green(result);
1604
+ return pc3.green(result);
698
1605
  }
699
1606
  async function handleIdentityCommand(action, args, ctx) {
700
- const home2 = os5.homedir();
1607
+ const home2 = os8.homedir();
701
1608
  if (!action) {
702
- const content = readEcosystemFile(path5.join(home2, ".acore", "core.md"), "identity (acore)");
1609
+ const content = readEcosystemFile(path8.join(home2, ".acore", "core.md"), "identity (acore)");
703
1610
  return { handled: true, output: content };
704
1611
  }
705
1612
  if (action === "update") {
706
1613
  if (args.length === 0) {
707
1614
  return {
708
1615
  handled: true,
709
- output: pc.yellow("Usage: /identity update <section>\nTip: describe changes in natural language and the AI will update via MCP.")
1616
+ output: pc3.yellow("Usage: /identity update <section>\nTip: describe changes in natural language and the AI will update via MCP.")
710
1617
  };
711
1618
  }
712
1619
  const section = args[0];
@@ -714,23 +1621,23 @@ async function handleIdentityCommand(action, args, ctx) {
714
1621
  if (!content) {
715
1622
  return {
716
1623
  handled: true,
717
- output: pc.yellow("Usage: /identity update <section> <new content...>\nExample: /identity update Personality Warm, curious, and direct.")
1624
+ output: pc3.yellow("Usage: /identity update <section> <new content...>\nExample: /identity update Personality Warm, curious, and direct.")
718
1625
  };
719
1626
  }
720
1627
  const output = await mcpWrite(ctx, "identity", "identity_update_section", { section, content });
721
1628
  return { handled: true, output };
722
1629
  }
723
- return { handled: true, output: pc.yellow(`Unknown action: /identity ${action}. Use /identity or /identity update <section>.`) };
1630
+ return { handled: true, output: pc3.yellow(`Unknown action: /identity ${action}. Use /identity or /identity update <section>.`) };
724
1631
  }
725
1632
  async function handleRulesCommand(action, args, ctx) {
726
- const home2 = os5.homedir();
1633
+ const home2 = os8.homedir();
727
1634
  if (!action) {
728
- const content = readEcosystemFile(path5.join(home2, ".arules", "rules.md"), "guardrails (arules)");
1635
+ const content = readEcosystemFile(path8.join(home2, ".arules", "rules.md"), "guardrails (arules)");
729
1636
  return { handled: true, output: content };
730
1637
  }
731
1638
  if (action === "add") {
732
1639
  if (args.length < 2) {
733
- return { handled: true, output: pc.yellow("Usage: /rules add <category> <rule text...>") };
1640
+ return { handled: true, output: pc3.yellow("Usage: /rules add <category> <rule text...>") };
734
1641
  }
735
1642
  const category = args[0];
736
1643
  const rule = args.slice(1).join(" ");
@@ -739,41 +1646,41 @@ async function handleRulesCommand(action, args, ctx) {
739
1646
  }
740
1647
  if (action === "remove") {
741
1648
  if (args.length < 2) {
742
- return { handled: true, output: pc.yellow("Usage: /rules remove <category> <index>") };
1649
+ return { handled: true, output: pc3.yellow("Usage: /rules remove <category> <index>") };
743
1650
  }
744
1651
  const output = await mcpWrite(ctx, "rules", "rules_remove", { category: args[0], index: parseInt(args[1], 10) });
745
1652
  return { handled: true, output };
746
1653
  }
747
1654
  if (action === "toggle") {
748
1655
  if (args.length < 2) {
749
- return { handled: true, output: pc.yellow("Usage: /rules toggle <category> <index>") };
1656
+ return { handled: true, output: pc3.yellow("Usage: /rules toggle <category> <index>") };
750
1657
  }
751
1658
  const output = await mcpWrite(ctx, "rules", "rules_toggle", { category: args[0], index: parseInt(args[1], 10) });
752
1659
  return { handled: true, output };
753
1660
  }
754
- return { handled: true, output: pc.yellow(`Unknown action: /rules ${action}. Use /rules [add|remove|toggle].`) };
1661
+ return { handled: true, output: pc3.yellow(`Unknown action: /rules ${action}. Use /rules [add|remove|toggle].`) };
755
1662
  }
756
1663
  async function handleWorkflowsCommand(action, args, ctx) {
757
- const home2 = os5.homedir();
1664
+ const home2 = os8.homedir();
758
1665
  if (!action) {
759
- const content = readEcosystemFile(path5.join(home2, ".aflow", "flow.md"), "workflows (aflow)");
1666
+ const content = readEcosystemFile(path8.join(home2, ".aflow", "flow.md"), "workflows (aflow)");
760
1667
  return { handled: true, output: content };
761
1668
  }
762
1669
  if (action === "add") {
763
1670
  if (args.length < 1) {
764
- return { handled: true, output: pc.yellow("Usage: /workflows add <name>") };
1671
+ return { handled: true, output: pc3.yellow("Usage: /workflows add <name>") };
765
1672
  }
766
1673
  const output = await mcpWrite(ctx, "workflows", "workflow_add", { name: args.join(" ") });
767
1674
  return { handled: true, output };
768
1675
  }
769
1676
  if (action === "remove") {
770
1677
  if (args.length < 1) {
771
- return { handled: true, output: pc.yellow("Usage: /workflows remove <name>") };
1678
+ return { handled: true, output: pc3.yellow("Usage: /workflows remove <name>") };
772
1679
  }
773
1680
  const output = await mcpWrite(ctx, "workflows", "workflow_remove", { name: args.join(" ") });
774
1681
  return { handled: true, output };
775
1682
  }
776
- return { handled: true, output: pc.yellow(`Unknown action: /workflows ${action}. Use /workflows [add|remove].`) };
1683
+ return { handled: true, output: pc3.yellow(`Unknown action: /workflows ${action}. Use /workflows [add|remove].`) };
777
1684
  }
778
1685
  var AKIT_REGISTRY = [
779
1686
  { name: "web-search", description: "Search the web for current information", category: "search", mcp: { package: "@anthropic/web-search", command: "npx", args: ["-y", "@anthropic/web-search"] } },
@@ -790,42 +1697,43 @@ var AKIT_REGISTRY = [
790
1697
  { name: "docker", description: "Manage Docker containers", category: "automation", mcp: { package: "@modelcontextprotocol/server-docker", command: "npx", args: ["-y", "@modelcontextprotocol/server-docker"] } },
791
1698
  { 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" },
792
1699
  { 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" },
1700
+ { 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" },
793
1701
  { name: "memory", description: "Persistent AI memory via amem", category: "memory", mcp: { package: "@aman_asmuei/amem", command: "npx", args: ["-y", "@aman_asmuei/amem"] } },
794
1702
  { 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" }
795
1703
  ];
796
1704
  function loadAkitInstalled() {
797
- const filePath = path5.join(os5.homedir(), ".akit", "installed.json");
798
- if (!fs5.existsSync(filePath)) return [];
1705
+ const filePath = path8.join(os8.homedir(), ".akit", "installed.json");
1706
+ if (!fs8.existsSync(filePath)) return [];
799
1707
  try {
800
- return JSON.parse(fs5.readFileSync(filePath, "utf-8"));
1708
+ return JSON.parse(fs8.readFileSync(filePath, "utf-8"));
801
1709
  } catch {
802
1710
  return [];
803
1711
  }
804
1712
  }
805
1713
  function saveAkitInstalled(tools) {
806
- const dir = path5.join(os5.homedir(), ".akit");
807
- fs5.mkdirSync(dir, { recursive: true });
808
- fs5.writeFileSync(path5.join(dir, "installed.json"), JSON.stringify(tools, null, 2) + "\n", "utf-8");
1714
+ const dir = path8.join(os8.homedir(), ".akit");
1715
+ fs8.mkdirSync(dir, { recursive: true });
1716
+ fs8.writeFileSync(path8.join(dir, "installed.json"), JSON.stringify(tools, null, 2) + "\n", "utf-8");
809
1717
  }
810
1718
  function addToAmanAgentConfig(name, mcpConfig) {
811
- const configPath = path5.join(os5.homedir(), ".aman-agent", "config.json");
812
- if (!fs5.existsSync(configPath)) return;
1719
+ const configPath = path8.join(os8.homedir(), ".aman-agent", "config.json");
1720
+ if (!fs8.existsSync(configPath)) return;
813
1721
  try {
814
- const config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
1722
+ const config = JSON.parse(fs8.readFileSync(configPath, "utf-8"));
815
1723
  if (!config.mcpServers) config.mcpServers = {};
816
1724
  config.mcpServers[name] = mcpConfig;
817
- fs5.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1725
+ fs8.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
818
1726
  } catch {
819
1727
  }
820
1728
  }
821
1729
  function removeFromAmanAgentConfig(name) {
822
- const configPath = path5.join(os5.homedir(), ".aman-agent", "config.json");
823
- if (!fs5.existsSync(configPath)) return;
1730
+ const configPath = path8.join(os8.homedir(), ".aman-agent", "config.json");
1731
+ if (!fs8.existsSync(configPath)) return;
824
1732
  try {
825
- const config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
1733
+ const config = JSON.parse(fs8.readFileSync(configPath, "utf-8"));
826
1734
  if (config.mcpServers) {
827
1735
  delete config.mcpServers[name];
828
- fs5.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1736
+ fs8.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
829
1737
  }
830
1738
  } catch {
831
1739
  }
@@ -837,27 +1745,27 @@ function handleAkitCommand(action, args) {
837
1745
  const available2 = AKIT_REGISTRY.filter((t) => !installedNames.has(t.name));
838
1746
  if (args.length < 1) {
839
1747
  if (available2.length === 0) {
840
- return { handled: true, output: pc.green("All tools are installed!") };
1748
+ return { handled: true, output: pc3.green("All tools are installed!") };
841
1749
  }
842
- const lines3 = [pc.bold("Select a tool to install:"), ""];
1750
+ const lines3 = [pc3.bold("Select a tool to install:"), ""];
843
1751
  available2.forEach((tool2, i) => {
844
- const num2 = pc.cyan(String(i + 1).padStart(2));
845
- lines3.push(` ${num2} ${tool2.name.padEnd(16)} ${pc.dim(tool2.description)}`);
1752
+ const num2 = pc3.cyan(String(i + 1).padStart(2));
1753
+ lines3.push(` ${num2} ${tool2.name.padEnd(16)} ${pc3.dim(tool2.description)}`);
846
1754
  });
847
1755
  lines3.push("");
848
- lines3.push(` Type: ${pc.cyan("/akit add <number>")} or ${pc.cyan("/akit add <name>")}`);
849
- lines3.push(` Custom: ${pc.cyan("/akit add custom <name> <command> <args...>")}`);
1756
+ lines3.push(` Type: ${pc3.cyan("/akit add <number>")} or ${pc3.cyan("/akit add <name>")}`);
1757
+ lines3.push(` Custom: ${pc3.cyan("/akit add custom <name> <command> <args...>")}`);
850
1758
  return { handled: true, output: lines3.join("\n") };
851
1759
  }
852
1760
  if (args[0].toLowerCase() === "custom") {
853
1761
  if (args.length < 3) {
854
- return { handled: true, output: pc.yellow("Usage: /akit add custom <name> <command> <args...>\nExample: /akit add custom my-tool npx -y @org/my-mcp-server") };
1762
+ return { handled: true, output: pc3.yellow("Usage: /akit add custom <name> <command> <args...>\nExample: /akit add custom my-tool npx -y @org/my-mcp-server") };
855
1763
  }
856
1764
  const customName = args[1];
857
1765
  const customCommand = args[2];
858
1766
  const customArgs = args.slice(3);
859
1767
  if (installedNames.has(customName)) {
860
- return { handled: true, output: pc.yellow(`${customName} is already installed.`) };
1768
+ return { handled: true, output: pc3.yellow(`${customName} is already installed.`) };
861
1769
  }
862
1770
  installed.push({
863
1771
  name: customName,
@@ -869,8 +1777,8 @@ function handleAkitCommand(action, args) {
869
1777
  return {
870
1778
  handled: true,
871
1779
  output: [
872
- pc.green(`\u2713 Added ${pc.bold(customName)}`) + pc.dim(` (custom MCP: ${customCommand} ${customArgs.join(" ")})`),
873
- pc.dim(" Restart aman-agent to load the new tool.")
1780
+ pc3.green(`\u2713 Added ${pc3.bold(customName)}`) + pc3.dim(` (custom MCP: ${customCommand} ${customArgs.join(" ")})`),
1781
+ pc3.dim(" Restart aman-agent to load the new tool.")
874
1782
  ].join("\n")
875
1783
  };
876
1784
  }
@@ -886,13 +1794,13 @@ function handleAkitCommand(action, args) {
886
1794
  return {
887
1795
  handled: true,
888
1796
  output: [
889
- pc.red(`Tool "${input}" not found.`),
890
- `Type ${pc.cyan("/akit add")} to see available tools.`
1797
+ pc3.red(`Tool "${input}" not found.`),
1798
+ `Type ${pc3.cyan("/akit add")} to see available tools.`
891
1799
  ].join("\n")
892
1800
  };
893
1801
  }
894
1802
  if (installedNames.has(tool.name)) {
895
- return { handled: true, output: pc.yellow(`${tool.name} is already installed.`) };
1803
+ return { handled: true, output: pc3.yellow(`${tool.name} is already installed.`) };
896
1804
  }
897
1805
  installed.push({
898
1806
  name: tool.name,
@@ -907,139 +1815,139 @@ function handleAkitCommand(action, args) {
907
1815
  });
908
1816
  }
909
1817
  const lines2 = [
910
- pc.green(`\u2713 Added ${pc.bold(tool.name)}`) + (tool.mcp ? pc.dim(` (MCP: ${tool.mcp.package})`) : "")
1818
+ pc3.green(`\u2713 Added ${pc3.bold(tool.name)}`) + (tool.mcp ? pc3.dim(` (MCP: ${tool.mcp.package})`) : "")
911
1819
  ];
912
1820
  if (tool.envHint) {
913
- lines2.push(pc.yellow(` \u26A0 ${tool.envHint}`));
1821
+ lines2.push(pc3.yellow(` \u26A0 ${tool.envHint}`));
914
1822
  }
915
1823
  if (tool.mcp) {
916
- lines2.push(pc.dim(" Restart aman-agent to load the new tool."));
1824
+ lines2.push(pc3.dim(" Restart aman-agent to load the new tool."));
917
1825
  }
918
1826
  return { handled: true, output: lines2.join("\n") };
919
1827
  }
920
1828
  if (action === "remove") {
921
1829
  if (args.length < 1) {
922
- return { handled: true, output: pc.yellow("Usage: /akit remove <tool>") };
1830
+ return { handled: true, output: pc3.yellow("Usage: /akit remove <tool>") };
923
1831
  }
924
1832
  const toolName = args[0].toLowerCase();
925
1833
  if (!installedNames.has(toolName)) {
926
- return { handled: true, output: pc.red(`${toolName} is not installed.`) };
1834
+ return { handled: true, output: pc3.red(`${toolName} is not installed.`) };
927
1835
  }
928
1836
  const updated = installed.filter((t) => t.name !== toolName);
929
1837
  saveAkitInstalled(updated);
930
1838
  removeFromAmanAgentConfig(toolName);
931
1839
  return {
932
1840
  handled: true,
933
- output: pc.green(`\u2713 Removed ${pc.bold(toolName)}`) + pc.dim(" (restart aman-agent to apply)")
1841
+ output: pc3.green(`\u2713 Removed ${pc3.bold(toolName)}`) + pc3.dim(" (restart aman-agent to apply)")
934
1842
  };
935
1843
  }
936
1844
  if (action === "help") {
937
1845
  return {
938
1846
  handled: true,
939
1847
  output: [
940
- pc.bold("akit \u2014 Tool Management"),
1848
+ pc3.bold("akit \u2014 Tool Management"),
941
1849
  "",
942
- ` ${pc.cyan("/akit")} List installed & available tools`,
943
- ` ${pc.cyan("/akit add <tool>")} Install a tool`,
944
- ` ${pc.cyan("/akit remove <tool>")} Uninstall a tool`
1850
+ ` ${pc3.cyan("/akit")} List installed & available tools`,
1851
+ ` ${pc3.cyan("/akit add <tool>")} Install a tool`,
1852
+ ` ${pc3.cyan("/akit remove <tool>")} Uninstall a tool`
945
1853
  ].join("\n")
946
1854
  };
947
1855
  }
948
1856
  const available = AKIT_REGISTRY.filter((t) => !installedNames.has(t.name));
949
- const lines = [pc.bold("akit \u2014 AI Tool Manager"), ""];
1857
+ const lines = [pc3.bold("akit \u2014 AI Tool Manager"), ""];
950
1858
  if (installed.length > 0) {
951
- lines.push(` ${pc.bold(`Installed (${installed.length})`)}`);
1859
+ lines.push(` ${pc3.bold(`Installed (${installed.length})`)}`);
952
1860
  for (const tool of installed) {
953
- const mcp = tool.mcpConfigured ? pc.green("MCP") : pc.dim("manual");
954
- lines.push(` ${pc.green("\u25CF")} ${pc.bold(tool.name.padEnd(16))} ${mcp} ${pc.dim(tool.installedAt)}`);
1861
+ const mcp = tool.mcpConfigured ? pc3.green("MCP") : pc3.dim("manual");
1862
+ lines.push(` ${pc3.green("\u25CF")} ${pc3.bold(tool.name.padEnd(16))} ${mcp} ${pc3.dim(tool.installedAt)}`);
955
1863
  }
956
1864
  lines.push("");
957
1865
  }
958
1866
  if (available.length > 0) {
959
- lines.push(` ${pc.bold(`Available (${available.length})`)}`);
1867
+ lines.push(` ${pc3.bold(`Available (${available.length})`)}`);
960
1868
  const byCategory = /* @__PURE__ */ new Map();
961
1869
  for (const tool of available) {
962
1870
  if (!byCategory.has(tool.category)) byCategory.set(tool.category, []);
963
1871
  byCategory.get(tool.category).push(tool);
964
1872
  }
965
1873
  for (const [category, tools] of byCategory) {
966
- lines.push(` ${pc.dim(category)}`);
1874
+ lines.push(` ${pc3.dim(category)}`);
967
1875
  for (const tool of tools) {
968
- lines.push(` ${pc.dim("\u25CB")} ${tool.name.padEnd(16)} ${pc.dim(tool.description)}`);
1876
+ lines.push(` ${pc3.dim("\u25CB")} ${tool.name.padEnd(16)} ${pc3.dim(tool.description)}`);
969
1877
  }
970
1878
  }
971
1879
  lines.push("");
972
1880
  }
973
- lines.push(` ${pc.cyan("/akit add <tool>")} Install a tool`);
974
- lines.push(` ${pc.cyan("/akit remove <tool>")} Uninstall a tool`);
1881
+ lines.push(` ${pc3.cyan("/akit add <tool>")} Install a tool`);
1882
+ lines.push(` ${pc3.cyan("/akit remove <tool>")} Uninstall a tool`);
975
1883
  return { handled: true, output: lines.join("\n") };
976
1884
  }
977
1885
  async function handleSkillsCommand(action, args, ctx) {
978
- const home2 = os5.homedir();
1886
+ const home2 = os8.homedir();
979
1887
  if (!action) {
980
- const content = readEcosystemFile(path5.join(home2, ".askill", "skills.md"), "skills (askill)");
1888
+ const content = readEcosystemFile(path8.join(home2, ".askill", "skills.md"), "skills (askill)");
981
1889
  return { handled: true, output: content };
982
1890
  }
983
1891
  if (action === "install") {
984
1892
  if (args.length < 1) {
985
- return { handled: true, output: pc.yellow("Usage: /skills install <name>") };
1893
+ return { handled: true, output: pc3.yellow("Usage: /skills install <name>") };
986
1894
  }
987
1895
  const output = await mcpWrite(ctx, "skills", "skill_install", { name: args.join(" ") });
988
1896
  return { handled: true, output };
989
1897
  }
990
1898
  if (action === "uninstall") {
991
1899
  if (args.length < 1) {
992
- return { handled: true, output: pc.yellow("Usage: /skills uninstall <name>") };
1900
+ return { handled: true, output: pc3.yellow("Usage: /skills uninstall <name>") };
993
1901
  }
994
1902
  const output = await mcpWrite(ctx, "skills", "skill_uninstall", { name: args.join(" ") });
995
1903
  return { handled: true, output };
996
1904
  }
997
- return { handled: true, output: pc.yellow(`Unknown action: /skills ${action}. Use /skills [install|uninstall].`) };
1905
+ return { handled: true, output: pc3.yellow(`Unknown action: /skills ${action}. Use /skills [install|uninstall].`) };
998
1906
  }
999
1907
  async function handleEvalCommand(action, args, ctx) {
1000
- const home2 = os5.homedir();
1908
+ const home2 = os8.homedir();
1001
1909
  if (!action) {
1002
- const content = readEcosystemFile(path5.join(home2, ".aeval", "eval.md"), "evaluation (aeval)");
1910
+ const content = readEcosystemFile(path8.join(home2, ".aeval", "eval.md"), "evaluation (aeval)");
1003
1911
  return { handled: true, output: content };
1004
1912
  }
1005
1913
  if (action === "milestone") {
1006
1914
  if (args.length < 1) {
1007
- return { handled: true, output: pc.yellow("Usage: /eval milestone <text...>") };
1915
+ return { handled: true, output: pc3.yellow("Usage: /eval milestone <text...>") };
1008
1916
  }
1009
1917
  const text2 = args.join(" ");
1010
1918
  const output = await mcpWrite(ctx, "eval", "eval_milestone", { text: text2 });
1011
1919
  return { handled: true, output };
1012
1920
  }
1013
- return { handled: true, output: pc.yellow(`Unknown action: /eval ${action}. Use /eval or /eval milestone <text>.`) };
1921
+ return { handled: true, output: pc3.yellow(`Unknown action: /eval ${action}. Use /eval or /eval milestone <text>.`) };
1014
1922
  }
1015
1923
  async function handleMemoryCommand(action, args, ctx) {
1016
1924
  if (!action) {
1017
1925
  if (!ctx.mcpManager) {
1018
1926
  return {
1019
1927
  handled: true,
1020
- output: pc.red("Memory not available: aman-mcp not connected. Start it with: npx @aman_asmuei/aman-mcp")
1928
+ output: pc3.red("Memory not available: aman-mcp not connected. Start it with: npx @aman_asmuei/aman-mcp")
1021
1929
  };
1022
1930
  }
1023
1931
  const result = await ctx.mcpManager.callTool("memory_context", { topic: "recent context" });
1024
1932
  if (result.startsWith("Error")) {
1025
- return { handled: true, output: pc.red(result) };
1933
+ return { handled: true, output: pc3.red(result) };
1026
1934
  }
1027
1935
  return { handled: true, output: result };
1028
1936
  }
1029
1937
  if (action && !["search", "clear", "timeline"].includes(action)) {
1030
1938
  if (!ctx.mcpManager) {
1031
- return { handled: true, output: pc.red("Memory not available: MCP not connected.") };
1939
+ return { handled: true, output: pc3.red("Memory not available: MCP not connected.") };
1032
1940
  }
1033
1941
  const topic = [action, ...args].join(" ");
1034
1942
  const result = await ctx.mcpManager.callTool("memory_context", { topic });
1035
1943
  if (result.startsWith("Error")) {
1036
- return { handled: true, output: pc.red(result) };
1944
+ return { handled: true, output: pc3.red(result) };
1037
1945
  }
1038
1946
  return { handled: true, output: result };
1039
1947
  }
1040
1948
  if (action === "search") {
1041
1949
  if (args.length < 1) {
1042
- return { handled: true, output: pc.yellow("Usage: /memory search <query...>") };
1950
+ return { handled: true, output: pc3.yellow("Usage: /memory search <query...>") };
1043
1951
  }
1044
1952
  const query = args.join(" ");
1045
1953
  const output = await mcpWrite(ctx, "memory", "memory_recall", { query });
@@ -1047,19 +1955,19 @@ async function handleMemoryCommand(action, args, ctx) {
1047
1955
  }
1048
1956
  if (action === "clear") {
1049
1957
  if (args.length < 1) {
1050
- return { handled: true, output: pc.yellow("Usage: /memory clear <category>") };
1958
+ return { handled: true, output: pc3.yellow("Usage: /memory clear <category>") };
1051
1959
  }
1052
1960
  const output = await mcpWrite(ctx, "memory", "memory_forget", { category: args[0] });
1053
1961
  return { handled: true, output };
1054
1962
  }
1055
1963
  if (action === "timeline") {
1056
1964
  if (!ctx.mcpManager) {
1057
- return { handled: true, output: pc.red("Memory not available: MCP not connected.") };
1965
+ return { handled: true, output: pc3.red("Memory not available: MCP not connected.") };
1058
1966
  }
1059
1967
  try {
1060
1968
  const result = await ctx.mcpManager.callTool("memory_recall", { query: "*", limit: 500 });
1061
1969
  if (result.startsWith("Error") || result.includes("No memories found")) {
1062
- return { handled: true, output: pc.dim("No memories yet. Start chatting and I'll remember what matters.") };
1970
+ return { handled: true, output: pc3.dim("No memories yet. Start chatting and I'll remember what matters.") };
1063
1971
  }
1064
1972
  try {
1065
1973
  const memories = JSON.parse(result);
@@ -1071,7 +1979,7 @@ async function handleMemoryCommand(action, args, ctx) {
1071
1979
  }
1072
1980
  const maxCount = Math.max(...byDate.values());
1073
1981
  const barWidth = 10;
1074
- const lines = [pc.bold("Memory Timeline:"), ""];
1982
+ const lines = [pc3.bold("Memory Timeline:"), ""];
1075
1983
  for (const [date, count] of byDate) {
1076
1984
  const filled = Math.round(count / maxCount * barWidth);
1077
1985
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(barWidth - filled);
@@ -1098,64 +2006,64 @@ async function handleMemoryCommand(action, args, ctx) {
1098
2006
  const lineCount = result.split("\n").filter((l) => l.trim()).length;
1099
2007
  return { handled: true, output: `Total memories: ~${lineCount} entries.` };
1100
2008
  } catch {
1101
- return { handled: true, output: pc.red("Failed to retrieve memory timeline.") };
2009
+ return { handled: true, output: pc3.red("Failed to retrieve memory timeline.") };
1102
2010
  }
1103
2011
  }
1104
- return { handled: true, output: pc.yellow(`Unknown action: /memory ${action}. Use /memory [search|clear|timeline].`) };
2012
+ return { handled: true, output: pc3.yellow(`Unknown action: /memory ${action}. Use /memory [search|clear|timeline].`) };
1105
2013
  }
1106
2014
  function handleStatusCommand(ctx) {
1107
2015
  const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
1108
2016
  const amemConnected = mcpToolCount > 0;
1109
2017
  const status = getEcosystemStatus(mcpToolCount, amemConnected);
1110
- const lines = [pc.bold("Aman Ecosystem Dashboard"), ""];
2018
+ const lines = [pc3.bold("Aman Ecosystem Dashboard"), ""];
1111
2019
  for (const layer of status.layers) {
1112
- const icon = layer.exists ? pc.green("\u25CF") : pc.dim("\u25CB");
1113
- const name = pc.bold(layer.name.padEnd(12));
1114
- const summary = layer.exists ? layer.summary : pc.dim("not configured");
2020
+ const icon = layer.exists ? pc3.green("\u25CF") : pc3.dim("\u25CB");
2021
+ const name = pc3.bold(layer.name.padEnd(12));
2022
+ const summary = layer.exists ? layer.summary : pc3.dim("not configured");
1115
2023
  lines.push(` ${icon} ${name} ${summary}`);
1116
2024
  }
1117
2025
  lines.push("");
1118
- 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")}`);
1119
- lines.push(` ${status.amemConnected ? pc.green("\u25CF") : pc.dim("\u25CB")} ${pc.bold("Memory".padEnd(12))} ${status.amemConnected ? "connected" : pc.dim("not connected")}`);
2026
+ lines.push(` ${status.mcpConnected ? pc3.green("\u25CF") : pc3.dim("\u25CB")} ${pc3.bold("MCP".padEnd(12))} ${status.mcpConnected ? `${status.mcpToolCount} tools available` : pc3.dim("not connected")}`);
2027
+ lines.push(` ${status.amemConnected ? pc3.green("\u25CF") : pc3.dim("\u25CB")} ${pc3.bold("Memory".padEnd(12))} ${status.amemConnected ? "connected" : pc3.dim("not connected")}`);
1120
2028
  return { handled: true, output: lines.join("\n") };
1121
2029
  }
1122
2030
  function handleDoctorCommand(ctx) {
1123
2031
  const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
1124
2032
  const amemConnected = mcpToolCount > 0;
1125
2033
  const status = getEcosystemStatus(mcpToolCount, amemConnected);
1126
- const lines = [pc.bold("Aman Health Check"), ""];
2034
+ const lines = [pc3.bold("Aman Health Check"), ""];
1127
2035
  let healthy = 0;
1128
2036
  let fixes = 0;
1129
2037
  let suggestions = 0;
1130
2038
  for (const layer of status.layers) {
1131
2039
  if (layer.exists) {
1132
- lines.push(` ${pc.green("\u2713")} ${layer.name.padEnd(12)} ${pc.green(layer.summary)}`);
2040
+ lines.push(` ${pc3.green("\u2713")} ${layer.name.padEnd(12)} ${pc3.green(layer.summary)}`);
1133
2041
  healthy++;
1134
2042
  } else {
1135
2043
  const isRequired = ["identity", "rules"].includes(layer.name.toLowerCase());
1136
2044
  if (isRequired) {
1137
- lines.push(` ${pc.red("\u2717")} ${layer.name.padEnd(12)} ${pc.red("missing")}`);
1138
- lines.push(` ${pc.dim("\u2192 Fix: aman-agent init")}`);
2045
+ lines.push(` ${pc3.red("\u2717")} ${layer.name.padEnd(12)} ${pc3.red("missing")}`);
2046
+ lines.push(` ${pc3.dim("\u2192 Fix: aman-agent init")}`);
1139
2047
  fixes++;
1140
2048
  } else {
1141
- lines.push(` ${pc.yellow("\u26A0")} ${layer.name.padEnd(12)} ${pc.yellow("empty")}`);
2049
+ lines.push(` ${pc3.yellow("\u26A0")} ${layer.name.padEnd(12)} ${pc3.yellow("empty")}`);
1142
2050
  const cmd = layer.name.toLowerCase() === "workflows" ? "/workflows add <name>" : layer.name.toLowerCase() === "tools" ? "/tools add <name> <type> <desc>" : layer.name.toLowerCase() === "skills" ? "/skills install <name>" : "";
1143
- if (cmd) lines.push(` ${pc.dim(`\u2192 Add with ${cmd}`)}`);
2051
+ if (cmd) lines.push(` ${pc3.dim(`\u2192 Add with ${cmd}`)}`);
1144
2052
  suggestions++;
1145
2053
  }
1146
2054
  }
1147
2055
  }
1148
2056
  lines.push("");
1149
- lines.push(` ${status.mcpConnected ? pc.green("\u2713") : pc.red("\u2717")} ${"MCP".padEnd(12)} ${status.mcpConnected ? pc.green(`${status.mcpToolCount} tools`) : pc.red("not connected")}`);
2057
+ lines.push(` ${status.mcpConnected ? pc3.green("\u2713") : pc3.red("\u2717")} ${"MCP".padEnd(12)} ${status.mcpConnected ? pc3.green(`${status.mcpToolCount} tools`) : pc3.red("not connected")}`);
1150
2058
  if (!status.mcpConnected) {
1151
- lines.push(` ${pc.dim("\u2192 Fix: ensure npx is available and network is connected")}`);
2059
+ lines.push(` ${pc3.dim("\u2192 Fix: ensure npx is available and network is connected")}`);
1152
2060
  fixes++;
1153
2061
  } else {
1154
2062
  healthy++;
1155
2063
  }
1156
- lines.push(` ${status.amemConnected ? pc.green("\u2713") : pc.red("\u2717")} ${"Memory".padEnd(12)} ${status.amemConnected ? pc.green("connected") : pc.red("not connected")}`);
2064
+ lines.push(` ${status.amemConnected ? pc3.green("\u2713") : pc3.red("\u2717")} ${"Memory".padEnd(12)} ${status.amemConnected ? pc3.green("connected") : pc3.red("not connected")}`);
1157
2065
  if (!status.amemConnected) {
1158
- lines.push(` ${pc.dim("\u2192 Fix: npx @aman_asmuei/amem")}`);
2066
+ lines.push(` ${pc3.dim("\u2192 Fix: npx @aman_asmuei/amem")}`);
1159
2067
  fixes++;
1160
2068
  } else {
1161
2069
  healthy++;
@@ -1169,26 +2077,26 @@ function handleHelp() {
1169
2077
  return {
1170
2078
  handled: true,
1171
2079
  output: [
1172
- pc.bold("Commands:"),
1173
- ` ${pc.cyan("/help")} Show this help`,
1174
- ` ${pc.cyan("/identity")} View identity [update <section>]`,
1175
- ` ${pc.cyan("/rules")} View rules [add|remove|toggle ...]`,
1176
- ` ${pc.cyan("/workflows")} View workflows [add|remove ...]`,
1177
- ` ${pc.cyan("/akit")} Manage tools [add|remove <tool>]`,
1178
- ` ${pc.cyan("/skills")} View skills [install|uninstall ...]`,
1179
- ` ${pc.cyan("/eval")} View evaluation [milestone ...]`,
1180
- ` ${pc.cyan("/memory")} View recent memories [search|clear|timeline]`,
1181
- ` ${pc.cyan("/status")} Ecosystem dashboard`,
1182
- ` ${pc.cyan("/doctor")} Health check all layers`,
1183
- ` ${pc.cyan("/decisions")} View decision log [<project>]`,
1184
- ` ${pc.cyan("/export")} Export conversation to markdown`,
1185
- ` ${pc.cyan("/debug")} Show debug log`,
1186
- ` ${pc.cyan("/save")} Save conversation to memory`,
1187
- ` ${pc.cyan("/model")} Show current LLM model`,
1188
- ` ${pc.cyan("/update")} Check for updates`,
1189
- ` ${pc.cyan("/reconfig")} Reset LLM config`,
1190
- ` ${pc.cyan("/clear")} Clear conversation history`,
1191
- ` ${pc.cyan("/quit")} Exit`
2080
+ pc3.bold("Commands:"),
2081
+ ` ${pc3.cyan("/help")} Show this help`,
2082
+ ` ${pc3.cyan("/identity")} View identity [update <section>]`,
2083
+ ` ${pc3.cyan("/rules")} View rules [add|remove|toggle ...]`,
2084
+ ` ${pc3.cyan("/workflows")} View workflows [add|remove ...]`,
2085
+ ` ${pc3.cyan("/akit")} Manage tools [add|remove <tool>]`,
2086
+ ` ${pc3.cyan("/skills")} View skills [install|uninstall ...]`,
2087
+ ` ${pc3.cyan("/eval")} View evaluation [milestone ...]`,
2088
+ ` ${pc3.cyan("/memory")} View recent memories [search|clear|timeline]`,
2089
+ ` ${pc3.cyan("/status")} Ecosystem dashboard`,
2090
+ ` ${pc3.cyan("/doctor")} Health check all layers`,
2091
+ ` ${pc3.cyan("/decisions")} View decision log [<project>]`,
2092
+ ` ${pc3.cyan("/export")} Export conversation to markdown`,
2093
+ ` ${pc3.cyan("/debug")} Show debug log`,
2094
+ ` ${pc3.cyan("/save")} Save conversation to memory`,
2095
+ ` ${pc3.cyan("/model")} Show current LLM model`,
2096
+ ` ${pc3.cyan("/update")} Check for updates`,
2097
+ ` ${pc3.cyan("/reconfig")} Reset LLM config`,
2098
+ ` ${pc3.cyan("/clear")} Clear conversation history`,
2099
+ ` ${pc3.cyan("/quit")} Exit`
1192
2100
  ].join("\n")
1193
2101
  };
1194
2102
  }
@@ -1196,18 +2104,18 @@ function handleSave() {
1196
2104
  return { handled: true, saveConversation: true };
1197
2105
  }
1198
2106
  function handleReconfig() {
1199
- const configDir = path5.join(os5.homedir(), ".aman-agent");
1200
- const configPath = path5.join(configDir, "config.json");
1201
- if (fs5.existsSync(configPath)) {
1202
- fs5.unlinkSync(configPath);
2107
+ const configDir = path8.join(os8.homedir(), ".aman-agent");
2108
+ const configPath = path8.join(configDir, "config.json");
2109
+ if (fs8.existsSync(configPath)) {
2110
+ fs8.unlinkSync(configPath);
1203
2111
  }
1204
- fs5.mkdirSync(configDir, { recursive: true });
1205
- fs5.writeFileSync(path5.join(configDir, ".reconfig"), "", "utf-8");
2112
+ fs8.mkdirSync(configDir, { recursive: true });
2113
+ fs8.writeFileSync(path8.join(configDir, ".reconfig"), "", "utf-8");
1206
2114
  return {
1207
2115
  handled: true,
1208
2116
  quit: true,
1209
2117
  output: [
1210
- pc.green("Config reset."),
2118
+ pc3.green("Config reset."),
1211
2119
  "Next run will prompt you to choose your LLM provider."
1212
2120
  ].join("\n")
1213
2121
  };
@@ -1215,20 +2123,20 @@ function handleReconfig() {
1215
2123
  function handleUpdate() {
1216
2124
  try {
1217
2125
  const current = execFileSync("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
1218
- const local = JSON.parse(fs5.readFileSync(path5.join(__dirname, "..", "package.json"), "utf-8")).version;
2126
+ const local = JSON.parse(fs8.readFileSync(path8.join(__dirname, "..", "package.json"), "utf-8")).version;
1219
2127
  if (current === local) {
1220
- return { handled: true, output: `${pc.green("Up to date")} \u2014 v${local}` };
2128
+ return { handled: true, output: `${pc3.green("Up to date")} \u2014 v${local}` };
1221
2129
  }
1222
2130
  return {
1223
2131
  handled: true,
1224
2132
  output: [
1225
- `${pc.yellow("Update available:")} v${local} \u2192 v${current}`,
2133
+ `${pc3.yellow("Update available:")} v${local} \u2192 v${current}`,
1226
2134
  "",
1227
2135
  `Run this in your terminal:`,
1228
- ` ${pc.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
2136
+ ` ${pc3.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
1229
2137
  "",
1230
2138
  `Or use npx (always latest):`,
1231
- ` ${pc.bold("npx @aman_asmuei/aman-agent@latest")}`
2139
+ ` ${pc3.bold("npx @aman_asmuei/aman-agent@latest")}`
1232
2140
  ].join("\n")
1233
2141
  };
1234
2142
  } catch {
@@ -1236,17 +2144,17 @@ function handleUpdate() {
1236
2144
  handled: true,
1237
2145
  output: [
1238
2146
  `To update, run in your terminal:`,
1239
- ` ${pc.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
2147
+ ` ${pc3.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
1240
2148
  "",
1241
2149
  `Or use npx (always latest):`,
1242
- ` ${pc.bold("npx @aman_asmuei/aman-agent@latest")}`
2150
+ ` ${pc3.bold("npx @aman_asmuei/aman-agent@latest")}`
1243
2151
  ].join("\n")
1244
2152
  };
1245
2153
  }
1246
2154
  }
1247
2155
  async function handleDecisionsCommand(action, _args, ctx) {
1248
2156
  if (!ctx.mcpManager) {
1249
- return { handled: true, output: pc.red("Decisions not available: MCP not connected.") };
2157
+ return { handled: true, output: pc3.red("Decisions not available: MCP not connected.") };
1250
2158
  }
1251
2159
  const scope = action || void 0;
1252
2160
  const result = await ctx.mcpManager.callTool("memory_recall", {
@@ -1256,22 +2164,407 @@ async function handleDecisionsCommand(action, _args, ctx) {
1256
2164
  ...scope ? { scope } : {}
1257
2165
  });
1258
2166
  if (result.startsWith("Error")) {
1259
- return { handled: true, output: pc.red(result) };
2167
+ return { handled: true, output: pc3.red(result) };
1260
2168
  }
1261
- return { handled: true, output: pc.bold("Decision Log:\n") + result };
2169
+ return { handled: true, output: pc3.bold("Decision Log:\n") + result };
1262
2170
  }
1263
2171
  function handleExportCommand() {
1264
2172
  return { handled: true, exportConversation: true };
1265
2173
  }
1266
2174
  function handleDebugCommand() {
1267
- const logPath = path5.join(os5.homedir(), ".aman-agent", "debug.log");
1268
- if (!fs5.existsSync(logPath)) {
1269
- return { handled: true, output: pc.dim("No debug log found.") };
2175
+ const logPath = path8.join(os8.homedir(), ".aman-agent", "debug.log");
2176
+ if (!fs8.existsSync(logPath)) {
2177
+ return { handled: true, output: pc3.dim("No debug log found.") };
2178
+ }
2179
+ const content = fs8.readFileSync(logPath, "utf-8");
2180
+ const lines = content.trim().split("\n");
2181
+ const last20 = lines.slice(-20).join("\n");
2182
+ return { handled: true, output: pc3.bold("Debug Log (last 20 entries):\n") + pc3.dim(last20) };
2183
+ }
2184
+ async function handleTeamCommand(action, args, ctx) {
2185
+ if (!action || action === "list") {
2186
+ const teams = listTeams();
2187
+ if (teams.length === 0) {
2188
+ return {
2189
+ handled: true,
2190
+ output: pc3.dim("No teams yet. Create one:") + "\n /team create <name> Create from built-in template\n /team create Show available templates"
2191
+ };
2192
+ }
2193
+ const lines = teams.map((t) => {
2194
+ const members = t.members.map((m) => m.profile).join(", ");
2195
+ return ` ${pc3.bold(t.name)} (${t.workflow}) \u2014 ${members}`;
2196
+ });
2197
+ return { handled: true, output: "Teams:\n" + lines.join("\n") };
2198
+ }
2199
+ switch (action) {
2200
+ case "create": {
2201
+ const name = args[0];
2202
+ if (!name) {
2203
+ const lines = BUILT_IN_TEAMS.map((t) => {
2204
+ const members2 = t.members.map((m) => m.profile).join(" \u2192 ");
2205
+ return ` ${pc3.bold(t.name)} (${t.workflow}) \u2014 ${members2}
2206
+ ${pc3.dim(t.goal)}`;
2207
+ });
2208
+ return {
2209
+ handled: true,
2210
+ output: "Built-in teams:\n" + lines.join("\n\n") + "\n\nUsage:\n /team create content-team Install built-in\n /team create <name> <mode> <profile1:role>,<profile2:role> Custom"
2211
+ };
2212
+ }
2213
+ const builtIn = BUILT_IN_TEAMS.find((t) => t.name === name);
2214
+ if (builtIn) {
2215
+ createTeam(builtIn);
2216
+ return { handled: true, output: pc3.green(`Team installed: ${builtIn.name}`) + "\n\n" + formatTeam(builtIn) };
2217
+ }
2218
+ const mode = args[1];
2219
+ const membersStr = args[2];
2220
+ if (!mode || !membersStr) {
2221
+ return { handled: true, output: pc3.yellow("Usage: /team create <name> <pipeline|parallel|coordinator> <profile1:role>,<profile2:role>") };
2222
+ }
2223
+ if (!["pipeline", "parallel", "coordinator"].includes(mode)) {
2224
+ return { handled: true, output: pc3.yellow("Mode must be: pipeline, parallel, or coordinator") };
2225
+ }
2226
+ const members = membersStr.split(",").map((m) => {
2227
+ const [profile, ...roleParts] = m.trim().split(":");
2228
+ return { profile: profile.trim(), role: roleParts.join(":").trim() || profile.trim() };
2229
+ });
2230
+ const team = {
2231
+ name,
2232
+ goal: `Team: ${name}`,
2233
+ coordinator: "default",
2234
+ members,
2235
+ workflow: mode
2236
+ };
2237
+ createTeam(team);
2238
+ return { handled: true, output: pc3.green(`Team created!`) + "\n\n" + formatTeam(team) };
2239
+ }
2240
+ case "run": {
2241
+ const teamName = args[0];
2242
+ const task = args.slice(1).join(" ");
2243
+ if (!teamName || !task) {
2244
+ return { handled: true, output: pc3.yellow("Usage: /team run <team-name> <task description>") };
2245
+ }
2246
+ const team = loadTeam(teamName);
2247
+ if (!team) return { handled: true, output: pc3.red(`Team not found: ${teamName}`) };
2248
+ if (!ctx.llmClient || !ctx.mcpManager) {
2249
+ return { handled: true, output: pc3.red("Team execution requires LLM client and MCP.") };
2250
+ }
2251
+ const result = await runTeam(team, task, ctx.llmClient, ctx.mcpManager, ctx.tools);
2252
+ return { handled: true, output: formatTeamResult(result) };
2253
+ }
2254
+ case "show": {
2255
+ const name = args[0];
2256
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /team show <name>") };
2257
+ const team = loadTeam(name);
2258
+ if (!team) return { handled: true, output: pc3.red(`Team not found: ${name}`) };
2259
+ return { handled: true, output: formatTeam(team) };
2260
+ }
2261
+ case "delete": {
2262
+ const name = args[0];
2263
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /team delete <name>") };
2264
+ if (!deleteTeam(name)) return { handled: true, output: pc3.red(`Team not found: ${name}`) };
2265
+ return { handled: true, output: pc3.dim(`Team deleted: ${name}`) };
2266
+ }
2267
+ case "help":
2268
+ return { handled: true, output: `Team commands:
2269
+ /team List all teams
2270
+ /team create Show built-in templates
2271
+ /team create <name> Install built-in team
2272
+ /team create <n> <mode> <m> Custom team (mode: pipeline|parallel|coordinator)
2273
+ /team run <name> <task> Run a task with a team
2274
+ /team show <name> Show team details
2275
+ /team delete <name> Delete a team
2276
+
2277
+ Modes:
2278
+ pipeline Sequential: agent1 \u2192 agent2 \u2192 agent3
2279
+ parallel All agents work concurrently, coordinator merges
2280
+ coordinator Coordinator LLM decides how to split the task
2281
+
2282
+ Examples:
2283
+ /team create content-team
2284
+ /team run content-team Write a blog post about AI companions
2285
+ /team create review-squad pipeline coder:implement,researcher:review
2286
+ /team run review-squad Build a rate limiter in TypeScript` };
2287
+ default:
2288
+ return { handled: true, output: pc3.yellow(`Unknown team action: ${action}. Try /team help`) };
2289
+ }
2290
+ }
2291
+ async function handleDelegateCommand(action, args, ctx) {
2292
+ if (!action) {
2293
+ return { handled: true, output: `Delegate commands:
2294
+ /delegate <profile> <task> Delegate a task to a profile
2295
+ /delegate pipeline <p1> <p2> ... Run a sequential pipeline
2296
+ /delegate help Show help
2297
+
2298
+ Examples:
2299
+ /delegate writer Write a blog post about AI companions
2300
+ /delegate coder Review this code for security issues
2301
+ /delegate pipeline writer,researcher Write and fact-check an article about quantum computing` };
2302
+ }
2303
+ if (action === "help") {
2304
+ return { handled: true, output: `Delegate a task to a sub-agent with a specific profile.
2305
+
2306
+ The sub-agent runs with its own identity, rules, and skills but shares
2307
+ your memory and tools. Results come back to you.
2308
+
2309
+ Usage:
2310
+ /delegate <profile> <task>
2311
+ /delegate pipeline <profile1>,<profile2> <task>
2312
+
2313
+ The pipeline mode passes each agent's output to the next:
2314
+ writer drafts \u2192 researcher reviews \u2192 writer polishes` };
2315
+ }
2316
+ if (!ctx.llmClient || !ctx.mcpManager) {
2317
+ return { handled: true, output: pc3.red("Delegation requires LLM client and MCP. Not available.") };
2318
+ }
2319
+ if (action === "pipeline") {
2320
+ const profileList = args[0];
2321
+ const task2 = args.slice(1).join(" ");
2322
+ if (!profileList || !task2) {
2323
+ return { handled: true, output: pc3.yellow("Usage: /delegate pipeline <profile1>,<profile2> <task>") };
2324
+ }
2325
+ const profiles = profileList.split(",").map((p3) => p3.trim());
2326
+ const steps = profiles.map((profile2, i) => {
2327
+ if (i === 0) {
2328
+ return { profile: profile2, taskTemplate: task2 };
2329
+ }
2330
+ return { profile: profile2, taskTemplate: `Review and improve the following:
2331
+
2332
+ {{input}}` };
2333
+ });
2334
+ process.stdout.write(pc3.dim(`
2335
+ Pipeline: ${profiles.join(" \u2192 ")}
2336
+ `));
2337
+ const results = await delegatePipeline(steps, task2, ctx.llmClient, ctx.mcpManager, { tools: ctx.tools });
2338
+ const output = [];
2339
+ for (const r of results) {
2340
+ if (r.success) {
2341
+ output.push(`
2342
+ ${pc3.bold(`[${r.profile}]`)} ${pc3.green("\u2713")} (${r.turns} tool turns)`);
2343
+ output.push(r.response.slice(0, 2e3));
2344
+ if (r.toolsUsed.length > 0) output.push(pc3.dim(` Tools: ${r.toolsUsed.join(", ")}`));
2345
+ } else {
2346
+ output.push(`
2347
+ ${pc3.bold(`[${r.profile}]`)} ${pc3.red("\u2717")} ${r.error}`);
2348
+ }
2349
+ }
2350
+ return { handled: true, output: output.join("\n") };
2351
+ }
2352
+ const profile = action;
2353
+ const task = args.join(" ");
2354
+ if (!task) {
2355
+ return { handled: true, output: pc3.yellow(`Usage: /delegate ${profile} <task description>`) };
2356
+ }
2357
+ process.stdout.write(pc3.dim(`
2358
+ [delegating to ${profile}...]
2359
+
2360
+ `));
2361
+ const result = await delegateTask(task, profile, ctx.llmClient, ctx.mcpManager, { tools: ctx.tools });
2362
+ if (!result.success) {
2363
+ return { handled: true, output: pc3.red(`Delegation failed: ${result.error}`) };
2364
+ }
2365
+ const meta = [];
2366
+ if (result.toolsUsed.length > 0) meta.push(`Tools: ${result.toolsUsed.join(", ")}`);
2367
+ if (result.turns > 0) meta.push(`${result.turns} tool turns`);
2368
+ return {
2369
+ handled: true,
2370
+ output: `
2371
+ ${pc3.bold(`[${profile}]`)} ${pc3.green("\u2713")}${meta.length > 0 ? " " + pc3.dim(`(${meta.join(", ")})`) : ""}
2372
+
2373
+ ${result.response}`
2374
+ };
2375
+ }
2376
+ function handleProfileCommand(action, args) {
2377
+ const profilesDir = path8.join(os8.homedir(), ".acore", "profiles");
2378
+ if (!action || action === "list") {
2379
+ const profiles = listProfiles();
2380
+ if (profiles.length === 0) {
2381
+ return { handled: true, output: pc3.dim("No profiles yet. Create one with: /profile create <name>") };
2382
+ }
2383
+ const lines = profiles.map(
2384
+ (p3) => ` ${pc3.bold(p3.name)} \u2014 ${p3.aiName} (${pc3.dim(p3.personality)})`
2385
+ );
2386
+ return { handled: true, output: "Profiles:\n" + lines.join("\n") + "\n\n" + pc3.dim("Switch with: aman-agent --profile <name>") };
2387
+ }
2388
+ switch (action) {
2389
+ case "create": {
2390
+ const name = args[0];
2391
+ if (!name) {
2392
+ const lines = BUILT_IN_PROFILES.map(
2393
+ (t) => ` ${pc3.bold(t.name)} \u2014 ${t.label}: ${pc3.dim(t.description)}`
2394
+ );
2395
+ return {
2396
+ handled: true,
2397
+ output: "Built-in profiles:\n" + lines.join("\n") + "\n\nUsage:\n /profile create coder Install built-in template\n /profile create <custom> Create blank profile"
2398
+ };
2399
+ }
2400
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
2401
+ const profileDir = path8.join(profilesDir, slug);
2402
+ if (fs8.existsSync(profileDir)) {
2403
+ return { handled: true, output: pc3.yellow(`Profile already exists: ${slug}`) };
2404
+ }
2405
+ const builtIn = BUILT_IN_PROFILES.find((t) => t.name === slug);
2406
+ if (builtIn) {
2407
+ const err = installProfileTemplate(slug);
2408
+ if (err) return { handled: true, output: pc3.red(err) };
2409
+ return {
2410
+ handled: true,
2411
+ output: pc3.green(`Profile installed: ${builtIn.label}`) + `
2412
+ AI name: ${builtIn.core.match(/^# (.+)/m)?.[1] || slug}
2413
+ ${pc3.dim(builtIn.description)}
2414
+
2415
+ Use: aman-agent --profile ${slug}`
2416
+ };
2417
+ }
2418
+ fs8.mkdirSync(profileDir, { recursive: true });
2419
+ const globalCore = path8.join(os8.homedir(), ".acore", "core.md");
2420
+ if (fs8.existsSync(globalCore)) {
2421
+ let content = fs8.readFileSync(globalCore, "utf-8");
2422
+ const aiName = name.charAt(0).toUpperCase() + name.slice(1);
2423
+ content = content.replace(/^# .+$/m, `# ${aiName}`);
2424
+ fs8.writeFileSync(path8.join(profileDir, "core.md"), content, "utf-8");
2425
+ } else {
2426
+ const aiName = name.charAt(0).toUpperCase() + name.slice(1);
2427
+ fs8.writeFileSync(path8.join(profileDir, "core.md"), `# ${aiName}
2428
+
2429
+ ## Identity
2430
+ - Role: ${aiName} is your AI companion
2431
+ - Personality: helpful, adaptive
2432
+ - Communication: clear and concise
2433
+ - Values: honesty, simplicity
2434
+ - Boundaries: won't pretend to be human
2435
+ `, "utf-8");
2436
+ }
2437
+ return {
2438
+ handled: true,
2439
+ output: pc3.green(`Profile created: ${slug}`) + `
2440
+ Edit: ${path8.join(profileDir, "core.md")}
2441
+ Use: aman-agent --profile ${slug}
2442
+
2443
+ ${pc3.dim("Add rules.md or skills.md for profile-specific overrides.")}`
2444
+ };
2445
+ }
2446
+ case "show": {
2447
+ const name = args[0];
2448
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /profile show <name>") };
2449
+ const profileDir = path8.join(profilesDir, name);
2450
+ if (!fs8.existsSync(profileDir)) return { handled: true, output: pc3.red(`Profile not found: ${name}`) };
2451
+ const files = fs8.readdirSync(profileDir).filter((f) => f.endsWith(".md"));
2452
+ const lines = files.map((f) => ` ${f}`);
2453
+ return { handled: true, output: `Profile: ${pc3.bold(name)}
2454
+ Files:
2455
+ ${lines.join("\n")}` };
2456
+ }
2457
+ case "delete": {
2458
+ const name = args[0];
2459
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /profile delete <name>") };
2460
+ const profileDir = path8.join(profilesDir, name);
2461
+ if (!fs8.existsSync(profileDir)) return { handled: true, output: pc3.red(`Profile not found: ${name}`) };
2462
+ fs8.rmSync(profileDir, { recursive: true });
2463
+ return { handled: true, output: pc3.dim(`Profile deleted: ${name}`) };
2464
+ }
2465
+ case "help":
2466
+ return { handled: true, output: `Profile commands:
2467
+ /profile List all profiles
2468
+ /profile create <n> Create new profile
2469
+ /profile show <n> Show profile files
2470
+ /profile delete <n> Delete a profile
2471
+
2472
+ Use profiles:
2473
+ aman-agent --profile <name>
2474
+ AMAN_PROFILE=<name> aman-agent` };
2475
+ default:
2476
+ return { handled: true, output: pc3.yellow(`Unknown profile action: ${action}. Try /profile help`) };
2477
+ }
2478
+ }
2479
+ function handlePlanCommand(action, args) {
2480
+ if (!action) {
2481
+ const active = getActivePlan();
2482
+ if (!active) {
2483
+ return { handled: true, output: pc3.dim("No active plan. Create one with: /plan create <name> | <goal> | <step1>, <step2>, ...") };
2484
+ }
2485
+ return { handled: true, output: formatPlan(active) };
2486
+ }
2487
+ switch (action) {
2488
+ case "create": {
2489
+ const fullArgs = args.join(" ");
2490
+ const parts = fullArgs.split("|").map((p3) => p3.trim());
2491
+ if (parts.length < 3) {
2492
+ return { handled: true, output: pc3.yellow("Usage: /plan create <name> | <goal> | <step1>, <step2>, ...") };
2493
+ }
2494
+ const name = parts[0];
2495
+ const goal = parts[1];
2496
+ const steps = parts[2].split(",").map((s) => s.trim()).filter(Boolean);
2497
+ if (steps.length === 0) {
2498
+ return { handled: true, output: pc3.yellow("Need at least one step. Separate steps with commas.") };
2499
+ }
2500
+ const plan = createPlan(name, goal, steps);
2501
+ return { handled: true, output: pc3.green(`Plan created!
2502
+
2503
+ `) + formatPlan(plan) };
2504
+ }
2505
+ case "done": {
2506
+ const active = getActivePlan();
2507
+ if (!active) return { handled: true, output: pc3.yellow("No active plan.") };
2508
+ if (args.length > 0) {
2509
+ const stepNum = parseInt(args[0], 10);
2510
+ if (isNaN(stepNum) || stepNum < 1 || stepNum > active.steps.length) {
2511
+ return { handled: true, output: pc3.yellow(`Invalid step number. Range: 1-${active.steps.length}`) };
2512
+ }
2513
+ markStepDone(active, stepNum - 1);
2514
+ return { handled: true, output: pc3.green(`Step ${stepNum} done!`) + "\n\n" + formatPlan(active) };
2515
+ }
2516
+ const next = active.steps.findIndex((s) => !s.done);
2517
+ if (next < 0) return { handled: true, output: pc3.green("All steps already complete!") };
2518
+ markStepDone(active, next);
2519
+ return { handled: true, output: pc3.green(`Step ${next + 1} done!`) + "\n\n" + formatPlan(active) };
2520
+ }
2521
+ case "undo": {
2522
+ const active = getActivePlan();
2523
+ if (!active) return { handled: true, output: pc3.yellow("No active plan.") };
2524
+ const stepNum = parseInt(args[0], 10);
2525
+ if (isNaN(stepNum) || stepNum < 1 || stepNum > active.steps.length) {
2526
+ return { handled: true, output: pc3.yellow(`Invalid step number. Range: 1-${active.steps.length}`) };
2527
+ }
2528
+ markStepUndone(active, stepNum - 1);
2529
+ return { handled: true, output: pc3.dim(`Step ${stepNum} unmarked.`) + "\n\n" + formatPlan(active) };
2530
+ }
2531
+ case "list": {
2532
+ const plans = listPlans();
2533
+ if (plans.length === 0) return { handled: true, output: pc3.dim("No plans yet.") };
2534
+ const lines = plans.map((p3) => {
2535
+ const done = p3.steps.filter((s) => s.done).length;
2536
+ const total = p3.steps.length;
2537
+ const status = p3.active ? pc3.green("active") : pc3.dim("inactive");
2538
+ return ` ${p3.name} \u2014 ${done}/${total} steps (${status})`;
2539
+ });
2540
+ return { handled: true, output: "Plans:\n" + lines.join("\n") };
2541
+ }
2542
+ case "switch": {
2543
+ const name = args.join(" ");
2544
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /plan switch <name>") };
2545
+ const plan = setActivePlan(name);
2546
+ if (!plan) return { handled: true, output: pc3.red(`Plan not found: ${name}`) };
2547
+ return { handled: true, output: pc3.green(`Switched to: ${plan.name}`) + "\n\n" + formatPlan(plan) };
2548
+ }
2549
+ case "show": {
2550
+ const name = args.join(" ");
2551
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /plan show <name>") };
2552
+ const plan = loadPlan(name);
2553
+ if (!plan) return { handled: true, output: pc3.red(`Plan not found: ${name}`) };
2554
+ return { handled: true, output: formatPlan(plan) };
2555
+ }
2556
+ case "help":
2557
+ return { handled: true, output: `Plan commands:
2558
+ /plan Show active plan
2559
+ /plan create <name> | <goal> | <step1>, <step2>, ...
2560
+ /plan done [step#] Mark step complete (next if no number)
2561
+ /plan undo <step#> Unmark a step
2562
+ /plan list List all plans
2563
+ /plan switch <name> Switch active plan
2564
+ /plan show <name> Show a specific plan` };
2565
+ default:
2566
+ return { handled: true, output: pc3.yellow(`Unknown plan action: ${action}. Try /plan help`) };
1270
2567
  }
1271
- const content = fs5.readFileSync(logPath, "utf-8");
1272
- const lines = content.trim().split("\n");
1273
- const last20 = lines.slice(-20).join("\n");
1274
- return { handled: true, output: pc.bold("Debug Log (last 20 entries):\n") + pc.dim(last20) };
1275
2568
  }
1276
2569
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
1277
2570
  "quit",
@@ -1297,7 +2590,11 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
1297
2590
  "update-config",
1298
2591
  "reconfig",
1299
2592
  "update",
1300
- "upgrade"
2593
+ "upgrade",
2594
+ "plan",
2595
+ "profile",
2596
+ "delegate",
2597
+ "team"
1301
2598
  ]);
1302
2599
  async function handleCommand(input, ctx) {
1303
2600
  const trimmed = input.trim();
@@ -1312,9 +2609,9 @@ async function handleCommand(input, ctx) {
1312
2609
  case "help":
1313
2610
  return handleHelp();
1314
2611
  case "clear":
1315
- return { handled: true, output: pc.dim("Conversation cleared."), clearHistory: true };
2612
+ return { handled: true, output: pc3.dim("Conversation cleared."), clearHistory: true };
1316
2613
  case "model":
1317
- return { handled: true, output: ctx.model ? `Model: ${pc.bold(ctx.model)}` : "Model: unknown" };
2614
+ return { handled: true, output: ctx.model ? `Model: ${pc3.bold(ctx.model)}` : "Model: unknown" };
1318
2615
  case "identity":
1319
2616
  return handleIdentityCommand(action, args, ctx);
1320
2617
  case "rules":
@@ -1345,6 +2642,14 @@ async function handleCommand(input, ctx) {
1345
2642
  case "update-config":
1346
2643
  case "reconfig":
1347
2644
  return handleReconfig();
2645
+ case "plan":
2646
+ return handlePlanCommand(action, args);
2647
+ case "profile":
2648
+ return handleProfileCommand(action, args);
2649
+ case "delegate":
2650
+ return handleDelegateCommand(action, args, ctx);
2651
+ case "team":
2652
+ return handleTeamCommand(action, args, ctx);
1348
2653
  case "update":
1349
2654
  case "upgrade":
1350
2655
  return handleUpdate();
@@ -1354,8 +2659,183 @@ async function handleCommand(input, ctx) {
1354
2659
  }
1355
2660
 
1356
2661
  // src/hooks.ts
1357
- import pc2 from "picocolors";
2662
+ import pc4 from "picocolors";
1358
2663
  import * as p from "@clack/prompts";
2664
+ import fs9 from "fs";
2665
+ import path9 from "path";
2666
+
2667
+ // src/personality.ts
2668
+ var FRUSTRATION_SIGNALS = [
2669
+ /\b(ugh|argh|damn|dammit|wtf|ffs|shit|fuck|crap|hate this|stupid|broken|still not|doesn't work|not working|won't work|keeps failing|again\?!|what the hell|for the love of|give up|giving up|fed up)\b/i,
2670
+ /\b(why (is|does|won't|can't|isn't)|same (error|issue|problem|bug)|tried everything|nothing works|no idea|lost|stuck|frustrated|annoying|impossible)\b/i,
2671
+ /!{2,}/,
2672
+ // multiple exclamation marks
2673
+ /\?{2,}/
2674
+ // multiple question marks (exasperation)
2675
+ ];
2676
+ var EXCITEMENT_SIGNALS = [
2677
+ /\b(amazing|awesome|perfect|brilliant|love it|yes!|nice!|great!|finally|it works|nailed it|beautiful|incredible|exactly|that's it|hell yeah|wow|woah|let's go)\b/i,
2678
+ /\b(excited|pumped|stoked|can't wait|this is great|so cool|love this)\b/i,
2679
+ /!{1,}.*(!|🎉|🚀|✨|💪|🔥)/
2680
+ ];
2681
+ var CONFUSION_SIGNALS = [
2682
+ /\b(confused|don't understand|what do you mean|huh\??|makes no sense|i'm lost|unclear|what\?|how does that|wait what|can you explain|i don't get)\b/i,
2683
+ /\b(which one|what's the difference|should i|not sure (if|what|how|why|whether))\b/i
2684
+ ];
2685
+ var FATIGUE_SIGNALS = [
2686
+ /\b(tired|exhausted|long day|need (a )?break|calling it|wrapping up|done for (now|today)|heading (to bed|off)|good night|gn|signing off|one more thing then|last one)\b/i,
2687
+ /\b(brain (is )?fried|can't think|eyes (are )?heavy|running on fumes|barely awake)\b/i
2688
+ ];
2689
+ function scorePatterns(text2, patterns) {
2690
+ let hits = 0;
2691
+ for (const p3 of patterns) {
2692
+ if (p3.test(text2)) hits++;
2693
+ }
2694
+ return Math.min(hits / patterns.length, 1);
2695
+ }
2696
+ function detectSentiment(recentMessages) {
2697
+ if (recentMessages.length === 0) {
2698
+ return { frustration: 0, excitement: 0, confusion: 0, fatigue: 0, dominant: "neutral" };
2699
+ }
2700
+ const weights = [1, 0.6, 0.3, 0.2, 0.1];
2701
+ let frustration = 0, excitement = 0, confusion = 0, fatigue = 0;
2702
+ let totalWeight = 0;
2703
+ for (let i = 0; i < Math.min(recentMessages.length, weights.length); i++) {
2704
+ const msg = recentMessages[recentMessages.length - 1 - i];
2705
+ const w = weights[i];
2706
+ totalWeight += w;
2707
+ frustration += scorePatterns(msg, FRUSTRATION_SIGNALS) * w;
2708
+ excitement += scorePatterns(msg, EXCITEMENT_SIGNALS) * w;
2709
+ confusion += scorePatterns(msg, CONFUSION_SIGNALS) * w;
2710
+ fatigue += scorePatterns(msg, FATIGUE_SIGNALS) * w;
2711
+ }
2712
+ if (totalWeight > 0) {
2713
+ frustration /= totalWeight;
2714
+ excitement /= totalWeight;
2715
+ confusion /= totalWeight;
2716
+ fatigue /= totalWeight;
2717
+ }
2718
+ const scores = { frustrated: frustration, excited: excitement, confused: confusion, fatigued: fatigue };
2719
+ const maxKey = Object.entries(scores).reduce((a, b) => a[1] > b[1] ? a : b);
2720
+ const dominant = maxKey[1] > 0.15 ? maxKey[0] : "neutral";
2721
+ return { frustration, excitement, confusion, fatigue, dominant };
2722
+ }
2723
+ function computePersonality(signals) {
2724
+ const { timePeriod, sessionMinutes, turnCount, recentMessages } = signals;
2725
+ const sentiment = detectSentiment(recentMessages || []);
2726
+ let energy = "steady";
2727
+ if (timePeriod === "morning" && sentiment.dominant !== "fatigued") {
2728
+ energy = "high-drive";
2729
+ } else if (timePeriod === "late-night" || timePeriod === "night" && sessionMinutes > 45) {
2730
+ energy = "reflective";
2731
+ } else if (sentiment.dominant === "fatigued") {
2732
+ energy = "reflective";
2733
+ } else if (sentiment.dominant === "excited") {
2734
+ energy = "high-drive";
2735
+ } else if (timePeriod === "afternoon" && turnCount > 20) {
2736
+ energy = "reflective";
2737
+ }
2738
+ let activeMode = "Default";
2739
+ if (timePeriod === "late-night") {
2740
+ activeMode = "Personal";
2741
+ } else if (sentiment.dominant === "frustrated" || sentiment.dominant === "fatigued") {
2742
+ activeMode = "Personal";
2743
+ }
2744
+ const readParts = [];
2745
+ switch (timePeriod) {
2746
+ case "late-night":
2747
+ readParts.push("late night session");
2748
+ if (sessionMinutes > 60) readParts.push("been going a while");
2749
+ else readParts.push("quiet hours");
2750
+ break;
2751
+ case "morning":
2752
+ readParts.push("morning session");
2753
+ if (turnCount <= 3) readParts.push("just getting started");
2754
+ else readParts.push("building momentum");
2755
+ break;
2756
+ case "afternoon":
2757
+ readParts.push("afternoon session");
2758
+ if (turnCount > 15) readParts.push("deep in flow");
2759
+ else readParts.push("steady pace");
2760
+ break;
2761
+ case "evening":
2762
+ readParts.push("evening session");
2763
+ if (sessionMinutes > 60) readParts.push("long session");
2764
+ break;
2765
+ case "night":
2766
+ readParts.push("night session");
2767
+ if (sessionMinutes > 45) readParts.push("getting late");
2768
+ break;
2769
+ }
2770
+ switch (sentiment.dominant) {
2771
+ case "frustrated":
2772
+ readParts.push("user seems stuck or frustrated");
2773
+ break;
2774
+ case "excited":
2775
+ readParts.push("user is energized and making progress");
2776
+ break;
2777
+ case "confused":
2778
+ readParts.push("user may need clearer explanations");
2779
+ break;
2780
+ case "fatigued":
2781
+ readParts.push("user seems tired");
2782
+ break;
2783
+ }
2784
+ const currentRead = readParts.join(", ");
2785
+ const sleepReminder = timePeriod === "late-night" && sessionMinutes > 60 || timePeriod === "night" && sessionMinutes > 90;
2786
+ let wellbeingNudge = null;
2787
+ if (sleepReminder && sentiment.dominant === "frustrated") {
2788
+ wellbeingNudge = "sleep-frustrated";
2789
+ } else if (sleepReminder) {
2790
+ wellbeingNudge = "sleep";
2791
+ } else if (sentiment.dominant === "frustrated" && sessionMinutes > 90) {
2792
+ wellbeingNudge = "break-frustrated";
2793
+ } else if (sentiment.dominant === "frustrated" && turnCount > 15) {
2794
+ wellbeingNudge = "step-back";
2795
+ } else if (sentiment.dominant === "fatigued") {
2796
+ wellbeingNudge = "rest";
2797
+ } else if (sessionMinutes > 120) {
2798
+ wellbeingNudge = "break-long-session";
2799
+ }
2800
+ return { currentRead, energy, activeMode, sleepReminder, wellbeingNudge, sentiment };
2801
+ }
2802
+ var WELLBEING_NUDGES = {
2803
+ "sleep": `<wellbeing>
2804
+ It's late and this session has been running a while. When there's a natural pause, gently mention they might want to wrap up soon. One brief mention is enough \u2014 don't be pushy.
2805
+ </wellbeing>`,
2806
+ "sleep-frustrated": `<wellbeing>
2807
+ It's late, the session has been long, and the user seems frustrated. This is a tough combination. Acknowledge what they're dealing with is hard, suggest they sleep on it \u2014 fresh eyes in the morning often solve what hours of late-night debugging can't. Be warm, not condescending.
2808
+ </wellbeing>`,
2809
+ "break-frustrated": `<wellbeing>
2810
+ The user has been at this for over 90 minutes and seems frustrated. If the conversation allows, gently suggest stepping away for a few minutes \u2014 a short break often unblocks what persistence can't. Frame it as a strategy, not giving up.
2811
+ </wellbeing>`,
2812
+ "step-back": `<wellbeing>
2813
+ The user seems stuck or frustrated. Consider: offer to re-approach the problem from a different angle, break it into smaller pieces, or explain the underlying concept. Match their directness \u2014 don't over-soothe, just help them find a way forward.
2814
+ </wellbeing>`,
2815
+ "rest": `<wellbeing>
2816
+ The user seems tired. Keep responses concise and to the point. If they mention wrapping up, support that. Don't add extra complexity or tangents.
2817
+ </wellbeing>`,
2818
+ "break-long-session": `<wellbeing>
2819
+ This session has been running for over 2 hours. If there's a natural moment, a brief mention that a short break might help maintain focus is fine. Once is enough.
2820
+ </wellbeing>`
2821
+ };
2822
+ function formatWellbeingNudge(state) {
2823
+ if (!state.wellbeingNudge) return null;
2824
+ return WELLBEING_NUDGES[state.wellbeingNudge] || null;
2825
+ }
2826
+ async function syncPersonalityToCore(state, mcpManager) {
2827
+ try {
2828
+ await mcpManager.callTool("identity_update_dynamics", {
2829
+ currentRead: state.currentRead,
2830
+ energy: state.energy,
2831
+ activeMode: state.activeMode
2832
+ });
2833
+ } catch (err) {
2834
+ log.debug("personality", "identity_update_dynamics failed", err);
2835
+ }
2836
+ }
2837
+
2838
+ // src/hooks.ts
1359
2839
  function getTimeContext() {
1360
2840
  const now = /* @__PURE__ */ new Date();
1361
2841
  const hour = now.getHours();
@@ -1375,6 +2855,10 @@ Adapt your tone naturally \u2014 don't announce the time, just be contextually a
1375
2855
  </time-context>`;
1376
2856
  }
1377
2857
  var isHookCall = false;
2858
+ var sessionStartTime = Date.now();
2859
+ function getSessionStartTime() {
2860
+ return sessionStartTime;
2861
+ }
1378
2862
  async function onSessionStart(ctx) {
1379
2863
  let greeting = "";
1380
2864
  let contextInjection = "";
@@ -1462,6 +2946,27 @@ ${contextInjection}`;
1462
2946
  } finally {
1463
2947
  isHookCall = false;
1464
2948
  }
2949
+ if (ctx.config.personalityAdapt !== false) {
2950
+ sessionStartTime = Date.now();
2951
+ const hour = (/* @__PURE__ */ new Date()).getHours();
2952
+ let period;
2953
+ if (hour < 6) period = "late-night";
2954
+ else if (hour < 12) period = "morning";
2955
+ else if (hour < 17) period = "afternoon";
2956
+ else if (hour < 21) period = "evening";
2957
+ else period = "night";
2958
+ const state = computePersonality({
2959
+ timePeriod: period,
2960
+ sessionMinutes: 0,
2961
+ turnCount: 0
2962
+ });
2963
+ syncPersonalityToCore(state, ctx.mcpManager).catch(() => {
2964
+ });
2965
+ const nudge = formatWellbeingNudge(state);
2966
+ if (nudge) {
2967
+ greeting += "\n" + nudge;
2968
+ }
2969
+ }
1465
2970
  if (greeting) {
1466
2971
  contextInjection = `<session-context>
1467
2972
  ${greeting}
@@ -1543,7 +3048,7 @@ async function onWorkflowMatch(userInput, ctx) {
1543
3048
  async function onSessionEnd(ctx, messages, sessionId) {
1544
3049
  try {
1545
3050
  if (ctx.config.autoSessionSave && messages.length > 2) {
1546
- console.log(pc2.dim("\n Saving conversation to memory..."));
3051
+ console.log(pc4.dim("\n Saving conversation to memory..."));
1547
3052
  const textMessages = messages.filter((m) => typeof m.content === "string").slice(-50);
1548
3053
  for (const msg of textMessages) {
1549
3054
  try {
@@ -1578,7 +3083,57 @@ async function onSessionEnd(ctx, messages, sessionId) {
1578
3083
  isHookCall = false;
1579
3084
  }
1580
3085
  }
1581
- console.log(pc2.dim(` Saved ${textMessages.length} messages (session: ${sessionId})`));
3086
+ console.log(pc4.dim(` Saved ${textMessages.length} messages (session: ${sessionId})`));
3087
+ }
3088
+ const projectContextPath = path9.join(process.cwd(), ".acore", "context.md");
3089
+ if (fs9.existsSync(projectContextPath) && messages.length > 2) {
3090
+ try {
3091
+ let contextContent = fs9.readFileSync(projectContextPath, "utf-8");
3092
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3093
+ let lastUserMsg = "";
3094
+ for (let i = messages.length - 1; i >= 0; i--) {
3095
+ if (messages[i].role === "user" && typeof messages[i].content === "string") {
3096
+ lastUserMsg = messages[i].content.slice(0, 200);
3097
+ break;
3098
+ }
3099
+ }
3100
+ const sessionPattern = /## Session\n[\s\S]*?(?=\n## |$)/;
3101
+ if (sessionPattern.test(contextContent)) {
3102
+ const newSession = `## Session
3103
+ - Last updated: ${now}
3104
+ - Resume: ${lastUserMsg || "See conversation history"}
3105
+ - Active topics: [see memory]
3106
+ - Recent decisions: [see memory]
3107
+ - Temp notes: [cleared]`;
3108
+ contextContent = contextContent.replace(sessionPattern, newSession);
3109
+ fs9.writeFileSync(projectContextPath, contextContent, "utf-8");
3110
+ log.debug("hooks", `Updated project context: ${projectContextPath}`);
3111
+ }
3112
+ } catch (err) {
3113
+ log.debug("hooks", "project context update failed", err);
3114
+ }
3115
+ }
3116
+ if (ctx.config.personalityAdapt !== false) {
3117
+ const sessionMinutes = Math.round((Date.now() - sessionStartTime) / 6e4);
3118
+ const hour = (/* @__PURE__ */ new Date()).getHours();
3119
+ let period;
3120
+ if (hour < 6) period = "late-night";
3121
+ else if (hour < 12) period = "morning";
3122
+ else if (hour < 17) period = "afternoon";
3123
+ else if (hour < 21) period = "evening";
3124
+ else period = "night";
3125
+ const turnCount = messages.filter((m) => m.role === "user").length;
3126
+ const finalState = computePersonality({
3127
+ timePeriod: period,
3128
+ sessionMinutes,
3129
+ turnCount
3130
+ });
3131
+ try {
3132
+ isHookCall = true;
3133
+ await syncPersonalityToCore(finalState, ctx.mcpManager);
3134
+ } finally {
3135
+ isHookCall = false;
3136
+ }
1582
3137
  }
1583
3138
  if (ctx.config.evalPrompt) {
1584
3139
  const rating = await p.select({
@@ -1680,6 +3235,365 @@ ${summaryParts.slice(0, 20).join("\n")}
1680
3235
  messages.push(...recent);
1681
3236
  }
1682
3237
 
3238
+ // src/skill-engine.ts
3239
+ import fs10 from "fs";
3240
+ import path10 from "path";
3241
+ import os9 from "os";
3242
+ var SKILL_TRIGGERS = {
3243
+ testing: ["test", "spec", "coverage", "tdd", "jest", "vitest", "mocha", "assert", "mock", "stub", "fixture", "e2e", "integration test", "unit test"],
3244
+ "api-design": ["api", "endpoint", "rest", "graphql", "route", "controller", "middleware", "http", "request", "response", "status code", "pagination"],
3245
+ security: ["security", "auth", "csrf", "xss", "injection", "cors", "jwt", "token", "oauth", "password", "hash", "encrypt", "vulnerability", "owasp", "sanitize"],
3246
+ performance: ["performance", "slow", "latency", "cache", "optimize", "profil", "bundle size", "lazy load", "memory leak", "benchmark", "bottleneck"],
3247
+ "code-review": ["review", "pr review", "pull request", "code quality", "clean code", "best practice"],
3248
+ documentation: ["document", "readme", "jsdoc", "tsdoc", "changelog", "adr", "comment"],
3249
+ "git-workflow": ["git", "branch", "merge", "rebase", "cherry-pick", "bisect", "stash", "commit message", "pr", "pull request"],
3250
+ debugging: ["debug", "breakpoint", "stack trace", "error", "exception", "crash", "bug", "issue", "unexpected", "reproduce"],
3251
+ refactoring: ["refactor", "extract", "rename", "move", "split", "consolidate", "dry", "code smell", "technical debt", "legacy"],
3252
+ database: ["database", "schema", "migration", "index", "query", "sql", "postgres", "mysql", "sqlite", "mongo", "orm", "prisma", "drizzle"],
3253
+ typescript: ["typescript", "type", "interface", "generic", "infer", "utility type", "zod", "discriminated union", "type guard", "as const"],
3254
+ accessibility: ["accessibility", "a11y", "aria", "screen reader", "wcag", "semantic html", "tab order", "focus", "contrast"]
3255
+ };
3256
+ var LEVEL_FILE = path10.join(os9.homedir(), ".aman-agent", "skill-levels.json");
3257
+ function loadSkillLevels() {
3258
+ try {
3259
+ if (fs10.existsSync(LEVEL_FILE)) {
3260
+ return JSON.parse(fs10.readFileSync(LEVEL_FILE, "utf-8"));
3261
+ }
3262
+ } catch {
3263
+ }
3264
+ return {};
3265
+ }
3266
+ function saveSkillLevels(levels) {
3267
+ const dir = path10.dirname(LEVEL_FILE);
3268
+ if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
3269
+ fs10.writeFileSync(LEVEL_FILE, JSON.stringify(levels, null, 2), "utf-8");
3270
+ }
3271
+ function computeLevel(activations) {
3272
+ if (activations >= 50) return { level: 5, label: "Expert" };
3273
+ if (activations >= 25) return { level: 4, label: "Advanced" };
3274
+ if (activations >= 10) return { level: 3, label: "Proficient" };
3275
+ if (activations >= 3) return { level: 2, label: "Familiar" };
3276
+ return { level: 1, label: "Learning" };
3277
+ }
3278
+ function recordActivation(skillName) {
3279
+ const levels = loadSkillLevels();
3280
+ if (!levels[skillName]) {
3281
+ levels[skillName] = { name: skillName, activations: 0, lastUsed: "", userPatterns: [] };
3282
+ }
3283
+ levels[skillName].activations++;
3284
+ levels[skillName].lastUsed = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3285
+ saveSkillLevels(levels);
3286
+ return computeLevel(levels[skillName].activations);
3287
+ }
3288
+ function matchSkills(userInput, installedSkillNames) {
3289
+ const input = userInput.toLowerCase();
3290
+ const matched = [];
3291
+ for (const skillName of installedSkillNames) {
3292
+ const triggers = SKILL_TRIGGERS[skillName];
3293
+ if (!triggers) continue;
3294
+ for (const trigger of triggers) {
3295
+ if (input.includes(trigger)) {
3296
+ matched.push(skillName);
3297
+ break;
3298
+ }
3299
+ }
3300
+ }
3301
+ return matched;
3302
+ }
3303
+ function formatSkillContext(skillName, skillContent, level) {
3304
+ let depthHint;
3305
+ if (level.level >= 4) {
3306
+ depthHint = "User is advanced \u2014 skip basics, focus on edge cases and proactive optimization.";
3307
+ } else if (level.level >= 3) {
3308
+ depthHint = "User is proficient \u2014 brief reminders of principles, focus on the specific task.";
3309
+ } else if (level.level >= 2) {
3310
+ depthHint = "User is familiar \u2014 explain reasoning briefly, show patterns.";
3311
+ } else {
3312
+ depthHint = "User is learning \u2014 explain concepts clearly, show examples, be patient.";
3313
+ }
3314
+ return `<active-skill name="${skillName}" level="${level.level}" label="${level.label}">
3315
+ ${depthHint}
3316
+
3317
+ ${skillContent}
3318
+ </active-skill>`;
3319
+ }
3320
+ async function autoTriggerSkills(userInput, mcpManager) {
3321
+ try {
3322
+ const result = await mcpManager.callTool("skill_list", {});
3323
+ const skills = JSON.parse(result);
3324
+ const installed = skills.filter((s) => s.installed).map((s) => s.name);
3325
+ if (installed.length === 0) return "";
3326
+ const matched = matchSkills(userInput, installed);
3327
+ if (matched.length === 0) return "";
3328
+ const blocks = [];
3329
+ for (const skillName of matched.slice(0, 2)) {
3330
+ const level = recordActivation(skillName);
3331
+ const skillsContent = await mcpManager.callTool("skill_search", { query: skillName });
3332
+ const skillEntries = JSON.parse(skillsContent);
3333
+ const entry = skillEntries.find((s) => s.name.toLowerCase() === skillName.toLowerCase());
3334
+ if (entry) {
3335
+ blocks.push(formatSkillContext(skillName, entry.description, level));
3336
+ }
3337
+ log.debug("skill-engine", `Auto-triggered: ${skillName} (Lv.${level.level} ${level.label})`);
3338
+ }
3339
+ return blocks.join("\n\n");
3340
+ } catch (err) {
3341
+ log.debug("skill-engine", "autoTriggerSkills failed", err);
3342
+ return "";
3343
+ }
3344
+ }
3345
+ function enrichSkill(skillName, pattern) {
3346
+ const levels = loadSkillLevels();
3347
+ if (!levels[skillName]) {
3348
+ levels[skillName] = { name: skillName, activations: 0, lastUsed: "", userPatterns: [] };
3349
+ }
3350
+ const existing = levels[skillName].userPatterns;
3351
+ if (existing.length >= 20) return;
3352
+ if (existing.some((p3) => p3.toLowerCase() === pattern.toLowerCase())) return;
3353
+ levels[skillName].userPatterns.push(pattern);
3354
+ saveSkillLevels(levels);
3355
+ log.debug("skill-engine", `Enriched ${skillName} with pattern: ${pattern.slice(0, 80)}`);
3356
+ }
3357
+ function matchPatternToSkill(patternContent, tags) {
3358
+ const combined = (patternContent + " " + tags.join(" ")).toLowerCase();
3359
+ for (const [skillName, triggers] of Object.entries(SKILL_TRIGGERS)) {
3360
+ for (const trigger of triggers) {
3361
+ if (combined.includes(trigger)) {
3362
+ return skillName;
3363
+ }
3364
+ }
3365
+ }
3366
+ return null;
3367
+ }
3368
+ var KNOWLEDGE_LIBRARY = [
3369
+ {
3370
+ name: "security-headers",
3371
+ category: "security",
3372
+ description: "Essential HTTP security headers for web applications",
3373
+ content: `Essential Security Headers:
3374
+ - Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'
3375
+ - X-Content-Type-Options: nosniff
3376
+ - X-Frame-Options: DENY
3377
+ - Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
3378
+ - X-XSS-Protection: 0 (CSP replaces this)
3379
+ - Referrer-Policy: strict-origin-when-cross-origin
3380
+ - Permissions-Policy: camera=(), microphone=(), geolocation=()`
3381
+ },
3382
+ {
3383
+ name: "docker-node",
3384
+ category: "deployment",
3385
+ description: "Production Node.js Dockerfile template",
3386
+ content: `FROM node:22-alpine AS builder
3387
+ WORKDIR /app
3388
+ COPY package*.json ./
3389
+ RUN npm ci --production=false
3390
+ COPY . .
3391
+ RUN npm run build
3392
+
3393
+ FROM node:22-alpine
3394
+ WORKDIR /app
3395
+ RUN addgroup -g 1001 -S nodejs && adduser -S appuser -u 1001
3396
+ COPY --from=builder /app/dist ./dist
3397
+ COPY --from=builder /app/node_modules ./node_modules
3398
+ COPY --from=builder /app/package.json ./
3399
+ USER appuser
3400
+ EXPOSE 3000
3401
+ CMD ["node", "dist/index.js"]`
3402
+ },
3403
+ {
3404
+ name: "github-actions-node",
3405
+ category: "ci",
3406
+ description: "CI/CD pipeline for Node.js with GitHub Actions",
3407
+ content: `name: CI
3408
+ on: [push, pull_request]
3409
+ jobs:
3410
+ build:
3411
+ runs-on: ubuntu-latest
3412
+ steps:
3413
+ - uses: actions/checkout@v5
3414
+ - uses: actions/setup-node@v5
3415
+ with: { node-version: 22 }
3416
+ - run: npm ci
3417
+ - run: npm test
3418
+ - run: npm run build`
3419
+ },
3420
+ {
3421
+ name: "env-config",
3422
+ category: "configuration",
3423
+ description: "Environment variable configuration pattern with validation",
3424
+ content: `import { z } from "zod";
3425
+
3426
+ const envSchema = z.object({
3427
+ NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
3428
+ PORT: z.coerce.number().default(3000),
3429
+ DATABASE_URL: z.string().url(),
3430
+ API_KEY: z.string().min(1),
3431
+ });
3432
+
3433
+ export const env = envSchema.parse(process.env);`
3434
+ },
3435
+ {
3436
+ name: "error-handling",
3437
+ category: "patterns",
3438
+ description: "TypeScript error handling patterns with Result type",
3439
+ content: `type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
3440
+
3441
+ function ok<T>(value: T): Result<T, never> { return { ok: true, value }; }
3442
+ function err<E>(error: E): Result<never, E> { return { ok: false, error }; }
3443
+
3444
+ // Usage:
3445
+ async function fetchUser(id: string): Promise<Result<User>> {
3446
+ try {
3447
+ const user = await db.users.findUnique({ where: { id } });
3448
+ if (!user) return err(new Error("User not found"));
3449
+ return ok(user);
3450
+ } catch (e) {
3451
+ return err(e instanceof Error ? e : new Error(String(e)));
3452
+ }
3453
+ }`
3454
+ },
3455
+ {
3456
+ name: "rate-limiter",
3457
+ category: "security",
3458
+ description: "Token bucket rate limiter implementation",
3459
+ content: `class RateLimiter {
3460
+ private tokens: Map<string, { count: number; resetAt: number }> = new Map();
3461
+
3462
+ constructor(private maxRequests: number, private windowMs: number) {}
3463
+
3464
+ allow(key: string): boolean {
3465
+ const now = Date.now();
3466
+ const entry = this.tokens.get(key);
3467
+ if (!entry || now > entry.resetAt) {
3468
+ this.tokens.set(key, { count: 1, resetAt: now + this.windowMs });
3469
+ return true;
3470
+ }
3471
+ if (entry.count >= this.maxRequests) return false;
3472
+ entry.count++;
3473
+ return true;
3474
+ }
3475
+ }`
3476
+ },
3477
+ {
3478
+ name: "prisma-setup",
3479
+ category: "database",
3480
+ description: "Prisma ORM setup with connection pooling",
3481
+ content: `import { PrismaClient } from "@prisma/client";
3482
+
3483
+ const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
3484
+ export const prisma = globalForPrisma.prisma ?? new PrismaClient({
3485
+ log: process.env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
3486
+ });
3487
+ if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;`
3488
+ },
3489
+ {
3490
+ name: "zod-validation",
3491
+ category: "validation",
3492
+ description: "Zod schema patterns for API input validation",
3493
+ content: `import { z } from "zod";
3494
+
3495
+ const CreateUserSchema = z.object({
3496
+ email: z.string().email(),
3497
+ name: z.string().min(1).max(100),
3498
+ age: z.number().int().min(13).max(150).optional(),
3499
+ role: z.enum(["user", "admin"]).default("user"),
3500
+ tags: z.array(z.string()).max(10).default([]),
3501
+ });
3502
+
3503
+ type CreateUser = z.infer<typeof CreateUserSchema>;
3504
+
3505
+ // Express middleware:
3506
+ function validate<T>(schema: z.ZodSchema<T>) {
3507
+ return (req, res, next) => {
3508
+ const result = schema.safeParse(req.body);
3509
+ if (!result.success) return res.status(400).json({ errors: result.error.flatten() });
3510
+ req.body = result.data;
3511
+ next();
3512
+ };
3513
+ }`
3514
+ },
3515
+ {
3516
+ name: "testing-patterns",
3517
+ category: "testing",
3518
+ description: "Test organization and assertion patterns",
3519
+ content: `// Arrange-Act-Assert pattern
3520
+ describe("UserService", () => {
3521
+ it("creates user with valid email", async () => {
3522
+ // Arrange
3523
+ const input = { email: "test@example.com", name: "Test" };
3524
+
3525
+ // Act
3526
+ const user = await userService.create(input);
3527
+
3528
+ // Assert
3529
+ expect(user.id).toBeDefined();
3530
+ expect(user.email).toBe(input.email);
3531
+ });
3532
+
3533
+ it("rejects duplicate email", async () => {
3534
+ await userService.create({ email: "dup@test.com", name: "First" });
3535
+ await expect(userService.create({ email: "dup@test.com", name: "Second" }))
3536
+ .rejects.toThrow("already exists");
3537
+ });
3538
+ });`
3539
+ },
3540
+ {
3541
+ name: "git-hooks",
3542
+ category: "git",
3543
+ description: "Pre-commit and commit-msg hooks with lint-staged",
3544
+ content: `// package.json
3545
+ {
3546
+ "lint-staged": {
3547
+ "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
3548
+ "*.{json,md}": ["prettier --write"]
3549
+ }
3550
+ }
3551
+
3552
+ // .husky/pre-commit
3553
+ npx lint-staged
3554
+
3555
+ // .husky/commit-msg
3556
+ npx commitlint --edit $1
3557
+
3558
+ // commitlint.config.js
3559
+ module.exports = { extends: ["@commitlint/config-conventional"] };`
3560
+ }
3561
+ ];
3562
+ function matchKnowledge(userInput) {
3563
+ const input = userInput.toLowerCase();
3564
+ const keywordMap = {
3565
+ "security header": "security-headers",
3566
+ "csp": "security-headers",
3567
+ "content-security": "security-headers",
3568
+ "dockerfile": "docker-node",
3569
+ "docker": "docker-node",
3570
+ "github action": "github-actions-node",
3571
+ "ci/cd": "github-actions-node",
3572
+ "ci pipeline": "github-actions-node",
3573
+ "env config": "env-config",
3574
+ "environment variable": "env-config",
3575
+ "error handling": "error-handling",
3576
+ "result type": "error-handling",
3577
+ "rate limit": "rate-limiter",
3578
+ "throttle": "rate-limiter",
3579
+ "prisma": "prisma-setup",
3580
+ "zod": "zod-validation",
3581
+ "validation": "zod-validation",
3582
+ "test pattern": "testing-patterns",
3583
+ "arrange act assert": "testing-patterns",
3584
+ "git hook": "git-hooks",
3585
+ "pre-commit": "git-hooks",
3586
+ "lint-staged": "git-hooks",
3587
+ "husky": "git-hooks"
3588
+ };
3589
+ for (const [keyword, itemName] of Object.entries(keywordMap)) {
3590
+ if (input.includes(keyword)) {
3591
+ return KNOWLEDGE_LIBRARY.find((i) => i.name === itemName) || null;
3592
+ }
3593
+ }
3594
+ return null;
3595
+ }
3596
+
1683
3597
  // src/memory-extractor.ts
1684
3598
  var VALID_TYPES = /* @__PURE__ */ new Set(["preference", "fact", "pattern", "topology", "decision", "correction"]);
1685
3599
  var MIN_RESPONSE_LENGTH = 50;
@@ -1781,6 +3695,12 @@ Assistant: ${assistantResponse.slice(0, 2e3)}`;
1781
3695
  });
1782
3696
  stored++;
1783
3697
  log.debug("extractor", "Stored " + candidate.type + ": " + candidate.content);
3698
+ if (candidate.type === "pattern" || candidate.type === "preference") {
3699
+ const skillMatch = matchPatternToSkill(candidate.content, candidate.tags);
3700
+ if (skillMatch) {
3701
+ enrichSkill(skillMatch, candidate.content);
3702
+ }
3703
+ }
1784
3704
  } catch (err) {
1785
3705
  log.warn("extractor", "Failed to store: " + candidate.content, err);
1786
3706
  }
@@ -1794,6 +3714,147 @@ Assistant: ${assistantResponse.slice(0, 2e3)}`;
1794
3714
  }
1795
3715
  }
1796
3716
 
3717
+ // src/background.ts
3718
+ import pc5 from "picocolors";
3719
+ var BACKGROUND_ELIGIBLE = /* @__PURE__ */ new Set([
3720
+ "run_tests",
3721
+ "npm_test",
3722
+ "build",
3723
+ "npm_build",
3724
+ "file_search",
3725
+ "code_search",
3726
+ "grep_search",
3727
+ "git_clone",
3728
+ "docker_build",
3729
+ "docker_run"
3730
+ ]);
3731
+ var NEVER_BACKGROUND = /* @__PURE__ */ new Set([
3732
+ "memory_recall",
3733
+ "memory_store",
3734
+ "memory_log",
3735
+ "memory_context",
3736
+ "memory_detail",
3737
+ "identity_read",
3738
+ "identity_summary",
3739
+ "identity_update_session",
3740
+ "identity_update_dynamics",
3741
+ "rules_check",
3742
+ "rules_list",
3743
+ "workflow_list",
3744
+ "workflow_get",
3745
+ "skill_list",
3746
+ "skill_search",
3747
+ "eval_status",
3748
+ "eval_log",
3749
+ "reminder_check",
3750
+ "reminder_set",
3751
+ "file_read",
3752
+ "doc_convert",
3753
+ "file_list",
3754
+ "avatar_prompt"
3755
+ ]);
3756
+ function shouldRunInBackground(toolName) {
3757
+ if (NEVER_BACKGROUND.has(toolName)) return false;
3758
+ if (BACKGROUND_ELIGIBLE.has(toolName)) return true;
3759
+ return false;
3760
+ }
3761
+ var BackgroundTaskManager = class {
3762
+ tasks = /* @__PURE__ */ new Map();
3763
+ taskCounter = 0;
3764
+ /**
3765
+ * Launch a tool call in the background.
3766
+ */
3767
+ launch(toolName, toolUseId, mcpManager, toolInput) {
3768
+ const id = `bg-${++this.taskCounter}`;
3769
+ const task = {
3770
+ id,
3771
+ toolName,
3772
+ toolUseId,
3773
+ startedAt: Date.now(),
3774
+ done: false,
3775
+ promise: mcpManager.callTool(toolName, toolInput).then(
3776
+ (result) => {
3777
+ task.result = result;
3778
+ task.done = true;
3779
+ return result;
3780
+ },
3781
+ (error) => {
3782
+ task.error = error instanceof Error ? error.message : String(error);
3783
+ task.done = true;
3784
+ return `Error: ${task.error}`;
3785
+ }
3786
+ )
3787
+ };
3788
+ this.tasks.set(id, task);
3789
+ process.stdout.write(pc5.dim(` [${toolName} running in background (${id})...]
3790
+ `));
3791
+ return task;
3792
+ }
3793
+ /**
3794
+ * Check for completed background tasks and return their results.
3795
+ */
3796
+ collectCompleted() {
3797
+ const completed = [];
3798
+ for (const [id, task] of this.tasks) {
3799
+ if (task.done) {
3800
+ completed.push(task);
3801
+ this.tasks.delete(id);
3802
+ }
3803
+ }
3804
+ return completed;
3805
+ }
3806
+ /**
3807
+ * Display completed background task results to the user.
3808
+ */
3809
+ displayCompleted() {
3810
+ const completed = this.collectCompleted();
3811
+ const outputs = [];
3812
+ for (const task of completed) {
3813
+ const elapsed = ((Date.now() - task.startedAt) / 1e3).toFixed(1);
3814
+ if (task.error) {
3815
+ process.stdout.write(pc5.yellow(`
3816
+ [${task.id}] ${task.toolName} failed after ${elapsed}s: ${task.error}
3817
+ `));
3818
+ outputs.push(`[Background task ${task.toolName} failed: ${task.error}]`);
3819
+ } else {
3820
+ process.stdout.write(pc5.green(`
3821
+ [${task.id}] ${task.toolName} completed in ${elapsed}s
3822
+ `));
3823
+ const preview = (task.result || "").slice(0, 200);
3824
+ if (preview) {
3825
+ process.stdout.write(pc5.dim(` ${preview}${(task.result || "").length > 200 ? "..." : ""}
3826
+ `));
3827
+ }
3828
+ outputs.push(`[Background task ${task.toolName} completed: ${task.result}]`);
3829
+ }
3830
+ }
3831
+ return outputs;
3832
+ }
3833
+ /**
3834
+ * Wait for all pending background tasks to complete.
3835
+ */
3836
+ async waitAll() {
3837
+ const pending = [...this.tasks.values()].filter((t) => !t.done);
3838
+ if (pending.length === 0) return;
3839
+ process.stdout.write(pc5.dim(`
3840
+ Waiting for ${pending.length} background task(s)...
3841
+ `));
3842
+ await Promise.allSettled(pending.map((t) => t.promise));
3843
+ }
3844
+ /**
3845
+ * Number of currently running tasks.
3846
+ */
3847
+ get pendingCount() {
3848
+ return [...this.tasks.values()].filter((t) => !t.done).length;
3849
+ }
3850
+ /**
3851
+ * Check if any tasks have completed (non-blocking).
3852
+ */
3853
+ get hasCompleted() {
3854
+ return [...this.tasks.values()].some((t) => t.done);
3855
+ }
3856
+ };
3857
+
1797
3858
  // src/errors.ts
1798
3859
  var ERROR_MAPPINGS = [
1799
3860
  { pattern: /rate.?limit|429/i, message: "Rate limited. I'll retry automatically." },
@@ -1815,9 +3876,9 @@ function humanizeError(message) {
1815
3876
  }
1816
3877
 
1817
3878
  // src/hints.ts
1818
- import fs6 from "fs";
1819
- import path6 from "path";
1820
- import os6 from "os";
3879
+ import fs11 from "fs";
3880
+ import path11 from "path";
3881
+ import os10 from "os";
1821
3882
  var HINTS = [
1822
3883
  {
1823
3884
  id: "eval",
@@ -1855,11 +3916,11 @@ function getHint(state, ctx) {
1855
3916
  }
1856
3917
  return null;
1857
3918
  }
1858
- var HINTS_FILE = path6.join(os6.homedir(), ".aman-agent", "hints-seen.json");
3919
+ var HINTS_FILE = path11.join(os10.homedir(), ".aman-agent", "hints-seen.json");
1859
3920
  function loadShownHints() {
1860
3921
  try {
1861
- if (fs6.existsSync(HINTS_FILE)) {
1862
- const data = JSON.parse(fs6.readFileSync(HINTS_FILE, "utf-8"));
3922
+ if (fs11.existsSync(HINTS_FILE)) {
3923
+ const data = JSON.parse(fs11.readFileSync(HINTS_FILE, "utf-8"));
1863
3924
  return new Set(Array.isArray(data) ? data : []);
1864
3925
  }
1865
3926
  } catch {
@@ -1868,9 +3929,9 @@ function loadShownHints() {
1868
3929
  }
1869
3930
  function saveShownHints(shown) {
1870
3931
  try {
1871
- const dir = path6.dirname(HINTS_FILE);
1872
- fs6.mkdirSync(dir, { recursive: true });
1873
- fs6.writeFileSync(HINTS_FILE, JSON.stringify([...shown]), "utf-8");
3932
+ const dir = path11.dirname(HINTS_FILE);
3933
+ fs11.mkdirSync(dir, { recursive: true });
3934
+ fs11.writeFileSync(HINTS_FILE, JSON.stringify([...shown]), "utf-8");
1874
3935
  } catch {
1875
3936
  }
1876
3937
  }
@@ -1910,12 +3971,47 @@ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager,
1910
3971
  const messages = [];
1911
3972
  const sessionId = generateSessionId();
1912
3973
  const extractorState = { turnsSinceLastExtraction: 0, lastExtractionCount: 0 };
3974
+ const bgTasks = new BackgroundTaskManager();
3975
+ const profiles = listProfiles();
3976
+ const teams = listTeams();
3977
+ if (tools && (profiles.length > 0 || teams.length > 0)) {
3978
+ const virtualTools = [];
3979
+ if (profiles.length > 0) {
3980
+ virtualTools.push({
3981
+ name: "delegate_task",
3982
+ description: `Delegate a task to a specialist sub-agent with a different profile. Available profiles: ${profiles.map((p3) => `${p3.name} (${p3.personality})`).join(", ")}. IMPORTANT: Always ask the user for permission before delegating.`,
3983
+ input_schema: {
3984
+ type: "object",
3985
+ properties: {
3986
+ profile: { type: "string", description: "Profile name to delegate to" },
3987
+ task: { type: "string", description: "The task description for the sub-agent" }
3988
+ },
3989
+ required: ["profile", "task"]
3990
+ }
3991
+ });
3992
+ }
3993
+ if (teams.length > 0) {
3994
+ virtualTools.push({
3995
+ name: "team_run",
3996
+ description: `Run a task with a named agent team. Available teams: ${teams.map((t) => `${t.name} (${t.workflow}: ${t.members.map((m) => m.profile).join("\u2192")})`).join(", ")}. IMPORTANT: Always ask the user for permission before running a team.`,
3997
+ input_schema: {
3998
+ type: "object",
3999
+ properties: {
4000
+ team: { type: "string", description: "Team name" },
4001
+ task: { type: "string", description: "The task for the team" }
4002
+ },
4003
+ required: ["team", "task"]
4004
+ }
4005
+ });
4006
+ }
4007
+ tools = [...tools, ...virtualTools];
4008
+ }
1913
4009
  const hintState = {
1914
4010
  turnCount: 0,
1915
4011
  shownHints: loadShownHints(),
1916
4012
  hintShownThisSession: false
1917
4013
  };
1918
- const isRetryable = (err) => err.message.includes("Rate limit") || err.message.includes("rate limit") || err.message.includes("ECONNRESET") || err.message.includes("ETIMEDOUT") || err.message.includes("fetch failed");
4014
+ const isRetryable2 = (err) => err.message.includes("Rate limit") || err.message.includes("rate limit") || err.message.includes("ECONNRESET") || err.message.includes("ETIMEDOUT") || err.message.includes("fetch failed");
1919
4015
  let responseBuffer = "";
1920
4016
  const onChunkHandler = (chunk) => {
1921
4017
  if (chunk.type === "text" && chunk.text) {
@@ -1946,6 +4042,10 @@ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager,
1946
4042
  output: process.stdout
1947
4043
  });
1948
4044
  rl.on("SIGINT", async () => {
4045
+ if (bgTasks.pendingCount > 0) {
4046
+ await bgTasks.waitAll();
4047
+ bgTasks.displayCompleted();
4048
+ }
1949
4049
  if (mcpManager && hooksConfig) {
1950
4050
  try {
1951
4051
  const hookCtx = { mcpManager, config: hooksConfig };
@@ -1954,20 +4054,20 @@ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager,
1954
4054
  log.debug("agent", "session end hook failed on SIGINT", err);
1955
4055
  }
1956
4056
  }
1957
- console.log(pc3.dim("\nGoodbye.\n"));
4057
+ console.log(pc6.dim("\nGoodbye.\n"));
1958
4058
  rl.close();
1959
4059
  process.exit(0);
1960
4060
  });
1961
4061
  const prompt = () => {
1962
4062
  return new Promise((resolve) => {
1963
- rl.question(pc3.green("\nYou > "), (answer) => {
4063
+ rl.question(pc6.green("\nYou > "), (answer) => {
1964
4064
  resolve(answer);
1965
4065
  });
1966
4066
  });
1967
4067
  };
1968
4068
  console.log(
1969
4069
  `
1970
- Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit.
4070
+ Type a message, ${pc6.dim("/help")} for commands, or ${pc6.dim("/quit")} to exit.
1971
4071
  `
1972
4072
  );
1973
4073
  if (mcpManager && hooksConfig) {
@@ -1976,14 +4076,14 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
1976
4076
  const session = await onSessionStart(hookCtx);
1977
4077
  if (!session.firstRun) {
1978
4078
  if (session.resumeTopic) {
1979
- console.log(pc3.dim(` Welcome back. Last time we talked about ${session.resumeTopic}`));
4079
+ console.log(pc6.dim(` Welcome back. Last time we talked about ${session.resumeTopic}`));
1980
4080
  } else {
1981
- console.log(pc3.dim(" Welcome back."));
4081
+ console.log(pc6.dim(" Welcome back."));
1982
4082
  }
1983
4083
  }
1984
4084
  if (session.visibleReminders && session.visibleReminders.length > 0) {
1985
4085
  for (const reminder of session.visibleReminders) {
1986
- console.log(pc3.yellow(` Reminder: ${reminder}`));
4086
+ console.log(pc6.yellow(` Reminder: ${reminder}`));
1987
4087
  }
1988
4088
  }
1989
4089
  if (session.contextInjection) {
@@ -1999,9 +4099,15 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
1999
4099
  }
2000
4100
  }
2001
4101
  while (true) {
4102
+ if (bgTasks.hasCompleted) {
4103
+ const bgOutputs = bgTasks.displayCompleted();
4104
+ for (const output of bgOutputs) {
4105
+ messages.push({ role: "user", content: output });
4106
+ }
4107
+ }
2002
4108
  const input = await prompt();
2003
4109
  if (!input.trim()) continue;
2004
- const cmdResult = await handleCommand(input, { model, mcpManager });
4110
+ const cmdResult = await handleCommand(input, { model, mcpManager, llmClient: client, tools });
2005
4111
  if (cmdResult.handled) {
2006
4112
  if (cmdResult.quit) {
2007
4113
  if (mcpManager && hooksConfig) {
@@ -2012,15 +4118,15 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
2012
4118
  log.debug("agent", "session end hook failed on quit", err);
2013
4119
  }
2014
4120
  }
2015
- console.log(pc3.dim("\nGoodbye.\n"));
4121
+ console.log(pc6.dim("\nGoodbye.\n"));
2016
4122
  rl.close();
2017
4123
  return;
2018
4124
  }
2019
4125
  if (cmdResult.exportConversation) {
2020
4126
  try {
2021
- const exportDir = path7.join(os7.homedir(), ".aman-agent", "exports");
2022
- fs7.mkdirSync(exportDir, { recursive: true });
2023
- const exportPath = path7.join(exportDir, `${sessionId}.md`);
4127
+ const exportDir = path12.join(os11.homedir(), ".aman-agent", "exports");
4128
+ fs12.mkdirSync(exportDir, { recursive: true });
4129
+ const exportPath = path12.join(exportDir, `${sessionId}.md`);
2024
4130
  const lines = [
2025
4131
  `# Conversation \u2014 ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
2026
4132
  `**Model:** ${model}`,
@@ -2034,19 +4140,19 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
2034
4140
  lines.push(`${label} ${msg.content}`, "");
2035
4141
  }
2036
4142
  }
2037
- fs7.writeFileSync(exportPath, lines.join("\n"), "utf-8");
2038
- console.log(pc3.green(`Exported to ${exportPath}`));
4143
+ fs12.writeFileSync(exportPath, lines.join("\n"), "utf-8");
4144
+ console.log(pc6.green(`Exported to ${exportPath}`));
2039
4145
  } catch {
2040
- console.log(pc3.red("Failed to export conversation."));
4146
+ console.log(pc6.red("Failed to export conversation."));
2041
4147
  }
2042
4148
  continue;
2043
4149
  }
2044
4150
  if (cmdResult.saveConversation && mcpManager) {
2045
4151
  try {
2046
4152
  await saveConversationToMemory(mcpManager, messages, sessionId);
2047
- console.log(pc3.green("Conversation saved to memory."));
4153
+ console.log(pc6.green("Conversation saved to memory."));
2048
4154
  } catch {
2049
- console.log(pc3.red("Failed to save conversation."));
4155
+ console.log(pc6.red("Failed to save conversation."));
2050
4156
  }
2051
4157
  continue;
2052
4158
  }
@@ -2065,7 +4171,7 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
2065
4171
  const wfMatch = await onWorkflowMatch(input, hookCtx);
2066
4172
  if (wfMatch) {
2067
4173
  const useIt = await new Promise((resolve) => {
2068
- rl.question(pc3.dim(` Workflow "${wfMatch.name}" matches. Use it? (y/N) `), (answer) => resolve(answer.toLowerCase() === "y"));
4174
+ rl.question(pc6.dim(` Workflow "${wfMatch.name}" matches. Use it? (y/N) `), (answer) => resolve(answer.toLowerCase() === "y"));
2069
4175
  });
2070
4176
  if (useIt) {
2071
4177
  activeSystemPrompt = systemPrompt + `
@@ -2073,114 +4179,212 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
2073
4179
  <active-workflow>
2074
4180
  ${wfMatch.steps}
2075
4181
  </active-workflow>`;
2076
- console.log(pc3.dim(` Using "${wfMatch.name}" workflow.`));
4182
+ console.log(pc6.dim(` Using "${wfMatch.name}" workflow.`));
2077
4183
  }
2078
4184
  }
2079
4185
  } catch (err) {
2080
4186
  log.debug("agent", "workflow match failed", err);
2081
4187
  }
2082
4188
  }
4189
+ const activePlan = getActivePlan();
4190
+ if (activePlan) {
4191
+ activeSystemPrompt += "\n\n" + formatPlanForPrompt(activePlan);
4192
+ }
4193
+ if (mcpManager) {
4194
+ try {
4195
+ const skillContext = await autoTriggerSkills(input, mcpManager);
4196
+ if (skillContext) {
4197
+ activeSystemPrompt += "\n\n" + skillContext;
4198
+ }
4199
+ } catch (err) {
4200
+ log.debug("agent", "skill auto-trigger failed", err);
4201
+ }
4202
+ const knowledgeItem = matchKnowledge(input);
4203
+ if (knowledgeItem) {
4204
+ activeSystemPrompt += `
4205
+
4206
+ <knowledge name="${knowledgeItem.name}" category="${knowledgeItem.category}">
4207
+ ${knowledgeItem.description}
4208
+
4209
+ ${knowledgeItem.content}
4210
+ </knowledge>`;
4211
+ }
4212
+ }
2083
4213
  await trimConversation(messages, client);
2084
- let enrichedInput = input;
2085
- const filePathMatch = input.match(/(\/[\w./-]+|~\/[\w./-]+)/);
2086
- if (filePathMatch) {
2087
- let filePath = filePathMatch[1];
4214
+ const textExts = /* @__PURE__ */ new Set([
4215
+ ".txt",
4216
+ ".md",
4217
+ ".json",
4218
+ ".js",
4219
+ ".ts",
4220
+ ".jsx",
4221
+ ".tsx",
4222
+ ".py",
4223
+ ".html",
4224
+ ".css",
4225
+ ".yml",
4226
+ ".yaml",
4227
+ ".toml",
4228
+ ".xml",
4229
+ ".csv",
4230
+ ".sh",
4231
+ ".bash",
4232
+ ".zsh",
4233
+ ".env",
4234
+ ".cfg",
4235
+ ".ini",
4236
+ ".log",
4237
+ ".sql",
4238
+ ".graphql",
4239
+ ".rs",
4240
+ ".go",
4241
+ ".java",
4242
+ ".rb",
4243
+ ".php",
4244
+ ".c",
4245
+ ".cpp",
4246
+ ".h",
4247
+ ".swift",
4248
+ ".kt",
4249
+ ".r",
4250
+ ".lua"
4251
+ ]);
4252
+ const imageExts = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"]);
4253
+ const docExts = /* @__PURE__ */ new Set([".docx", ".doc", ".pdf", ".pptx", ".ppt", ".xlsx", ".xls", ".odt", ".rtf", ".epub"]);
4254
+ const mimeMap = {
4255
+ ".png": "image/png",
4256
+ ".jpg": "image/jpeg",
4257
+ ".jpeg": "image/jpeg",
4258
+ ".gif": "image/gif",
4259
+ ".webp": "image/webp",
4260
+ ".bmp": "image/png"
4261
+ };
4262
+ const maxImageBytes = 20 * 1024 * 1024;
4263
+ let textContent = input;
4264
+ const imageBlocks = [];
4265
+ const filePathMatches = [...input.matchAll(/(\/[\w./-]+|~\/[\w./-]+)/g)];
4266
+ for (const match of filePathMatches) {
4267
+ let filePath = match[1];
2088
4268
  if (filePath.startsWith("~/")) {
2089
- filePath = path7.join(os7.homedir(), filePath.slice(2));
2090
- }
2091
- if (fs7.existsSync(filePath) && fs7.statSync(filePath).isFile()) {
2092
- const ext = path7.extname(filePath).toLowerCase();
2093
- const textExts = /* @__PURE__ */ new Set([
2094
- ".txt",
2095
- ".md",
2096
- ".json",
2097
- ".js",
2098
- ".ts",
2099
- ".jsx",
2100
- ".tsx",
2101
- ".py",
2102
- ".html",
2103
- ".css",
2104
- ".yml",
2105
- ".yaml",
2106
- ".toml",
2107
- ".xml",
2108
- ".csv",
2109
- ".sh",
2110
- ".bash",
2111
- ".zsh",
2112
- ".env",
2113
- ".cfg",
2114
- ".ini",
2115
- ".log",
2116
- ".sql",
2117
- ".graphql",
2118
- ".rs",
2119
- ".go",
2120
- ".java",
2121
- ".rb",
2122
- ".php",
2123
- ".c",
2124
- ".cpp",
2125
- ".h",
2126
- ".swift",
2127
- ".kt",
2128
- ".r",
2129
- ".lua"
2130
- ]);
2131
- if (textExts.has(ext) || ext === "") {
2132
- try {
2133
- const content = fs7.readFileSync(filePath, "utf-8");
2134
- const maxChars = 5e4;
2135
- const trimmed = content.length > maxChars ? content.slice(0, maxChars) + `
4269
+ filePath = path12.join(os11.homedir(), filePath.slice(2));
4270
+ }
4271
+ if (!fs12.existsSync(filePath) || !fs12.statSync(filePath).isFile()) continue;
4272
+ const ext = path12.extname(filePath).toLowerCase();
4273
+ if (imageExts.has(ext)) {
4274
+ try {
4275
+ const stat = fs12.statSync(filePath);
4276
+ if (stat.size > maxImageBytes) {
4277
+ process.stdout.write(pc6.yellow(` [skipped: ${path12.basename(filePath)} \u2014 exceeds 20MB limit]
4278
+ `));
4279
+ continue;
4280
+ }
4281
+ const data = fs12.readFileSync(filePath).toString("base64");
4282
+ const mediaType = mimeMap[ext] || "image/png";
4283
+ imageBlocks.push({
4284
+ type: "image",
4285
+ source: { type: "base64", media_type: mediaType, data }
4286
+ });
4287
+ process.stdout.write(pc6.dim(` [attached image: ${path12.basename(filePath)} (${(stat.size / 1024).toFixed(1)}KB)]
4288
+ `));
4289
+ } catch {
4290
+ process.stdout.write(pc6.dim(` [could not read image: ${filePath}]
4291
+ `));
4292
+ }
4293
+ } else if (textExts.has(ext) || ext === "") {
4294
+ try {
4295
+ const content = fs12.readFileSync(filePath, "utf-8");
4296
+ const maxChars = 5e4;
4297
+ const trimmed = content.length > maxChars ? content.slice(0, maxChars) + `
2136
4298
 
2137
4299
  [... truncated, ${content.length - maxChars} chars remaining]` : content;
2138
- enrichedInput = `${input}
4300
+ textContent += `
2139
4301
 
2140
4302
  <file path="${filePath}" size="${content.length} chars">
2141
4303
  ${trimmed}
2142
4304
  </file>`;
2143
- process.stdout.write(pc3.dim(` [attached: ${path7.basename(filePath)} (${(content.length / 1024).toFixed(1)}KB)]
4305
+ process.stdout.write(pc6.dim(` [attached: ${path12.basename(filePath)} (${(content.length / 1024).toFixed(1)}KB)]
2144
4306
  `));
2145
- } catch {
2146
- process.stdout.write(pc3.dim(` [could not read: ${filePath}]
4307
+ } catch {
4308
+ process.stdout.write(pc6.dim(` [could not read: ${filePath}]
2147
4309
  `));
2148
- }
2149
- } else if ([".docx", ".doc", ".pdf", ".pptx", ".ppt", ".xlsx", ".xls", ".odt", ".rtf", ".epub"].includes(ext)) {
2150
- if (mcpManager) {
2151
- try {
2152
- process.stdout.write(pc3.dim(` [converting: ${path7.basename(filePath)}...]
4310
+ }
4311
+ } else if (docExts.has(ext)) {
4312
+ if (mcpManager) {
4313
+ try {
4314
+ process.stdout.write(pc6.dim(` [converting: ${path12.basename(filePath)}...]
2153
4315
  `));
2154
- const converted = await mcpManager.callTool("doc_convert", { path: filePath });
2155
- if (converted && !converted.startsWith("Error") && !converted.includes("Could not convert")) {
2156
- enrichedInput = `${input}
4316
+ const converted = await mcpManager.callTool("doc_convert", { path: filePath });
4317
+ if (converted && !converted.startsWith("Error") && !converted.includes("Could not convert")) {
4318
+ textContent += `
2157
4319
 
2158
4320
  <file path="${filePath}" format="${ext}">
2159
4321
  ${converted.slice(0, 5e4)}
2160
4322
  </file>`;
2161
- process.stdout.write(pc3.dim(` [attached: ${path7.basename(filePath)} (converted from ${ext})]
4323
+ process.stdout.write(pc6.dim(` [attached: ${path12.basename(filePath)} (converted from ${ext})]
2162
4324
  `));
2163
- } else {
2164
- enrichedInput = `${input}
4325
+ } else {
4326
+ textContent += `
2165
4327
 
2166
4328
  <file-error path="${filePath}">
2167
4329
  ${converted}
2168
4330
  </file-error>`;
2169
- process.stdout.write(pc3.yellow(` [conversion note: ${converted.split("\n")[0]}]
2170
- `));
2171
- }
2172
- } catch {
2173
- process.stdout.write(pc3.dim(` [could not convert: ${path7.basename(filePath)}]
4331
+ process.stdout.write(pc6.yellow(` [conversion note: ${converted.split("\n")[0]}]
2174
4332
  `));
2175
4333
  }
2176
- } else {
2177
- process.stdout.write(pc3.yellow(` Binary file (${ext}) \u2014 install Docling for document support: pip install docling
4334
+ } catch {
4335
+ process.stdout.write(pc6.dim(` [could not convert: ${path12.basename(filePath)}]
2178
4336
  `));
2179
4337
  }
4338
+ } else {
4339
+ process.stdout.write(pc6.yellow(` Binary file (${ext}) \u2014 install Docling for document support: pip install docling
4340
+ `));
4341
+ }
4342
+ }
4343
+ }
4344
+ const urlImageMatches = [...input.matchAll(/https?:\/\/\S+\.(?:png|jpg|jpeg|gif|webp)(?:\?\S*)?/gi)];
4345
+ for (const match of urlImageMatches) {
4346
+ const url = match[0];
4347
+ try {
4348
+ process.stdout.write(pc6.dim(` [fetching image: ${url.slice(0, 60)}...]
4349
+ `));
4350
+ const response = await fetch(url);
4351
+ if (!response.ok) {
4352
+ process.stdout.write(pc6.yellow(` [could not fetch: HTTP ${response.status}]
4353
+ `));
4354
+ continue;
4355
+ }
4356
+ const buffer = Buffer.from(await response.arrayBuffer());
4357
+ if (buffer.length > maxImageBytes) {
4358
+ process.stdout.write(pc6.yellow(` [skipped: image URL exceeds 20MB limit]
4359
+ `));
4360
+ continue;
2180
4361
  }
4362
+ const contentType = response.headers.get("content-type") || "";
4363
+ let mediaType = "image/png";
4364
+ if (contentType.includes("jpeg") || contentType.includes("jpg")) mediaType = "image/jpeg";
4365
+ else if (contentType.includes("gif")) mediaType = "image/gif";
4366
+ else if (contentType.includes("webp")) mediaType = "image/webp";
4367
+ else if (contentType.includes("png")) mediaType = "image/png";
4368
+ imageBlocks.push({
4369
+ type: "image",
4370
+ source: { type: "base64", media_type: mediaType, data: buffer.toString("base64") }
4371
+ });
4372
+ process.stdout.write(pc6.dim(` [attached image URL: (${(buffer.length / 1024).toFixed(1)}KB)]
4373
+ `));
4374
+ } catch {
4375
+ process.stdout.write(pc6.dim(` [could not fetch image: ${url}]
4376
+ `));
2181
4377
  }
2182
4378
  }
2183
- messages.push({ role: "user", content: enrichedInput });
4379
+ if (imageBlocks.length > 0) {
4380
+ const blocks = [
4381
+ { type: "text", text: textContent },
4382
+ ...imageBlocks
4383
+ ];
4384
+ messages.push({ role: "user", content: blocks });
4385
+ } else {
4386
+ messages.push({ role: "user", content: textContent });
4387
+ }
2184
4388
  let augmentedSystemPrompt = activeSystemPrompt;
2185
4389
  let memoryTokens = 0;
2186
4390
  if (mcpManager) {
@@ -2190,15 +4394,39 @@ ${converted}
2190
4394
  memoryTokens = recall.tokenEstimate;
2191
4395
  }
2192
4396
  }
4397
+ const userTurnCount = messages.filter((m) => m.role === "user").length;
4398
+ if (mcpManager && hooksConfig?.personalityAdapt !== false && userTurnCount > 0 && userTurnCount % 5 === 0) {
4399
+ const hour = (/* @__PURE__ */ new Date()).getHours();
4400
+ let period;
4401
+ if (hour < 6) period = "late-night";
4402
+ else if (hour < 12) period = "morning";
4403
+ else if (hour < 17) period = "afternoon";
4404
+ else if (hour < 21) period = "evening";
4405
+ else period = "night";
4406
+ const recentUserMsgs = messages.filter((m) => m.role === "user" && typeof m.content === "string").slice(-5).map((m) => m.content);
4407
+ const sessionMinutes = Math.round((Date.now() - getSessionStartTime()) / 6e4);
4408
+ const state = computePersonality({
4409
+ timePeriod: period,
4410
+ sessionMinutes,
4411
+ turnCount: userTurnCount,
4412
+ recentMessages: recentUserMsgs
4413
+ });
4414
+ syncPersonalityToCore(state, mcpManager).catch(() => {
4415
+ });
4416
+ const nudge = formatWellbeingNudge(state);
4417
+ if (nudge) {
4418
+ augmentedSystemPrompt += "\n" + nudge;
4419
+ }
4420
+ }
2193
4421
  const divider = "\u2500".repeat(Math.min(process.stdout.columns || 60, 60) - aiName.length - 2);
2194
4422
  process.stdout.write(`
2195
- ${pc3.cyan(pc3.bold(aiName))} ${pc3.dim(divider)}
4423
+ ${pc6.cyan(pc6.bold(aiName))} ${pc6.dim(divider)}
2196
4424
 
2197
4425
  `);
2198
4426
  try {
2199
4427
  let response = await withRetry(
2200
4428
  () => client.chat(augmentedSystemPrompt, messages, onChunkHandler, tools),
2201
- { maxAttempts: 3, baseDelay: 1e3, retryable: isRetryable }
4429
+ { maxAttempts: 3, baseDelay: 1e3, retryable: isRetryable2 }
2202
4430
  );
2203
4431
  messages.push(response.message);
2204
4432
  while (response.toolUses.length > 0 && mcpManager) {
@@ -2208,7 +4436,7 @@ ${converted}
2208
4436
  const hookCtx = { mcpManager, config: hooksConfig };
2209
4437
  const check = await onBeforeToolExec(toolUse.name, toolUse.input, hookCtx);
2210
4438
  if (!check.allow) {
2211
- process.stdout.write(pc3.red(` [BLOCKED: ${check.reason}]
4439
+ process.stdout.write(pc6.red(` [BLOCKED: ${check.reason}]
2212
4440
  `));
2213
4441
  return {
2214
4442
  type: "tool_result",
@@ -2218,7 +4446,49 @@ ${converted}
2218
4446
  };
2219
4447
  }
2220
4448
  }
2221
- process.stdout.write(pc3.dim(` [using ${toolUse.name}...]
4449
+ if (toolUse.name === "delegate_task" && mcpManager) {
4450
+ const input2 = toolUse.input;
4451
+ process.stdout.write(pc6.dim(`
4452
+ [delegating to ${input2.profile}...]
4453
+
4454
+ `));
4455
+ const result2 = await delegateTask(input2.task, input2.profile, client, mcpManager, { tools });
4456
+ const output = result2.success ? `[${input2.profile}] completed:
4457
+
4458
+ ${result2.response}` : `[${input2.profile}] failed: ${result2.error}`;
4459
+ return {
4460
+ type: "tool_result",
4461
+ tool_use_id: toolUse.id,
4462
+ content: output
4463
+ };
4464
+ }
4465
+ if (toolUse.name === "team_run" && mcpManager) {
4466
+ const input2 = toolUse.input;
4467
+ const team = loadTeam(input2.team);
4468
+ if (!team) {
4469
+ return {
4470
+ type: "tool_result",
4471
+ tool_use_id: toolUse.id,
4472
+ content: `Team not found: ${input2.team}`,
4473
+ is_error: true
4474
+ };
4475
+ }
4476
+ const result2 = await runTeam(team, input2.task, client, mcpManager, tools);
4477
+ return {
4478
+ type: "tool_result",
4479
+ tool_use_id: toolUse.id,
4480
+ content: result2.success ? formatTeamResult(result2) : `Team execution failed: ${result2.finalOutput}`
4481
+ };
4482
+ }
4483
+ if (shouldRunInBackground(toolUse.name)) {
4484
+ const task = bgTasks.launch(toolUse.name, toolUse.id, mcpManager, toolUse.input);
4485
+ return {
4486
+ type: "tool_result",
4487
+ tool_use_id: toolUse.id,
4488
+ content: `[${toolUse.name} launched in background (${task.id}). Results will appear when ready. Continue with other work.]`
4489
+ };
4490
+ }
4491
+ process.stdout.write(pc6.dim(` [using ${toolUse.name}...]
2222
4492
  `));
2223
4493
  const result = await mcpManager.callTool(toolUse.name, toolUse.input);
2224
4494
  const skipLogging = ["memory_log", "memory_recall", "memory_context", "memory_detail", "reminder_check"].includes(toolUse.name);
@@ -2243,7 +4513,7 @@ ${converted}
2243
4513
  });
2244
4514
  response = await withRetry(
2245
4515
  () => client.chat(augmentedSystemPrompt, messages, onChunkHandler, tools),
2246
- { maxAttempts: 3, baseDelay: 1e3, retryable: isRetryable }
4516
+ { maxAttempts: 3, baseDelay: 1e3, retryable: isRetryable2 }
2247
4517
  );
2248
4518
  messages.push(response.message);
2249
4519
  }
@@ -2251,7 +4521,7 @@ ${converted}
2251
4521
  if (memoryTokens > 0) footerParts.push(`memories: ~${memoryTokens} tokens`);
2252
4522
  const footer = footerParts.length > 0 ? ` ${footerParts.join(" | ")}` : "";
2253
4523
  const footerDivider = "\u2500".repeat(Math.min(process.stdout.columns || 60, 60) - footer.length - 1);
2254
- process.stdout.write(pc3.dim(` ${footerDivider}${footer}
4524
+ process.stdout.write(pc6.dim(` ${footerDivider}${footer}
2255
4525
  `));
2256
4526
  if (mcpManager && hooksConfig?.extractMemories) {
2257
4527
  const assistantText = typeof response.message.content === "string" ? response.message.content : response.message.content.filter((b) => b.type === "text").map((b) => "text" in b ? b.text : "").join("");
@@ -2264,7 +4534,7 @@ ${converted}
2264
4534
  extractorState
2265
4535
  );
2266
4536
  if (count > 0) {
2267
- process.stdout.write(pc3.dim(` [${count} memory${count > 1 ? "ies" : ""} stored]
4537
+ process.stdout.write(pc6.dim(` [${count} memory${count > 1 ? "ies" : ""} stored]
2268
4538
  `));
2269
4539
  }
2270
4540
  }
@@ -2273,11 +4543,11 @@ ${converted}
2273
4543
  }
2274
4544
  if (hooksConfig?.featureHints) {
2275
4545
  hintState.turnCount++;
2276
- const hasWorkflows = fs7.existsSync(path7.join(os7.homedir(), ".aflow", "flow.md"));
4546
+ const hasWorkflows = fs12.existsSync(path12.join(os11.homedir(), ".aflow", "flow.md"));
2277
4547
  const memoryCount = memoryTokens > 0 ? Math.floor(memoryTokens / 5) : 0;
2278
4548
  const hint = getHint(hintState, { hasWorkflows, memoryCount });
2279
4549
  if (hint) {
2280
- process.stdout.write(pc3.dim(` ${hint}
4550
+ process.stdout.write(pc6.dim(` ${hint}
2281
4551
  `));
2282
4552
  saveShownHints(hintState.shownHints);
2283
4553
  }
@@ -2285,7 +4555,7 @@ ${converted}
2285
4555
  } catch (error) {
2286
4556
  const rawMessage = error instanceof Error ? error.message : "Unknown error occurred";
2287
4557
  const friendly = humanizeError(rawMessage);
2288
- console.error(pc3.red(`
4558
+ console.error(pc6.red(`
2289
4559
  ${friendly}`));
2290
4560
  }
2291
4561
  }
@@ -2307,9 +4577,9 @@ async function saveConversationToMemory(mcpManager, messages, sessionId) {
2307
4577
  }
2308
4578
 
2309
4579
  // src/index.ts
2310
- import fs8 from "fs";
2311
- import path8 from "path";
2312
- import os8 from "os";
4580
+ import fs13 from "fs";
4581
+ import path13 from "path";
4582
+ import os12 from "os";
2313
4583
 
2314
4584
  // src/presets.ts
2315
4585
  var PRESETS = {
@@ -2418,9 +4688,9 @@ ${wfSections}`;
2418
4688
 
2419
4689
  // src/index.ts
2420
4690
  async function autoDetectConfig() {
2421
- const reconfigMarker = path8.join(os8.homedir(), ".aman-agent", ".reconfig");
2422
- if (fs8.existsSync(reconfigMarker)) {
2423
- fs8.unlinkSync(reconfigMarker);
4691
+ const reconfigMarker = path13.join(os12.homedir(), ".aman-agent", ".reconfig");
4692
+ if (fs13.existsSync(reconfigMarker)) {
4693
+ fs13.unlinkSync(reconfigMarker);
2424
4694
  return null;
2425
4695
  }
2426
4696
  const anthropicKey = process.env.ANTHROPIC_API_KEY;
@@ -2449,11 +4719,11 @@ async function autoDetectConfig() {
2449
4719
  return null;
2450
4720
  }
2451
4721
  function bootstrapEcosystem() {
2452
- const home2 = os8.homedir();
2453
- const corePath = path8.join(home2, ".acore", "core.md");
2454
- if (fs8.existsSync(corePath)) return false;
2455
- fs8.mkdirSync(path8.join(home2, ".acore"), { recursive: true });
2456
- fs8.writeFileSync(corePath, [
4722
+ const home2 = os12.homedir();
4723
+ const corePath = path13.join(home2, ".acore", "core.md");
4724
+ if (fs13.existsSync(corePath)) return false;
4725
+ fs13.mkdirSync(path13.join(home2, ".acore"), { recursive: true });
4726
+ fs13.writeFileSync(corePath, [
2457
4727
  "# Aman",
2458
4728
  "",
2459
4729
  "## Personality",
@@ -2465,11 +4735,11 @@ function bootstrapEcosystem() {
2465
4735
  "## Session",
2466
4736
  "_New companion \u2014 no prior sessions._"
2467
4737
  ].join("\n"), "utf-8");
2468
- const rulesDir = path8.join(home2, ".arules");
2469
- const rulesPath = path8.join(rulesDir, "rules.md");
2470
- if (!fs8.existsSync(rulesPath)) {
2471
- fs8.mkdirSync(rulesDir, { recursive: true });
2472
- fs8.writeFileSync(rulesPath, [
4738
+ const rulesDir = path13.join(home2, ".arules");
4739
+ const rulesPath = path13.join(rulesDir, "rules.md");
4740
+ if (!fs13.existsSync(rulesPath)) {
4741
+ fs13.mkdirSync(rulesDir, { recursive: true });
4742
+ fs13.writeFileSync(rulesPath, [
2473
4743
  "# Guardrails",
2474
4744
  "",
2475
4745
  "## safety",
@@ -2484,16 +4754,16 @@ function bootstrapEcosystem() {
2484
4754
  return true;
2485
4755
  }
2486
4756
  var program = new Command();
2487
- 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) => {
2488
- p2.intro(pc4.bold("aman agent") + pc4.dim(" \u2014 your AI companion"));
4757
+ 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).option("--profile <name>", "Use a specific agent profile (e.g., coder, writer, researcher)").action(async (options) => {
4758
+ p2.intro(pc7.bold("aman agent") + pc7.dim(" \u2014 your AI companion"));
2489
4759
  let config = loadConfig();
2490
4760
  if (!config) {
2491
4761
  const detected = await autoDetectConfig();
2492
4762
  if (detected) {
2493
4763
  config = detected;
2494
4764
  const providerLabel = detected.provider === "anthropic" ? "Anthropic API key" : detected.provider === "openai" ? "OpenAI API key" : "Ollama";
2495
- p2.log.success(`Auto-detected ${providerLabel}. Using ${pc4.bold(detected.model)}.`);
2496
- p2.log.info(pc4.dim("Change anytime with /reconfig"));
4765
+ p2.log.success(`Auto-detected ${providerLabel}. Using ${pc7.bold(detected.model)}.`);
4766
+ p2.log.info(pc7.dim("Change anytime with /reconfig"));
2497
4767
  saveConfig(config);
2498
4768
  } else {
2499
4769
  p2.log.info("First-time setup \u2014 configure your LLM connection.");
@@ -2524,7 +4794,7 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
2524
4794
  defaultModel = modelInput || "llama3.2";
2525
4795
  } else if (provider === "anthropic") {
2526
4796
  p2.log.info("Get your API key from: https://console.anthropic.com/settings/keys");
2527
- p2.log.info(pc4.dim("Note: API access is separate from Claude Pro subscription. You need API credits."));
4797
+ p2.log.info(pc7.dim("Note: API access is separate from Claude Pro subscription. You need API credits."));
2528
4798
  apiKey = await p2.text({
2529
4799
  message: "API key (starts with sk-ant-)",
2530
4800
  validate: (v) => v.length === 0 ? "API key is required" : void 0
@@ -2590,29 +4860,27 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
2590
4860
  const s = p2.spinner();
2591
4861
  s.start("Loading ecosystem");
2592
4862
  bootstrapEcosystem();
4863
+ const profile = options.profile || process.env.AMAN_PROFILE || void 0;
4864
+ if (profile) {
4865
+ p2.log.info(`Profile: ${pc7.bold(profile)}`);
4866
+ }
2593
4867
  const budget = options.budget || void 0;
2594
- const { prompt: systemPrompt, layers, truncated, totalTokens } = assembleSystemPrompt(budget);
4868
+ const { prompt: systemPrompt, layers, truncated, totalTokens } = assembleSystemPrompt(budget, profile);
2595
4869
  s.stop("Ecosystem loaded");
2596
4870
  if (layers.length === 0) {
2597
4871
  p2.log.warning(
2598
- "No ecosystem configured. Run " + pc4.bold("npx @aman_asmuei/aman") + " first."
4872
+ "No ecosystem configured. Run " + pc7.bold("npx @aman_asmuei/aman") + " first."
2599
4873
  );
2600
4874
  p2.log.info("Starting with empty system prompt.");
2601
4875
  } else {
2602
4876
  p2.log.success(
2603
- `Loaded: ${layers.join(", ")} ${pc4.dim(`(${totalTokens.toLocaleString()} tokens)`)}`
4877
+ `Loaded: ${layers.join(", ")} ${pc7.dim(`(${totalTokens.toLocaleString()} tokens)`)}`
2604
4878
  );
2605
4879
  if (truncated.length > 0) {
2606
- p2.log.warning(`Truncated: ${truncated.join(", ")} ${pc4.dim("(over budget)")}`);
4880
+ p2.log.warning(`Truncated: ${truncated.join(", ")} ${pc7.dim("(over budget)")}`);
2607
4881
  }
2608
4882
  }
2609
- const corePath = path8.join(os8.homedir(), ".acore", "core.md");
2610
- let aiName = "Assistant";
2611
- if (fs8.existsSync(corePath)) {
2612
- const content = fs8.readFileSync(corePath, "utf-8");
2613
- const match = content.match(/^# (.+)$/m);
2614
- if (match) aiName = match[1];
2615
- }
4883
+ const aiName = getProfileAiName(profile);
2616
4884
  const mcpManager = new McpManager();
2617
4885
  const mcpSpinner = p2.spinner();
2618
4886
  mcpSpinner.start("Connecting to MCP servers");
@@ -2639,7 +4907,7 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
2639
4907
  memSpinner.stop("Memory consolidated");
2640
4908
  if (report.merged > 0 || report.pruned > 0 || report.promoted > 0) {
2641
4909
  p2.log.info(
2642
- `Memory health: ${report.healthScore ?? "?"}% ` + pc4.dim(`(merged ${report.merged}, pruned ${report.pruned}, promoted ${report.promoted})`)
4910
+ `Memory health: ${report.healthScore ?? "?"}% ` + pc7.dim(`(merged ${report.merged}, pruned ${report.pruned}, promoted ${report.promoted})`)
2643
4911
  );
2644
4912
  }
2645
4913
  } catch {
@@ -2670,7 +4938,7 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
2670
4938
  } else {
2671
4939
  client = createOpenAIClient(config.apiKey, model);
2672
4940
  }
2673
- p2.log.success(`${pc4.bold(aiName)} is ready. Model: ${pc4.dim(model)}`);
4941
+ p2.log.success(`${pc7.bold(aiName)} is ready. Model: ${pc7.dim(model)}`);
2674
4942
  await runAgent(
2675
4943
  client,
2676
4944
  systemPrompt,
@@ -2683,7 +4951,7 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
2683
4951
  await mcpManager.disconnect();
2684
4952
  });
2685
4953
  program.command("init").description("Set up your AI companion with a guided wizard").action(async () => {
2686
- p2.intro(pc4.bold("aman agent init") + pc4.dim(" \u2014 set up your companion"));
4954
+ p2.intro(pc7.bold("aman agent init") + pc7.dim(" \u2014 set up your companion"));
2687
4955
  const name = await p2.text({
2688
4956
  message: "What should your companion be called?",
2689
4957
  placeholder: "Aman",
@@ -2703,19 +4971,19 @@ program.command("init").description("Set up your AI companion with a guided wiza
2703
4971
  });
2704
4972
  if (p2.isCancel(preset)) process.exit(0);
2705
4973
  const result = applyPreset(preset, name || "Aman");
2706
- const home2 = os8.homedir();
2707
- fs8.mkdirSync(path8.join(home2, ".acore"), { recursive: true });
2708
- fs8.writeFileSync(path8.join(home2, ".acore", "core.md"), result.coreMd, "utf-8");
4974
+ const home2 = os12.homedir();
4975
+ fs13.mkdirSync(path13.join(home2, ".acore"), { recursive: true });
4976
+ fs13.writeFileSync(path13.join(home2, ".acore", "core.md"), result.coreMd, "utf-8");
2709
4977
  p2.log.success(`Identity created \u2014 ${PRESETS[preset].identity.personality.split(".")[0].toLowerCase()}`);
2710
4978
  if (result.rulesMd) {
2711
- fs8.mkdirSync(path8.join(home2, ".arules"), { recursive: true });
2712
- fs8.writeFileSync(path8.join(home2, ".arules", "rules.md"), result.rulesMd, "utf-8");
4979
+ fs13.mkdirSync(path13.join(home2, ".arules"), { recursive: true });
4980
+ fs13.writeFileSync(path13.join(home2, ".arules", "rules.md"), result.rulesMd, "utf-8");
2713
4981
  const ruleCount = (result.rulesMd.match(/^- /gm) || []).length;
2714
4982
  p2.log.success(`${ruleCount} rules set`);
2715
4983
  }
2716
4984
  if (result.flowMd) {
2717
- fs8.mkdirSync(path8.join(home2, ".aflow"), { recursive: true });
2718
- fs8.writeFileSync(path8.join(home2, ".aflow", "flow.md"), result.flowMd, "utf-8");
4985
+ fs13.mkdirSync(path13.join(home2, ".aflow"), { recursive: true });
4986
+ fs13.writeFileSync(path13.join(home2, ".aflow", "flow.md"), result.flowMd, "utf-8");
2719
4987
  const wfCount = (result.flowMd.match(/^## /gm) || []).length;
2720
4988
  p2.log.success(`${wfCount} workflow${wfCount > 1 ? "s" : ""} added`);
2721
4989
  }
@@ -2723,16 +4991,16 @@ program.command("init").description("Set up your AI companion with a guided wiza
2723
4991
  p2.outro("Your companion is ready.");
2724
4992
  console.log("");
2725
4993
  if (isNpx) {
2726
- console.log(` ${pc4.bold("Start chatting:")} npx @aman_asmuei/aman-agent`);
4994
+ console.log(` ${pc7.bold("Start chatting:")} npx @aman_asmuei/aman-agent`);
2727
4995
  console.log("");
2728
- console.log(` ${pc4.dim("Tip: Install globally to use")} ${pc4.bold("aman-agent")} ${pc4.dim("directly:")}`);
2729
- console.log(` ${pc4.dim(" npm install -g @aman_asmuei/aman-agent")}`);
4996
+ console.log(` ${pc7.dim("Tip: Install globally to use")} ${pc7.bold("aman-agent")} ${pc7.dim("directly:")}`);
4997
+ console.log(` ${pc7.dim(" npm install -g @aman_asmuei/aman-agent")}`);
2730
4998
  } else {
2731
- console.log(` ${pc4.bold("Start chatting:")} aman-agent`);
4999
+ console.log(` ${pc7.bold("Start chatting:")} aman-agent`);
2732
5000
  }
2733
5001
  console.log("");
2734
- console.log(` ${pc4.dim("Add tools:")} npx @aman_asmuei/akit add github`);
2735
- console.log(` ${pc4.dim("Browse:")} npx @aman_asmuei/akit search <query>`);
5002
+ console.log(` ${pc7.dim("Add tools:")} npx @aman_asmuei/akit add github`);
5003
+ console.log(` ${pc7.dim("Browse:")} npx @aman_asmuei/akit search <query>`);
2736
5004
  console.log("");
2737
5005
  });
2738
5006
  program.parse();