@aman_asmuei/aman-agent 0.8.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,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";
@@ -745,20 +797,20 @@ var McpManager = class {
745
797
 
746
798
  // src/agent.ts
747
799
  import * as readline from "readline";
748
- import fs7 from "fs";
749
- import path7 from "path";
750
- import os7 from "os";
751
- import pc3 from "picocolors";
800
+ import fs13 from "fs";
801
+ import path13 from "path";
802
+ import os12 from "os";
803
+ import pc6 from "picocolors";
752
804
  import { marked } from "marked";
753
805
  import { markedTerminal } from "marked-terminal";
754
806
  import logUpdate from "log-update";
755
807
 
756
808
  // src/commands.ts
757
- import fs5 from "fs";
758
- import path5 from "path";
759
- import os5 from "os";
809
+ import fs9 from "fs";
810
+ import path9 from "path";
811
+ import os9 from "os";
760
812
  import { execFileSync } from "child_process";
761
- import pc from "picocolors";
813
+ import pc3 from "picocolors";
762
814
 
763
815
  // src/layers/parsers.ts
764
816
  import fs4 from "fs";
@@ -817,12 +869,796 @@ function getEcosystemStatus(mcpToolCount, amemConnected) {
817
869
  };
818
870
  }
819
871
 
872
+ // src/memory.ts
873
+ import {
874
+ createDatabase,
875
+ recall,
876
+ buildContext,
877
+ storeMemory,
878
+ consolidateMemories,
879
+ cosineSimilarity,
880
+ preloadEmbeddings,
881
+ buildVectorIndex
882
+ } from "@aman_asmuei/amem-core";
883
+ import path5 from "path";
884
+ import os5 from "os";
885
+ import fs5 from "fs";
886
+ var db = null;
887
+ var currentProject = "global";
888
+ async function initMemory(project) {
889
+ if (db) return db;
890
+ const amemDir = process.env.AMEM_DIR ?? path5.join(os5.homedir(), ".amem");
891
+ if (!fs5.existsSync(amemDir)) fs5.mkdirSync(amemDir, { recursive: true });
892
+ const dbPath = process.env.AMEM_DB ?? path5.join(amemDir, "memory.db");
893
+ db = createDatabase(dbPath);
894
+ currentProject = project ?? "global";
895
+ preloadEmbeddings();
896
+ setTimeout(() => {
897
+ try {
898
+ buildVectorIndex(db);
899
+ } catch {
900
+ }
901
+ }, 1e3);
902
+ return db;
903
+ }
904
+ function getDb() {
905
+ if (!db) throw new Error("Memory not initialized \u2014 call initMemory() first");
906
+ return db;
907
+ }
908
+ async function memoryRecall(query, opts) {
909
+ return recall(getDb(), {
910
+ query,
911
+ limit: opts?.limit ?? 10,
912
+ compact: opts?.compact ?? true,
913
+ type: opts?.type,
914
+ tag: opts?.tag,
915
+ minConfidence: opts?.minConfidence,
916
+ explain: opts?.explain,
917
+ scope: currentProject
918
+ });
919
+ }
920
+ async function memoryContext(topic, maxTokens) {
921
+ return buildContext(getDb(), topic, { maxTokens, scope: currentProject });
922
+ }
923
+ async function memoryStore(opts) {
924
+ return storeMemory(getDb(), opts);
925
+ }
926
+ function memoryLog(sessionId, role, content) {
927
+ return getDb().appendLog({
928
+ sessionId,
929
+ role,
930
+ content,
931
+ project: currentProject,
932
+ metadata: {}
933
+ });
934
+ }
935
+ function reminderCheck() {
936
+ return getDb().checkReminders();
937
+ }
938
+ function memoryConsolidate(dryRun = false) {
939
+ return consolidateMemories(getDb(), cosineSimilarity, {
940
+ dryRun,
941
+ maxStaleDays: 90,
942
+ minConfidence: 0.3,
943
+ minAccessCount: 0
944
+ });
945
+ }
946
+
947
+ // src/profile-templates.ts
948
+ import fs6 from "fs";
949
+ import path6 from "path";
950
+ import os6 from "os";
951
+ var BUILT_IN_PROFILES = [
952
+ {
953
+ name: "coder",
954
+ label: "Coder",
955
+ description: "Direct, technical, code-first. Skips pleasantries, shows code.",
956
+ core: `# Coder
957
+
958
+ ## Identity
959
+ - Role: Coder is your technical pair programmer
960
+ - Personality: direct, precise, efficient \u2014 code speaks louder than words
961
+ - Communication: lead with code, explain after. No fluff.
962
+ - Values: simplicity over cleverness, working code over perfect code, tests over trust
963
+ - Boundaries: won't pretend to be human, flags when out of depth
964
+
965
+ ### Appearance
966
+ - Base: focused developer, dark hoodie, terminal glow
967
+ - Style: minimal
968
+ - Palette: green on black`,
969
+ rules: `# Coder Rules
970
+
971
+ ## Always
972
+ - Show code before explaining
973
+ - Include error handling
974
+ - Suggest tests for new code
975
+
976
+ ## Never
977
+ - Write code without understanding the requirement
978
+ - Push to main without tests
979
+ - Ignore security implications`
980
+ },
981
+ {
982
+ name: "writer",
983
+ label: "Writer",
984
+ description: "Creative, eloquent, story-driven. Focuses on narrative and engagement.",
985
+ core: `# Muse
986
+
987
+ ## Identity
988
+ - Role: Muse is your creative writing partner
989
+ - Personality: eloquent, imaginative, encouraging \u2014 finds the story in everything
990
+ - Communication: explore ideas together, offer alternatives, celebrate good writing
991
+ - Values: authenticity over formulas, voice over grammar, emotion over information
992
+ - Boundaries: won't write without understanding the audience, flags when content is sensitive
993
+
994
+ ### Appearance
995
+ - Base: warm expression, creative energy, pen in hand
996
+ - Style: illustrated
997
+ - Palette: warm amber and cream`,
998
+ rules: `# Writer Rules
999
+
1000
+ ## Always
1001
+ - Ask about the target audience
1002
+ - Offer 2-3 angle options before drafting
1003
+ - Read drafts aloud mentally for rhythm
1004
+
1005
+ ## Never
1006
+ - Use cliches without subverting them
1007
+ - Write without a clear hook
1008
+ - Ignore tone consistency`
1009
+ },
1010
+ {
1011
+ name: "researcher",
1012
+ label: "Researcher",
1013
+ description: "Analytical, thorough, citation-focused. Digs deep, verifies claims.",
1014
+ core: `# Scholar
1015
+
1016
+ ## Identity
1017
+ - Role: Scholar is your research analyst
1018
+ - Personality: analytical, thorough, intellectually curious \u2014 never takes claims at face value
1019
+ - Communication: present findings with evidence, flag uncertainty, compare perspectives
1020
+ - Values: accuracy over speed, nuance over simplification, primary sources over summaries
1021
+ - Boundaries: clearly marks speculation vs fact, flags when evidence is insufficient
1022
+
1023
+ ### Appearance
1024
+ - Base: thoughtful expression, glasses, surrounded by notes
1025
+ - Style: minimal
1026
+ - Palette: navy and white`,
1027
+ rules: `# Researcher Rules
1028
+
1029
+ ## Always
1030
+ - Cite sources when making factual claims
1031
+ - Flag confidence level (high/medium/low)
1032
+ - Present multiple perspectives on contested topics
1033
+
1034
+ ## Never
1035
+ - Present speculation as fact
1036
+ - Ignore contradicting evidence
1037
+ - Oversimplify complex topics`
1038
+ }
1039
+ ];
1040
+ function installProfileTemplate(templateName, userName) {
1041
+ const template = BUILT_IN_PROFILES.find((t) => t.name === templateName);
1042
+ if (!template) return null;
1043
+ const profileDir = path6.join(os6.homedir(), ".acore", "profiles", template.name);
1044
+ if (fs6.existsSync(profileDir)) return `Profile already exists: ${template.name}`;
1045
+ fs6.mkdirSync(profileDir, { recursive: true });
1046
+ let core = template.core;
1047
+ if (userName) {
1048
+ core += `
1049
+
1050
+ ---
1051
+
1052
+ ## Relationship
1053
+ - Name: ${userName}
1054
+ - Nicknames: []
1055
+ - Communication: [updated over time]
1056
+ - Detail level: balanced
1057
+ `;
1058
+ }
1059
+ fs6.writeFileSync(path6.join(profileDir, "core.md"), core, "utf-8");
1060
+ if (template.rules) {
1061
+ fs6.writeFileSync(path6.join(profileDir, "rules.md"), template.rules, "utf-8");
1062
+ }
1063
+ if (template.skills) {
1064
+ fs6.writeFileSync(path6.join(profileDir, "skills.md"), template.skills, "utf-8");
1065
+ }
1066
+ return null;
1067
+ }
1068
+
1069
+ // src/delegate.ts
1070
+ import pc from "picocolors";
1071
+ var isRetryable = (err) => {
1072
+ if (err instanceof Error) {
1073
+ const msg = err.message.toLowerCase();
1074
+ return msg.includes("rate") || msg.includes("timeout") || msg.includes("econnreset");
1075
+ }
1076
+ return false;
1077
+ };
1078
+ async function delegateTask(task, profile, client, mcpManager, options = {}) {
1079
+ const maxTurns = options.maxTurns ?? 10;
1080
+ const silent = options.silent ?? false;
1081
+ const tools = options.tools;
1082
+ try {
1083
+ const { prompt: systemPrompt } = assembleSystemPrompt(void 0, profile);
1084
+ const delegationPrompt = `${systemPrompt}
1085
+
1086
+ <delegation>
1087
+ 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.
1088
+ </delegation>`;
1089
+ const messages = [
1090
+ { role: "user", content: task }
1091
+ ];
1092
+ const toolsUsed = [];
1093
+ let turns = 0;
1094
+ const onChunk = silent ? () => {
1095
+ } : (chunk) => {
1096
+ if (chunk.type === "text" && chunk.text) {
1097
+ process.stdout.write(chunk.text);
1098
+ }
1099
+ };
1100
+ let response = await withRetry(
1101
+ () => client.chat(delegationPrompt, messages, onChunk, tools),
1102
+ { maxAttempts: 2, baseDelay: 1e3, retryable: isRetryable }
1103
+ );
1104
+ messages.push(response.message);
1105
+ while (response.toolUses.length > 0 && turns < maxTurns) {
1106
+ turns++;
1107
+ const toolResults = await Promise.all(
1108
+ response.toolUses.map(async (toolUse) => {
1109
+ if (!silent) {
1110
+ process.stdout.write(pc.dim(` [${profile}:${toolUse.name}...]
1111
+ `));
1112
+ }
1113
+ toolsUsed.push(toolUse.name);
1114
+ try {
1115
+ const result = await mcpManager.callTool(toolUse.name, toolUse.input);
1116
+ return {
1117
+ type: "tool_result",
1118
+ tool_use_id: toolUse.id,
1119
+ content: result
1120
+ };
1121
+ } catch (err) {
1122
+ return {
1123
+ type: "tool_result",
1124
+ tool_use_id: toolUse.id,
1125
+ content: `Error: ${err instanceof Error ? err.message : String(err)}`,
1126
+ is_error: true
1127
+ };
1128
+ }
1129
+ })
1130
+ );
1131
+ messages.push({ role: "user", content: toolResults });
1132
+ response = await withRetry(
1133
+ () => client.chat(delegationPrompt, messages, onChunk, tools),
1134
+ { maxAttempts: 2, baseDelay: 1e3, retryable: isRetryable }
1135
+ );
1136
+ messages.push(response.message);
1137
+ }
1138
+ const finalMessage = response.message;
1139
+ const responseText = typeof finalMessage.content === "string" ? finalMessage.content : finalMessage.content.filter((b) => b.type === "text").map((b) => "text" in b ? b.text : "").join("");
1140
+ return {
1141
+ profile,
1142
+ task,
1143
+ response: responseText,
1144
+ toolsUsed: [...new Set(toolsUsed)],
1145
+ turns,
1146
+ success: true
1147
+ };
1148
+ } catch (err) {
1149
+ const error = err instanceof Error ? err.message : String(err);
1150
+ log.warn("delegate", `Delegation to ${profile} failed: ${error}`);
1151
+ return {
1152
+ profile,
1153
+ task,
1154
+ response: "",
1155
+ toolsUsed: [],
1156
+ turns: 0,
1157
+ success: false,
1158
+ error
1159
+ };
1160
+ }
1161
+ }
1162
+ async function delegateParallel(tasks, client, mcpManager, options = {}) {
1163
+ return Promise.all(
1164
+ tasks.map(
1165
+ ({ task, profile }) => delegateTask(task, profile, client, mcpManager, { ...options, silent: true })
1166
+ )
1167
+ );
1168
+ }
1169
+ async function delegatePipeline(steps, initialInput, client, mcpManager, options = {}) {
1170
+ const results = [];
1171
+ let previousResult = initialInput;
1172
+ for (const step of steps) {
1173
+ const task = step.taskTemplate.replace("{{input}}", previousResult);
1174
+ if (!options.silent) {
1175
+ process.stdout.write(pc.dim(`
1176
+ [delegating to ${step.profile}...]
1177
+ `));
1178
+ }
1179
+ const result = await delegateTask(task, step.profile, client, mcpManager, {
1180
+ ...options,
1181
+ silent: true
1182
+ });
1183
+ results.push(result);
1184
+ if (!result.success) break;
1185
+ previousResult = result.response;
1186
+ }
1187
+ return results;
1188
+ }
1189
+
1190
+ // src/teams.ts
1191
+ import fs7 from "fs";
1192
+ import path7 from "path";
1193
+ import os7 from "os";
1194
+ import pc2 from "picocolors";
1195
+ function getTeamsDir() {
1196
+ return path7.join(os7.homedir(), ".acore", "teams");
1197
+ }
1198
+ function ensureTeamsDir() {
1199
+ const dir = getTeamsDir();
1200
+ if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
1201
+ return dir;
1202
+ }
1203
+ function teamPath(name) {
1204
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
1205
+ return path7.join(ensureTeamsDir(), `${slug}.json`);
1206
+ }
1207
+ function createTeam(team) {
1208
+ const fp = teamPath(team.name);
1209
+ fs7.writeFileSync(fp, JSON.stringify(team, null, 2), "utf-8");
1210
+ }
1211
+ function loadTeam(name) {
1212
+ const fp = teamPath(name);
1213
+ if (!fs7.existsSync(fp)) return null;
1214
+ try {
1215
+ return JSON.parse(fs7.readFileSync(fp, "utf-8"));
1216
+ } catch {
1217
+ return null;
1218
+ }
1219
+ }
1220
+ function listTeams() {
1221
+ const dir = getTeamsDir();
1222
+ if (!fs7.existsSync(dir)) return [];
1223
+ const teams = [];
1224
+ for (const file of fs7.readdirSync(dir)) {
1225
+ if (!file.endsWith(".json")) continue;
1226
+ try {
1227
+ const content = fs7.readFileSync(path7.join(dir, file), "utf-8");
1228
+ teams.push(JSON.parse(content));
1229
+ } catch {
1230
+ }
1231
+ }
1232
+ return teams;
1233
+ }
1234
+ function deleteTeam(name) {
1235
+ const fp = teamPath(name);
1236
+ if (!fs7.existsSync(fp)) return false;
1237
+ fs7.unlinkSync(fp);
1238
+ return true;
1239
+ }
1240
+ async function runTeam(team, task, client, mcpManager, tools) {
1241
+ process.stdout.write(pc2.dim(`
1242
+ Team: ${team.name} (${team.workflow} mode)
1243
+ `));
1244
+ process.stdout.write(pc2.dim(` Members: ${team.members.map((m) => m.profile).join(", ")}
1245
+
1246
+ `));
1247
+ switch (team.workflow) {
1248
+ case "pipeline":
1249
+ return runPipeline(team, task, client, mcpManager, tools);
1250
+ case "parallel":
1251
+ return runParallel(team, task, client, mcpManager, tools);
1252
+ case "coordinator":
1253
+ return runCoordinator(team, task, client, mcpManager, tools);
1254
+ default:
1255
+ return {
1256
+ team: team.name,
1257
+ task,
1258
+ workflow: team.workflow,
1259
+ results: [],
1260
+ finalOutput: `Unknown workflow mode: ${team.workflow}`,
1261
+ success: false
1262
+ };
1263
+ }
1264
+ }
1265
+ async function runPipeline(team, task, client, mcpManager, tools) {
1266
+ const steps = team.members.map((m, i) => ({
1267
+ profile: m.profile,
1268
+ taskTemplate: i === 0 ? `${task}
1269
+
1270
+ Your role: ${m.role}` : `${m.role}. Here is the previous agent's work:
1271
+
1272
+ {{input}}`
1273
+ }));
1274
+ for (const step of steps) {
1275
+ process.stdout.write(pc2.dim(` [${step.profile}: ${team.members.find((m) => m.profile === step.profile)?.role}...]
1276
+ `));
1277
+ }
1278
+ const results = await delegatePipeline(steps, task, client, mcpManager, {
1279
+ tools,
1280
+ silent: true
1281
+ });
1282
+ const lastResult = results[results.length - 1];
1283
+ const success = results.every((r) => r.success);
1284
+ return {
1285
+ team: team.name,
1286
+ task,
1287
+ workflow: "pipeline",
1288
+ results,
1289
+ finalOutput: lastResult?.response || "",
1290
+ success
1291
+ };
1292
+ }
1293
+ async function runParallel(team, task, client, mcpManager, tools) {
1294
+ const tasks = team.members.map((m) => ({
1295
+ profile: m.profile,
1296
+ task: `${task}
1297
+
1298
+ Your specific role: ${m.role}. Focus only on your role.`
1299
+ }));
1300
+ for (const m of team.members) {
1301
+ process.stdout.write(pc2.dim(` [${m.profile}: ${m.role} (parallel)...]
1302
+ `));
1303
+ }
1304
+ const results = await delegateParallel(tasks, client, mcpManager, { tools });
1305
+ const mergeInput = results.filter((r) => r.success).map((r) => `[${r.profile} \u2014 ${team.members.find((m) => m.profile === r.profile)?.role}]:
1306
+ ${r.response}`).join("\n\n---\n\n");
1307
+ process.stdout.write(pc2.dim(` [merging results...]
1308
+ `));
1309
+ const mergeResult = await delegateTask(
1310
+ `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.
1311
+
1312
+ Original task: ${task}
1313
+
1314
+ ${mergeInput}`,
1315
+ team.coordinator === "default" ? team.members[0]?.profile || "default" : team.coordinator,
1316
+ client,
1317
+ mcpManager,
1318
+ { tools, silent: true }
1319
+ );
1320
+ return {
1321
+ team: team.name,
1322
+ task,
1323
+ workflow: "parallel",
1324
+ results: [...results, mergeResult],
1325
+ finalOutput: mergeResult.response,
1326
+ success: results.some((r) => r.success)
1327
+ };
1328
+ }
1329
+ async function runCoordinator(team, task, client, mcpManager, tools) {
1330
+ const memberDescriptions = team.members.map((m) => `- ${m.profile}: ${m.role}`).join("\n");
1331
+ process.stdout.write(pc2.dim(` [coordinator planning...]
1332
+ `));
1333
+ const planResult = await delegateTask(
1334
+ `You are the coordinator of a team. Your job is to break down this task and decide which team members should handle each part.
1335
+
1336
+ Team members:
1337
+ ${memberDescriptions}
1338
+
1339
+ Task: ${task}
1340
+
1341
+ Respond with a JSON array of assignments:
1342
+ [{"profile": "member-name", "subtask": "what they should do"}]
1343
+
1344
+ Only use the JSON array, no other text.`,
1345
+ team.coordinator === "default" ? team.members[0]?.profile || "default" : team.coordinator,
1346
+ client,
1347
+ mcpManager,
1348
+ { tools: void 0, silent: true, maxTurns: 0 }
1349
+ );
1350
+ if (!planResult.success) {
1351
+ return {
1352
+ team: team.name,
1353
+ task,
1354
+ workflow: "coordinator",
1355
+ results: [planResult],
1356
+ finalOutput: `Coordinator failed to plan: ${planResult.error}`,
1357
+ success: false
1358
+ };
1359
+ }
1360
+ let assignments;
1361
+ try {
1362
+ let cleaned = planResult.response.trim();
1363
+ const codeBlockMatch = cleaned.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
1364
+ if (codeBlockMatch) cleaned = codeBlockMatch[1].trim();
1365
+ assignments = JSON.parse(cleaned);
1366
+ } catch {
1367
+ assignments = team.members.map((m) => ({ profile: m.profile, subtask: `${m.role}: ${task}` }));
1368
+ }
1369
+ for (const a of assignments) {
1370
+ process.stdout.write(pc2.dim(` [${a.profile}: ${a.subtask.slice(0, 60)}...]
1371
+ `));
1372
+ }
1373
+ const results = await delegateParallel(
1374
+ assignments.map((a) => ({ profile: a.profile, task: a.subtask })),
1375
+ client,
1376
+ mcpManager,
1377
+ { tools }
1378
+ );
1379
+ const mergeInput = results.filter((r) => r.success).map((r, i) => `[${assignments[i]?.profile} \u2014 ${assignments[i]?.subtask}]:
1380
+ ${r.response}`).join("\n\n---\n\n");
1381
+ process.stdout.write(pc2.dim(` [coordinator merging...]
1382
+ `));
1383
+ const mergeResult = await delegateTask(
1384
+ `You are the team coordinator. Your team members completed their assigned work. Combine their outputs into a single cohesive, polished result.
1385
+
1386
+ Original task: ${task}
1387
+
1388
+ ${mergeInput}`,
1389
+ team.coordinator === "default" ? team.members[0]?.profile || "default" : team.coordinator,
1390
+ client,
1391
+ mcpManager,
1392
+ { tools, silent: true }
1393
+ );
1394
+ return {
1395
+ team: team.name,
1396
+ task,
1397
+ workflow: "coordinator",
1398
+ results: [...results, mergeResult],
1399
+ finalOutput: mergeResult.response,
1400
+ success: results.some((r) => r.success)
1401
+ };
1402
+ }
1403
+ function formatTeam(team) {
1404
+ const lines = [];
1405
+ lines.push(`Team: ${pc2.bold(team.name)}`);
1406
+ lines.push(`Goal: ${team.goal}`);
1407
+ lines.push(`Mode: ${team.workflow}`);
1408
+ lines.push(`Coordinator: ${team.coordinator}`);
1409
+ lines.push("");
1410
+ lines.push("Members:");
1411
+ for (const m of team.members) {
1412
+ lines.push(` ${pc2.bold(m.profile)} \u2014 ${m.role}`);
1413
+ }
1414
+ return lines.join("\n");
1415
+ }
1416
+ function formatTeamResult(result) {
1417
+ const lines = [];
1418
+ lines.push(`
1419
+ ${pc2.bold(`Team: ${result.team}`)} (${result.workflow})`);
1420
+ for (const r of result.results) {
1421
+ const status = r.success ? pc2.green("\u2713") : pc2.red("\u2717");
1422
+ const tools = r.toolsUsed.length > 0 ? pc2.dim(` (${r.toolsUsed.join(", ")})`) : "";
1423
+ lines.push(` ${status} ${pc2.bold(r.profile)}${tools}`);
1424
+ }
1425
+ lines.push("");
1426
+ lines.push(result.finalOutput);
1427
+ return lines.join("\n");
1428
+ }
1429
+ var BUILT_IN_TEAMS = [
1430
+ {
1431
+ name: "content-team",
1432
+ goal: "Create and publish high-quality content",
1433
+ coordinator: "default",
1434
+ members: [
1435
+ { profile: "writer", role: "Draft compelling content with engaging narrative" },
1436
+ { profile: "researcher", role: "Fact-check claims and add citations" }
1437
+ ],
1438
+ workflow: "pipeline"
1439
+ },
1440
+ {
1441
+ name: "dev-team",
1442
+ goal: "Build and review code with quality assurance",
1443
+ coordinator: "default",
1444
+ members: [
1445
+ { profile: "coder", role: "Write clean, tested implementation code" },
1446
+ { profile: "researcher", role: "Review for security, performance, and best practices" }
1447
+ ],
1448
+ workflow: "pipeline"
1449
+ },
1450
+ {
1451
+ name: "research-team",
1452
+ goal: "Deep research with multiple perspectives",
1453
+ coordinator: "default",
1454
+ members: [
1455
+ { profile: "researcher", role: "Research the topic thoroughly with citations" },
1456
+ { profile: "writer", role: "Synthesize findings into clear, readable format" }
1457
+ ],
1458
+ workflow: "pipeline"
1459
+ }
1460
+ ];
1461
+
1462
+ // src/plans.ts
1463
+ import fs8 from "fs";
1464
+ import path8 from "path";
1465
+ import os8 from "os";
1466
+ function getPlansDir() {
1467
+ const localDir = path8.join(process.cwd(), ".acore", "plans");
1468
+ const localAcore = path8.join(process.cwd(), ".acore");
1469
+ if (fs8.existsSync(localAcore)) return localDir;
1470
+ return path8.join(os8.homedir(), ".acore", "plans");
1471
+ }
1472
+ function ensurePlansDir() {
1473
+ const dir = getPlansDir();
1474
+ if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
1475
+ return dir;
1476
+ }
1477
+ function planPath(name) {
1478
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
1479
+ return path8.join(ensurePlansDir(), `${slug}.md`);
1480
+ }
1481
+ function serializePlan(plan) {
1482
+ const lines = [];
1483
+ lines.push(`# ${plan.name}`);
1484
+ lines.push("");
1485
+ lines.push(`**Goal:** ${plan.goal}`);
1486
+ lines.push(`**Created:** ${plan.createdAt}`);
1487
+ lines.push(`**Updated:** ${plan.updatedAt}`);
1488
+ lines.push(`**Active:** ${plan.active}`);
1489
+ lines.push("");
1490
+ lines.push("## Steps");
1491
+ lines.push("");
1492
+ for (const step of plan.steps) {
1493
+ lines.push(`- [${step.done ? "x" : " "}] ${step.text}`);
1494
+ }
1495
+ lines.push("");
1496
+ return lines.join("\n");
1497
+ }
1498
+ function parsePlan(content, filePath) {
1499
+ try {
1500
+ const nameMatch = content.match(/^# (.+)/m);
1501
+ const goalMatch = content.match(/\*\*Goal:\*\*\s*(.+)/);
1502
+ const createdMatch = content.match(/\*\*Created:\*\*\s*(.+)/);
1503
+ const updatedMatch = content.match(/\*\*Updated:\*\*\s*(.+)/);
1504
+ const activeMatch = content.match(/\*\*Active:\*\*\s*(.+)/);
1505
+ const name = nameMatch?.[1]?.trim() || path8.basename(filePath, ".md");
1506
+ const goal = goalMatch?.[1]?.trim() || "";
1507
+ const createdAt = createdMatch?.[1]?.trim() || "";
1508
+ const updatedAt = updatedMatch?.[1]?.trim() || "";
1509
+ const active = activeMatch?.[1]?.trim() === "true";
1510
+ const steps = [];
1511
+ const stepMatches = content.matchAll(/- \[([ x])\] (.+)/g);
1512
+ for (const match of stepMatches) {
1513
+ steps.push({
1514
+ done: match[1] === "x",
1515
+ text: match[2].trim()
1516
+ });
1517
+ }
1518
+ return { name, goal, steps, createdAt, updatedAt, active };
1519
+ } catch (err) {
1520
+ log.debug("plans", "Failed to parse plan: " + filePath, err);
1521
+ return null;
1522
+ }
1523
+ }
1524
+ function createPlan(name, goal, steps) {
1525
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1526
+ const plan = {
1527
+ name,
1528
+ goal,
1529
+ steps: steps.map((text2) => ({ text: text2, done: false })),
1530
+ createdAt: now,
1531
+ updatedAt: now,
1532
+ active: true
1533
+ };
1534
+ const existing = listPlans();
1535
+ for (const p3 of existing) {
1536
+ if (p3.active) {
1537
+ p3.active = false;
1538
+ p3.updatedAt = now;
1539
+ savePlan(p3);
1540
+ }
1541
+ }
1542
+ savePlan(plan);
1543
+ return plan;
1544
+ }
1545
+ function savePlan(plan) {
1546
+ const fp = planPath(plan.name);
1547
+ fs8.writeFileSync(fp, serializePlan(plan), "utf-8");
1548
+ }
1549
+ function loadPlan(name) {
1550
+ const fp = planPath(name);
1551
+ if (!fs8.existsSync(fp)) return null;
1552
+ const content = fs8.readFileSync(fp, "utf-8");
1553
+ return parsePlan(content, fp);
1554
+ }
1555
+ function listPlans() {
1556
+ const dir = getPlansDir();
1557
+ if (!fs8.existsSync(dir)) return [];
1558
+ const plans = [];
1559
+ for (const file of fs8.readdirSync(dir)) {
1560
+ if (!file.endsWith(".md")) continue;
1561
+ const fp = path8.join(dir, file);
1562
+ const content = fs8.readFileSync(fp, "utf-8");
1563
+ const plan = parsePlan(content, fp);
1564
+ if (plan) plans.push(plan);
1565
+ }
1566
+ return plans;
1567
+ }
1568
+ function getActivePlan() {
1569
+ const plans = listPlans();
1570
+ return plans.find((p3) => p3.active) || null;
1571
+ }
1572
+ function markStepDone(plan, stepIndex) {
1573
+ if (stepIndex < 0 || stepIndex >= plan.steps.length) return false;
1574
+ plan.steps[stepIndex].done = true;
1575
+ plan.updatedAt = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1576
+ savePlan(plan);
1577
+ return true;
1578
+ }
1579
+ function markStepUndone(plan, stepIndex) {
1580
+ if (stepIndex < 0 || stepIndex >= plan.steps.length) return false;
1581
+ plan.steps[stepIndex].done = false;
1582
+ plan.updatedAt = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1583
+ savePlan(plan);
1584
+ return true;
1585
+ }
1586
+ function setActivePlan(name) {
1587
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1588
+ const plans = listPlans();
1589
+ for (const p3 of plans) {
1590
+ if (p3.active) {
1591
+ p3.active = false;
1592
+ p3.updatedAt = now;
1593
+ savePlan(p3);
1594
+ }
1595
+ }
1596
+ const target = loadPlan(name);
1597
+ if (!target) return null;
1598
+ target.active = true;
1599
+ target.updatedAt = now;
1600
+ savePlan(target);
1601
+ return target;
1602
+ }
1603
+ function formatPlan(plan) {
1604
+ const total = plan.steps.length;
1605
+ const done = plan.steps.filter((s) => s.done).length;
1606
+ const pct = total > 0 ? Math.round(done / total * 100) : 0;
1607
+ const bar = progressBar(pct);
1608
+ const lines = [];
1609
+ lines.push(`Plan: ${plan.name} ${plan.active ? "(active)" : "(inactive)"}`);
1610
+ lines.push(`Goal: ${plan.goal}`);
1611
+ lines.push(`Progress: ${bar} ${done}/${total} (${pct}%)`);
1612
+ lines.push("");
1613
+ for (let i = 0; i < plan.steps.length; i++) {
1614
+ const step = plan.steps[i];
1615
+ const marker = step.done ? "\u2713" : " ";
1616
+ const num = String(i + 1).padStart(2, " ");
1617
+ lines.push(` ${num}. [${marker}] ${step.text}`);
1618
+ }
1619
+ if (done === total && total > 0) {
1620
+ lines.push("\n All steps complete!");
1621
+ } else {
1622
+ const next = plan.steps.findIndex((s) => !s.done);
1623
+ if (next >= 0) {
1624
+ lines.push(`
1625
+ Next: Step ${next + 1} \u2014 ${plan.steps[next].text}`);
1626
+ }
1627
+ }
1628
+ return lines.join("\n");
1629
+ }
1630
+ function formatPlanForPrompt(plan) {
1631
+ const total = plan.steps.length;
1632
+ const done = plan.steps.filter((s) => s.done).length;
1633
+ const lines = [];
1634
+ lines.push(`<active-plan name="${plan.name}" progress="${done}/${total}">`);
1635
+ lines.push(`Goal: ${plan.goal}`);
1636
+ lines.push("");
1637
+ for (let i = 0; i < plan.steps.length; i++) {
1638
+ const step = plan.steps[i];
1639
+ lines.push(`- [${step.done ? "x" : " "}] Step ${i + 1}: ${step.text}`);
1640
+ }
1641
+ const next = plan.steps.findIndex((s) => !s.done);
1642
+ if (next >= 0) {
1643
+ lines.push("");
1644
+ lines.push(`Current focus: Step ${next + 1} \u2014 ${plan.steps[next].text}`);
1645
+ lines.push("After completing the current step, remind the user to mark it done with /plan done and suggest committing their work.");
1646
+ }
1647
+ lines.push("</active-plan>");
1648
+ return lines.join("\n");
1649
+ }
1650
+ function progressBar(pct) {
1651
+ const filled = Math.round(pct / 5);
1652
+ const empty = 20 - filled;
1653
+ return `[${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}]`;
1654
+ }
1655
+
820
1656
  // src/commands.ts
821
1657
  function readEcosystemFile(filePath, label) {
822
- if (!fs5.existsSync(filePath)) {
823
- return pc.dim(`No ${label} file found at ${filePath}`);
1658
+ if (!fs9.existsSync(filePath)) {
1659
+ return pc3.dim(`No ${label} file found at ${filePath}`);
824
1660
  }
825
- return fs5.readFileSync(filePath, "utf-8").trim();
1661
+ return fs9.readFileSync(filePath, "utf-8").trim();
826
1662
  }
827
1663
  function parseCommand(input) {
828
1664
  const trimmed = input.trim();
@@ -834,25 +1670,25 @@ function parseCommand(input) {
834
1670
  }
835
1671
  async function mcpWrite(ctx, layer, tool, args) {
836
1672
  if (!ctx.mcpManager) {
837
- return pc.red(`Cannot modify ${layer}: aman-mcp not connected. Start it with: npx @aman_asmuei/aman-mcp`);
1673
+ return pc3.red(`Cannot modify ${layer}: aman-mcp not connected. Start it with: npx @aman_asmuei/aman-mcp`);
838
1674
  }
839
1675
  const result = await ctx.mcpManager.callTool(tool, args);
840
1676
  if (result.startsWith("Error")) {
841
- return pc.red(result);
1677
+ return pc3.red(result);
842
1678
  }
843
- return pc.green(result);
1679
+ return pc3.green(result);
844
1680
  }
845
1681
  async function handleIdentityCommand(action, args, ctx) {
846
- const home2 = os5.homedir();
1682
+ const home2 = os9.homedir();
847
1683
  if (!action) {
848
- const content = readEcosystemFile(path5.join(home2, ".acore", "core.md"), "identity (acore)");
1684
+ const content = readEcosystemFile(path9.join(home2, ".acore", "core.md"), "identity (acore)");
849
1685
  return { handled: true, output: content };
850
1686
  }
851
1687
  if (action === "update") {
852
1688
  if (args.length === 0) {
853
1689
  return {
854
1690
  handled: true,
855
- output: pc.yellow("Usage: /identity update <section>\nTip: describe changes in natural language and the AI will update via MCP.")
1691
+ output: pc3.yellow("Usage: /identity update <section>\nTip: describe changes in natural language and the AI will update via MCP.")
856
1692
  };
857
1693
  }
858
1694
  const section = args[0];
@@ -860,23 +1696,23 @@ async function handleIdentityCommand(action, args, ctx) {
860
1696
  if (!content) {
861
1697
  return {
862
1698
  handled: true,
863
- output: pc.yellow("Usage: /identity update <section> <new content...>\nExample: /identity update Personality Warm, curious, and direct.")
1699
+ output: pc3.yellow("Usage: /identity update <section> <new content...>\nExample: /identity update Personality Warm, curious, and direct.")
864
1700
  };
865
1701
  }
866
1702
  const output = await mcpWrite(ctx, "identity", "identity_update_section", { section, content });
867
1703
  return { handled: true, output };
868
1704
  }
869
- return { handled: true, output: pc.yellow(`Unknown action: /identity ${action}. Use /identity or /identity update <section>.`) };
1705
+ return { handled: true, output: pc3.yellow(`Unknown action: /identity ${action}. Use /identity or /identity update <section>.`) };
870
1706
  }
871
1707
  async function handleRulesCommand(action, args, ctx) {
872
- const home2 = os5.homedir();
1708
+ const home2 = os9.homedir();
873
1709
  if (!action) {
874
- const content = readEcosystemFile(path5.join(home2, ".arules", "rules.md"), "guardrails (arules)");
1710
+ const content = readEcosystemFile(path9.join(home2, ".arules", "rules.md"), "guardrails (arules)");
875
1711
  return { handled: true, output: content };
876
1712
  }
877
1713
  if (action === "add") {
878
1714
  if (args.length < 2) {
879
- return { handled: true, output: pc.yellow("Usage: /rules add <category> <rule text...>") };
1715
+ return { handled: true, output: pc3.yellow("Usage: /rules add <category> <rule text...>") };
880
1716
  }
881
1717
  const category = args[0];
882
1718
  const rule = args.slice(1).join(" ");
@@ -885,41 +1721,41 @@ async function handleRulesCommand(action, args, ctx) {
885
1721
  }
886
1722
  if (action === "remove") {
887
1723
  if (args.length < 2) {
888
- return { handled: true, output: pc.yellow("Usage: /rules remove <category> <index>") };
1724
+ return { handled: true, output: pc3.yellow("Usage: /rules remove <category> <index>") };
889
1725
  }
890
1726
  const output = await mcpWrite(ctx, "rules", "rules_remove", { category: args[0], index: parseInt(args[1], 10) });
891
1727
  return { handled: true, output };
892
1728
  }
893
1729
  if (action === "toggle") {
894
1730
  if (args.length < 2) {
895
- return { handled: true, output: pc.yellow("Usage: /rules toggle <category> <index>") };
1731
+ return { handled: true, output: pc3.yellow("Usage: /rules toggle <category> <index>") };
896
1732
  }
897
1733
  const output = await mcpWrite(ctx, "rules", "rules_toggle", { category: args[0], index: parseInt(args[1], 10) });
898
1734
  return { handled: true, output };
899
1735
  }
900
- return { handled: true, output: pc.yellow(`Unknown action: /rules ${action}. Use /rules [add|remove|toggle].`) };
1736
+ return { handled: true, output: pc3.yellow(`Unknown action: /rules ${action}. Use /rules [add|remove|toggle].`) };
901
1737
  }
902
1738
  async function handleWorkflowsCommand(action, args, ctx) {
903
- const home2 = os5.homedir();
1739
+ const home2 = os9.homedir();
904
1740
  if (!action) {
905
- const content = readEcosystemFile(path5.join(home2, ".aflow", "flow.md"), "workflows (aflow)");
1741
+ const content = readEcosystemFile(path9.join(home2, ".aflow", "flow.md"), "workflows (aflow)");
906
1742
  return { handled: true, output: content };
907
1743
  }
908
1744
  if (action === "add") {
909
1745
  if (args.length < 1) {
910
- return { handled: true, output: pc.yellow("Usage: /workflows add <name>") };
1746
+ return { handled: true, output: pc3.yellow("Usage: /workflows add <name>") };
911
1747
  }
912
1748
  const output = await mcpWrite(ctx, "workflows", "workflow_add", { name: args.join(" ") });
913
1749
  return { handled: true, output };
914
1750
  }
915
1751
  if (action === "remove") {
916
1752
  if (args.length < 1) {
917
- return { handled: true, output: pc.yellow("Usage: /workflows remove <name>") };
1753
+ return { handled: true, output: pc3.yellow("Usage: /workflows remove <name>") };
918
1754
  }
919
1755
  const output = await mcpWrite(ctx, "workflows", "workflow_remove", { name: args.join(" ") });
920
1756
  return { handled: true, output };
921
1757
  }
922
- return { handled: true, output: pc.yellow(`Unknown action: /workflows ${action}. Use /workflows [add|remove].`) };
1758
+ return { handled: true, output: pc3.yellow(`Unknown action: /workflows ${action}. Use /workflows [add|remove].`) };
923
1759
  }
924
1760
  var AKIT_REGISTRY = [
925
1761
  { name: "web-search", description: "Search the web for current information", category: "search", mcp: { package: "@anthropic/web-search", command: "npx", args: ["-y", "@anthropic/web-search"] } },
@@ -936,42 +1772,43 @@ var AKIT_REGISTRY = [
936
1772
  { name: "docker", description: "Manage Docker containers", category: "automation", mcp: { package: "@modelcontextprotocol/server-docker", command: "npx", args: ["-y", "@modelcontextprotocol/server-docker"] } },
937
1773
  { 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" },
938
1774
  { 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" },
1775
+ { 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" },
939
1776
  { name: "memory", description: "Persistent AI memory via amem", category: "memory", mcp: { package: "@aman_asmuei/amem", command: "npx", args: ["-y", "@aman_asmuei/amem"] } },
940
1777
  { 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" }
941
1778
  ];
942
1779
  function loadAkitInstalled() {
943
- const filePath = path5.join(os5.homedir(), ".akit", "installed.json");
944
- if (!fs5.existsSync(filePath)) return [];
1780
+ const filePath = path9.join(os9.homedir(), ".akit", "installed.json");
1781
+ if (!fs9.existsSync(filePath)) return [];
945
1782
  try {
946
- return JSON.parse(fs5.readFileSync(filePath, "utf-8"));
1783
+ return JSON.parse(fs9.readFileSync(filePath, "utf-8"));
947
1784
  } catch {
948
1785
  return [];
949
1786
  }
950
1787
  }
951
1788
  function saveAkitInstalled(tools) {
952
- const dir = path5.join(os5.homedir(), ".akit");
953
- fs5.mkdirSync(dir, { recursive: true });
954
- fs5.writeFileSync(path5.join(dir, "installed.json"), JSON.stringify(tools, null, 2) + "\n", "utf-8");
1789
+ const dir = path9.join(os9.homedir(), ".akit");
1790
+ fs9.mkdirSync(dir, { recursive: true });
1791
+ fs9.writeFileSync(path9.join(dir, "installed.json"), JSON.stringify(tools, null, 2) + "\n", "utf-8");
955
1792
  }
956
1793
  function addToAmanAgentConfig(name, mcpConfig) {
957
- const configPath = path5.join(os5.homedir(), ".aman-agent", "config.json");
958
- if (!fs5.existsSync(configPath)) return;
1794
+ const configPath = path9.join(os9.homedir(), ".aman-agent", "config.json");
1795
+ if (!fs9.existsSync(configPath)) return;
959
1796
  try {
960
- const config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
1797
+ const config = JSON.parse(fs9.readFileSync(configPath, "utf-8"));
961
1798
  if (!config.mcpServers) config.mcpServers = {};
962
1799
  config.mcpServers[name] = mcpConfig;
963
- fs5.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1800
+ fs9.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
964
1801
  } catch {
965
1802
  }
966
1803
  }
967
1804
  function removeFromAmanAgentConfig(name) {
968
- const configPath = path5.join(os5.homedir(), ".aman-agent", "config.json");
969
- if (!fs5.existsSync(configPath)) return;
1805
+ const configPath = path9.join(os9.homedir(), ".aman-agent", "config.json");
1806
+ if (!fs9.existsSync(configPath)) return;
970
1807
  try {
971
- const config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
1808
+ const config = JSON.parse(fs9.readFileSync(configPath, "utf-8"));
972
1809
  if (config.mcpServers) {
973
1810
  delete config.mcpServers[name];
974
- fs5.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
1811
+ fs9.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
975
1812
  }
976
1813
  } catch {
977
1814
  }
@@ -983,27 +1820,27 @@ function handleAkitCommand(action, args) {
983
1820
  const available2 = AKIT_REGISTRY.filter((t) => !installedNames.has(t.name));
984
1821
  if (args.length < 1) {
985
1822
  if (available2.length === 0) {
986
- return { handled: true, output: pc.green("All tools are installed!") };
1823
+ return { handled: true, output: pc3.green("All tools are installed!") };
987
1824
  }
988
- const lines3 = [pc.bold("Select a tool to install:"), ""];
1825
+ const lines3 = [pc3.bold("Select a tool to install:"), ""];
989
1826
  available2.forEach((tool2, i) => {
990
- const num2 = pc.cyan(String(i + 1).padStart(2));
991
- lines3.push(` ${num2} ${tool2.name.padEnd(16)} ${pc.dim(tool2.description)}`);
1827
+ const num2 = pc3.cyan(String(i + 1).padStart(2));
1828
+ lines3.push(` ${num2} ${tool2.name.padEnd(16)} ${pc3.dim(tool2.description)}`);
992
1829
  });
993
1830
  lines3.push("");
994
- lines3.push(` Type: ${pc.cyan("/akit add <number>")} or ${pc.cyan("/akit add <name>")}`);
995
- lines3.push(` Custom: ${pc.cyan("/akit add custom <name> <command> <args...>")}`);
1831
+ lines3.push(` Type: ${pc3.cyan("/akit add <number>")} or ${pc3.cyan("/akit add <name>")}`);
1832
+ lines3.push(` Custom: ${pc3.cyan("/akit add custom <name> <command> <args...>")}`);
996
1833
  return { handled: true, output: lines3.join("\n") };
997
1834
  }
998
1835
  if (args[0].toLowerCase() === "custom") {
999
1836
  if (args.length < 3) {
1000
- 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") };
1837
+ 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") };
1001
1838
  }
1002
1839
  const customName = args[1];
1003
1840
  const customCommand = args[2];
1004
1841
  const customArgs = args.slice(3);
1005
1842
  if (installedNames.has(customName)) {
1006
- return { handled: true, output: pc.yellow(`${customName} is already installed.`) };
1843
+ return { handled: true, output: pc3.yellow(`${customName} is already installed.`) };
1007
1844
  }
1008
1845
  installed.push({
1009
1846
  name: customName,
@@ -1015,8 +1852,8 @@ function handleAkitCommand(action, args) {
1015
1852
  return {
1016
1853
  handled: true,
1017
1854
  output: [
1018
- pc.green(`\u2713 Added ${pc.bold(customName)}`) + pc.dim(` (custom MCP: ${customCommand} ${customArgs.join(" ")})`),
1019
- pc.dim(" Restart aman-agent to load the new tool.")
1855
+ pc3.green(`\u2713 Added ${pc3.bold(customName)}`) + pc3.dim(` (custom MCP: ${customCommand} ${customArgs.join(" ")})`),
1856
+ pc3.dim(" Restart aman-agent to load the new tool.")
1020
1857
  ].join("\n")
1021
1858
  };
1022
1859
  }
@@ -1032,13 +1869,13 @@ function handleAkitCommand(action, args) {
1032
1869
  return {
1033
1870
  handled: true,
1034
1871
  output: [
1035
- pc.red(`Tool "${input}" not found.`),
1036
- `Type ${pc.cyan("/akit add")} to see available tools.`
1872
+ pc3.red(`Tool "${input}" not found.`),
1873
+ `Type ${pc3.cyan("/akit add")} to see available tools.`
1037
1874
  ].join("\n")
1038
1875
  };
1039
1876
  }
1040
1877
  if (installedNames.has(tool.name)) {
1041
- return { handled: true, output: pc.yellow(`${tool.name} is already installed.`) };
1878
+ return { handled: true, output: pc3.yellow(`${tool.name} is already installed.`) };
1042
1879
  }
1043
1880
  installed.push({
1044
1881
  name: tool.name,
@@ -1053,255 +1890,253 @@ function handleAkitCommand(action, args) {
1053
1890
  });
1054
1891
  }
1055
1892
  const lines2 = [
1056
- pc.green(`\u2713 Added ${pc.bold(tool.name)}`) + (tool.mcp ? pc.dim(` (MCP: ${tool.mcp.package})`) : "")
1893
+ pc3.green(`\u2713 Added ${pc3.bold(tool.name)}`) + (tool.mcp ? pc3.dim(` (MCP: ${tool.mcp.package})`) : "")
1057
1894
  ];
1058
1895
  if (tool.envHint) {
1059
- lines2.push(pc.yellow(` \u26A0 ${tool.envHint}`));
1896
+ lines2.push(pc3.yellow(` \u26A0 ${tool.envHint}`));
1060
1897
  }
1061
1898
  if (tool.mcp) {
1062
- lines2.push(pc.dim(" Restart aman-agent to load the new tool."));
1899
+ lines2.push(pc3.dim(" Restart aman-agent to load the new tool."));
1063
1900
  }
1064
1901
  return { handled: true, output: lines2.join("\n") };
1065
1902
  }
1066
1903
  if (action === "remove") {
1067
1904
  if (args.length < 1) {
1068
- return { handled: true, output: pc.yellow("Usage: /akit remove <tool>") };
1905
+ return { handled: true, output: pc3.yellow("Usage: /akit remove <tool>") };
1069
1906
  }
1070
1907
  const toolName = args[0].toLowerCase();
1071
1908
  if (!installedNames.has(toolName)) {
1072
- return { handled: true, output: pc.red(`${toolName} is not installed.`) };
1909
+ return { handled: true, output: pc3.red(`${toolName} is not installed.`) };
1073
1910
  }
1074
1911
  const updated = installed.filter((t) => t.name !== toolName);
1075
1912
  saveAkitInstalled(updated);
1076
1913
  removeFromAmanAgentConfig(toolName);
1077
1914
  return {
1078
1915
  handled: true,
1079
- output: pc.green(`\u2713 Removed ${pc.bold(toolName)}`) + pc.dim(" (restart aman-agent to apply)")
1916
+ output: pc3.green(`\u2713 Removed ${pc3.bold(toolName)}`) + pc3.dim(" (restart aman-agent to apply)")
1080
1917
  };
1081
1918
  }
1082
1919
  if (action === "help") {
1083
1920
  return {
1084
1921
  handled: true,
1085
1922
  output: [
1086
- pc.bold("akit \u2014 Tool Management"),
1923
+ pc3.bold("akit \u2014 Tool Management"),
1087
1924
  "",
1088
- ` ${pc.cyan("/akit")} List installed & available tools`,
1089
- ` ${pc.cyan("/akit add <tool>")} Install a tool`,
1090
- ` ${pc.cyan("/akit remove <tool>")} Uninstall a tool`
1925
+ ` ${pc3.cyan("/akit")} List installed & available tools`,
1926
+ ` ${pc3.cyan("/akit add <tool>")} Install a tool`,
1927
+ ` ${pc3.cyan("/akit remove <tool>")} Uninstall a tool`
1091
1928
  ].join("\n")
1092
1929
  };
1093
1930
  }
1094
1931
  const available = AKIT_REGISTRY.filter((t) => !installedNames.has(t.name));
1095
- const lines = [pc.bold("akit \u2014 AI Tool Manager"), ""];
1932
+ const lines = [pc3.bold("akit \u2014 AI Tool Manager"), ""];
1096
1933
  if (installed.length > 0) {
1097
- lines.push(` ${pc.bold(`Installed (${installed.length})`)}`);
1934
+ lines.push(` ${pc3.bold(`Installed (${installed.length})`)}`);
1098
1935
  for (const tool of installed) {
1099
- const mcp = tool.mcpConfigured ? pc.green("MCP") : pc.dim("manual");
1100
- lines.push(` ${pc.green("\u25CF")} ${pc.bold(tool.name.padEnd(16))} ${mcp} ${pc.dim(tool.installedAt)}`);
1936
+ const mcp = tool.mcpConfigured ? pc3.green("MCP") : pc3.dim("manual");
1937
+ lines.push(` ${pc3.green("\u25CF")} ${pc3.bold(tool.name.padEnd(16))} ${mcp} ${pc3.dim(tool.installedAt)}`);
1101
1938
  }
1102
1939
  lines.push("");
1103
1940
  }
1104
1941
  if (available.length > 0) {
1105
- lines.push(` ${pc.bold(`Available (${available.length})`)}`);
1942
+ lines.push(` ${pc3.bold(`Available (${available.length})`)}`);
1106
1943
  const byCategory = /* @__PURE__ */ new Map();
1107
1944
  for (const tool of available) {
1108
1945
  if (!byCategory.has(tool.category)) byCategory.set(tool.category, []);
1109
1946
  byCategory.get(tool.category).push(tool);
1110
1947
  }
1111
1948
  for (const [category, tools] of byCategory) {
1112
- lines.push(` ${pc.dim(category)}`);
1949
+ lines.push(` ${pc3.dim(category)}`);
1113
1950
  for (const tool of tools) {
1114
- lines.push(` ${pc.dim("\u25CB")} ${tool.name.padEnd(16)} ${pc.dim(tool.description)}`);
1951
+ lines.push(` ${pc3.dim("\u25CB")} ${tool.name.padEnd(16)} ${pc3.dim(tool.description)}`);
1115
1952
  }
1116
1953
  }
1117
1954
  lines.push("");
1118
1955
  }
1119
- lines.push(` ${pc.cyan("/akit add <tool>")} Install a tool`);
1120
- lines.push(` ${pc.cyan("/akit remove <tool>")} Uninstall a tool`);
1956
+ lines.push(` ${pc3.cyan("/akit add <tool>")} Install a tool`);
1957
+ lines.push(` ${pc3.cyan("/akit remove <tool>")} Uninstall a tool`);
1121
1958
  return { handled: true, output: lines.join("\n") };
1122
1959
  }
1123
1960
  async function handleSkillsCommand(action, args, ctx) {
1124
- const home2 = os5.homedir();
1961
+ const home2 = os9.homedir();
1125
1962
  if (!action) {
1126
- const content = readEcosystemFile(path5.join(home2, ".askill", "skills.md"), "skills (askill)");
1963
+ const content = readEcosystemFile(path9.join(home2, ".askill", "skills.md"), "skills (askill)");
1127
1964
  return { handled: true, output: content };
1128
1965
  }
1129
1966
  if (action === "install") {
1130
1967
  if (args.length < 1) {
1131
- return { handled: true, output: pc.yellow("Usage: /skills install <name>") };
1968
+ return { handled: true, output: pc3.yellow("Usage: /skills install <name>") };
1132
1969
  }
1133
1970
  const output = await mcpWrite(ctx, "skills", "skill_install", { name: args.join(" ") });
1134
1971
  return { handled: true, output };
1135
1972
  }
1136
1973
  if (action === "uninstall") {
1137
1974
  if (args.length < 1) {
1138
- return { handled: true, output: pc.yellow("Usage: /skills uninstall <name>") };
1975
+ return { handled: true, output: pc3.yellow("Usage: /skills uninstall <name>") };
1139
1976
  }
1140
1977
  const output = await mcpWrite(ctx, "skills", "skill_uninstall", { name: args.join(" ") });
1141
1978
  return { handled: true, output };
1142
1979
  }
1143
- return { handled: true, output: pc.yellow(`Unknown action: /skills ${action}. Use /skills [install|uninstall].`) };
1980
+ return { handled: true, output: pc3.yellow(`Unknown action: /skills ${action}. Use /skills [install|uninstall].`) };
1144
1981
  }
1145
1982
  async function handleEvalCommand(action, args, ctx) {
1146
- const home2 = os5.homedir();
1983
+ const home2 = os9.homedir();
1147
1984
  if (!action) {
1148
- const content = readEcosystemFile(path5.join(home2, ".aeval", "eval.md"), "evaluation (aeval)");
1985
+ const content = readEcosystemFile(path9.join(home2, ".aeval", "eval.md"), "evaluation (aeval)");
1149
1986
  return { handled: true, output: content };
1150
1987
  }
1151
1988
  if (action === "milestone") {
1152
1989
  if (args.length < 1) {
1153
- return { handled: true, output: pc.yellow("Usage: /eval milestone <text...>") };
1990
+ return { handled: true, output: pc3.yellow("Usage: /eval milestone <text...>") };
1154
1991
  }
1155
1992
  const text2 = args.join(" ");
1156
1993
  const output = await mcpWrite(ctx, "eval", "eval_milestone", { text: text2 });
1157
1994
  return { handled: true, output };
1158
1995
  }
1159
- return { handled: true, output: pc.yellow(`Unknown action: /eval ${action}. Use /eval or /eval milestone <text>.`) };
1996
+ return { handled: true, output: pc3.yellow(`Unknown action: /eval ${action}. Use /eval or /eval milestone <text>.`) };
1160
1997
  }
1161
1998
  async function handleMemoryCommand(action, args, ctx) {
1162
1999
  if (!action) {
1163
- if (!ctx.mcpManager) {
1164
- return {
1165
- handled: true,
1166
- output: pc.red("Memory not available: aman-mcp not connected. Start it with: npx @aman_asmuei/aman-mcp")
1167
- };
1168
- }
1169
- const result = await ctx.mcpManager.callTool("memory_context", { topic: "recent context" });
1170
- if (result.startsWith("Error")) {
1171
- return { handled: true, output: pc.red(result) };
2000
+ try {
2001
+ const result = await memoryContext("recent context");
2002
+ if (result.memoriesUsed === 0) {
2003
+ return { handled: true, output: pc3.dim("No memories yet. Start chatting and I'll remember what matters.") };
2004
+ }
2005
+ return { handled: true, output: result.text };
2006
+ } catch (err) {
2007
+ return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
1172
2008
  }
1173
- return { handled: true, output: result };
1174
2009
  }
1175
2010
  if (action && !["search", "clear", "timeline"].includes(action)) {
1176
- if (!ctx.mcpManager) {
1177
- return { handled: true, output: pc.red("Memory not available: MCP not connected.") };
1178
- }
1179
- const topic = [action, ...args].join(" ");
1180
- const result = await ctx.mcpManager.callTool("memory_context", { topic });
1181
- if (result.startsWith("Error")) {
1182
- return { handled: true, output: pc.red(result) };
2011
+ try {
2012
+ const topic = [action, ...args].join(" ");
2013
+ const result = await memoryContext(topic);
2014
+ if (result.memoriesUsed === 0) {
2015
+ return { handled: true, output: pc3.dim(`No memories found for: "${topic}".`) };
2016
+ }
2017
+ return { handled: true, output: result.text };
2018
+ } catch (err) {
2019
+ return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
1183
2020
  }
1184
- return { handled: true, output: result };
1185
2021
  }
1186
2022
  if (action === "search") {
1187
2023
  if (args.length < 1) {
1188
- return { handled: true, output: pc.yellow("Usage: /memory search <query...>") };
2024
+ return { handled: true, output: pc3.yellow("Usage: /memory search <query...>") };
1189
2025
  }
1190
2026
  const query = args.join(" ");
1191
- const output = await mcpWrite(ctx, "memory", "memory_recall", { query });
1192
- return { handled: true, output };
2027
+ try {
2028
+ const result = await memoryRecall(query);
2029
+ return { handled: true, output: result.total === 0 ? pc3.dim("No memories found.") : result.text };
2030
+ } catch (err) {
2031
+ return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
2032
+ }
1193
2033
  }
1194
2034
  if (action === "clear") {
1195
2035
  if (args.length < 1) {
1196
- return { handled: true, output: pc.yellow("Usage: /memory clear <category>") };
2036
+ return { handled: true, output: pc3.yellow("Usage: /memory clear <category>") };
1197
2037
  }
1198
2038
  const output = await mcpWrite(ctx, "memory", "memory_forget", { category: args[0] });
1199
2039
  return { handled: true, output };
1200
2040
  }
1201
2041
  if (action === "timeline") {
1202
- if (!ctx.mcpManager) {
1203
- return { handled: true, output: pc.red("Memory not available: MCP not connected.") };
1204
- }
1205
2042
  try {
1206
- const result = await ctx.mcpManager.callTool("memory_recall", { query: "*", limit: 500 });
1207
- if (result.startsWith("Error") || result.includes("No memories found")) {
1208
- return { handled: true, output: pc.dim("No memories yet. Start chatting and I'll remember what matters.") };
2043
+ const result = await memoryRecall("*", { limit: 500, compact: false });
2044
+ if (result.total === 0) {
2045
+ return { handled: true, output: pc3.dim("No memories yet. Start chatting and I'll remember what matters.") };
1209
2046
  }
1210
- try {
1211
- const memories = JSON.parse(result);
1212
- if (Array.isArray(memories) && memories.length > 0) {
1213
- const byDate = /* @__PURE__ */ new Map();
1214
- for (const mem of memories) {
1215
- const date = mem.created_at ? new Date(mem.created_at).toLocaleDateString("en-US", { month: "short", day: "numeric" }) : "Unknown";
1216
- byDate.set(date, (byDate.get(date) || 0) + 1);
1217
- }
1218
- const maxCount = Math.max(...byDate.values());
1219
- const barWidth = 10;
1220
- const lines = [pc.bold("Memory Timeline:"), ""];
1221
- for (const [date, count] of byDate) {
1222
- const filled = Math.round(count / maxCount * barWidth);
1223
- const bar = "\u2588".repeat(filled) + "\u2591".repeat(barWidth - filled);
1224
- lines.push(` ${date.padEnd(8)} ${bar} ${count} memories`);
1225
- }
1226
- const tags = /* @__PURE__ */ new Map();
1227
- for (const mem of memories) {
1228
- if (Array.isArray(mem.tags)) {
1229
- for (const tag of mem.tags) {
1230
- tags.set(tag, (tags.get(tag) || 0) + 1);
1231
- }
2047
+ const memories = result.memories;
2048
+ if (memories.length > 0) {
2049
+ const byDate = /* @__PURE__ */ new Map();
2050
+ for (const mem of memories) {
2051
+ const createdAt = mem.created_at;
2052
+ const date = createdAt ? new Date(createdAt).toLocaleDateString("en-US", { month: "short", day: "numeric" }) : "Unknown";
2053
+ byDate.set(date, (byDate.get(date) || 0) + 1);
2054
+ }
2055
+ const maxCount = Math.max(...byDate.values());
2056
+ const barWidth = 10;
2057
+ const lines = [pc3.bold("Memory Timeline:"), ""];
2058
+ for (const [date, count] of byDate) {
2059
+ const filled = Math.round(count / maxCount * barWidth);
2060
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(barWidth - filled);
2061
+ lines.push(` ${date.padEnd(8)} ${bar} ${count} memories`);
2062
+ }
2063
+ const tags = /* @__PURE__ */ new Map();
2064
+ for (const mem of memories) {
2065
+ const memTags = mem.tags;
2066
+ if (Array.isArray(memTags)) {
2067
+ for (const tag of memTags) {
2068
+ tags.set(tag, (tags.get(tag) || 0) + 1);
1232
2069
  }
1233
2070
  }
1234
- lines.push("");
1235
- lines.push(` Total: ${memories.length} memories`);
1236
- if (tags.size > 0) {
1237
- const topTags = [...tags.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([tag, count]) => `#${tag} (${count})`).join(", ");
1238
- lines.push(` Top tags: ${topTags}`);
1239
- }
1240
- return { handled: true, output: lines.join("\n") };
1241
2071
  }
1242
- } catch {
2072
+ lines.push("");
2073
+ lines.push(` Total: ${result.total} memories`);
2074
+ if (tags.size > 0) {
2075
+ const topTags = [...tags.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([tag, count]) => `#${tag} (${count})`).join(", ");
2076
+ lines.push(` Top tags: ${topTags}`);
2077
+ }
2078
+ return { handled: true, output: lines.join("\n") };
1243
2079
  }
1244
- const lineCount = result.split("\n").filter((l) => l.trim()).length;
1245
- return { handled: true, output: `Total memories: ~${lineCount} entries.` };
2080
+ return { handled: true, output: `Total memories: ${result.total} entries.` };
1246
2081
  } catch {
1247
- return { handled: true, output: pc.red("Failed to retrieve memory timeline.") };
2082
+ return { handled: true, output: pc3.red("Failed to retrieve memory timeline.") };
1248
2083
  }
1249
2084
  }
1250
- return { handled: true, output: pc.yellow(`Unknown action: /memory ${action}. Use /memory [search|clear|timeline].`) };
2085
+ return { handled: true, output: pc3.yellow(`Unknown action: /memory ${action}. Use /memory [search|clear|timeline].`) };
1251
2086
  }
1252
2087
  function handleStatusCommand(ctx) {
1253
2088
  const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
1254
2089
  const amemConnected = mcpToolCount > 0;
1255
2090
  const status = getEcosystemStatus(mcpToolCount, amemConnected);
1256
- const lines = [pc.bold("Aman Ecosystem Dashboard"), ""];
2091
+ const lines = [pc3.bold("Aman Ecosystem Dashboard"), ""];
1257
2092
  for (const layer of status.layers) {
1258
- const icon = layer.exists ? pc.green("\u25CF") : pc.dim("\u25CB");
1259
- const name = pc.bold(layer.name.padEnd(12));
1260
- const summary = layer.exists ? layer.summary : pc.dim("not configured");
2093
+ const icon = layer.exists ? pc3.green("\u25CF") : pc3.dim("\u25CB");
2094
+ const name = pc3.bold(layer.name.padEnd(12));
2095
+ const summary = layer.exists ? layer.summary : pc3.dim("not configured");
1261
2096
  lines.push(` ${icon} ${name} ${summary}`);
1262
2097
  }
1263
2098
  lines.push("");
1264
- 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")}`);
1265
- lines.push(` ${status.amemConnected ? pc.green("\u25CF") : pc.dim("\u25CB")} ${pc.bold("Memory".padEnd(12))} ${status.amemConnected ? "connected" : pc.dim("not connected")}`);
2099
+ 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")}`);
2100
+ lines.push(` ${status.amemConnected ? pc3.green("\u25CF") : pc3.dim("\u25CB")} ${pc3.bold("Memory".padEnd(12))} ${status.amemConnected ? "connected" : pc3.dim("not connected")}`);
1266
2101
  return { handled: true, output: lines.join("\n") };
1267
2102
  }
1268
2103
  function handleDoctorCommand(ctx) {
1269
2104
  const mcpToolCount = ctx.mcpManager ? ctx.mcpManager.getTools().length : 0;
1270
2105
  const amemConnected = mcpToolCount > 0;
1271
2106
  const status = getEcosystemStatus(mcpToolCount, amemConnected);
1272
- const lines = [pc.bold("Aman Health Check"), ""];
2107
+ const lines = [pc3.bold("Aman Health Check"), ""];
1273
2108
  let healthy = 0;
1274
2109
  let fixes = 0;
1275
2110
  let suggestions = 0;
1276
2111
  for (const layer of status.layers) {
1277
2112
  if (layer.exists) {
1278
- lines.push(` ${pc.green("\u2713")} ${layer.name.padEnd(12)} ${pc.green(layer.summary)}`);
2113
+ lines.push(` ${pc3.green("\u2713")} ${layer.name.padEnd(12)} ${pc3.green(layer.summary)}`);
1279
2114
  healthy++;
1280
2115
  } else {
1281
2116
  const isRequired = ["identity", "rules"].includes(layer.name.toLowerCase());
1282
2117
  if (isRequired) {
1283
- lines.push(` ${pc.red("\u2717")} ${layer.name.padEnd(12)} ${pc.red("missing")}`);
1284
- lines.push(` ${pc.dim("\u2192 Fix: aman-agent init")}`);
2118
+ lines.push(` ${pc3.red("\u2717")} ${layer.name.padEnd(12)} ${pc3.red("missing")}`);
2119
+ lines.push(` ${pc3.dim("\u2192 Fix: aman-agent init")}`);
1285
2120
  fixes++;
1286
2121
  } else {
1287
- lines.push(` ${pc.yellow("\u26A0")} ${layer.name.padEnd(12)} ${pc.yellow("empty")}`);
2122
+ lines.push(` ${pc3.yellow("\u26A0")} ${layer.name.padEnd(12)} ${pc3.yellow("empty")}`);
1288
2123
  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>" : "";
1289
- if (cmd) lines.push(` ${pc.dim(`\u2192 Add with ${cmd}`)}`);
2124
+ if (cmd) lines.push(` ${pc3.dim(`\u2192 Add with ${cmd}`)}`);
1290
2125
  suggestions++;
1291
2126
  }
1292
2127
  }
1293
2128
  }
1294
2129
  lines.push("");
1295
- lines.push(` ${status.mcpConnected ? pc.green("\u2713") : pc.red("\u2717")} ${"MCP".padEnd(12)} ${status.mcpConnected ? pc.green(`${status.mcpToolCount} tools`) : pc.red("not connected")}`);
2130
+ lines.push(` ${status.mcpConnected ? pc3.green("\u2713") : pc3.red("\u2717")} ${"MCP".padEnd(12)} ${status.mcpConnected ? pc3.green(`${status.mcpToolCount} tools`) : pc3.red("not connected")}`);
1296
2131
  if (!status.mcpConnected) {
1297
- lines.push(` ${pc.dim("\u2192 Fix: ensure npx is available and network is connected")}`);
2132
+ lines.push(` ${pc3.dim("\u2192 Fix: ensure npx is available and network is connected")}`);
1298
2133
  fixes++;
1299
2134
  } else {
1300
2135
  healthy++;
1301
2136
  }
1302
- lines.push(` ${status.amemConnected ? pc.green("\u2713") : pc.red("\u2717")} ${"Memory".padEnd(12)} ${status.amemConnected ? pc.green("connected") : pc.red("not connected")}`);
2137
+ lines.push(` ${status.amemConnected ? pc3.green("\u2713") : pc3.red("\u2717")} ${"Memory".padEnd(12)} ${status.amemConnected ? pc3.green("connected") : pc3.red("not connected")}`);
1303
2138
  if (!status.amemConnected) {
1304
- lines.push(` ${pc.dim("\u2192 Fix: npx @aman_asmuei/amem")}`);
2139
+ lines.push(` ${pc3.dim("\u2192 Fix: npx @aman_asmuei/amem")}`);
1305
2140
  fixes++;
1306
2141
  } else {
1307
2142
  healthy++;
@@ -1315,26 +2150,26 @@ function handleHelp() {
1315
2150
  return {
1316
2151
  handled: true,
1317
2152
  output: [
1318
- pc.bold("Commands:"),
1319
- ` ${pc.cyan("/help")} Show this help`,
1320
- ` ${pc.cyan("/identity")} View identity [update <section>]`,
1321
- ` ${pc.cyan("/rules")} View rules [add|remove|toggle ...]`,
1322
- ` ${pc.cyan("/workflows")} View workflows [add|remove ...]`,
1323
- ` ${pc.cyan("/akit")} Manage tools [add|remove <tool>]`,
1324
- ` ${pc.cyan("/skills")} View skills [install|uninstall ...]`,
1325
- ` ${pc.cyan("/eval")} View evaluation [milestone ...]`,
1326
- ` ${pc.cyan("/memory")} View recent memories [search|clear|timeline]`,
1327
- ` ${pc.cyan("/status")} Ecosystem dashboard`,
1328
- ` ${pc.cyan("/doctor")} Health check all layers`,
1329
- ` ${pc.cyan("/decisions")} View decision log [<project>]`,
1330
- ` ${pc.cyan("/export")} Export conversation to markdown`,
1331
- ` ${pc.cyan("/debug")} Show debug log`,
1332
- ` ${pc.cyan("/save")} Save conversation to memory`,
1333
- ` ${pc.cyan("/model")} Show current LLM model`,
1334
- ` ${pc.cyan("/update")} Check for updates`,
1335
- ` ${pc.cyan("/reconfig")} Reset LLM config`,
1336
- ` ${pc.cyan("/clear")} Clear conversation history`,
1337
- ` ${pc.cyan("/quit")} Exit`
2153
+ pc3.bold("Commands:"),
2154
+ ` ${pc3.cyan("/help")} Show this help`,
2155
+ ` ${pc3.cyan("/identity")} View identity [update <section>]`,
2156
+ ` ${pc3.cyan("/rules")} View rules [add|remove|toggle ...]`,
2157
+ ` ${pc3.cyan("/workflows")} View workflows [add|remove ...]`,
2158
+ ` ${pc3.cyan("/akit")} Manage tools [add|remove <tool>]`,
2159
+ ` ${pc3.cyan("/skills")} View skills [install|uninstall ...]`,
2160
+ ` ${pc3.cyan("/eval")} View evaluation [milestone ...]`,
2161
+ ` ${pc3.cyan("/memory")} View recent memories [search|clear|timeline]`,
2162
+ ` ${pc3.cyan("/status")} Ecosystem dashboard`,
2163
+ ` ${pc3.cyan("/doctor")} Health check all layers`,
2164
+ ` ${pc3.cyan("/decisions")} View decision log [<project>]`,
2165
+ ` ${pc3.cyan("/export")} Export conversation to markdown`,
2166
+ ` ${pc3.cyan("/debug")} Show debug log`,
2167
+ ` ${pc3.cyan("/save")} Save conversation to memory`,
2168
+ ` ${pc3.cyan("/model")} Show current LLM model`,
2169
+ ` ${pc3.cyan("/update")} Check for updates`,
2170
+ ` ${pc3.cyan("/reconfig")} Reset LLM config`,
2171
+ ` ${pc3.cyan("/clear")} Clear conversation history`,
2172
+ ` ${pc3.cyan("/quit")} Exit`
1338
2173
  ].join("\n")
1339
2174
  };
1340
2175
  }
@@ -1342,18 +2177,18 @@ function handleSave() {
1342
2177
  return { handled: true, saveConversation: true };
1343
2178
  }
1344
2179
  function handleReconfig() {
1345
- const configDir = path5.join(os5.homedir(), ".aman-agent");
1346
- const configPath = path5.join(configDir, "config.json");
1347
- if (fs5.existsSync(configPath)) {
1348
- fs5.unlinkSync(configPath);
2180
+ const configDir = path9.join(os9.homedir(), ".aman-agent");
2181
+ const configPath = path9.join(configDir, "config.json");
2182
+ if (fs9.existsSync(configPath)) {
2183
+ fs9.unlinkSync(configPath);
1349
2184
  }
1350
- fs5.mkdirSync(configDir, { recursive: true });
1351
- fs5.writeFileSync(path5.join(configDir, ".reconfig"), "", "utf-8");
2185
+ fs9.mkdirSync(configDir, { recursive: true });
2186
+ fs9.writeFileSync(path9.join(configDir, ".reconfig"), "", "utf-8");
1352
2187
  return {
1353
2188
  handled: true,
1354
2189
  quit: true,
1355
2190
  output: [
1356
- pc.green("Config reset."),
2191
+ pc3.green("Config reset."),
1357
2192
  "Next run will prompt you to choose your LLM provider."
1358
2193
  ].join("\n")
1359
2194
  };
@@ -1361,20 +2196,20 @@ function handleReconfig() {
1361
2196
  function handleUpdate() {
1362
2197
  try {
1363
2198
  const current = execFileSync("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
1364
- const local = JSON.parse(fs5.readFileSync(path5.join(__dirname, "..", "package.json"), "utf-8")).version;
2199
+ const local = JSON.parse(fs9.readFileSync(path9.join(__dirname, "..", "package.json"), "utf-8")).version;
1365
2200
  if (current === local) {
1366
- return { handled: true, output: `${pc.green("Up to date")} \u2014 v${local}` };
2201
+ return { handled: true, output: `${pc3.green("Up to date")} \u2014 v${local}` };
1367
2202
  }
1368
2203
  return {
1369
2204
  handled: true,
1370
2205
  output: [
1371
- `${pc.yellow("Update available:")} v${local} \u2192 v${current}`,
2206
+ `${pc3.yellow("Update available:")} v${local} \u2192 v${current}`,
1372
2207
  "",
1373
2208
  `Run this in your terminal:`,
1374
- ` ${pc.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
2209
+ ` ${pc3.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
1375
2210
  "",
1376
2211
  `Or use npx (always latest):`,
1377
- ` ${pc.bold("npx @aman_asmuei/aman-agent@latest")}`
2212
+ ` ${pc3.bold("npx @aman_asmuei/aman-agent@latest")}`
1378
2213
  ].join("\n")
1379
2214
  };
1380
2215
  } catch {
@@ -1382,42 +2217,422 @@ function handleUpdate() {
1382
2217
  handled: true,
1383
2218
  output: [
1384
2219
  `To update, run in your terminal:`,
1385
- ` ${pc.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
2220
+ ` ${pc3.bold("npm install -g @aman_asmuei/aman-agent@latest")}`,
1386
2221
  "",
1387
2222
  `Or use npx (always latest):`,
1388
- ` ${pc.bold("npx @aman_asmuei/aman-agent@latest")}`
2223
+ ` ${pc3.bold("npx @aman_asmuei/aman-agent@latest")}`
1389
2224
  ].join("\n")
1390
2225
  };
1391
2226
  }
1392
2227
  }
1393
- async function handleDecisionsCommand(action, _args, ctx) {
1394
- if (!ctx.mcpManager) {
1395
- return { handled: true, output: pc.red("Decisions not available: MCP not connected.") };
1396
- }
1397
- const scope = action || void 0;
1398
- const result = await ctx.mcpManager.callTool("memory_recall", {
1399
- query: "decision",
1400
- type: "decision",
1401
- limit: 20,
1402
- ...scope ? { scope } : {}
1403
- });
1404
- if (result.startsWith("Error")) {
1405
- return { handled: true, output: pc.red(result) };
2228
+ async function handleDecisionsCommand(action, _args, _ctx) {
2229
+ try {
2230
+ const result = await memoryRecall("decision", { type: "decision", limit: 20 });
2231
+ if (result.total === 0) {
2232
+ return { handled: true, output: pc3.dim("No decisions recorded yet.") };
2233
+ }
2234
+ return { handled: true, output: pc3.bold("Decision Log:\n") + result.text };
2235
+ } catch (err) {
2236
+ return { handled: true, output: pc3.red(`Memory error: ${err instanceof Error ? err.message : String(err)}`) };
2237
+ }
2238
+ }
2239
+ function handleExportCommand() {
2240
+ return { handled: true, exportConversation: true };
2241
+ }
2242
+ function handleDebugCommand() {
2243
+ const logPath = path9.join(os9.homedir(), ".aman-agent", "debug.log");
2244
+ if (!fs9.existsSync(logPath)) {
2245
+ return { handled: true, output: pc3.dim("No debug log found.") };
2246
+ }
2247
+ const content = fs9.readFileSync(logPath, "utf-8");
2248
+ const lines = content.trim().split("\n");
2249
+ const last20 = lines.slice(-20).join("\n");
2250
+ return { handled: true, output: pc3.bold("Debug Log (last 20 entries):\n") + pc3.dim(last20) };
2251
+ }
2252
+ async function handleTeamCommand(action, args, ctx) {
2253
+ if (!action || action === "list") {
2254
+ const teams = listTeams();
2255
+ if (teams.length === 0) {
2256
+ return {
2257
+ handled: true,
2258
+ output: pc3.dim("No teams yet. Create one:") + "\n /team create <name> Create from built-in template\n /team create Show available templates"
2259
+ };
2260
+ }
2261
+ const lines = teams.map((t) => {
2262
+ const members = t.members.map((m) => m.profile).join(", ");
2263
+ return ` ${pc3.bold(t.name)} (${t.workflow}) \u2014 ${members}`;
2264
+ });
2265
+ return { handled: true, output: "Teams:\n" + lines.join("\n") };
2266
+ }
2267
+ switch (action) {
2268
+ case "create": {
2269
+ const name = args[0];
2270
+ if (!name) {
2271
+ const lines = BUILT_IN_TEAMS.map((t) => {
2272
+ const members2 = t.members.map((m) => m.profile).join(" \u2192 ");
2273
+ return ` ${pc3.bold(t.name)} (${t.workflow}) \u2014 ${members2}
2274
+ ${pc3.dim(t.goal)}`;
2275
+ });
2276
+ return {
2277
+ handled: true,
2278
+ 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"
2279
+ };
2280
+ }
2281
+ const builtIn = BUILT_IN_TEAMS.find((t) => t.name === name);
2282
+ if (builtIn) {
2283
+ createTeam(builtIn);
2284
+ return { handled: true, output: pc3.green(`Team installed: ${builtIn.name}`) + "\n\n" + formatTeam(builtIn) };
2285
+ }
2286
+ const mode = args[1];
2287
+ const membersStr = args[2];
2288
+ if (!mode || !membersStr) {
2289
+ return { handled: true, output: pc3.yellow("Usage: /team create <name> <pipeline|parallel|coordinator> <profile1:role>,<profile2:role>") };
2290
+ }
2291
+ if (!["pipeline", "parallel", "coordinator"].includes(mode)) {
2292
+ return { handled: true, output: pc3.yellow("Mode must be: pipeline, parallel, or coordinator") };
2293
+ }
2294
+ const members = membersStr.split(",").map((m) => {
2295
+ const [profile, ...roleParts] = m.trim().split(":");
2296
+ return { profile: profile.trim(), role: roleParts.join(":").trim() || profile.trim() };
2297
+ });
2298
+ const team = {
2299
+ name,
2300
+ goal: `Team: ${name}`,
2301
+ coordinator: "default",
2302
+ members,
2303
+ workflow: mode
2304
+ };
2305
+ createTeam(team);
2306
+ return { handled: true, output: pc3.green(`Team created!`) + "\n\n" + formatTeam(team) };
2307
+ }
2308
+ case "run": {
2309
+ const teamName = args[0];
2310
+ const task = args.slice(1).join(" ");
2311
+ if (!teamName || !task) {
2312
+ return { handled: true, output: pc3.yellow("Usage: /team run <team-name> <task description>") };
2313
+ }
2314
+ const team = loadTeam(teamName);
2315
+ if (!team) return { handled: true, output: pc3.red(`Team not found: ${teamName}`) };
2316
+ if (!ctx.llmClient || !ctx.mcpManager) {
2317
+ return { handled: true, output: pc3.red("Team execution requires LLM client and MCP.") };
2318
+ }
2319
+ const result = await runTeam(team, task, ctx.llmClient, ctx.mcpManager, ctx.tools);
2320
+ return { handled: true, output: formatTeamResult(result) };
2321
+ }
2322
+ case "show": {
2323
+ const name = args[0];
2324
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /team show <name>") };
2325
+ const team = loadTeam(name);
2326
+ if (!team) return { handled: true, output: pc3.red(`Team not found: ${name}`) };
2327
+ return { handled: true, output: formatTeam(team) };
2328
+ }
2329
+ case "delete": {
2330
+ const name = args[0];
2331
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /team delete <name>") };
2332
+ if (!deleteTeam(name)) return { handled: true, output: pc3.red(`Team not found: ${name}`) };
2333
+ return { handled: true, output: pc3.dim(`Team deleted: ${name}`) };
2334
+ }
2335
+ case "help":
2336
+ return { handled: true, output: `Team commands:
2337
+ /team List all teams
2338
+ /team create Show built-in templates
2339
+ /team create <name> Install built-in team
2340
+ /team create <n> <mode> <m> Custom team (mode: pipeline|parallel|coordinator)
2341
+ /team run <name> <task> Run a task with a team
2342
+ /team show <name> Show team details
2343
+ /team delete <name> Delete a team
2344
+
2345
+ Modes:
2346
+ pipeline Sequential: agent1 \u2192 agent2 \u2192 agent3
2347
+ parallel All agents work concurrently, coordinator merges
2348
+ coordinator Coordinator LLM decides how to split the task
2349
+
2350
+ Examples:
2351
+ /team create content-team
2352
+ /team run content-team Write a blog post about AI companions
2353
+ /team create review-squad pipeline coder:implement,researcher:review
2354
+ /team run review-squad Build a rate limiter in TypeScript` };
2355
+ default:
2356
+ return { handled: true, output: pc3.yellow(`Unknown team action: ${action}. Try /team help`) };
2357
+ }
2358
+ }
2359
+ async function handleDelegateCommand(action, args, ctx) {
2360
+ if (!action) {
2361
+ return { handled: true, output: `Delegate commands:
2362
+ /delegate <profile> <task> Delegate a task to a profile
2363
+ /delegate pipeline <p1> <p2> ... Run a sequential pipeline
2364
+ /delegate help Show help
2365
+
2366
+ Examples:
2367
+ /delegate writer Write a blog post about AI companions
2368
+ /delegate coder Review this code for security issues
2369
+ /delegate pipeline writer,researcher Write and fact-check an article about quantum computing` };
2370
+ }
2371
+ if (action === "help") {
2372
+ return { handled: true, output: `Delegate a task to a sub-agent with a specific profile.
2373
+
2374
+ The sub-agent runs with its own identity, rules, and skills but shares
2375
+ your memory and tools. Results come back to you.
2376
+
2377
+ Usage:
2378
+ /delegate <profile> <task>
2379
+ /delegate pipeline <profile1>,<profile2> <task>
2380
+
2381
+ The pipeline mode passes each agent's output to the next:
2382
+ writer drafts \u2192 researcher reviews \u2192 writer polishes` };
2383
+ }
2384
+ if (!ctx.llmClient || !ctx.mcpManager) {
2385
+ return { handled: true, output: pc3.red("Delegation requires LLM client and MCP. Not available.") };
2386
+ }
2387
+ if (action === "pipeline") {
2388
+ const profileList = args[0];
2389
+ const task2 = args.slice(1).join(" ");
2390
+ if (!profileList || !task2) {
2391
+ return { handled: true, output: pc3.yellow("Usage: /delegate pipeline <profile1>,<profile2> <task>") };
2392
+ }
2393
+ const profiles = profileList.split(",").map((p3) => p3.trim());
2394
+ const steps = profiles.map((profile2, i) => {
2395
+ if (i === 0) {
2396
+ return { profile: profile2, taskTemplate: task2 };
2397
+ }
2398
+ return { profile: profile2, taskTemplate: `Review and improve the following:
2399
+
2400
+ {{input}}` };
2401
+ });
2402
+ process.stdout.write(pc3.dim(`
2403
+ Pipeline: ${profiles.join(" \u2192 ")}
2404
+ `));
2405
+ const results = await delegatePipeline(steps, task2, ctx.llmClient, ctx.mcpManager, { tools: ctx.tools });
2406
+ const output = [];
2407
+ for (const r of results) {
2408
+ if (r.success) {
2409
+ output.push(`
2410
+ ${pc3.bold(`[${r.profile}]`)} ${pc3.green("\u2713")} (${r.turns} tool turns)`);
2411
+ output.push(r.response.slice(0, 2e3));
2412
+ if (r.toolsUsed.length > 0) output.push(pc3.dim(` Tools: ${r.toolsUsed.join(", ")}`));
2413
+ } else {
2414
+ output.push(`
2415
+ ${pc3.bold(`[${r.profile}]`)} ${pc3.red("\u2717")} ${r.error}`);
2416
+ }
2417
+ }
2418
+ return { handled: true, output: output.join("\n") };
2419
+ }
2420
+ const profile = action;
2421
+ const task = args.join(" ");
2422
+ if (!task) {
2423
+ return { handled: true, output: pc3.yellow(`Usage: /delegate ${profile} <task description>`) };
2424
+ }
2425
+ process.stdout.write(pc3.dim(`
2426
+ [delegating to ${profile}...]
2427
+
2428
+ `));
2429
+ const result = await delegateTask(task, profile, ctx.llmClient, ctx.mcpManager, { tools: ctx.tools });
2430
+ if (!result.success) {
2431
+ return { handled: true, output: pc3.red(`Delegation failed: ${result.error}`) };
2432
+ }
2433
+ const meta = [];
2434
+ if (result.toolsUsed.length > 0) meta.push(`Tools: ${result.toolsUsed.join(", ")}`);
2435
+ if (result.turns > 0) meta.push(`${result.turns} tool turns`);
2436
+ return {
2437
+ handled: true,
2438
+ output: `
2439
+ ${pc3.bold(`[${profile}]`)} ${pc3.green("\u2713")}${meta.length > 0 ? " " + pc3.dim(`(${meta.join(", ")})`) : ""}
2440
+
2441
+ ${result.response}`
2442
+ };
2443
+ }
2444
+ function handleProfileCommand(action, args) {
2445
+ const profilesDir = path9.join(os9.homedir(), ".acore", "profiles");
2446
+ if (!action || action === "list") {
2447
+ const profiles = listProfiles();
2448
+ if (profiles.length === 0) {
2449
+ return { handled: true, output: pc3.dim("No profiles yet. Create one with: /profile create <name>") };
2450
+ }
2451
+ const lines = profiles.map(
2452
+ (p3) => ` ${pc3.bold(p3.name)} \u2014 ${p3.aiName} (${pc3.dim(p3.personality)})`
2453
+ );
2454
+ return { handled: true, output: "Profiles:\n" + lines.join("\n") + "\n\n" + pc3.dim("Switch with: aman-agent --profile <name>") };
2455
+ }
2456
+ switch (action) {
2457
+ case "create": {
2458
+ const name = args[0];
2459
+ if (!name) {
2460
+ const lines = BUILT_IN_PROFILES.map(
2461
+ (t) => ` ${pc3.bold(t.name)} \u2014 ${t.label}: ${pc3.dim(t.description)}`
2462
+ );
2463
+ return {
2464
+ handled: true,
2465
+ 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"
2466
+ };
2467
+ }
2468
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
2469
+ const profileDir = path9.join(profilesDir, slug);
2470
+ if (fs9.existsSync(profileDir)) {
2471
+ return { handled: true, output: pc3.yellow(`Profile already exists: ${slug}`) };
2472
+ }
2473
+ const builtIn = BUILT_IN_PROFILES.find((t) => t.name === slug);
2474
+ if (builtIn) {
2475
+ const err = installProfileTemplate(slug);
2476
+ if (err) return { handled: true, output: pc3.red(err) };
2477
+ return {
2478
+ handled: true,
2479
+ output: pc3.green(`Profile installed: ${builtIn.label}`) + `
2480
+ AI name: ${builtIn.core.match(/^# (.+)/m)?.[1] || slug}
2481
+ ${pc3.dim(builtIn.description)}
2482
+
2483
+ Use: aman-agent --profile ${slug}`
2484
+ };
2485
+ }
2486
+ fs9.mkdirSync(profileDir, { recursive: true });
2487
+ const globalCore = path9.join(os9.homedir(), ".acore", "core.md");
2488
+ if (fs9.existsSync(globalCore)) {
2489
+ let content = fs9.readFileSync(globalCore, "utf-8");
2490
+ const aiName = name.charAt(0).toUpperCase() + name.slice(1);
2491
+ content = content.replace(/^# .+$/m, `# ${aiName}`);
2492
+ fs9.writeFileSync(path9.join(profileDir, "core.md"), content, "utf-8");
2493
+ } else {
2494
+ const aiName = name.charAt(0).toUpperCase() + name.slice(1);
2495
+ fs9.writeFileSync(path9.join(profileDir, "core.md"), `# ${aiName}
2496
+
2497
+ ## Identity
2498
+ - Role: ${aiName} is your AI companion
2499
+ - Personality: helpful, adaptive
2500
+ - Communication: clear and concise
2501
+ - Values: honesty, simplicity
2502
+ - Boundaries: won't pretend to be human
2503
+ `, "utf-8");
2504
+ }
2505
+ return {
2506
+ handled: true,
2507
+ output: pc3.green(`Profile created: ${slug}`) + `
2508
+ Edit: ${path9.join(profileDir, "core.md")}
2509
+ Use: aman-agent --profile ${slug}
2510
+
2511
+ ${pc3.dim("Add rules.md or skills.md for profile-specific overrides.")}`
2512
+ };
2513
+ }
2514
+ case "show": {
2515
+ const name = args[0];
2516
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /profile show <name>") };
2517
+ const profileDir = path9.join(profilesDir, name);
2518
+ if (!fs9.existsSync(profileDir)) return { handled: true, output: pc3.red(`Profile not found: ${name}`) };
2519
+ const files = fs9.readdirSync(profileDir).filter((f) => f.endsWith(".md"));
2520
+ const lines = files.map((f) => ` ${f}`);
2521
+ return { handled: true, output: `Profile: ${pc3.bold(name)}
2522
+ Files:
2523
+ ${lines.join("\n")}` };
2524
+ }
2525
+ case "delete": {
2526
+ const name = args[0];
2527
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /profile delete <name>") };
2528
+ const profileDir = path9.join(profilesDir, name);
2529
+ if (!fs9.existsSync(profileDir)) return { handled: true, output: pc3.red(`Profile not found: ${name}`) };
2530
+ fs9.rmSync(profileDir, { recursive: true });
2531
+ return { handled: true, output: pc3.dim(`Profile deleted: ${name}`) };
2532
+ }
2533
+ case "help":
2534
+ return { handled: true, output: `Profile commands:
2535
+ /profile List all profiles
2536
+ /profile create <n> Create new profile
2537
+ /profile show <n> Show profile files
2538
+ /profile delete <n> Delete a profile
2539
+
2540
+ Use profiles:
2541
+ aman-agent --profile <name>
2542
+ AMAN_PROFILE=<name> aman-agent` };
2543
+ default:
2544
+ return { handled: true, output: pc3.yellow(`Unknown profile action: ${action}. Try /profile help`) };
2545
+ }
2546
+ }
2547
+ function handlePlanCommand(action, args) {
2548
+ if (!action) {
2549
+ const active = getActivePlan();
2550
+ if (!active) {
2551
+ return { handled: true, output: pc3.dim("No active plan. Create one with: /plan create <name> | <goal> | <step1>, <step2>, ...") };
2552
+ }
2553
+ return { handled: true, output: formatPlan(active) };
1406
2554
  }
1407
- return { handled: true, output: pc.bold("Decision Log:\n") + result };
1408
- }
1409
- function handleExportCommand() {
1410
- return { handled: true, exportConversation: true };
1411
- }
1412
- function handleDebugCommand() {
1413
- const logPath = path5.join(os5.homedir(), ".aman-agent", "debug.log");
1414
- if (!fs5.existsSync(logPath)) {
1415
- return { handled: true, output: pc.dim("No debug log found.") };
2555
+ switch (action) {
2556
+ case "create": {
2557
+ const fullArgs = args.join(" ");
2558
+ const parts = fullArgs.split("|").map((p3) => p3.trim());
2559
+ if (parts.length < 3) {
2560
+ return { handled: true, output: pc3.yellow("Usage: /plan create <name> | <goal> | <step1>, <step2>, ...") };
2561
+ }
2562
+ const name = parts[0];
2563
+ const goal = parts[1];
2564
+ const steps = parts[2].split(",").map((s) => s.trim()).filter(Boolean);
2565
+ if (steps.length === 0) {
2566
+ return { handled: true, output: pc3.yellow("Need at least one step. Separate steps with commas.") };
2567
+ }
2568
+ const plan = createPlan(name, goal, steps);
2569
+ return { handled: true, output: pc3.green(`Plan created!
2570
+
2571
+ `) + formatPlan(plan) };
2572
+ }
2573
+ case "done": {
2574
+ const active = getActivePlan();
2575
+ if (!active) return { handled: true, output: pc3.yellow("No active plan.") };
2576
+ if (args.length > 0) {
2577
+ const stepNum = parseInt(args[0], 10);
2578
+ if (isNaN(stepNum) || stepNum < 1 || stepNum > active.steps.length) {
2579
+ return { handled: true, output: pc3.yellow(`Invalid step number. Range: 1-${active.steps.length}`) };
2580
+ }
2581
+ markStepDone(active, stepNum - 1);
2582
+ return { handled: true, output: pc3.green(`Step ${stepNum} done!`) + "\n\n" + formatPlan(active) };
2583
+ }
2584
+ const next = active.steps.findIndex((s) => !s.done);
2585
+ if (next < 0) return { handled: true, output: pc3.green("All steps already complete!") };
2586
+ markStepDone(active, next);
2587
+ return { handled: true, output: pc3.green(`Step ${next + 1} done!`) + "\n\n" + formatPlan(active) };
2588
+ }
2589
+ case "undo": {
2590
+ const active = getActivePlan();
2591
+ if (!active) return { handled: true, output: pc3.yellow("No active plan.") };
2592
+ const stepNum = parseInt(args[0], 10);
2593
+ if (isNaN(stepNum) || stepNum < 1 || stepNum > active.steps.length) {
2594
+ return { handled: true, output: pc3.yellow(`Invalid step number. Range: 1-${active.steps.length}`) };
2595
+ }
2596
+ markStepUndone(active, stepNum - 1);
2597
+ return { handled: true, output: pc3.dim(`Step ${stepNum} unmarked.`) + "\n\n" + formatPlan(active) };
2598
+ }
2599
+ case "list": {
2600
+ const plans = listPlans();
2601
+ if (plans.length === 0) return { handled: true, output: pc3.dim("No plans yet.") };
2602
+ const lines = plans.map((p3) => {
2603
+ const done = p3.steps.filter((s) => s.done).length;
2604
+ const total = p3.steps.length;
2605
+ const status = p3.active ? pc3.green("active") : pc3.dim("inactive");
2606
+ return ` ${p3.name} \u2014 ${done}/${total} steps (${status})`;
2607
+ });
2608
+ return { handled: true, output: "Plans:\n" + lines.join("\n") };
2609
+ }
2610
+ case "switch": {
2611
+ const name = args.join(" ");
2612
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /plan switch <name>") };
2613
+ const plan = setActivePlan(name);
2614
+ if (!plan) return { handled: true, output: pc3.red(`Plan not found: ${name}`) };
2615
+ return { handled: true, output: pc3.green(`Switched to: ${plan.name}`) + "\n\n" + formatPlan(plan) };
2616
+ }
2617
+ case "show": {
2618
+ const name = args.join(" ");
2619
+ if (!name) return { handled: true, output: pc3.yellow("Usage: /plan show <name>") };
2620
+ const plan = loadPlan(name);
2621
+ if (!plan) return { handled: true, output: pc3.red(`Plan not found: ${name}`) };
2622
+ return { handled: true, output: formatPlan(plan) };
2623
+ }
2624
+ case "help":
2625
+ return { handled: true, output: `Plan commands:
2626
+ /plan Show active plan
2627
+ /plan create <name> | <goal> | <step1>, <step2>, ...
2628
+ /plan done [step#] Mark step complete (next if no number)
2629
+ /plan undo <step#> Unmark a step
2630
+ /plan list List all plans
2631
+ /plan switch <name> Switch active plan
2632
+ /plan show <name> Show a specific plan` };
2633
+ default:
2634
+ return { handled: true, output: pc3.yellow(`Unknown plan action: ${action}. Try /plan help`) };
1416
2635
  }
1417
- const content = fs5.readFileSync(logPath, "utf-8");
1418
- const lines = content.trim().split("\n");
1419
- const last20 = lines.slice(-20).join("\n");
1420
- return { handled: true, output: pc.bold("Debug Log (last 20 entries):\n") + pc.dim(last20) };
1421
2636
  }
1422
2637
  var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
1423
2638
  "quit",
@@ -1443,7 +2658,11 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
1443
2658
  "update-config",
1444
2659
  "reconfig",
1445
2660
  "update",
1446
- "upgrade"
2661
+ "upgrade",
2662
+ "plan",
2663
+ "profile",
2664
+ "delegate",
2665
+ "team"
1447
2666
  ]);
1448
2667
  async function handleCommand(input, ctx) {
1449
2668
  const trimmed = input.trim();
@@ -1458,9 +2677,9 @@ async function handleCommand(input, ctx) {
1458
2677
  case "help":
1459
2678
  return handleHelp();
1460
2679
  case "clear":
1461
- return { handled: true, output: pc.dim("Conversation cleared."), clearHistory: true };
2680
+ return { handled: true, output: pc3.dim("Conversation cleared."), clearHistory: true };
1462
2681
  case "model":
1463
- return { handled: true, output: ctx.model ? `Model: ${pc.bold(ctx.model)}` : "Model: unknown" };
2682
+ return { handled: true, output: ctx.model ? `Model: ${pc3.bold(ctx.model)}` : "Model: unknown" };
1464
2683
  case "identity":
1465
2684
  return handleIdentityCommand(action, args, ctx);
1466
2685
  case "rules":
@@ -1491,6 +2710,14 @@ async function handleCommand(input, ctx) {
1491
2710
  case "update-config":
1492
2711
  case "reconfig":
1493
2712
  return handleReconfig();
2713
+ case "plan":
2714
+ return handlePlanCommand(action, args);
2715
+ case "profile":
2716
+ return handleProfileCommand(action, args);
2717
+ case "delegate":
2718
+ return handleDelegateCommand(action, args, ctx);
2719
+ case "team":
2720
+ return handleTeamCommand(action, args, ctx);
1494
2721
  case "update":
1495
2722
  case "upgrade":
1496
2723
  return handleUpdate();
@@ -1500,8 +2727,183 @@ async function handleCommand(input, ctx) {
1500
2727
  }
1501
2728
 
1502
2729
  // src/hooks.ts
1503
- import pc2 from "picocolors";
2730
+ import pc4 from "picocolors";
1504
2731
  import * as p from "@clack/prompts";
2732
+ import fs10 from "fs";
2733
+ import path10 from "path";
2734
+
2735
+ // src/personality.ts
2736
+ var FRUSTRATION_SIGNALS = [
2737
+ /\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,
2738
+ /\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,
2739
+ /!{2,}/,
2740
+ // multiple exclamation marks
2741
+ /\?{2,}/
2742
+ // multiple question marks (exasperation)
2743
+ ];
2744
+ var EXCITEMENT_SIGNALS = [
2745
+ /\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,
2746
+ /\b(excited|pumped|stoked|can't wait|this is great|so cool|love this)\b/i,
2747
+ /!{1,}.*(!|🎉|🚀|✨|💪|🔥)/
2748
+ ];
2749
+ var CONFUSION_SIGNALS = [
2750
+ /\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,
2751
+ /\b(which one|what's the difference|should i|not sure (if|what|how|why|whether))\b/i
2752
+ ];
2753
+ var FATIGUE_SIGNALS = [
2754
+ /\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,
2755
+ /\b(brain (is )?fried|can't think|eyes (are )?heavy|running on fumes|barely awake)\b/i
2756
+ ];
2757
+ function scorePatterns(text2, patterns) {
2758
+ let hits = 0;
2759
+ for (const p3 of patterns) {
2760
+ if (p3.test(text2)) hits++;
2761
+ }
2762
+ return Math.min(hits / patterns.length, 1);
2763
+ }
2764
+ function detectSentiment(recentMessages) {
2765
+ if (recentMessages.length === 0) {
2766
+ return { frustration: 0, excitement: 0, confusion: 0, fatigue: 0, dominant: "neutral" };
2767
+ }
2768
+ const weights = [1, 0.6, 0.3, 0.2, 0.1];
2769
+ let frustration = 0, excitement = 0, confusion = 0, fatigue = 0;
2770
+ let totalWeight = 0;
2771
+ for (let i = 0; i < Math.min(recentMessages.length, weights.length); i++) {
2772
+ const msg = recentMessages[recentMessages.length - 1 - i];
2773
+ const w = weights[i];
2774
+ totalWeight += w;
2775
+ frustration += scorePatterns(msg, FRUSTRATION_SIGNALS) * w;
2776
+ excitement += scorePatterns(msg, EXCITEMENT_SIGNALS) * w;
2777
+ confusion += scorePatterns(msg, CONFUSION_SIGNALS) * w;
2778
+ fatigue += scorePatterns(msg, FATIGUE_SIGNALS) * w;
2779
+ }
2780
+ if (totalWeight > 0) {
2781
+ frustration /= totalWeight;
2782
+ excitement /= totalWeight;
2783
+ confusion /= totalWeight;
2784
+ fatigue /= totalWeight;
2785
+ }
2786
+ const scores = { frustrated: frustration, excited: excitement, confused: confusion, fatigued: fatigue };
2787
+ const maxKey = Object.entries(scores).reduce((a, b) => a[1] > b[1] ? a : b);
2788
+ const dominant = maxKey[1] > 0.15 ? maxKey[0] : "neutral";
2789
+ return { frustration, excitement, confusion, fatigue, dominant };
2790
+ }
2791
+ function computePersonality(signals) {
2792
+ const { timePeriod, sessionMinutes, turnCount, recentMessages } = signals;
2793
+ const sentiment = detectSentiment(recentMessages || []);
2794
+ let energy = "steady";
2795
+ if (timePeriod === "morning" && sentiment.dominant !== "fatigued") {
2796
+ energy = "high-drive";
2797
+ } else if (timePeriod === "late-night" || timePeriod === "night" && sessionMinutes > 45) {
2798
+ energy = "reflective";
2799
+ } else if (sentiment.dominant === "fatigued") {
2800
+ energy = "reflective";
2801
+ } else if (sentiment.dominant === "excited") {
2802
+ energy = "high-drive";
2803
+ } else if (timePeriod === "afternoon" && turnCount > 20) {
2804
+ energy = "reflective";
2805
+ }
2806
+ let activeMode = "Default";
2807
+ if (timePeriod === "late-night") {
2808
+ activeMode = "Personal";
2809
+ } else if (sentiment.dominant === "frustrated" || sentiment.dominant === "fatigued") {
2810
+ activeMode = "Personal";
2811
+ }
2812
+ const readParts = [];
2813
+ switch (timePeriod) {
2814
+ case "late-night":
2815
+ readParts.push("late night session");
2816
+ if (sessionMinutes > 60) readParts.push("been going a while");
2817
+ else readParts.push("quiet hours");
2818
+ break;
2819
+ case "morning":
2820
+ readParts.push("morning session");
2821
+ if (turnCount <= 3) readParts.push("just getting started");
2822
+ else readParts.push("building momentum");
2823
+ break;
2824
+ case "afternoon":
2825
+ readParts.push("afternoon session");
2826
+ if (turnCount > 15) readParts.push("deep in flow");
2827
+ else readParts.push("steady pace");
2828
+ break;
2829
+ case "evening":
2830
+ readParts.push("evening session");
2831
+ if (sessionMinutes > 60) readParts.push("long session");
2832
+ break;
2833
+ case "night":
2834
+ readParts.push("night session");
2835
+ if (sessionMinutes > 45) readParts.push("getting late");
2836
+ break;
2837
+ }
2838
+ switch (sentiment.dominant) {
2839
+ case "frustrated":
2840
+ readParts.push("user seems stuck or frustrated");
2841
+ break;
2842
+ case "excited":
2843
+ readParts.push("user is energized and making progress");
2844
+ break;
2845
+ case "confused":
2846
+ readParts.push("user may need clearer explanations");
2847
+ break;
2848
+ case "fatigued":
2849
+ readParts.push("user seems tired");
2850
+ break;
2851
+ }
2852
+ const currentRead = readParts.join(", ");
2853
+ const sleepReminder = timePeriod === "late-night" && sessionMinutes > 60 || timePeriod === "night" && sessionMinutes > 90;
2854
+ let wellbeingNudge = null;
2855
+ if (sleepReminder && sentiment.dominant === "frustrated") {
2856
+ wellbeingNudge = "sleep-frustrated";
2857
+ } else if (sleepReminder) {
2858
+ wellbeingNudge = "sleep";
2859
+ } else if (sentiment.dominant === "frustrated" && sessionMinutes > 90) {
2860
+ wellbeingNudge = "break-frustrated";
2861
+ } else if (sentiment.dominant === "frustrated" && turnCount > 15) {
2862
+ wellbeingNudge = "step-back";
2863
+ } else if (sentiment.dominant === "fatigued") {
2864
+ wellbeingNudge = "rest";
2865
+ } else if (sessionMinutes > 120) {
2866
+ wellbeingNudge = "break-long-session";
2867
+ }
2868
+ return { currentRead, energy, activeMode, sleepReminder, wellbeingNudge, sentiment };
2869
+ }
2870
+ var WELLBEING_NUDGES = {
2871
+ "sleep": `<wellbeing>
2872
+ 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.
2873
+ </wellbeing>`,
2874
+ "sleep-frustrated": `<wellbeing>
2875
+ 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.
2876
+ </wellbeing>`,
2877
+ "break-frustrated": `<wellbeing>
2878
+ 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.
2879
+ </wellbeing>`,
2880
+ "step-back": `<wellbeing>
2881
+ 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.
2882
+ </wellbeing>`,
2883
+ "rest": `<wellbeing>
2884
+ 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.
2885
+ </wellbeing>`,
2886
+ "break-long-session": `<wellbeing>
2887
+ 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.
2888
+ </wellbeing>`
2889
+ };
2890
+ function formatWellbeingNudge(state) {
2891
+ if (!state.wellbeingNudge) return null;
2892
+ return WELLBEING_NUDGES[state.wellbeingNudge] || null;
2893
+ }
2894
+ async function syncPersonalityToCore(state, mcpManager) {
2895
+ try {
2896
+ await mcpManager.callTool("identity_update_dynamics", {
2897
+ currentRead: state.currentRead,
2898
+ energy: state.energy,
2899
+ activeMode: state.activeMode
2900
+ });
2901
+ } catch (err) {
2902
+ log.debug("personality", "identity_update_dynamics failed", err);
2903
+ }
2904
+ }
2905
+
2906
+ // src/hooks.ts
1505
2907
  function getTimeContext() {
1506
2908
  const now = /* @__PURE__ */ new Date();
1507
2909
  const hour = now.getHours();
@@ -1521,6 +2923,10 @@ Adapt your tone naturally \u2014 don't announce the time, just be contextually a
1521
2923
  </time-context>`;
1522
2924
  }
1523
2925
  var isHookCall = false;
2926
+ var sessionStartTime = Date.now();
2927
+ function getSessionStartTime() {
2928
+ return sessionStartTime;
2929
+ }
1524
2930
  async function onSessionStart(ctx) {
1525
2931
  let greeting = "";
1526
2932
  let contextInjection = "";
@@ -1529,10 +2935,8 @@ async function onSessionStart(ctx) {
1529
2935
  const visibleReminders = [];
1530
2936
  try {
1531
2937
  isHookCall = true;
1532
- const recallResult = await ctx.mcpManager.callTool("memory_recall", { query: "*", limit: 1 });
1533
- if (!recallResult || recallResult.startsWith("Error") || recallResult.includes("No memories found")) {
1534
- firstRun = true;
1535
- }
2938
+ const recallResult = await memoryRecall("*", { limit: 1 });
2939
+ firstRun = recallResult.total === 0;
1536
2940
  } catch {
1537
2941
  firstRun = true;
1538
2942
  } finally {
@@ -1562,9 +2966,9 @@ ${contextInjection}`;
1562
2966
  if (ctx.config.memoryRecall) {
1563
2967
  try {
1564
2968
  isHookCall = true;
1565
- const result = await ctx.mcpManager.callTool("memory_context", { topic: "session context" });
1566
- if (result && !result.startsWith("Error")) {
1567
- greeting += result;
2969
+ const contextResult = await memoryContext("session context");
2970
+ if (contextResult.memoriesUsed > 0) {
2971
+ greeting += contextResult.text;
1568
2972
  }
1569
2973
  } catch (err) {
1570
2974
  log.warn("hooks", "memory_context recall failed", err);
@@ -1595,12 +2999,12 @@ ${contextInjection}`;
1595
2999
  else greeting = timeContext;
1596
3000
  try {
1597
3001
  isHookCall = true;
1598
- const reminderResult = await ctx.mcpManager.callTool("reminder_check", {});
1599
- if (reminderResult && !reminderResult.startsWith("Error") && !reminderResult.includes("No pending")) {
1600
- greeting += "\n\n<pending-reminders>\n" + reminderResult + "\n</pending-reminders>";
1601
- const lines = reminderResult.split("\n").filter((l) => l.trim().length > 0);
1602
- for (const line of lines) {
1603
- visibleReminders.push(line.trim());
3002
+ const reminders = reminderCheck();
3003
+ if (reminders.length > 0) {
3004
+ const reminderText = reminders.map((r) => r.content).join("\n");
3005
+ greeting += "\n\n<pending-reminders>\n" + reminderText + "\n</pending-reminders>";
3006
+ for (const r of reminders) {
3007
+ visibleReminders.push(r.content);
1604
3008
  }
1605
3009
  }
1606
3010
  } catch (err) {
@@ -1608,6 +3012,27 @@ ${contextInjection}`;
1608
3012
  } finally {
1609
3013
  isHookCall = false;
1610
3014
  }
3015
+ if (ctx.config.personalityAdapt !== false) {
3016
+ sessionStartTime = Date.now();
3017
+ const hour = (/* @__PURE__ */ new Date()).getHours();
3018
+ let period;
3019
+ if (hour < 6) period = "late-night";
3020
+ else if (hour < 12) period = "morning";
3021
+ else if (hour < 17) period = "afternoon";
3022
+ else if (hour < 21) period = "evening";
3023
+ else period = "night";
3024
+ const state = computePersonality({
3025
+ timePeriod: period,
3026
+ sessionMinutes: 0,
3027
+ turnCount: 0
3028
+ });
3029
+ syncPersonalityToCore(state, ctx.mcpManager).catch(() => {
3030
+ });
3031
+ const nudge = formatWellbeingNudge(state);
3032
+ if (nudge) {
3033
+ greeting += "\n" + nudge;
3034
+ }
3035
+ }
1611
3036
  if (greeting) {
1612
3037
  contextInjection = `<session-context>
1613
3038
  ${greeting}
@@ -1689,16 +3114,12 @@ async function onWorkflowMatch(userInput, ctx) {
1689
3114
  async function onSessionEnd(ctx, messages, sessionId) {
1690
3115
  try {
1691
3116
  if (ctx.config.autoSessionSave && messages.length > 2) {
1692
- console.log(pc2.dim("\n Saving conversation to memory..."));
3117
+ console.log(pc4.dim("\n Saving conversation to memory..."));
1693
3118
  const textMessages = messages.filter((m) => typeof m.content === "string").slice(-50);
1694
3119
  for (const msg of textMessages) {
1695
3120
  try {
1696
3121
  isHookCall = true;
1697
- await ctx.mcpManager.callTool("memory_log", {
1698
- session_id: sessionId,
1699
- role: msg.role,
1700
- content: msg.content.slice(0, 5e3)
1701
- });
3122
+ memoryLog(sessionId, msg.role, msg.content.slice(0, 5e3));
1702
3123
  } catch (err) {
1703
3124
  log.debug("hooks", "memory_log write failed for " + sessionId, err);
1704
3125
  } finally {
@@ -1724,7 +3145,57 @@ async function onSessionEnd(ctx, messages, sessionId) {
1724
3145
  isHookCall = false;
1725
3146
  }
1726
3147
  }
1727
- console.log(pc2.dim(` Saved ${textMessages.length} messages (session: ${sessionId})`));
3148
+ console.log(pc4.dim(` Saved ${textMessages.length} messages (session: ${sessionId})`));
3149
+ }
3150
+ const projectContextPath = path10.join(process.cwd(), ".acore", "context.md");
3151
+ if (fs10.existsSync(projectContextPath) && messages.length > 2) {
3152
+ try {
3153
+ let contextContent = fs10.readFileSync(projectContextPath, "utf-8");
3154
+ const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3155
+ let lastUserMsg = "";
3156
+ for (let i = messages.length - 1; i >= 0; i--) {
3157
+ if (messages[i].role === "user" && typeof messages[i].content === "string") {
3158
+ lastUserMsg = messages[i].content.slice(0, 200);
3159
+ break;
3160
+ }
3161
+ }
3162
+ const sessionPattern = /## Session\n[\s\S]*?(?=\n## |$)/;
3163
+ if (sessionPattern.test(contextContent)) {
3164
+ const newSession = `## Session
3165
+ - Last updated: ${now}
3166
+ - Resume: ${lastUserMsg || "See conversation history"}
3167
+ - Active topics: [see memory]
3168
+ - Recent decisions: [see memory]
3169
+ - Temp notes: [cleared]`;
3170
+ contextContent = contextContent.replace(sessionPattern, newSession);
3171
+ fs10.writeFileSync(projectContextPath, contextContent, "utf-8");
3172
+ log.debug("hooks", `Updated project context: ${projectContextPath}`);
3173
+ }
3174
+ } catch (err) {
3175
+ log.debug("hooks", "project context update failed", err);
3176
+ }
3177
+ }
3178
+ if (ctx.config.personalityAdapt !== false) {
3179
+ const sessionMinutes = Math.round((Date.now() - sessionStartTime) / 6e4);
3180
+ const hour = (/* @__PURE__ */ new Date()).getHours();
3181
+ let period;
3182
+ if (hour < 6) period = "late-night";
3183
+ else if (hour < 12) period = "morning";
3184
+ else if (hour < 17) period = "afternoon";
3185
+ else if (hour < 21) period = "evening";
3186
+ else period = "night";
3187
+ const turnCount = messages.filter((m) => m.role === "user").length;
3188
+ const finalState = computePersonality({
3189
+ timePeriod: period,
3190
+ sessionMinutes,
3191
+ turnCount
3192
+ });
3193
+ try {
3194
+ isHookCall = true;
3195
+ await syncPersonalityToCore(finalState, ctx.mcpManager);
3196
+ } finally {
3197
+ isHookCall = false;
3198
+ }
1728
3199
  }
1729
3200
  if (ctx.config.evalPrompt) {
1730
3201
  const rating = await p.select({
@@ -1826,6 +3297,365 @@ ${summaryParts.slice(0, 20).join("\n")}
1826
3297
  messages.push(...recent);
1827
3298
  }
1828
3299
 
3300
+ // src/skill-engine.ts
3301
+ import fs11 from "fs";
3302
+ import path11 from "path";
3303
+ import os10 from "os";
3304
+ var SKILL_TRIGGERS = {
3305
+ testing: ["test", "spec", "coverage", "tdd", "jest", "vitest", "mocha", "assert", "mock", "stub", "fixture", "e2e", "integration test", "unit test"],
3306
+ "api-design": ["api", "endpoint", "rest", "graphql", "route", "controller", "middleware", "http", "request", "response", "status code", "pagination"],
3307
+ security: ["security", "auth", "csrf", "xss", "injection", "cors", "jwt", "token", "oauth", "password", "hash", "encrypt", "vulnerability", "owasp", "sanitize"],
3308
+ performance: ["performance", "slow", "latency", "cache", "optimize", "profil", "bundle size", "lazy load", "memory leak", "benchmark", "bottleneck"],
3309
+ "code-review": ["review", "pr review", "pull request", "code quality", "clean code", "best practice"],
3310
+ documentation: ["document", "readme", "jsdoc", "tsdoc", "changelog", "adr", "comment"],
3311
+ "git-workflow": ["git", "branch", "merge", "rebase", "cherry-pick", "bisect", "stash", "commit message", "pr", "pull request"],
3312
+ debugging: ["debug", "breakpoint", "stack trace", "error", "exception", "crash", "bug", "issue", "unexpected", "reproduce"],
3313
+ refactoring: ["refactor", "extract", "rename", "move", "split", "consolidate", "dry", "code smell", "technical debt", "legacy"],
3314
+ database: ["database", "schema", "migration", "index", "query", "sql", "postgres", "mysql", "sqlite", "mongo", "orm", "prisma", "drizzle"],
3315
+ typescript: ["typescript", "type", "interface", "generic", "infer", "utility type", "zod", "discriminated union", "type guard", "as const"],
3316
+ accessibility: ["accessibility", "a11y", "aria", "screen reader", "wcag", "semantic html", "tab order", "focus", "contrast"]
3317
+ };
3318
+ var LEVEL_FILE = path11.join(os10.homedir(), ".aman-agent", "skill-levels.json");
3319
+ function loadSkillLevels() {
3320
+ try {
3321
+ if (fs11.existsSync(LEVEL_FILE)) {
3322
+ return JSON.parse(fs11.readFileSync(LEVEL_FILE, "utf-8"));
3323
+ }
3324
+ } catch {
3325
+ }
3326
+ return {};
3327
+ }
3328
+ function saveSkillLevels(levels) {
3329
+ const dir = path11.dirname(LEVEL_FILE);
3330
+ if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
3331
+ fs11.writeFileSync(LEVEL_FILE, JSON.stringify(levels, null, 2), "utf-8");
3332
+ }
3333
+ function computeLevel(activations) {
3334
+ if (activations >= 50) return { level: 5, label: "Expert" };
3335
+ if (activations >= 25) return { level: 4, label: "Advanced" };
3336
+ if (activations >= 10) return { level: 3, label: "Proficient" };
3337
+ if (activations >= 3) return { level: 2, label: "Familiar" };
3338
+ return { level: 1, label: "Learning" };
3339
+ }
3340
+ function recordActivation(skillName) {
3341
+ const levels = loadSkillLevels();
3342
+ if (!levels[skillName]) {
3343
+ levels[skillName] = { name: skillName, activations: 0, lastUsed: "", userPatterns: [] };
3344
+ }
3345
+ levels[skillName].activations++;
3346
+ levels[skillName].lastUsed = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3347
+ saveSkillLevels(levels);
3348
+ return computeLevel(levels[skillName].activations);
3349
+ }
3350
+ function matchSkills(userInput, installedSkillNames) {
3351
+ const input = userInput.toLowerCase();
3352
+ const matched = [];
3353
+ for (const skillName of installedSkillNames) {
3354
+ const triggers = SKILL_TRIGGERS[skillName];
3355
+ if (!triggers) continue;
3356
+ for (const trigger of triggers) {
3357
+ if (input.includes(trigger)) {
3358
+ matched.push(skillName);
3359
+ break;
3360
+ }
3361
+ }
3362
+ }
3363
+ return matched;
3364
+ }
3365
+ function formatSkillContext(skillName, skillContent, level) {
3366
+ let depthHint;
3367
+ if (level.level >= 4) {
3368
+ depthHint = "User is advanced \u2014 skip basics, focus on edge cases and proactive optimization.";
3369
+ } else if (level.level >= 3) {
3370
+ depthHint = "User is proficient \u2014 brief reminders of principles, focus on the specific task.";
3371
+ } else if (level.level >= 2) {
3372
+ depthHint = "User is familiar \u2014 explain reasoning briefly, show patterns.";
3373
+ } else {
3374
+ depthHint = "User is learning \u2014 explain concepts clearly, show examples, be patient.";
3375
+ }
3376
+ return `<active-skill name="${skillName}" level="${level.level}" label="${level.label}">
3377
+ ${depthHint}
3378
+
3379
+ ${skillContent}
3380
+ </active-skill>`;
3381
+ }
3382
+ async function autoTriggerSkills(userInput, mcpManager) {
3383
+ try {
3384
+ const result = await mcpManager.callTool("skill_list", {});
3385
+ const skills = JSON.parse(result);
3386
+ const installed = skills.filter((s) => s.installed).map((s) => s.name);
3387
+ if (installed.length === 0) return "";
3388
+ const matched = matchSkills(userInput, installed);
3389
+ if (matched.length === 0) return "";
3390
+ const blocks = [];
3391
+ for (const skillName of matched.slice(0, 2)) {
3392
+ const level = recordActivation(skillName);
3393
+ const skillsContent = await mcpManager.callTool("skill_search", { query: skillName });
3394
+ const skillEntries = JSON.parse(skillsContent);
3395
+ const entry = skillEntries.find((s) => s.name.toLowerCase() === skillName.toLowerCase());
3396
+ if (entry) {
3397
+ blocks.push(formatSkillContext(skillName, entry.description, level));
3398
+ }
3399
+ log.debug("skill-engine", `Auto-triggered: ${skillName} (Lv.${level.level} ${level.label})`);
3400
+ }
3401
+ return blocks.join("\n\n");
3402
+ } catch (err) {
3403
+ log.debug("skill-engine", "autoTriggerSkills failed", err);
3404
+ return "";
3405
+ }
3406
+ }
3407
+ function enrichSkill(skillName, pattern) {
3408
+ const levels = loadSkillLevels();
3409
+ if (!levels[skillName]) {
3410
+ levels[skillName] = { name: skillName, activations: 0, lastUsed: "", userPatterns: [] };
3411
+ }
3412
+ const existing = levels[skillName].userPatterns;
3413
+ if (existing.length >= 20) return;
3414
+ if (existing.some((p3) => p3.toLowerCase() === pattern.toLowerCase())) return;
3415
+ levels[skillName].userPatterns.push(pattern);
3416
+ saveSkillLevels(levels);
3417
+ log.debug("skill-engine", `Enriched ${skillName} with pattern: ${pattern.slice(0, 80)}`);
3418
+ }
3419
+ function matchPatternToSkill(patternContent, tags) {
3420
+ const combined = (patternContent + " " + tags.join(" ")).toLowerCase();
3421
+ for (const [skillName, triggers] of Object.entries(SKILL_TRIGGERS)) {
3422
+ for (const trigger of triggers) {
3423
+ if (combined.includes(trigger)) {
3424
+ return skillName;
3425
+ }
3426
+ }
3427
+ }
3428
+ return null;
3429
+ }
3430
+ var KNOWLEDGE_LIBRARY = [
3431
+ {
3432
+ name: "security-headers",
3433
+ category: "security",
3434
+ description: "Essential HTTP security headers for web applications",
3435
+ content: `Essential Security Headers:
3436
+ - Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'
3437
+ - X-Content-Type-Options: nosniff
3438
+ - X-Frame-Options: DENY
3439
+ - Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
3440
+ - X-XSS-Protection: 0 (CSP replaces this)
3441
+ - Referrer-Policy: strict-origin-when-cross-origin
3442
+ - Permissions-Policy: camera=(), microphone=(), geolocation=()`
3443
+ },
3444
+ {
3445
+ name: "docker-node",
3446
+ category: "deployment",
3447
+ description: "Production Node.js Dockerfile template",
3448
+ content: `FROM node:22-alpine AS builder
3449
+ WORKDIR /app
3450
+ COPY package*.json ./
3451
+ RUN npm ci --production=false
3452
+ COPY . .
3453
+ RUN npm run build
3454
+
3455
+ FROM node:22-alpine
3456
+ WORKDIR /app
3457
+ RUN addgroup -g 1001 -S nodejs && adduser -S appuser -u 1001
3458
+ COPY --from=builder /app/dist ./dist
3459
+ COPY --from=builder /app/node_modules ./node_modules
3460
+ COPY --from=builder /app/package.json ./
3461
+ USER appuser
3462
+ EXPOSE 3000
3463
+ CMD ["node", "dist/index.js"]`
3464
+ },
3465
+ {
3466
+ name: "github-actions-node",
3467
+ category: "ci",
3468
+ description: "CI/CD pipeline for Node.js with GitHub Actions",
3469
+ content: `name: CI
3470
+ on: [push, pull_request]
3471
+ jobs:
3472
+ build:
3473
+ runs-on: ubuntu-latest
3474
+ steps:
3475
+ - uses: actions/checkout@v5
3476
+ - uses: actions/setup-node@v5
3477
+ with: { node-version: 22 }
3478
+ - run: npm ci
3479
+ - run: npm test
3480
+ - run: npm run build`
3481
+ },
3482
+ {
3483
+ name: "env-config",
3484
+ category: "configuration",
3485
+ description: "Environment variable configuration pattern with validation",
3486
+ content: `import { z } from "zod";
3487
+
3488
+ const envSchema = z.object({
3489
+ NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
3490
+ PORT: z.coerce.number().default(3000),
3491
+ DATABASE_URL: z.string().url(),
3492
+ API_KEY: z.string().min(1),
3493
+ });
3494
+
3495
+ export const env = envSchema.parse(process.env);`
3496
+ },
3497
+ {
3498
+ name: "error-handling",
3499
+ category: "patterns",
3500
+ description: "TypeScript error handling patterns with Result type",
3501
+ content: `type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
3502
+
3503
+ function ok<T>(value: T): Result<T, never> { return { ok: true, value }; }
3504
+ function err<E>(error: E): Result<never, E> { return { ok: false, error }; }
3505
+
3506
+ // Usage:
3507
+ async function fetchUser(id: string): Promise<Result<User>> {
3508
+ try {
3509
+ const user = await db.users.findUnique({ where: { id } });
3510
+ if (!user) return err(new Error("User not found"));
3511
+ return ok(user);
3512
+ } catch (e) {
3513
+ return err(e instanceof Error ? e : new Error(String(e)));
3514
+ }
3515
+ }`
3516
+ },
3517
+ {
3518
+ name: "rate-limiter",
3519
+ category: "security",
3520
+ description: "Token bucket rate limiter implementation",
3521
+ content: `class RateLimiter {
3522
+ private tokens: Map<string, { count: number; resetAt: number }> = new Map();
3523
+
3524
+ constructor(private maxRequests: number, private windowMs: number) {}
3525
+
3526
+ allow(key: string): boolean {
3527
+ const now = Date.now();
3528
+ const entry = this.tokens.get(key);
3529
+ if (!entry || now > entry.resetAt) {
3530
+ this.tokens.set(key, { count: 1, resetAt: now + this.windowMs });
3531
+ return true;
3532
+ }
3533
+ if (entry.count >= this.maxRequests) return false;
3534
+ entry.count++;
3535
+ return true;
3536
+ }
3537
+ }`
3538
+ },
3539
+ {
3540
+ name: "prisma-setup",
3541
+ category: "database",
3542
+ description: "Prisma ORM setup with connection pooling",
3543
+ content: `import { PrismaClient } from "@prisma/client";
3544
+
3545
+ const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
3546
+ export const prisma = globalForPrisma.prisma ?? new PrismaClient({
3547
+ log: process.env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"],
3548
+ });
3549
+ if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;`
3550
+ },
3551
+ {
3552
+ name: "zod-validation",
3553
+ category: "validation",
3554
+ description: "Zod schema patterns for API input validation",
3555
+ content: `import { z } from "zod";
3556
+
3557
+ const CreateUserSchema = z.object({
3558
+ email: z.string().email(),
3559
+ name: z.string().min(1).max(100),
3560
+ age: z.number().int().min(13).max(150).optional(),
3561
+ role: z.enum(["user", "admin"]).default("user"),
3562
+ tags: z.array(z.string()).max(10).default([]),
3563
+ });
3564
+
3565
+ type CreateUser = z.infer<typeof CreateUserSchema>;
3566
+
3567
+ // Express middleware:
3568
+ function validate<T>(schema: z.ZodSchema<T>) {
3569
+ return (req, res, next) => {
3570
+ const result = schema.safeParse(req.body);
3571
+ if (!result.success) return res.status(400).json({ errors: result.error.flatten() });
3572
+ req.body = result.data;
3573
+ next();
3574
+ };
3575
+ }`
3576
+ },
3577
+ {
3578
+ name: "testing-patterns",
3579
+ category: "testing",
3580
+ description: "Test organization and assertion patterns",
3581
+ content: `// Arrange-Act-Assert pattern
3582
+ describe("UserService", () => {
3583
+ it("creates user with valid email", async () => {
3584
+ // Arrange
3585
+ const input = { email: "test@example.com", name: "Test" };
3586
+
3587
+ // Act
3588
+ const user = await userService.create(input);
3589
+
3590
+ // Assert
3591
+ expect(user.id).toBeDefined();
3592
+ expect(user.email).toBe(input.email);
3593
+ });
3594
+
3595
+ it("rejects duplicate email", async () => {
3596
+ await userService.create({ email: "dup@test.com", name: "First" });
3597
+ await expect(userService.create({ email: "dup@test.com", name: "Second" }))
3598
+ .rejects.toThrow("already exists");
3599
+ });
3600
+ });`
3601
+ },
3602
+ {
3603
+ name: "git-hooks",
3604
+ category: "git",
3605
+ description: "Pre-commit and commit-msg hooks with lint-staged",
3606
+ content: `// package.json
3607
+ {
3608
+ "lint-staged": {
3609
+ "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
3610
+ "*.{json,md}": ["prettier --write"]
3611
+ }
3612
+ }
3613
+
3614
+ // .husky/pre-commit
3615
+ npx lint-staged
3616
+
3617
+ // .husky/commit-msg
3618
+ npx commitlint --edit $1
3619
+
3620
+ // commitlint.config.js
3621
+ module.exports = { extends: ["@commitlint/config-conventional"] };`
3622
+ }
3623
+ ];
3624
+ function matchKnowledge(userInput) {
3625
+ const input = userInput.toLowerCase();
3626
+ const keywordMap = {
3627
+ "security header": "security-headers",
3628
+ "csp": "security-headers",
3629
+ "content-security": "security-headers",
3630
+ "dockerfile": "docker-node",
3631
+ "docker": "docker-node",
3632
+ "github action": "github-actions-node",
3633
+ "ci/cd": "github-actions-node",
3634
+ "ci pipeline": "github-actions-node",
3635
+ "env config": "env-config",
3636
+ "environment variable": "env-config",
3637
+ "error handling": "error-handling",
3638
+ "result type": "error-handling",
3639
+ "rate limit": "rate-limiter",
3640
+ "throttle": "rate-limiter",
3641
+ "prisma": "prisma-setup",
3642
+ "zod": "zod-validation",
3643
+ "validation": "zod-validation",
3644
+ "test pattern": "testing-patterns",
3645
+ "arrange act assert": "testing-patterns",
3646
+ "git hook": "git-hooks",
3647
+ "pre-commit": "git-hooks",
3648
+ "lint-staged": "git-hooks",
3649
+ "husky": "git-hooks"
3650
+ };
3651
+ for (const [keyword, itemName] of Object.entries(keywordMap)) {
3652
+ if (input.includes(keyword)) {
3653
+ return KNOWLEDGE_LIBRARY.find((i) => i.name === itemName) || null;
3654
+ }
3655
+ }
3656
+ return null;
3657
+ }
3658
+
1829
3659
  // src/memory-extractor.ts
1830
3660
  var VALID_TYPES = /* @__PURE__ */ new Set(["preference", "fact", "pattern", "topology", "decision", "correction"]);
1831
3661
  var MIN_RESPONSE_LENGTH = 50;
@@ -1876,7 +3706,7 @@ function parseExtractionResult(raw) {
1876
3706
  return [];
1877
3707
  }
1878
3708
  }
1879
- async function extractMemories(userMessage, assistantResponse, client, mcpManager, state) {
3709
+ async function extractMemories(userMessage, assistantResponse, client, state) {
1880
3710
  if (!shouldExtract(assistantResponse, state.turnsSinceLastExtraction, state.lastExtractionCount)) {
1881
3711
  state.turnsSinceLastExtraction++;
1882
3712
  return 0;
@@ -1900,24 +3730,18 @@ Assistant: ${assistantResponse.slice(0, 2e3)}`;
1900
3730
  let stored = 0;
1901
3731
  for (const candidate of candidates) {
1902
3732
  try {
1903
- const existing = await mcpManager.callTool("memory_recall", {
1904
- query: candidate.content,
1905
- limit: 1
1906
- });
1907
- if (existing && !existing.startsWith("Error")) {
1908
- try {
1909
- const parsed = JSON.parse(existing);
1910
- if (Array.isArray(parsed) && parsed.length > 0 && parsed[0].score > 0.85) {
1911
- log.debug("extractor", "Skipping duplicate: " + candidate.content);
1912
- continue;
1913
- }
1914
- } catch {
3733
+ const existing = await memoryRecall(candidate.content, { limit: 1 });
3734
+ if (existing.total > 0 && existing.memories.length > 0) {
3735
+ const topScore = existing.memories[0]?.score;
3736
+ if (topScore && topScore > 0.85) {
3737
+ log.debug("extractor", "Skipping duplicate: " + candidate.content);
3738
+ continue;
1915
3739
  }
1916
3740
  }
1917
3741
  } catch {
1918
3742
  }
1919
3743
  try {
1920
- await mcpManager.callTool("memory_store", {
3744
+ await memoryStore({
1921
3745
  content: candidate.content,
1922
3746
  type: candidate.type,
1923
3747
  tags: candidate.tags,
@@ -1927,6 +3751,12 @@ Assistant: ${assistantResponse.slice(0, 2e3)}`;
1927
3751
  });
1928
3752
  stored++;
1929
3753
  log.debug("extractor", "Stored " + candidate.type + ": " + candidate.content);
3754
+ if (candidate.type === "pattern" || candidate.type === "preference") {
3755
+ const skillMatch = matchPatternToSkill(candidate.content, candidate.tags);
3756
+ if (skillMatch) {
3757
+ enrichSkill(skillMatch, candidate.content);
3758
+ }
3759
+ }
1930
3760
  } catch (err) {
1931
3761
  log.warn("extractor", "Failed to store: " + candidate.content, err);
1932
3762
  }
@@ -1940,6 +3770,147 @@ Assistant: ${assistantResponse.slice(0, 2e3)}`;
1940
3770
  }
1941
3771
  }
1942
3772
 
3773
+ // src/background.ts
3774
+ import pc5 from "picocolors";
3775
+ var BACKGROUND_ELIGIBLE = /* @__PURE__ */ new Set([
3776
+ "run_tests",
3777
+ "npm_test",
3778
+ "build",
3779
+ "npm_build",
3780
+ "file_search",
3781
+ "code_search",
3782
+ "grep_search",
3783
+ "git_clone",
3784
+ "docker_build",
3785
+ "docker_run"
3786
+ ]);
3787
+ var NEVER_BACKGROUND = /* @__PURE__ */ new Set([
3788
+ "memory_recall",
3789
+ "memory_store",
3790
+ "memory_log",
3791
+ "memory_context",
3792
+ "memory_detail",
3793
+ "identity_read",
3794
+ "identity_summary",
3795
+ "identity_update_session",
3796
+ "identity_update_dynamics",
3797
+ "rules_check",
3798
+ "rules_list",
3799
+ "workflow_list",
3800
+ "workflow_get",
3801
+ "skill_list",
3802
+ "skill_search",
3803
+ "eval_status",
3804
+ "eval_log",
3805
+ "reminder_check",
3806
+ "reminder_set",
3807
+ "file_read",
3808
+ "doc_convert",
3809
+ "file_list",
3810
+ "avatar_prompt"
3811
+ ]);
3812
+ function shouldRunInBackground(toolName) {
3813
+ if (NEVER_BACKGROUND.has(toolName)) return false;
3814
+ if (BACKGROUND_ELIGIBLE.has(toolName)) return true;
3815
+ return false;
3816
+ }
3817
+ var BackgroundTaskManager = class {
3818
+ tasks = /* @__PURE__ */ new Map();
3819
+ taskCounter = 0;
3820
+ /**
3821
+ * Launch a tool call in the background.
3822
+ */
3823
+ launch(toolName, toolUseId, mcpManager, toolInput) {
3824
+ const id = `bg-${++this.taskCounter}`;
3825
+ const task = {
3826
+ id,
3827
+ toolName,
3828
+ toolUseId,
3829
+ startedAt: Date.now(),
3830
+ done: false,
3831
+ promise: mcpManager.callTool(toolName, toolInput).then(
3832
+ (result) => {
3833
+ task.result = result;
3834
+ task.done = true;
3835
+ return result;
3836
+ },
3837
+ (error) => {
3838
+ task.error = error instanceof Error ? error.message : String(error);
3839
+ task.done = true;
3840
+ return `Error: ${task.error}`;
3841
+ }
3842
+ )
3843
+ };
3844
+ this.tasks.set(id, task);
3845
+ process.stdout.write(pc5.dim(` [${toolName} running in background (${id})...]
3846
+ `));
3847
+ return task;
3848
+ }
3849
+ /**
3850
+ * Check for completed background tasks and return their results.
3851
+ */
3852
+ collectCompleted() {
3853
+ const completed = [];
3854
+ for (const [id, task] of this.tasks) {
3855
+ if (task.done) {
3856
+ completed.push(task);
3857
+ this.tasks.delete(id);
3858
+ }
3859
+ }
3860
+ return completed;
3861
+ }
3862
+ /**
3863
+ * Display completed background task results to the user.
3864
+ */
3865
+ displayCompleted() {
3866
+ const completed = this.collectCompleted();
3867
+ const outputs = [];
3868
+ for (const task of completed) {
3869
+ const elapsed = ((Date.now() - task.startedAt) / 1e3).toFixed(1);
3870
+ if (task.error) {
3871
+ process.stdout.write(pc5.yellow(`
3872
+ [${task.id}] ${task.toolName} failed after ${elapsed}s: ${task.error}
3873
+ `));
3874
+ outputs.push(`[Background task ${task.toolName} failed: ${task.error}]`);
3875
+ } else {
3876
+ process.stdout.write(pc5.green(`
3877
+ [${task.id}] ${task.toolName} completed in ${elapsed}s
3878
+ `));
3879
+ const preview = (task.result || "").slice(0, 200);
3880
+ if (preview) {
3881
+ process.stdout.write(pc5.dim(` ${preview}${(task.result || "").length > 200 ? "..." : ""}
3882
+ `));
3883
+ }
3884
+ outputs.push(`[Background task ${task.toolName} completed: ${task.result}]`);
3885
+ }
3886
+ }
3887
+ return outputs;
3888
+ }
3889
+ /**
3890
+ * Wait for all pending background tasks to complete.
3891
+ */
3892
+ async waitAll() {
3893
+ const pending = [...this.tasks.values()].filter((t) => !t.done);
3894
+ if (pending.length === 0) return;
3895
+ process.stdout.write(pc5.dim(`
3896
+ Waiting for ${pending.length} background task(s)...
3897
+ `));
3898
+ await Promise.allSettled(pending.map((t) => t.promise));
3899
+ }
3900
+ /**
3901
+ * Number of currently running tasks.
3902
+ */
3903
+ get pendingCount() {
3904
+ return [...this.tasks.values()].filter((t) => !t.done).length;
3905
+ }
3906
+ /**
3907
+ * Check if any tasks have completed (non-blocking).
3908
+ */
3909
+ get hasCompleted() {
3910
+ return [...this.tasks.values()].some((t) => t.done);
3911
+ }
3912
+ };
3913
+
1943
3914
  // src/errors.ts
1944
3915
  var ERROR_MAPPINGS = [
1945
3916
  { pattern: /rate.?limit|429/i, message: "Rate limited. I'll retry automatically." },
@@ -1961,9 +3932,9 @@ function humanizeError(message) {
1961
3932
  }
1962
3933
 
1963
3934
  // src/hints.ts
1964
- import fs6 from "fs";
1965
- import path6 from "path";
1966
- import os6 from "os";
3935
+ import fs12 from "fs";
3936
+ import path12 from "path";
3937
+ import os11 from "os";
1967
3938
  var HINTS = [
1968
3939
  {
1969
3940
  id: "eval",
@@ -2001,11 +3972,11 @@ function getHint(state, ctx) {
2001
3972
  }
2002
3973
  return null;
2003
3974
  }
2004
- var HINTS_FILE = path6.join(os6.homedir(), ".aman-agent", "hints-seen.json");
3975
+ var HINTS_FILE = path12.join(os11.homedir(), ".aman-agent", "hints-seen.json");
2005
3976
  function loadShownHints() {
2006
3977
  try {
2007
- if (fs6.existsSync(HINTS_FILE)) {
2008
- const data = JSON.parse(fs6.readFileSync(HINTS_FILE, "utf-8"));
3978
+ if (fs12.existsSync(HINTS_FILE)) {
3979
+ const data = JSON.parse(fs12.readFileSync(HINTS_FILE, "utf-8"));
2009
3980
  return new Set(Array.isArray(data) ? data : []);
2010
3981
  }
2011
3982
  } catch {
@@ -2014,31 +3985,27 @@ function loadShownHints() {
2014
3985
  }
2015
3986
  function saveShownHints(shown) {
2016
3987
  try {
2017
- const dir = path6.dirname(HINTS_FILE);
2018
- fs6.mkdirSync(dir, { recursive: true });
2019
- fs6.writeFileSync(HINTS_FILE, JSON.stringify([...shown]), "utf-8");
3988
+ const dir = path12.dirname(HINTS_FILE);
3989
+ fs12.mkdirSync(dir, { recursive: true });
3990
+ fs12.writeFileSync(HINTS_FILE, JSON.stringify([...shown]), "utf-8");
2020
3991
  } catch {
2021
3992
  }
2022
3993
  }
2023
3994
 
2024
3995
  // src/agent.ts
2025
3996
  marked.use(markedTerminal());
2026
- async function recallForMessage(input, mcpManager) {
3997
+ async function recallForMessage(input) {
2027
3998
  try {
2028
- const result = await mcpManager.callTool("memory_recall", {
2029
- query: input,
2030
- limit: 5,
2031
- compact: true
2032
- });
2033
- if (!result || result.startsWith("Error") || result.includes("No memories found")) {
3999
+ const result = await memoryRecall(input, { limit: 5, compact: true });
4000
+ if (result.total === 0) {
2034
4001
  return null;
2035
4002
  }
2036
- const tokenEstimate = Math.round(result.split(/\s+/).filter(Boolean).length * 1.3);
4003
+ const tokenEstimate = result.tokenEstimate ?? Math.round(result.text.split(/\s+/).filter(Boolean).length * 1.3);
2037
4004
  return {
2038
4005
  text: `
2039
4006
 
2040
4007
  <relevant-memories>
2041
- ${result}
4008
+ ${result.text}
2042
4009
  </relevant-memories>`,
2043
4010
  tokenEstimate
2044
4011
  };
@@ -2056,12 +4023,47 @@ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager,
2056
4023
  const messages = [];
2057
4024
  const sessionId = generateSessionId();
2058
4025
  const extractorState = { turnsSinceLastExtraction: 0, lastExtractionCount: 0 };
4026
+ const bgTasks = new BackgroundTaskManager();
4027
+ const profiles = listProfiles();
4028
+ const teams = listTeams();
4029
+ if (tools && (profiles.length > 0 || teams.length > 0)) {
4030
+ const virtualTools = [];
4031
+ if (profiles.length > 0) {
4032
+ virtualTools.push({
4033
+ name: "delegate_task",
4034
+ 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.`,
4035
+ input_schema: {
4036
+ type: "object",
4037
+ properties: {
4038
+ profile: { type: "string", description: "Profile name to delegate to" },
4039
+ task: { type: "string", description: "The task description for the sub-agent" }
4040
+ },
4041
+ required: ["profile", "task"]
4042
+ }
4043
+ });
4044
+ }
4045
+ if (teams.length > 0) {
4046
+ virtualTools.push({
4047
+ name: "team_run",
4048
+ 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.`,
4049
+ input_schema: {
4050
+ type: "object",
4051
+ properties: {
4052
+ team: { type: "string", description: "Team name" },
4053
+ task: { type: "string", description: "The task for the team" }
4054
+ },
4055
+ required: ["team", "task"]
4056
+ }
4057
+ });
4058
+ }
4059
+ tools = [...tools, ...virtualTools];
4060
+ }
2059
4061
  const hintState = {
2060
4062
  turnCount: 0,
2061
4063
  shownHints: loadShownHints(),
2062
4064
  hintShownThisSession: false
2063
4065
  };
2064
- 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");
4066
+ 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");
2065
4067
  let responseBuffer = "";
2066
4068
  const onChunkHandler = (chunk) => {
2067
4069
  if (chunk.type === "text" && chunk.text) {
@@ -2092,6 +4094,10 @@ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager,
2092
4094
  output: process.stdout
2093
4095
  });
2094
4096
  rl.on("SIGINT", async () => {
4097
+ if (bgTasks.pendingCount > 0) {
4098
+ await bgTasks.waitAll();
4099
+ bgTasks.displayCompleted();
4100
+ }
2095
4101
  if (mcpManager && hooksConfig) {
2096
4102
  try {
2097
4103
  const hookCtx = { mcpManager, config: hooksConfig };
@@ -2100,20 +4106,20 @@ async function runAgent(client, systemPrompt, aiName, model, tools, mcpManager,
2100
4106
  log.debug("agent", "session end hook failed on SIGINT", err);
2101
4107
  }
2102
4108
  }
2103
- console.log(pc3.dim("\nGoodbye.\n"));
4109
+ console.log(pc6.dim("\nGoodbye.\n"));
2104
4110
  rl.close();
2105
4111
  process.exit(0);
2106
4112
  });
2107
4113
  const prompt = () => {
2108
4114
  return new Promise((resolve) => {
2109
- rl.question(pc3.green("\nYou > "), (answer) => {
4115
+ rl.question(pc6.green("\nYou > "), (answer) => {
2110
4116
  resolve(answer);
2111
4117
  });
2112
4118
  });
2113
4119
  };
2114
4120
  console.log(
2115
4121
  `
2116
- Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit.
4122
+ Type a message, ${pc6.dim("/help")} for commands, or ${pc6.dim("/quit")} to exit.
2117
4123
  `
2118
4124
  );
2119
4125
  if (mcpManager && hooksConfig) {
@@ -2122,14 +4128,14 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
2122
4128
  const session = await onSessionStart(hookCtx);
2123
4129
  if (!session.firstRun) {
2124
4130
  if (session.resumeTopic) {
2125
- console.log(pc3.dim(` Welcome back. Last time we talked about ${session.resumeTopic}`));
4131
+ console.log(pc6.dim(` Welcome back. Last time we talked about ${session.resumeTopic}`));
2126
4132
  } else {
2127
- console.log(pc3.dim(" Welcome back."));
4133
+ console.log(pc6.dim(" Welcome back."));
2128
4134
  }
2129
4135
  }
2130
4136
  if (session.visibleReminders && session.visibleReminders.length > 0) {
2131
4137
  for (const reminder of session.visibleReminders) {
2132
- console.log(pc3.yellow(` Reminder: ${reminder}`));
4138
+ console.log(pc6.yellow(` Reminder: ${reminder}`));
2133
4139
  }
2134
4140
  }
2135
4141
  if (session.contextInjection) {
@@ -2145,9 +4151,15 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
2145
4151
  }
2146
4152
  }
2147
4153
  while (true) {
4154
+ if (bgTasks.hasCompleted) {
4155
+ const bgOutputs = bgTasks.displayCompleted();
4156
+ for (const output of bgOutputs) {
4157
+ messages.push({ role: "user", content: output });
4158
+ }
4159
+ }
2148
4160
  const input = await prompt();
2149
4161
  if (!input.trim()) continue;
2150
- const cmdResult = await handleCommand(input, { model, mcpManager });
4162
+ const cmdResult = await handleCommand(input, { model, mcpManager, llmClient: client, tools });
2151
4163
  if (cmdResult.handled) {
2152
4164
  if (cmdResult.quit) {
2153
4165
  if (mcpManager && hooksConfig) {
@@ -2158,15 +4170,15 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
2158
4170
  log.debug("agent", "session end hook failed on quit", err);
2159
4171
  }
2160
4172
  }
2161
- console.log(pc3.dim("\nGoodbye.\n"));
4173
+ console.log(pc6.dim("\nGoodbye.\n"));
2162
4174
  rl.close();
2163
4175
  return;
2164
4176
  }
2165
4177
  if (cmdResult.exportConversation) {
2166
4178
  try {
2167
- const exportDir = path7.join(os7.homedir(), ".aman-agent", "exports");
2168
- fs7.mkdirSync(exportDir, { recursive: true });
2169
- const exportPath = path7.join(exportDir, `${sessionId}.md`);
4179
+ const exportDir = path13.join(os12.homedir(), ".aman-agent", "exports");
4180
+ fs13.mkdirSync(exportDir, { recursive: true });
4181
+ const exportPath = path13.join(exportDir, `${sessionId}.md`);
2170
4182
  const lines = [
2171
4183
  `# Conversation \u2014 ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
2172
4184
  `**Model:** ${model}`,
@@ -2180,19 +4192,19 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
2180
4192
  lines.push(`${label} ${msg.content}`, "");
2181
4193
  }
2182
4194
  }
2183
- fs7.writeFileSync(exportPath, lines.join("\n"), "utf-8");
2184
- console.log(pc3.green(`Exported to ${exportPath}`));
4195
+ fs13.writeFileSync(exportPath, lines.join("\n"), "utf-8");
4196
+ console.log(pc6.green(`Exported to ${exportPath}`));
2185
4197
  } catch {
2186
- console.log(pc3.red("Failed to export conversation."));
4198
+ console.log(pc6.red("Failed to export conversation."));
2187
4199
  }
2188
4200
  continue;
2189
4201
  }
2190
- if (cmdResult.saveConversation && mcpManager) {
4202
+ if (cmdResult.saveConversation) {
2191
4203
  try {
2192
- await saveConversationToMemory(mcpManager, messages, sessionId);
2193
- console.log(pc3.green("Conversation saved to memory."));
4204
+ await saveConversationToMemory(messages, sessionId);
4205
+ console.log(pc6.green("Conversation saved to memory."));
2194
4206
  } catch {
2195
- console.log(pc3.red("Failed to save conversation."));
4207
+ console.log(pc6.red("Failed to save conversation."));
2196
4208
  }
2197
4209
  continue;
2198
4210
  }
@@ -2211,7 +4223,7 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
2211
4223
  const wfMatch = await onWorkflowMatch(input, hookCtx);
2212
4224
  if (wfMatch) {
2213
4225
  const useIt = await new Promise((resolve) => {
2214
- rl.question(pc3.dim(` Workflow "${wfMatch.name}" matches. Use it? (y/N) `), (answer) => resolve(answer.toLowerCase() === "y"));
4226
+ rl.question(pc6.dim(` Workflow "${wfMatch.name}" matches. Use it? (y/N) `), (answer) => resolve(answer.toLowerCase() === "y"));
2215
4227
  });
2216
4228
  if (useIt) {
2217
4229
  activeSystemPrompt = systemPrompt + `
@@ -2219,13 +4231,37 @@ Type a message, ${pc3.dim("/help")} for commands, or ${pc3.dim("/quit")} to exit
2219
4231
  <active-workflow>
2220
4232
  ${wfMatch.steps}
2221
4233
  </active-workflow>`;
2222
- console.log(pc3.dim(` Using "${wfMatch.name}" workflow.`));
4234
+ console.log(pc6.dim(` Using "${wfMatch.name}" workflow.`));
2223
4235
  }
2224
4236
  }
2225
4237
  } catch (err) {
2226
4238
  log.debug("agent", "workflow match failed", err);
2227
4239
  }
2228
4240
  }
4241
+ const activePlan = getActivePlan();
4242
+ if (activePlan) {
4243
+ activeSystemPrompt += "\n\n" + formatPlanForPrompt(activePlan);
4244
+ }
4245
+ if (mcpManager) {
4246
+ try {
4247
+ const skillContext = await autoTriggerSkills(input, mcpManager);
4248
+ if (skillContext) {
4249
+ activeSystemPrompt += "\n\n" + skillContext;
4250
+ }
4251
+ } catch (err) {
4252
+ log.debug("agent", "skill auto-trigger failed", err);
4253
+ }
4254
+ const knowledgeItem = matchKnowledge(input);
4255
+ if (knowledgeItem) {
4256
+ activeSystemPrompt += `
4257
+
4258
+ <knowledge name="${knowledgeItem.name}" category="${knowledgeItem.category}">
4259
+ ${knowledgeItem.description}
4260
+
4261
+ ${knowledgeItem.content}
4262
+ </knowledge>`;
4263
+ }
4264
+ }
2229
4265
  await trimConversation(messages, client);
2230
4266
  const textExts = /* @__PURE__ */ new Set([
2231
4267
  ".txt",
@@ -2282,33 +4318,33 @@ ${wfMatch.steps}
2282
4318
  for (const match of filePathMatches) {
2283
4319
  let filePath = match[1];
2284
4320
  if (filePath.startsWith("~/")) {
2285
- filePath = path7.join(os7.homedir(), filePath.slice(2));
4321
+ filePath = path13.join(os12.homedir(), filePath.slice(2));
2286
4322
  }
2287
- if (!fs7.existsSync(filePath) || !fs7.statSync(filePath).isFile()) continue;
2288
- const ext = path7.extname(filePath).toLowerCase();
4323
+ if (!fs13.existsSync(filePath) || !fs13.statSync(filePath).isFile()) continue;
4324
+ const ext = path13.extname(filePath).toLowerCase();
2289
4325
  if (imageExts.has(ext)) {
2290
4326
  try {
2291
- const stat = fs7.statSync(filePath);
4327
+ const stat = fs13.statSync(filePath);
2292
4328
  if (stat.size > maxImageBytes) {
2293
- process.stdout.write(pc3.yellow(` [skipped: ${path7.basename(filePath)} \u2014 exceeds 20MB limit]
4329
+ process.stdout.write(pc6.yellow(` [skipped: ${path13.basename(filePath)} \u2014 exceeds 20MB limit]
2294
4330
  `));
2295
4331
  continue;
2296
4332
  }
2297
- const data = fs7.readFileSync(filePath).toString("base64");
4333
+ const data = fs13.readFileSync(filePath).toString("base64");
2298
4334
  const mediaType = mimeMap[ext] || "image/png";
2299
4335
  imageBlocks.push({
2300
4336
  type: "image",
2301
4337
  source: { type: "base64", media_type: mediaType, data }
2302
4338
  });
2303
- process.stdout.write(pc3.dim(` [attached image: ${path7.basename(filePath)} (${(stat.size / 1024).toFixed(1)}KB)]
4339
+ process.stdout.write(pc6.dim(` [attached image: ${path13.basename(filePath)} (${(stat.size / 1024).toFixed(1)}KB)]
2304
4340
  `));
2305
4341
  } catch {
2306
- process.stdout.write(pc3.dim(` [could not read image: ${filePath}]
4342
+ process.stdout.write(pc6.dim(` [could not read image: ${filePath}]
2307
4343
  `));
2308
4344
  }
2309
4345
  } else if (textExts.has(ext) || ext === "") {
2310
4346
  try {
2311
- const content = fs7.readFileSync(filePath, "utf-8");
4347
+ const content = fs13.readFileSync(filePath, "utf-8");
2312
4348
  const maxChars = 5e4;
2313
4349
  const trimmed = content.length > maxChars ? content.slice(0, maxChars) + `
2314
4350
 
@@ -2318,16 +4354,16 @@ ${wfMatch.steps}
2318
4354
  <file path="${filePath}" size="${content.length} chars">
2319
4355
  ${trimmed}
2320
4356
  </file>`;
2321
- process.stdout.write(pc3.dim(` [attached: ${path7.basename(filePath)} (${(content.length / 1024).toFixed(1)}KB)]
4357
+ process.stdout.write(pc6.dim(` [attached: ${path13.basename(filePath)} (${(content.length / 1024).toFixed(1)}KB)]
2322
4358
  `));
2323
4359
  } catch {
2324
- process.stdout.write(pc3.dim(` [could not read: ${filePath}]
4360
+ process.stdout.write(pc6.dim(` [could not read: ${filePath}]
2325
4361
  `));
2326
4362
  }
2327
4363
  } else if (docExts.has(ext)) {
2328
4364
  if (mcpManager) {
2329
4365
  try {
2330
- process.stdout.write(pc3.dim(` [converting: ${path7.basename(filePath)}...]
4366
+ process.stdout.write(pc6.dim(` [converting: ${path13.basename(filePath)}...]
2331
4367
  `));
2332
4368
  const converted = await mcpManager.callTool("doc_convert", { path: filePath });
2333
4369
  if (converted && !converted.startsWith("Error") && !converted.includes("Could not convert")) {
@@ -2336,7 +4372,7 @@ ${trimmed}
2336
4372
  <file path="${filePath}" format="${ext}">
2337
4373
  ${converted.slice(0, 5e4)}
2338
4374
  </file>`;
2339
- process.stdout.write(pc3.dim(` [attached: ${path7.basename(filePath)} (converted from ${ext})]
4375
+ process.stdout.write(pc6.dim(` [attached: ${path13.basename(filePath)} (converted from ${ext})]
2340
4376
  `));
2341
4377
  } else {
2342
4378
  textContent += `
@@ -2344,15 +4380,15 @@ ${converted.slice(0, 5e4)}
2344
4380
  <file-error path="${filePath}">
2345
4381
  ${converted}
2346
4382
  </file-error>`;
2347
- process.stdout.write(pc3.yellow(` [conversion note: ${converted.split("\n")[0]}]
4383
+ process.stdout.write(pc6.yellow(` [conversion note: ${converted.split("\n")[0]}]
2348
4384
  `));
2349
4385
  }
2350
4386
  } catch {
2351
- process.stdout.write(pc3.dim(` [could not convert: ${path7.basename(filePath)}]
4387
+ process.stdout.write(pc6.dim(` [could not convert: ${path13.basename(filePath)}]
2352
4388
  `));
2353
4389
  }
2354
4390
  } else {
2355
- process.stdout.write(pc3.yellow(` Binary file (${ext}) \u2014 install Docling for document support: pip install docling
4391
+ process.stdout.write(pc6.yellow(` Binary file (${ext}) \u2014 install Docling for document support: pip install docling
2356
4392
  `));
2357
4393
  }
2358
4394
  }
@@ -2361,17 +4397,17 @@ ${converted}
2361
4397
  for (const match of urlImageMatches) {
2362
4398
  const url = match[0];
2363
4399
  try {
2364
- process.stdout.write(pc3.dim(` [fetching image: ${url.slice(0, 60)}...]
4400
+ process.stdout.write(pc6.dim(` [fetching image: ${url.slice(0, 60)}...]
2365
4401
  `));
2366
4402
  const response = await fetch(url);
2367
4403
  if (!response.ok) {
2368
- process.stdout.write(pc3.yellow(` [could not fetch: HTTP ${response.status}]
4404
+ process.stdout.write(pc6.yellow(` [could not fetch: HTTP ${response.status}]
2369
4405
  `));
2370
4406
  continue;
2371
4407
  }
2372
4408
  const buffer = Buffer.from(await response.arrayBuffer());
2373
4409
  if (buffer.length > maxImageBytes) {
2374
- process.stdout.write(pc3.yellow(` [skipped: image URL exceeds 20MB limit]
4410
+ process.stdout.write(pc6.yellow(` [skipped: image URL exceeds 20MB limit]
2375
4411
  `));
2376
4412
  continue;
2377
4413
  }
@@ -2385,10 +4421,10 @@ ${converted}
2385
4421
  type: "image",
2386
4422
  source: { type: "base64", media_type: mediaType, data: buffer.toString("base64") }
2387
4423
  });
2388
- process.stdout.write(pc3.dim(` [attached image URL: (${(buffer.length / 1024).toFixed(1)}KB)]
4424
+ process.stdout.write(pc6.dim(` [attached image URL: (${(buffer.length / 1024).toFixed(1)}KB)]
2389
4425
  `));
2390
4426
  } catch {
2391
- process.stdout.write(pc3.dim(` [could not fetch image: ${url}]
4427
+ process.stdout.write(pc6.dim(` [could not fetch image: ${url}]
2392
4428
  `));
2393
4429
  }
2394
4430
  }
@@ -2403,22 +4439,46 @@ ${converted}
2403
4439
  }
2404
4440
  let augmentedSystemPrompt = activeSystemPrompt;
2405
4441
  let memoryTokens = 0;
2406
- if (mcpManager) {
2407
- const recall = await recallForMessage(input, mcpManager);
2408
- if (recall) {
2409
- augmentedSystemPrompt = activeSystemPrompt + recall.text;
2410
- memoryTokens = recall.tokenEstimate;
4442
+ {
4443
+ const recall2 = await recallForMessage(input);
4444
+ if (recall2) {
4445
+ augmentedSystemPrompt = activeSystemPrompt + recall2.text;
4446
+ memoryTokens = recall2.tokenEstimate;
4447
+ }
4448
+ }
4449
+ const userTurnCount = messages.filter((m) => m.role === "user").length;
4450
+ if (mcpManager && hooksConfig?.personalityAdapt !== false && userTurnCount > 0 && userTurnCount % 5 === 0) {
4451
+ const hour = (/* @__PURE__ */ new Date()).getHours();
4452
+ let period;
4453
+ if (hour < 6) period = "late-night";
4454
+ else if (hour < 12) period = "morning";
4455
+ else if (hour < 17) period = "afternoon";
4456
+ else if (hour < 21) period = "evening";
4457
+ else period = "night";
4458
+ const recentUserMsgs = messages.filter((m) => m.role === "user" && typeof m.content === "string").slice(-5).map((m) => m.content);
4459
+ const sessionMinutes = Math.round((Date.now() - getSessionStartTime()) / 6e4);
4460
+ const state = computePersonality({
4461
+ timePeriod: period,
4462
+ sessionMinutes,
4463
+ turnCount: userTurnCount,
4464
+ recentMessages: recentUserMsgs
4465
+ });
4466
+ syncPersonalityToCore(state, mcpManager).catch(() => {
4467
+ });
4468
+ const nudge = formatWellbeingNudge(state);
4469
+ if (nudge) {
4470
+ augmentedSystemPrompt += "\n" + nudge;
2411
4471
  }
2412
4472
  }
2413
4473
  const divider = "\u2500".repeat(Math.min(process.stdout.columns || 60, 60) - aiName.length - 2);
2414
4474
  process.stdout.write(`
2415
- ${pc3.cyan(pc3.bold(aiName))} ${pc3.dim(divider)}
4475
+ ${pc6.cyan(pc6.bold(aiName))} ${pc6.dim(divider)}
2416
4476
 
2417
4477
  `);
2418
4478
  try {
2419
4479
  let response = await withRetry(
2420
4480
  () => client.chat(augmentedSystemPrompt, messages, onChunkHandler, tools),
2421
- { maxAttempts: 3, baseDelay: 1e3, retryable: isRetryable }
4481
+ { maxAttempts: 3, baseDelay: 1e3, retryable: isRetryable2 }
2422
4482
  );
2423
4483
  messages.push(response.message);
2424
4484
  while (response.toolUses.length > 0 && mcpManager) {
@@ -2428,7 +4488,7 @@ ${converted}
2428
4488
  const hookCtx = { mcpManager, config: hooksConfig };
2429
4489
  const check = await onBeforeToolExec(toolUse.name, toolUse.input, hookCtx);
2430
4490
  if (!check.allow) {
2431
- process.stdout.write(pc3.red(` [BLOCKED: ${check.reason}]
4491
+ process.stdout.write(pc6.red(` [BLOCKED: ${check.reason}]
2432
4492
  `));
2433
4493
  return {
2434
4494
  type: "tool_result",
@@ -2438,17 +4498,57 @@ ${converted}
2438
4498
  };
2439
4499
  }
2440
4500
  }
2441
- process.stdout.write(pc3.dim(` [using ${toolUse.name}...]
4501
+ if (toolUse.name === "delegate_task" && mcpManager) {
4502
+ const input2 = toolUse.input;
4503
+ process.stdout.write(pc6.dim(`
4504
+ [delegating to ${input2.profile}...]
4505
+
4506
+ `));
4507
+ const result2 = await delegateTask(input2.task, input2.profile, client, mcpManager, { tools });
4508
+ const output = result2.success ? `[${input2.profile}] completed:
4509
+
4510
+ ${result2.response}` : `[${input2.profile}] failed: ${result2.error}`;
4511
+ return {
4512
+ type: "tool_result",
4513
+ tool_use_id: toolUse.id,
4514
+ content: output
4515
+ };
4516
+ }
4517
+ if (toolUse.name === "team_run" && mcpManager) {
4518
+ const input2 = toolUse.input;
4519
+ const team = loadTeam(input2.team);
4520
+ if (!team) {
4521
+ return {
4522
+ type: "tool_result",
4523
+ tool_use_id: toolUse.id,
4524
+ content: `Team not found: ${input2.team}`,
4525
+ is_error: true
4526
+ };
4527
+ }
4528
+ const result2 = await runTeam(team, input2.task, client, mcpManager, tools);
4529
+ return {
4530
+ type: "tool_result",
4531
+ tool_use_id: toolUse.id,
4532
+ content: result2.success ? formatTeamResult(result2) : `Team execution failed: ${result2.finalOutput}`
4533
+ };
4534
+ }
4535
+ if (shouldRunInBackground(toolUse.name)) {
4536
+ const task = bgTasks.launch(toolUse.name, toolUse.id, mcpManager, toolUse.input);
4537
+ return {
4538
+ type: "tool_result",
4539
+ tool_use_id: toolUse.id,
4540
+ content: `[${toolUse.name} launched in background (${task.id}). Results will appear when ready. Continue with other work.]`
4541
+ };
4542
+ }
4543
+ process.stdout.write(pc6.dim(` [using ${toolUse.name}...]
2442
4544
  `));
2443
4545
  const result = await mcpManager.callTool(toolUse.name, toolUse.input);
2444
4546
  const skipLogging = ["memory_log", "memory_recall", "memory_context", "memory_detail", "reminder_check"].includes(toolUse.name);
2445
4547
  if (!skipLogging) {
2446
- mcpManager.callTool("memory_log", {
2447
- session_id: sessionId,
2448
- role: "system",
2449
- content: `[tool:${toolUse.name}] input=${JSON.stringify(toolUse.input).slice(0, 500)} result=${result.slice(0, 500)}`
2450
- }).catch(() => {
2451
- });
4548
+ try {
4549
+ memoryLog(sessionId, "system", `[tool:${toolUse.name}] input=${JSON.stringify(toolUse.input).slice(0, 500)} result=${result.slice(0, 500)}`);
4550
+ } catch {
4551
+ }
2452
4552
  }
2453
4553
  return {
2454
4554
  type: "tool_result",
@@ -2463,7 +4563,7 @@ ${converted}
2463
4563
  });
2464
4564
  response = await withRetry(
2465
4565
  () => client.chat(augmentedSystemPrompt, messages, onChunkHandler, tools),
2466
- { maxAttempts: 3, baseDelay: 1e3, retryable: isRetryable }
4566
+ { maxAttempts: 3, baseDelay: 1e3, retryable: isRetryable2 }
2467
4567
  );
2468
4568
  messages.push(response.message);
2469
4569
  }
@@ -2471,20 +4571,19 @@ ${converted}
2471
4571
  if (memoryTokens > 0) footerParts.push(`memories: ~${memoryTokens} tokens`);
2472
4572
  const footer = footerParts.length > 0 ? ` ${footerParts.join(" | ")}` : "";
2473
4573
  const footerDivider = "\u2500".repeat(Math.min(process.stdout.columns || 60, 60) - footer.length - 1);
2474
- process.stdout.write(pc3.dim(` ${footerDivider}${footer}
4574
+ process.stdout.write(pc6.dim(` ${footerDivider}${footer}
2475
4575
  `));
2476
- if (mcpManager && hooksConfig?.extractMemories) {
4576
+ if (hooksConfig?.extractMemories) {
2477
4577
  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("");
2478
4578
  if (assistantText) {
2479
4579
  const count = await extractMemories(
2480
4580
  input,
2481
4581
  assistantText,
2482
4582
  client,
2483
- mcpManager,
2484
4583
  extractorState
2485
4584
  );
2486
4585
  if (count > 0) {
2487
- process.stdout.write(pc3.dim(` [${count} memory${count > 1 ? "ies" : ""} stored]
4586
+ process.stdout.write(pc6.dim(` [${count} memory${count > 1 ? "ies" : ""} stored]
2488
4587
  `));
2489
4588
  }
2490
4589
  }
@@ -2493,11 +4592,11 @@ ${converted}
2493
4592
  }
2494
4593
  if (hooksConfig?.featureHints) {
2495
4594
  hintState.turnCount++;
2496
- const hasWorkflows = fs7.existsSync(path7.join(os7.homedir(), ".aflow", "flow.md"));
4595
+ const hasWorkflows = fs13.existsSync(path13.join(os12.homedir(), ".aflow", "flow.md"));
2497
4596
  const memoryCount = memoryTokens > 0 ? Math.floor(memoryTokens / 5) : 0;
2498
4597
  const hint = getHint(hintState, { hasWorkflows, memoryCount });
2499
4598
  if (hint) {
2500
- process.stdout.write(pc3.dim(` ${hint}
4599
+ process.stdout.write(pc6.dim(` ${hint}
2501
4600
  `));
2502
4601
  saveShownHints(hintState.shownHints);
2503
4602
  }
@@ -2505,21 +4604,17 @@ ${converted}
2505
4604
  } catch (error) {
2506
4605
  const rawMessage = error instanceof Error ? error.message : "Unknown error occurred";
2507
4606
  const friendly = humanizeError(rawMessage);
2508
- console.error(pc3.red(`
4607
+ console.error(pc6.red(`
2509
4608
  ${friendly}`));
2510
4609
  }
2511
4610
  }
2512
4611
  }
2513
- async function saveConversationToMemory(mcpManager, messages, sessionId) {
4612
+ async function saveConversationToMemory(messages, sessionId) {
2514
4613
  const recentMessages = messages.slice(-50);
2515
4614
  for (const msg of recentMessages) {
2516
4615
  if (typeof msg.content !== "string") continue;
2517
4616
  try {
2518
- await mcpManager.callTool("memory_log", {
2519
- session_id: sessionId,
2520
- role: msg.role,
2521
- content: msg.content.slice(0, 5e3)
2522
- });
4617
+ memoryLog(sessionId, msg.role, msg.content.slice(0, 5e3));
2523
4618
  } catch (err) {
2524
4619
  log.debug("agent", "memory_log write failed", err);
2525
4620
  }
@@ -2527,9 +4622,9 @@ async function saveConversationToMemory(mcpManager, messages, sessionId) {
2527
4622
  }
2528
4623
 
2529
4624
  // src/index.ts
2530
- import fs8 from "fs";
2531
- import path8 from "path";
2532
- import os8 from "os";
4625
+ import fs14 from "fs";
4626
+ import path14 from "path";
4627
+ import os13 from "os";
2533
4628
 
2534
4629
  // src/presets.ts
2535
4630
  var PRESETS = {
@@ -2638,9 +4733,9 @@ ${wfSections}`;
2638
4733
 
2639
4734
  // src/index.ts
2640
4735
  async function autoDetectConfig() {
2641
- const reconfigMarker = path8.join(os8.homedir(), ".aman-agent", ".reconfig");
2642
- if (fs8.existsSync(reconfigMarker)) {
2643
- fs8.unlinkSync(reconfigMarker);
4736
+ const reconfigMarker = path14.join(os13.homedir(), ".aman-agent", ".reconfig");
4737
+ if (fs14.existsSync(reconfigMarker)) {
4738
+ fs14.unlinkSync(reconfigMarker);
2644
4739
  return null;
2645
4740
  }
2646
4741
  const anthropicKey = process.env.ANTHROPIC_API_KEY;
@@ -2669,11 +4764,11 @@ async function autoDetectConfig() {
2669
4764
  return null;
2670
4765
  }
2671
4766
  function bootstrapEcosystem() {
2672
- const home2 = os8.homedir();
2673
- const corePath = path8.join(home2, ".acore", "core.md");
2674
- if (fs8.existsSync(corePath)) return false;
2675
- fs8.mkdirSync(path8.join(home2, ".acore"), { recursive: true });
2676
- fs8.writeFileSync(corePath, [
4767
+ const home2 = os13.homedir();
4768
+ const corePath = path14.join(home2, ".acore", "core.md");
4769
+ if (fs14.existsSync(corePath)) return false;
4770
+ fs14.mkdirSync(path14.join(home2, ".acore"), { recursive: true });
4771
+ fs14.writeFileSync(corePath, [
2677
4772
  "# Aman",
2678
4773
  "",
2679
4774
  "## Personality",
@@ -2685,11 +4780,11 @@ function bootstrapEcosystem() {
2685
4780
  "## Session",
2686
4781
  "_New companion \u2014 no prior sessions._"
2687
4782
  ].join("\n"), "utf-8");
2688
- const rulesDir = path8.join(home2, ".arules");
2689
- const rulesPath = path8.join(rulesDir, "rules.md");
2690
- if (!fs8.existsSync(rulesPath)) {
2691
- fs8.mkdirSync(rulesDir, { recursive: true });
2692
- fs8.writeFileSync(rulesPath, [
4783
+ const rulesDir = path14.join(home2, ".arules");
4784
+ const rulesPath = path14.join(rulesDir, "rules.md");
4785
+ if (!fs14.existsSync(rulesPath)) {
4786
+ fs14.mkdirSync(rulesDir, { recursive: true });
4787
+ fs14.writeFileSync(rulesPath, [
2693
4788
  "# Guardrails",
2694
4789
  "",
2695
4790
  "## safety",
@@ -2704,16 +4799,16 @@ function bootstrapEcosystem() {
2704
4799
  return true;
2705
4800
  }
2706
4801
  var program = new Command();
2707
- 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) => {
2708
- p2.intro(pc4.bold("aman agent") + pc4.dim(" \u2014 your AI companion"));
4802
+ 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) => {
4803
+ p2.intro(pc7.bold("aman agent") + pc7.dim(" \u2014 your AI companion"));
2709
4804
  let config = loadConfig();
2710
4805
  if (!config) {
2711
4806
  const detected = await autoDetectConfig();
2712
4807
  if (detected) {
2713
4808
  config = detected;
2714
4809
  const providerLabel = detected.provider === "anthropic" ? "Anthropic API key" : detected.provider === "openai" ? "OpenAI API key" : "Ollama";
2715
- p2.log.success(`Auto-detected ${providerLabel}. Using ${pc4.bold(detected.model)}.`);
2716
- p2.log.info(pc4.dim("Change anytime with /reconfig"));
4810
+ p2.log.success(`Auto-detected ${providerLabel}. Using ${pc7.bold(detected.model)}.`);
4811
+ p2.log.info(pc7.dim("Change anytime with /reconfig"));
2717
4812
  saveConfig(config);
2718
4813
  } else {
2719
4814
  p2.log.info("First-time setup \u2014 configure your LLM connection.");
@@ -2744,7 +4839,7 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
2744
4839
  defaultModel = modelInput || "llama3.2";
2745
4840
  } else if (provider === "anthropic") {
2746
4841
  p2.log.info("Get your API key from: https://console.anthropic.com/settings/keys");
2747
- p2.log.info(pc4.dim("Note: API access is separate from Claude Pro subscription. You need API credits."));
4842
+ p2.log.info(pc7.dim("Note: API access is separate from Claude Pro subscription. You need API credits."));
2748
4843
  apiKey = await p2.text({
2749
4844
  message: "API key (starts with sk-ant-)",
2750
4845
  validate: (v) => v.length === 0 ? "API key is required" : void 0
@@ -2810,34 +4905,32 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
2810
4905
  const s = p2.spinner();
2811
4906
  s.start("Loading ecosystem");
2812
4907
  bootstrapEcosystem();
4908
+ const profile = options.profile || process.env.AMAN_PROFILE || void 0;
4909
+ if (profile) {
4910
+ p2.log.info(`Profile: ${pc7.bold(profile)}`);
4911
+ }
2813
4912
  const budget = options.budget || void 0;
2814
- const { prompt: systemPrompt, layers, truncated, totalTokens } = assembleSystemPrompt(budget);
4913
+ const { prompt: systemPrompt, layers, truncated, totalTokens } = assembleSystemPrompt(budget, profile);
2815
4914
  s.stop("Ecosystem loaded");
2816
4915
  if (layers.length === 0) {
2817
4916
  p2.log.warning(
2818
- "No ecosystem configured. Run " + pc4.bold("npx @aman_asmuei/aman") + " first."
4917
+ "No ecosystem configured. Run " + pc7.bold("npx @aman_asmuei/aman") + " first."
2819
4918
  );
2820
4919
  p2.log.info("Starting with empty system prompt.");
2821
4920
  } else {
2822
4921
  p2.log.success(
2823
- `Loaded: ${layers.join(", ")} ${pc4.dim(`(${totalTokens.toLocaleString()} tokens)`)}`
4922
+ `Loaded: ${layers.join(", ")} ${pc7.dim(`(${totalTokens.toLocaleString()} tokens)`)}`
2824
4923
  );
2825
4924
  if (truncated.length > 0) {
2826
- p2.log.warning(`Truncated: ${truncated.join(", ")} ${pc4.dim("(over budget)")}`);
4925
+ p2.log.warning(`Truncated: ${truncated.join(", ")} ${pc7.dim("(over budget)")}`);
2827
4926
  }
2828
4927
  }
2829
- const corePath = path8.join(os8.homedir(), ".acore", "core.md");
2830
- let aiName = "Assistant";
2831
- if (fs8.existsSync(corePath)) {
2832
- const content = fs8.readFileSync(corePath, "utf-8");
2833
- const match = content.match(/^# (.+)$/m);
2834
- if (match) aiName = match[1];
2835
- }
4928
+ const aiName = getProfileAiName(profile);
4929
+ await initMemory();
2836
4930
  const mcpManager = new McpManager();
2837
4931
  const mcpSpinner = p2.spinner();
2838
4932
  mcpSpinner.start("Connecting to MCP servers");
2839
4933
  await mcpManager.connect("aman", "npx", ["-y", "@aman_asmuei/aman-mcp"]);
2840
- await mcpManager.connect("amem", "npx", ["-y", "@aman_asmuei/amem"]);
2841
4934
  if (config.mcpServers) {
2842
4935
  for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
2843
4936
  if (name === "aman" || name === "amem") continue;
@@ -2848,25 +4941,16 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
2848
4941
  mcpSpinner.stop("MCP connected");
2849
4942
  if (mcpTools.length > 0) {
2850
4943
  p2.log.success(`${mcpTools.length} MCP tools available`);
2851
- if (mcpTools.some((t) => t.name === "memory_consolidate")) {
4944
+ {
2852
4945
  const memSpinner = p2.spinner();
2853
4946
  memSpinner.start("Consolidating memory");
2854
4947
  try {
2855
- const consolidateResult = await mcpManager.callTool("memory_consolidate", { dry_run: false });
2856
- if (consolidateResult && !consolidateResult.startsWith("Error")) {
2857
- try {
2858
- const report = JSON.parse(consolidateResult);
2859
- memSpinner.stop("Memory consolidated");
2860
- if (report.merged > 0 || report.pruned > 0 || report.promoted > 0) {
2861
- p2.log.info(
2862
- `Memory health: ${report.healthScore ?? "?"}% ` + pc4.dim(`(merged ${report.merged}, pruned ${report.pruned}, promoted ${report.promoted})`)
2863
- );
2864
- }
2865
- } catch {
2866
- memSpinner.stop("Memory consolidated");
2867
- }
2868
- } else {
2869
- memSpinner.stop("Memory consolidated");
4948
+ const report = memoryConsolidate();
4949
+ memSpinner.stop("Memory consolidated");
4950
+ if (report.merged > 0 || report.pruned > 0 || report.promoted > 0) {
4951
+ p2.log.info(
4952
+ `Memory health: ${report.healthScore ?? "?"}% ` + pc7.dim(`(merged ${report.merged}, pruned ${report.pruned}, promoted ${report.promoted})`)
4953
+ );
2870
4954
  }
2871
4955
  } catch {
2872
4956
  memSpinner.stop("Memory consolidation skipped");
@@ -2890,7 +4974,7 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
2890
4974
  } else {
2891
4975
  client = createOpenAIClient(config.apiKey, model);
2892
4976
  }
2893
- p2.log.success(`${pc4.bold(aiName)} is ready. Model: ${pc4.dim(model)}`);
4977
+ p2.log.success(`${pc7.bold(aiName)} is ready. Model: ${pc7.dim(model)}`);
2894
4978
  await runAgent(
2895
4979
  client,
2896
4980
  systemPrompt,
@@ -2903,7 +4987,7 @@ program.name("aman-agent").description("Your AI companion, running locally").ver
2903
4987
  await mcpManager.disconnect();
2904
4988
  });
2905
4989
  program.command("init").description("Set up your AI companion with a guided wizard").action(async () => {
2906
- p2.intro(pc4.bold("aman agent init") + pc4.dim(" \u2014 set up your companion"));
4990
+ p2.intro(pc7.bold("aman agent init") + pc7.dim(" \u2014 set up your companion"));
2907
4991
  const name = await p2.text({
2908
4992
  message: "What should your companion be called?",
2909
4993
  placeholder: "Aman",
@@ -2923,19 +5007,19 @@ program.command("init").description("Set up your AI companion with a guided wiza
2923
5007
  });
2924
5008
  if (p2.isCancel(preset)) process.exit(0);
2925
5009
  const result = applyPreset(preset, name || "Aman");
2926
- const home2 = os8.homedir();
2927
- fs8.mkdirSync(path8.join(home2, ".acore"), { recursive: true });
2928
- fs8.writeFileSync(path8.join(home2, ".acore", "core.md"), result.coreMd, "utf-8");
5010
+ const home2 = os13.homedir();
5011
+ fs14.mkdirSync(path14.join(home2, ".acore"), { recursive: true });
5012
+ fs14.writeFileSync(path14.join(home2, ".acore", "core.md"), result.coreMd, "utf-8");
2929
5013
  p2.log.success(`Identity created \u2014 ${PRESETS[preset].identity.personality.split(".")[0].toLowerCase()}`);
2930
5014
  if (result.rulesMd) {
2931
- fs8.mkdirSync(path8.join(home2, ".arules"), { recursive: true });
2932
- fs8.writeFileSync(path8.join(home2, ".arules", "rules.md"), result.rulesMd, "utf-8");
5015
+ fs14.mkdirSync(path14.join(home2, ".arules"), { recursive: true });
5016
+ fs14.writeFileSync(path14.join(home2, ".arules", "rules.md"), result.rulesMd, "utf-8");
2933
5017
  const ruleCount = (result.rulesMd.match(/^- /gm) || []).length;
2934
5018
  p2.log.success(`${ruleCount} rules set`);
2935
5019
  }
2936
5020
  if (result.flowMd) {
2937
- fs8.mkdirSync(path8.join(home2, ".aflow"), { recursive: true });
2938
- fs8.writeFileSync(path8.join(home2, ".aflow", "flow.md"), result.flowMd, "utf-8");
5021
+ fs14.mkdirSync(path14.join(home2, ".aflow"), { recursive: true });
5022
+ fs14.writeFileSync(path14.join(home2, ".aflow", "flow.md"), result.flowMd, "utf-8");
2939
5023
  const wfCount = (result.flowMd.match(/^## /gm) || []).length;
2940
5024
  p2.log.success(`${wfCount} workflow${wfCount > 1 ? "s" : ""} added`);
2941
5025
  }
@@ -2943,16 +5027,16 @@ program.command("init").description("Set up your AI companion with a guided wiza
2943
5027
  p2.outro("Your companion is ready.");
2944
5028
  console.log("");
2945
5029
  if (isNpx) {
2946
- console.log(` ${pc4.bold("Start chatting:")} npx @aman_asmuei/aman-agent`);
5030
+ console.log(` ${pc7.bold("Start chatting:")} npx @aman_asmuei/aman-agent`);
2947
5031
  console.log("");
2948
- console.log(` ${pc4.dim("Tip: Install globally to use")} ${pc4.bold("aman-agent")} ${pc4.dim("directly:")}`);
2949
- console.log(` ${pc4.dim(" npm install -g @aman_asmuei/aman-agent")}`);
5032
+ console.log(` ${pc7.dim("Tip: Install globally to use")} ${pc7.bold("aman-agent")} ${pc7.dim("directly:")}`);
5033
+ console.log(` ${pc7.dim(" npm install -g @aman_asmuei/aman-agent")}`);
2950
5034
  } else {
2951
- console.log(` ${pc4.bold("Start chatting:")} aman-agent`);
5035
+ console.log(` ${pc7.bold("Start chatting:")} aman-agent`);
2952
5036
  }
2953
5037
  console.log("");
2954
- console.log(` ${pc4.dim("Add tools:")} npx @aman_asmuei/akit add github`);
2955
- console.log(` ${pc4.dim("Browse:")} npx @aman_asmuei/akit search <query>`);
5038
+ console.log(` ${pc7.dim("Add tools:")} npx @aman_asmuei/akit add github`);
5039
+ console.log(` ${pc7.dim("Browse:")} npx @aman_asmuei/akit search <query>`);
2956
5040
  console.log("");
2957
5041
  });
2958
5042
  program.parse();