@aman_asmuei/aman-agent 0.25.0 → 0.26.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/README.md +17 -13
- package/dist/index.js +648 -175
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1387,18 +1387,18 @@ var McpManager = class {
|
|
|
1387
1387
|
|
|
1388
1388
|
// src/agent.ts
|
|
1389
1389
|
import * as readline from "readline";
|
|
1390
|
-
import
|
|
1391
|
-
import
|
|
1392
|
-
import
|
|
1390
|
+
import fs19 from "fs";
|
|
1391
|
+
import path19 from "path";
|
|
1392
|
+
import os18 from "os";
|
|
1393
1393
|
import pc7 from "picocolors";
|
|
1394
1394
|
import { marked } from "marked";
|
|
1395
1395
|
import { markedTerminal } from "marked-terminal";
|
|
1396
1396
|
import logUpdate from "log-update";
|
|
1397
1397
|
|
|
1398
1398
|
// src/commands.ts
|
|
1399
|
-
import
|
|
1400
|
-
import
|
|
1401
|
-
import
|
|
1399
|
+
import fs16 from "fs";
|
|
1400
|
+
import path16 from "path";
|
|
1401
|
+
import os15 from "os";
|
|
1402
1402
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1403
1403
|
import pc5 from "picocolors";
|
|
1404
1404
|
|
|
@@ -2361,8 +2361,9 @@ import pc3 from "picocolors";
|
|
|
2361
2361
|
// src/hooks.ts
|
|
2362
2362
|
import pc2 from "picocolors";
|
|
2363
2363
|
import * as p2 from "@clack/prompts";
|
|
2364
|
-
import
|
|
2365
|
-
import
|
|
2364
|
+
import fs13 from "fs";
|
|
2365
|
+
import path13 from "path";
|
|
2366
|
+
import os12 from "os";
|
|
2366
2367
|
|
|
2367
2368
|
// src/personality.ts
|
|
2368
2369
|
var FRUSTRATION_SIGNALS = [
|
|
@@ -2689,8 +2690,28 @@ Return ONLY valid JSON matching this schema (no markdown, no explanation):
|
|
|
2689
2690
|
"decisions": ["key choices made with rationale"],
|
|
2690
2691
|
"sentimentArc": "how mood evolved during session",
|
|
2691
2692
|
"patterns": ["recurring behaviors worth remembering for future sessions"],
|
|
2692
|
-
"recommendations": ["actionable suggestions for next session"]
|
|
2693
|
-
|
|
2693
|
+
"recommendations": ["actionable suggestions for next session"],
|
|
2694
|
+
"crystallizationCandidates": [
|
|
2695
|
+
{
|
|
2696
|
+
"name": "lowercase-kebab-name",
|
|
2697
|
+
"description": "1-sentence description of when this would be useful",
|
|
2698
|
+
"triggers": ["3-8", "trigger", "keywords"],
|
|
2699
|
+
"approach": "1-paragraph context: when and why to use this procedure",
|
|
2700
|
+
"steps": ["ordered step 1", "ordered step 2"],
|
|
2701
|
+
"gotchas": ["common mistake 1"],
|
|
2702
|
+
"confidence": 0.0
|
|
2703
|
+
}
|
|
2704
|
+
]
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
CRYSTALLIZATION RULES:
|
|
2708
|
+
- Only suggest 0-2 candidates per session \u2014 if nothing qualifies, return an empty array
|
|
2709
|
+
- Only suggest REUSABLE procedures (not one-off tasks specific to today's work)
|
|
2710
|
+
- The user must have demonstrated the procedure in this session
|
|
2711
|
+
- Confidence < 0.6 \u2192 don't suggest at all
|
|
2712
|
+
- Skip vague things like "use library X" \u2014 that's not procedural knowledge
|
|
2713
|
+
- Prefer narrow specific procedures over broad generalizations
|
|
2714
|
+
- Trigger keywords should be highly specific (avoid generic words like "code", "fix", "the")`;
|
|
2694
2715
|
async function generatePostmortemReport(sessionId, messages, session, client, obsDir) {
|
|
2695
2716
|
try {
|
|
2696
2717
|
const events = await readObservationEvents(sessionId, obsDir ?? defaultObservationsDir2());
|
|
@@ -2770,7 +2791,8 @@ ${obsSnapshot.join("\n")}`;
|
|
|
2770
2791
|
topicProgression: [...new Set(topicProgression)],
|
|
2771
2792
|
sentimentArc: parsed.sentimentArc ?? "",
|
|
2772
2793
|
patterns: parsed.patterns ?? [],
|
|
2773
|
-
recommendations: parsed.recommendations ?? []
|
|
2794
|
+
recommendations: parsed.recommendations ?? [],
|
|
2795
|
+
crystallizationCandidates: Array.isArray(parsed.crystallizationCandidates) ? parsed.crystallizationCandidates : void 0
|
|
2774
2796
|
};
|
|
2775
2797
|
} catch (err) {
|
|
2776
2798
|
log.debug("postmortem", "Failed to generate post-mortem", err);
|
|
@@ -2841,6 +2863,14 @@ function formatPostmortemMarkdown(report) {
|
|
|
2841
2863
|
report.recommendations.forEach((r) => lines.push(`- ${r}`));
|
|
2842
2864
|
lines.push("");
|
|
2843
2865
|
}
|
|
2866
|
+
if (report.crystallizationCandidates && report.crystallizationCandidates.length > 0) {
|
|
2867
|
+
lines.push("## Crystallization Candidates");
|
|
2868
|
+
report.crystallizationCandidates.forEach((c) => {
|
|
2869
|
+
lines.push(`- **${c.name}** (confidence ${c.confidence})`);
|
|
2870
|
+
lines.push(` ${c.description}`);
|
|
2871
|
+
});
|
|
2872
|
+
lines.push("");
|
|
2873
|
+
}
|
|
2844
2874
|
return lines.join("\n");
|
|
2845
2875
|
}
|
|
2846
2876
|
async function savePostmortem(report, dir) {
|
|
@@ -2851,6 +2881,12 @@ async function savePostmortem(report, dir) {
|
|
|
2851
2881
|
const filePath = path11.join(pmDir, fileName);
|
|
2852
2882
|
const markdown = formatPostmortemMarkdown(report);
|
|
2853
2883
|
await fs11.writeFile(filePath, markdown, "utf-8");
|
|
2884
|
+
const jsonPath = filePath.replace(/\.md$/, ".json");
|
|
2885
|
+
try {
|
|
2886
|
+
await fs11.writeFile(jsonPath, JSON.stringify(report, null, 2), "utf-8");
|
|
2887
|
+
} catch (err) {
|
|
2888
|
+
log.debug("postmortem", "JSON sidecar write failed", err);
|
|
2889
|
+
}
|
|
2854
2890
|
return filePath;
|
|
2855
2891
|
}
|
|
2856
2892
|
async function listPostmortems(dir) {
|
|
@@ -2911,6 +2947,259 @@ ${contents.join("\n\n---\n\n")}`
|
|
|
2911
2947
|
}
|
|
2912
2948
|
}
|
|
2913
2949
|
|
|
2950
|
+
// src/crystallization.ts
|
|
2951
|
+
import fs12 from "fs/promises";
|
|
2952
|
+
import path12 from "path";
|
|
2953
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
2954
|
+
"the",
|
|
2955
|
+
"and",
|
|
2956
|
+
"is",
|
|
2957
|
+
"to",
|
|
2958
|
+
"of",
|
|
2959
|
+
"a",
|
|
2960
|
+
"in",
|
|
2961
|
+
"for",
|
|
2962
|
+
"on",
|
|
2963
|
+
"with",
|
|
2964
|
+
"this",
|
|
2965
|
+
"that",
|
|
2966
|
+
"it",
|
|
2967
|
+
"as",
|
|
2968
|
+
"be",
|
|
2969
|
+
"by",
|
|
2970
|
+
"or",
|
|
2971
|
+
"at",
|
|
2972
|
+
"an",
|
|
2973
|
+
"from",
|
|
2974
|
+
"code",
|
|
2975
|
+
"fix",
|
|
2976
|
+
"do",
|
|
2977
|
+
"use",
|
|
2978
|
+
"make",
|
|
2979
|
+
"get",
|
|
2980
|
+
"set",
|
|
2981
|
+
"run",
|
|
2982
|
+
"we",
|
|
2983
|
+
"i"
|
|
2984
|
+
]);
|
|
2985
|
+
var MAX_REJECTIONS = 100;
|
|
2986
|
+
var MARKER_RE = /<!--\s*aman-auto\s+([^>]+?)\s*-->/;
|
|
2987
|
+
function sanitizeName(input) {
|
|
2988
|
+
const cleaned = input.toLowerCase().trim().replace(/[^a-z0-9\s-]/g, " ").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2989
|
+
if (cleaned.length === 0) {
|
|
2990
|
+
throw new Error(`Cannot sanitize name: "${input}" produced empty result`);
|
|
2991
|
+
}
|
|
2992
|
+
return cleaned;
|
|
2993
|
+
}
|
|
2994
|
+
function validateCandidate(raw) {
|
|
2995
|
+
if (!raw || typeof raw !== "object") return null;
|
|
2996
|
+
const c = raw;
|
|
2997
|
+
if (typeof c.name !== "string" || c.name.trim() === "") return null;
|
|
2998
|
+
if (typeof c.description !== "string") return null;
|
|
2999
|
+
if (typeof c.approach !== "string") return null;
|
|
3000
|
+
if (!Array.isArray(c.triggers) || c.triggers.length === 0) return null;
|
|
3001
|
+
if (c.triggers.length > 10) return null;
|
|
3002
|
+
if (!Array.isArray(c.steps)) return null;
|
|
3003
|
+
if (typeof c.confidence !== "number") return null;
|
|
3004
|
+
if (!Number.isFinite(c.confidence)) return null;
|
|
3005
|
+
if (c.confidence < 0.6) return null;
|
|
3006
|
+
const triggers = Array.from(
|
|
3007
|
+
new Set(
|
|
3008
|
+
c.triggers.filter((t) => typeof t === "string").map((t) => t.toLowerCase().trim()).filter((t) => t.length > 0 && !STOPWORDS.has(t))
|
|
3009
|
+
)
|
|
3010
|
+
);
|
|
3011
|
+
if (triggers.length === 0) return null;
|
|
3012
|
+
let name;
|
|
3013
|
+
try {
|
|
3014
|
+
name = sanitizeName(c.name);
|
|
3015
|
+
} catch {
|
|
3016
|
+
return null;
|
|
3017
|
+
}
|
|
3018
|
+
return {
|
|
3019
|
+
name,
|
|
3020
|
+
description: c.description,
|
|
3021
|
+
triggers,
|
|
3022
|
+
approach: c.approach,
|
|
3023
|
+
steps: c.steps.filter((s) => typeof s === "string"),
|
|
3024
|
+
gotchas: Array.isArray(c.gotchas) ? c.gotchas.filter((g) => typeof g === "string") : [],
|
|
3025
|
+
confidence: Math.min(1, Math.max(0, c.confidence))
|
|
3026
|
+
};
|
|
3027
|
+
}
|
|
3028
|
+
function toTitleCase(kebab) {
|
|
3029
|
+
return kebab.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3030
|
+
}
|
|
3031
|
+
function formatSkillMarkdown(candidate, postmortemFilename) {
|
|
3032
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3033
|
+
const heading = toTitleCase(candidate.name);
|
|
3034
|
+
const triggerStr = candidate.triggers.join(",");
|
|
3035
|
+
const lines = [
|
|
3036
|
+
`# ${heading}`,
|
|
3037
|
+
`<!-- aman-auto source=postmortem date=${date} confidence=${candidate.confidence} triggers="${triggerStr}" -->`,
|
|
3038
|
+
"",
|
|
3039
|
+
"## When to use",
|
|
3040
|
+
candidate.approach,
|
|
3041
|
+
"",
|
|
3042
|
+
"## Steps",
|
|
3043
|
+
...candidate.steps.map((s, i) => `${i + 1}. ${s}`),
|
|
3044
|
+
""
|
|
3045
|
+
];
|
|
3046
|
+
if (candidate.gotchas.length > 0) {
|
|
3047
|
+
lines.push("## Gotchas");
|
|
3048
|
+
lines.push(...candidate.gotchas.map((g) => `- ${g}`));
|
|
3049
|
+
lines.push("");
|
|
3050
|
+
}
|
|
3051
|
+
lines.push(`<!-- generated from ${postmortemFilename} -->`);
|
|
3052
|
+
lines.push("");
|
|
3053
|
+
return lines.join("\n");
|
|
3054
|
+
}
|
|
3055
|
+
function parseMarkerComment(line) {
|
|
3056
|
+
const match = line.match(MARKER_RE);
|
|
3057
|
+
if (!match) return null;
|
|
3058
|
+
const attrs = {};
|
|
3059
|
+
const attrRe = /(\w+)=(?:"([^"]*)"|(\S+))/g;
|
|
3060
|
+
let m;
|
|
3061
|
+
while ((m = attrRe.exec(match[1])) !== null) {
|
|
3062
|
+
attrs[m[1]] = m[2] ?? m[3] ?? "";
|
|
3063
|
+
}
|
|
3064
|
+
if (!attrs.triggers) return null;
|
|
3065
|
+
const triggers = attrs.triggers.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
3066
|
+
if (triggers.length === 0) return null;
|
|
3067
|
+
return {
|
|
3068
|
+
source: attrs.source ?? "unknown",
|
|
3069
|
+
date: attrs.date ?? "",
|
|
3070
|
+
confidence: attrs.confidence ? Number(attrs.confidence) : 0,
|
|
3071
|
+
triggers
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
function extractSkillsWithMarkers(skillsMdContent) {
|
|
3075
|
+
const result = /* @__PURE__ */ new Map();
|
|
3076
|
+
const lines = skillsMdContent.split("\n");
|
|
3077
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3078
|
+
const line = lines[i];
|
|
3079
|
+
if (line.startsWith("# ") && i + 1 < lines.length) {
|
|
3080
|
+
const headingText = line.slice(2).trim();
|
|
3081
|
+
const nextLine = lines[i + 1];
|
|
3082
|
+
const marker = parseMarkerComment(nextLine);
|
|
3083
|
+
if (marker) {
|
|
3084
|
+
try {
|
|
3085
|
+
const skillName = sanitizeName(headingText);
|
|
3086
|
+
result.set(skillName, marker);
|
|
3087
|
+
} catch {
|
|
3088
|
+
log.debug("crystallization", `cannot sanitize heading: ${headingText}`);
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
return result;
|
|
3094
|
+
}
|
|
3095
|
+
function findCollision(name, triggers, existing) {
|
|
3096
|
+
if (existing.has(name)) {
|
|
3097
|
+
return { collides: true, collidesWith: name, reason: "exact name match" };
|
|
3098
|
+
}
|
|
3099
|
+
const triggerSet = new Set(triggers);
|
|
3100
|
+
for (const [otherName, otherData] of existing) {
|
|
3101
|
+
const otherTriggers = new Set(otherData.triggers);
|
|
3102
|
+
const intersection = [...triggerSet].filter((t) => otherTriggers.has(t)).length;
|
|
3103
|
+
const union = (/* @__PURE__ */ new Set([...triggerSet, ...otherTriggers])).size;
|
|
3104
|
+
const overlap = union > 0 ? intersection / union : 0;
|
|
3105
|
+
if (overlap >= 0.8) {
|
|
3106
|
+
return {
|
|
3107
|
+
collides: true,
|
|
3108
|
+
collidesWith: otherName,
|
|
3109
|
+
reason: `${Math.round(overlap * 100)}% trigger overlap`
|
|
3110
|
+
};
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
return { collides: false };
|
|
3114
|
+
}
|
|
3115
|
+
async function writeSkillToFile(candidate, skillsMdPath, postmortemFilename) {
|
|
3116
|
+
try {
|
|
3117
|
+
await fs12.mkdir(path12.dirname(skillsMdPath), { recursive: true });
|
|
3118
|
+
let existingContent = "";
|
|
3119
|
+
try {
|
|
3120
|
+
existingContent = await fs12.readFile(skillsMdPath, "utf-8");
|
|
3121
|
+
} catch {
|
|
3122
|
+
existingContent = "# Skills\n\n";
|
|
3123
|
+
}
|
|
3124
|
+
if (existingContent.trim() === "") {
|
|
3125
|
+
existingContent = "# Skills\n\n";
|
|
3126
|
+
}
|
|
3127
|
+
const existingSkills = extractSkillsWithMarkers(existingContent);
|
|
3128
|
+
const collision = findCollision(candidate.name, candidate.triggers, existingSkills);
|
|
3129
|
+
if (collision.collides) {
|
|
3130
|
+
log.debug("crystallization", `collision detected: ${collision.reason}`);
|
|
3131
|
+
return {
|
|
3132
|
+
written: false,
|
|
3133
|
+
filePath: skillsMdPath,
|
|
3134
|
+
skillName: candidate.name,
|
|
3135
|
+
reason: `collision with "${collision.collidesWith}" (${collision.reason})`
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
3138
|
+
const skillMarkdown = formatSkillMarkdown(candidate, postmortemFilename);
|
|
3139
|
+
const separator = existingContent.endsWith("\n\n") ? "" : existingContent.endsWith("\n") ? "\n" : "\n\n";
|
|
3140
|
+
await fs12.writeFile(
|
|
3141
|
+
skillsMdPath,
|
|
3142
|
+
existingContent + separator + skillMarkdown,
|
|
3143
|
+
"utf-8"
|
|
3144
|
+
);
|
|
3145
|
+
return {
|
|
3146
|
+
written: true,
|
|
3147
|
+
filePath: skillsMdPath,
|
|
3148
|
+
skillName: candidate.name
|
|
3149
|
+
};
|
|
3150
|
+
} catch (err) {
|
|
3151
|
+
log.warn("crystallization", "writeSkillToFile failed", err);
|
|
3152
|
+
return {
|
|
3153
|
+
written: false,
|
|
3154
|
+
filePath: skillsMdPath,
|
|
3155
|
+
skillName: candidate.name,
|
|
3156
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
3157
|
+
};
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
async function appendCrystallizationLog(entry, logPath) {
|
|
3161
|
+
try {
|
|
3162
|
+
await fs12.mkdir(path12.dirname(logPath), { recursive: true });
|
|
3163
|
+
let existing = [];
|
|
3164
|
+
try {
|
|
3165
|
+
const content = await fs12.readFile(logPath, "utf-8");
|
|
3166
|
+
existing = JSON.parse(content);
|
|
3167
|
+
if (!Array.isArray(existing)) existing = [];
|
|
3168
|
+
} catch {
|
|
3169
|
+
existing = [];
|
|
3170
|
+
}
|
|
3171
|
+
existing.push(entry);
|
|
3172
|
+
await fs12.writeFile(logPath, JSON.stringify(existing, null, 2), "utf-8");
|
|
3173
|
+
} catch (err) {
|
|
3174
|
+
log.debug("crystallization", "appendCrystallizationLog failed", err);
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
async function appendRejection(candidate, postmortemFilename, rejectionsPath) {
|
|
3178
|
+
try {
|
|
3179
|
+
await fs12.mkdir(path12.dirname(rejectionsPath), { recursive: true });
|
|
3180
|
+
let existing = [];
|
|
3181
|
+
try {
|
|
3182
|
+
const content = await fs12.readFile(rejectionsPath, "utf-8");
|
|
3183
|
+
existing = JSON.parse(content);
|
|
3184
|
+
if (!Array.isArray(existing)) existing = [];
|
|
3185
|
+
} catch {
|
|
3186
|
+
existing = [];
|
|
3187
|
+
}
|
|
3188
|
+
existing.push({
|
|
3189
|
+
name: candidate.name,
|
|
3190
|
+
rejectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3191
|
+
fromPostmortem: postmortemFilename,
|
|
3192
|
+
triggers: candidate.triggers
|
|
3193
|
+
});
|
|
3194
|
+
while (existing.length > MAX_REJECTIONS) {
|
|
3195
|
+
existing.shift();
|
|
3196
|
+
}
|
|
3197
|
+
await fs12.writeFile(rejectionsPath, JSON.stringify(existing, null, 2), "utf-8");
|
|
3198
|
+
} catch (err) {
|
|
3199
|
+
log.debug("crystallization", "appendRejection failed", err);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
|
|
2914
3203
|
// src/hooks.ts
|
|
2915
3204
|
function getTimeContext() {
|
|
2916
3205
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -3179,10 +3468,10 @@ async function onSessionEnd(ctx, messages, sessionId, observationSession) {
|
|
|
3179
3468
|
}
|
|
3180
3469
|
console.log(pc2.dim(` Saved ${textMessages.length} messages (session: ${sessionId})`));
|
|
3181
3470
|
}
|
|
3182
|
-
const projectContextPath =
|
|
3183
|
-
if (
|
|
3471
|
+
const projectContextPath = path13.join(process.cwd(), ".acore", "context.md");
|
|
3472
|
+
if (fs13.existsSync(projectContextPath) && messages.length > 2) {
|
|
3184
3473
|
try {
|
|
3185
|
-
let contextContent =
|
|
3474
|
+
let contextContent = fs13.readFileSync(projectContextPath, "utf-8");
|
|
3186
3475
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3187
3476
|
let lastUserMsg = "";
|
|
3188
3477
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -3200,7 +3489,7 @@ async function onSessionEnd(ctx, messages, sessionId, observationSession) {
|
|
|
3200
3489
|
- Recent decisions: [see memory]
|
|
3201
3490
|
- Temp notes: [cleared]`;
|
|
3202
3491
|
contextContent = contextContent.replace(sessionPattern, newSession);
|
|
3203
|
-
|
|
3492
|
+
fs13.writeFileSync(projectContextPath, contextContent, "utf-8");
|
|
3204
3493
|
log.debug("hooks", `Updated project context: ${projectContextPath}`);
|
|
3205
3494
|
}
|
|
3206
3495
|
} catch (err) {
|
|
@@ -3278,6 +3567,75 @@ async function onSessionEnd(ctx, messages, sessionId, observationSession) {
|
|
|
3278
3567
|
} catch {
|
|
3279
3568
|
}
|
|
3280
3569
|
}
|
|
3570
|
+
if (report.crystallizationCandidates && report.crystallizationCandidates.length > 0) {
|
|
3571
|
+
const skillsMdPath = path13.join(os12.homedir(), ".askill", "skills.md");
|
|
3572
|
+
const logPath = path13.join(
|
|
3573
|
+
os12.homedir(),
|
|
3574
|
+
".aman-agent",
|
|
3575
|
+
"crystallization-log.json"
|
|
3576
|
+
);
|
|
3577
|
+
const rejectionsPath = path13.join(
|
|
3578
|
+
os12.homedir(),
|
|
3579
|
+
".aman-agent",
|
|
3580
|
+
"crystallization-rejections.json"
|
|
3581
|
+
);
|
|
3582
|
+
const postmortemFilename = `${report.date}-${report.sessionId.slice(0, 4)}.md`;
|
|
3583
|
+
console.log(
|
|
3584
|
+
pc2.dim(`
|
|
3585
|
+
Crystallization candidates: ${report.crystallizationCandidates.length}`)
|
|
3586
|
+
);
|
|
3587
|
+
let skipAll = false;
|
|
3588
|
+
for (const rawCandidate of report.crystallizationCandidates) {
|
|
3589
|
+
if (skipAll) break;
|
|
3590
|
+
const candidate = validateCandidate(rawCandidate);
|
|
3591
|
+
if (!candidate) {
|
|
3592
|
+
log.debug("hooks", "candidate failed validation");
|
|
3593
|
+
continue;
|
|
3594
|
+
}
|
|
3595
|
+
const choice = await p2.select({
|
|
3596
|
+
message: `Crystallize "${candidate.name}" as a reusable skill?`,
|
|
3597
|
+
options: [
|
|
3598
|
+
{ value: "accept", label: "Yes \u2014 write to ~/.askill/skills.md" },
|
|
3599
|
+
{ value: "reject", label: "No \u2014 skip this one" },
|
|
3600
|
+
{ value: "skip-all", label: "Skip all crystallization for this session" }
|
|
3601
|
+
],
|
|
3602
|
+
initialValue: "reject"
|
|
3603
|
+
});
|
|
3604
|
+
if (p2.isCancel(choice) || choice === "skip-all") {
|
|
3605
|
+
skipAll = true;
|
|
3606
|
+
break;
|
|
3607
|
+
}
|
|
3608
|
+
if (choice === "accept") {
|
|
3609
|
+
const result = await writeSkillToFile(
|
|
3610
|
+
candidate,
|
|
3611
|
+
skillsMdPath,
|
|
3612
|
+
postmortemFilename
|
|
3613
|
+
);
|
|
3614
|
+
if (result.written) {
|
|
3615
|
+
console.log(
|
|
3616
|
+
pc2.green(` \u2713 Crystallized: ${candidate.name} \u2192 ${result.filePath}`)
|
|
3617
|
+
);
|
|
3618
|
+
console.log(pc2.dim(` Triggers: ${candidate.triggers.join(", ")}`));
|
|
3619
|
+
console.log(pc2.dim(` Will auto-activate next session.`));
|
|
3620
|
+
await appendCrystallizationLog(
|
|
3621
|
+
{
|
|
3622
|
+
name: candidate.name,
|
|
3623
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3624
|
+
fromPostmortem: postmortemFilename,
|
|
3625
|
+
confidence: candidate.confidence,
|
|
3626
|
+
triggers: candidate.triggers
|
|
3627
|
+
},
|
|
3628
|
+
logPath
|
|
3629
|
+
);
|
|
3630
|
+
} else {
|
|
3631
|
+
console.log(pc2.yellow(` \u2298 Could not crystallize: ${result.reason}`));
|
|
3632
|
+
}
|
|
3633
|
+
} else {
|
|
3634
|
+
console.log(pc2.dim(` Skipped: ${candidate.name}`));
|
|
3635
|
+
await appendRejection(candidate, postmortemFilename, rejectionsPath);
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3281
3639
|
}
|
|
3282
3640
|
}
|
|
3283
3641
|
} catch (err) {
|
|
@@ -3437,43 +3795,43 @@ async function delegatePipeline(steps, initialInput, client, mcpManager, options
|
|
|
3437
3795
|
}
|
|
3438
3796
|
|
|
3439
3797
|
// src/teams.ts
|
|
3440
|
-
import
|
|
3441
|
-
import
|
|
3442
|
-
import
|
|
3798
|
+
import fs14 from "fs";
|
|
3799
|
+
import path14 from "path";
|
|
3800
|
+
import os13 from "os";
|
|
3443
3801
|
import pc4 from "picocolors";
|
|
3444
3802
|
function getTeamsDir() {
|
|
3445
|
-
return
|
|
3803
|
+
return path14.join(os13.homedir(), ".acore", "teams");
|
|
3446
3804
|
}
|
|
3447
3805
|
function ensureTeamsDir() {
|
|
3448
3806
|
const dir = getTeamsDir();
|
|
3449
|
-
if (!
|
|
3807
|
+
if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
|
|
3450
3808
|
return dir;
|
|
3451
3809
|
}
|
|
3452
3810
|
function teamPath(name) {
|
|
3453
3811
|
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
3454
|
-
return
|
|
3812
|
+
return path14.join(ensureTeamsDir(), `${slug}.json`);
|
|
3455
3813
|
}
|
|
3456
3814
|
function createTeam(team) {
|
|
3457
3815
|
const fp = teamPath(team.name);
|
|
3458
|
-
|
|
3816
|
+
fs14.writeFileSync(fp, JSON.stringify(team, null, 2), "utf-8");
|
|
3459
3817
|
}
|
|
3460
3818
|
function loadTeam(name) {
|
|
3461
3819
|
const fp = teamPath(name);
|
|
3462
|
-
if (!
|
|
3820
|
+
if (!fs14.existsSync(fp)) return null;
|
|
3463
3821
|
try {
|
|
3464
|
-
return JSON.parse(
|
|
3822
|
+
return JSON.parse(fs14.readFileSync(fp, "utf-8"));
|
|
3465
3823
|
} catch {
|
|
3466
3824
|
return null;
|
|
3467
3825
|
}
|
|
3468
3826
|
}
|
|
3469
3827
|
function listTeams() {
|
|
3470
3828
|
const dir = getTeamsDir();
|
|
3471
|
-
if (!
|
|
3829
|
+
if (!fs14.existsSync(dir)) return [];
|
|
3472
3830
|
const teams = [];
|
|
3473
|
-
for (const file of
|
|
3831
|
+
for (const file of fs14.readdirSync(dir)) {
|
|
3474
3832
|
if (!file.endsWith(".json")) continue;
|
|
3475
3833
|
try {
|
|
3476
|
-
const content =
|
|
3834
|
+
const content = fs14.readFileSync(path14.join(dir, file), "utf-8");
|
|
3477
3835
|
teams.push(JSON.parse(content));
|
|
3478
3836
|
} catch {
|
|
3479
3837
|
}
|
|
@@ -3482,8 +3840,8 @@ function listTeams() {
|
|
|
3482
3840
|
}
|
|
3483
3841
|
function deleteTeam(name) {
|
|
3484
3842
|
const fp = teamPath(name);
|
|
3485
|
-
if (!
|
|
3486
|
-
|
|
3843
|
+
if (!fs14.existsSync(fp)) return false;
|
|
3844
|
+
fs14.unlinkSync(fp);
|
|
3487
3845
|
return true;
|
|
3488
3846
|
}
|
|
3489
3847
|
async function runTeam(team, task, client, mcpManager, tools) {
|
|
@@ -3709,23 +4067,23 @@ var BUILT_IN_TEAMS = [
|
|
|
3709
4067
|
];
|
|
3710
4068
|
|
|
3711
4069
|
// src/plans.ts
|
|
3712
|
-
import
|
|
3713
|
-
import
|
|
3714
|
-
import
|
|
4070
|
+
import fs15 from "fs";
|
|
4071
|
+
import path15 from "path";
|
|
4072
|
+
import os14 from "os";
|
|
3715
4073
|
function getPlansDir() {
|
|
3716
|
-
const localDir =
|
|
3717
|
-
const localAcore =
|
|
3718
|
-
if (
|
|
3719
|
-
return
|
|
4074
|
+
const localDir = path15.join(process.cwd(), ".acore", "plans");
|
|
4075
|
+
const localAcore = path15.join(process.cwd(), ".acore");
|
|
4076
|
+
if (fs15.existsSync(localAcore)) return localDir;
|
|
4077
|
+
return path15.join(os14.homedir(), ".acore", "plans");
|
|
3720
4078
|
}
|
|
3721
4079
|
function ensurePlansDir() {
|
|
3722
4080
|
const dir = getPlansDir();
|
|
3723
|
-
if (!
|
|
4081
|
+
if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
|
|
3724
4082
|
return dir;
|
|
3725
4083
|
}
|
|
3726
4084
|
function planPath(name) {
|
|
3727
4085
|
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
3728
|
-
return
|
|
4086
|
+
return path15.join(ensurePlansDir(), `${slug}.md`);
|
|
3729
4087
|
}
|
|
3730
4088
|
function serializePlan(plan) {
|
|
3731
4089
|
const lines = [];
|
|
@@ -3751,7 +4109,7 @@ function parsePlan(content, filePath) {
|
|
|
3751
4109
|
const createdMatch = content.match(/\*\*Created:\*\*\s*(.+)/);
|
|
3752
4110
|
const updatedMatch = content.match(/\*\*Updated:\*\*\s*(.+)/);
|
|
3753
4111
|
const activeMatch = content.match(/\*\*Active:\*\*\s*(.+)/);
|
|
3754
|
-
const name = nameMatch?.[1]?.trim() ||
|
|
4112
|
+
const name = nameMatch?.[1]?.trim() || path15.basename(filePath, ".md");
|
|
3755
4113
|
const goal = goalMatch?.[1]?.trim() || "";
|
|
3756
4114
|
const createdAt = createdMatch?.[1]?.trim() || "";
|
|
3757
4115
|
const updatedAt = updatedMatch?.[1]?.trim() || "";
|
|
@@ -3793,22 +4151,22 @@ function createPlan(name, goal, steps) {
|
|
|
3793
4151
|
}
|
|
3794
4152
|
function savePlan(plan) {
|
|
3795
4153
|
const fp = planPath(plan.name);
|
|
3796
|
-
|
|
4154
|
+
fs15.writeFileSync(fp, serializePlan(plan), "utf-8");
|
|
3797
4155
|
}
|
|
3798
4156
|
function loadPlan(name) {
|
|
3799
4157
|
const fp = planPath(name);
|
|
3800
|
-
if (!
|
|
3801
|
-
const content =
|
|
4158
|
+
if (!fs15.existsSync(fp)) return null;
|
|
4159
|
+
const content = fs15.readFileSync(fp, "utf-8");
|
|
3802
4160
|
return parsePlan(content, fp);
|
|
3803
4161
|
}
|
|
3804
4162
|
function listPlans() {
|
|
3805
4163
|
const dir = getPlansDir();
|
|
3806
|
-
if (!
|
|
4164
|
+
if (!fs15.existsSync(dir)) return [];
|
|
3807
4165
|
const plans = [];
|
|
3808
|
-
for (const file of
|
|
4166
|
+
for (const file of fs15.readdirSync(dir)) {
|
|
3809
4167
|
if (!file.endsWith(".md")) continue;
|
|
3810
|
-
const fp =
|
|
3811
|
-
const content =
|
|
4168
|
+
const fp = path15.join(dir, file);
|
|
4169
|
+
const content = fs15.readFileSync(fp, "utf-8");
|
|
3812
4170
|
const plan = parsePlan(content, fp);
|
|
3813
4171
|
if (plan) plans.push(plan);
|
|
3814
4172
|
}
|
|
@@ -3917,10 +4275,10 @@ import {
|
|
|
3917
4275
|
} from "@aman_asmuei/arules-core";
|
|
3918
4276
|
var AGENT_SCOPE = process.env.AMAN_AGENT_SCOPE ?? "dev:agent";
|
|
3919
4277
|
function readEcosystemFile(filePath, label) {
|
|
3920
|
-
if (!
|
|
4278
|
+
if (!fs16.existsSync(filePath)) {
|
|
3921
4279
|
return pc5.dim(`No ${label} file found at ${filePath}`);
|
|
3922
4280
|
}
|
|
3923
|
-
return
|
|
4281
|
+
return fs16.readFileSync(filePath, "utf-8").trim();
|
|
3924
4282
|
}
|
|
3925
4283
|
function parseCommand(input) {
|
|
3926
4284
|
const trimmed = input.trim();
|
|
@@ -4195,9 +4553,9 @@ ${result.violations.map((v) => ` - ${v}`).join("\n")}`)
|
|
|
4195
4553
|
};
|
|
4196
4554
|
}
|
|
4197
4555
|
async function handleWorkflowsCommand(action, args, ctx) {
|
|
4198
|
-
const home2 =
|
|
4556
|
+
const home2 = os15.homedir();
|
|
4199
4557
|
if (!action) {
|
|
4200
|
-
const content = readEcosystemFile(
|
|
4558
|
+
const content = readEcosystemFile(path16.join(home2, ".aflow", "flow.md"), "workflows (aflow)");
|
|
4201
4559
|
return { handled: true, output: content };
|
|
4202
4560
|
}
|
|
4203
4561
|
if (action === "add") {
|
|
@@ -4219,7 +4577,7 @@ async function handleWorkflowsCommand(action, args, ctx) {
|
|
|
4219
4577
|
return { handled: true, output: pc5.yellow("Usage: /workflows get <name>") };
|
|
4220
4578
|
}
|
|
4221
4579
|
const name = args.join(" ").toLowerCase();
|
|
4222
|
-
const raw = readEcosystemFile(
|
|
4580
|
+
const raw = readEcosystemFile(path16.join(home2, ".aflow", "flow.md"), "workflows (aflow)");
|
|
4223
4581
|
if (raw.startsWith("No ")) {
|
|
4224
4582
|
return { handled: true, output: raw };
|
|
4225
4583
|
}
|
|
@@ -4278,12 +4636,12 @@ async function handleToolsCommand(action, args, _ctx) {
|
|
|
4278
4636
|
return { handled: true, output: pc5.yellow("Usage: /tools search <query...>") };
|
|
4279
4637
|
}
|
|
4280
4638
|
const query = args.join(" ").toLowerCase();
|
|
4281
|
-
const home2 =
|
|
4282
|
-
const toolsFile =
|
|
4283
|
-
if (!
|
|
4639
|
+
const home2 = os15.homedir();
|
|
4640
|
+
const toolsFile = path16.join(home2, ".akit", "tools.md");
|
|
4641
|
+
if (!fs16.existsSync(toolsFile)) {
|
|
4284
4642
|
return { handled: true, output: pc5.dim(`No tools file found. Use 'npx @aman_asmuei/akit search ${args.join(" ")}' to search the registry.`) };
|
|
4285
4643
|
}
|
|
4286
|
-
const raw =
|
|
4644
|
+
const raw = fs16.readFileSync(toolsFile, "utf-8").trim();
|
|
4287
4645
|
const lines = raw.split("\n");
|
|
4288
4646
|
const matches = lines.filter((l) => l.toLowerCase().includes(query));
|
|
4289
4647
|
if (matches.length === 0) {
|
|
@@ -4294,9 +4652,9 @@ async function handleToolsCommand(action, args, _ctx) {
|
|
|
4294
4652
|
return handleAkitCommand(action, args);
|
|
4295
4653
|
}
|
|
4296
4654
|
async function handleSkillsCommand(action, args, ctx) {
|
|
4297
|
-
const home2 =
|
|
4655
|
+
const home2 = os15.homedir();
|
|
4298
4656
|
if (!action) {
|
|
4299
|
-
const content = readEcosystemFile(
|
|
4657
|
+
const content = readEcosystemFile(path16.join(home2, ".askill", "skills.md"), "skills (askill)");
|
|
4300
4658
|
return { handled: true, output: content };
|
|
4301
4659
|
}
|
|
4302
4660
|
if (action === "install") {
|
|
@@ -4318,8 +4676,8 @@ async function handleSkillsCommand(action, args, ctx) {
|
|
|
4318
4676
|
return { handled: true, output: pc5.yellow("Usage: /skills search <query...>") };
|
|
4319
4677
|
}
|
|
4320
4678
|
const query = args.join(" ").toLowerCase();
|
|
4321
|
-
const home3 =
|
|
4322
|
-
const raw = readEcosystemFile(
|
|
4679
|
+
const home3 = os15.homedir();
|
|
4680
|
+
const raw = readEcosystemFile(path16.join(home3, ".askill", "skills.md"), "skills (askill)");
|
|
4323
4681
|
if (raw.startsWith("No ")) {
|
|
4324
4682
|
return { handled: true, output: raw };
|
|
4325
4683
|
}
|
|
@@ -4330,21 +4688,111 @@ async function handleSkillsCommand(action, args, ctx) {
|
|
|
4330
4688
|
}
|
|
4331
4689
|
return { handled: true, output: [pc5.bold(`Skills matching "${query}":`), ...matches].join("\n") };
|
|
4332
4690
|
}
|
|
4691
|
+
if (action === "list") {
|
|
4692
|
+
const autoOnly = args.includes("--auto");
|
|
4693
|
+
if (autoOnly) {
|
|
4694
|
+
const logPath = path16.join(os15.homedir(), ".aman-agent", "crystallization-log.json");
|
|
4695
|
+
try {
|
|
4696
|
+
const content2 = fs16.readFileSync(logPath, "utf-8");
|
|
4697
|
+
const entries = JSON.parse(content2);
|
|
4698
|
+
if (entries.length === 0) {
|
|
4699
|
+
return { handled: true, output: pc5.dim("No crystallized skills yet.") };
|
|
4700
|
+
}
|
|
4701
|
+
const lines = [pc5.bold(`Crystallized skills (${entries.length}):`)];
|
|
4702
|
+
for (const entry of entries) {
|
|
4703
|
+
const date = entry.createdAt.slice(0, 10);
|
|
4704
|
+
lines.push(` ${pc5.cyan(entry.name)} (${date}, conf ${entry.confidence})`);
|
|
4705
|
+
lines.push(pc5.dim(` triggers: ${entry.triggers.join(", ")}`));
|
|
4706
|
+
}
|
|
4707
|
+
return { handled: true, output: lines.join("\n") };
|
|
4708
|
+
} catch {
|
|
4709
|
+
return { handled: true, output: pc5.dim("No crystallized skills yet.") };
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
const content = readEcosystemFile(path16.join(home2, ".askill", "skills.md"), "skills (askill)");
|
|
4713
|
+
return { handled: true, output: content };
|
|
4714
|
+
}
|
|
4715
|
+
if (action === "crystallize") {
|
|
4716
|
+
const pmDir = path16.join(os15.homedir(), ".acore", "postmortems");
|
|
4717
|
+
try {
|
|
4718
|
+
const files = fs16.readdirSync(pmDir);
|
|
4719
|
+
const jsonFiles = files.filter((f) => f.endsWith(".json")).sort().reverse();
|
|
4720
|
+
if (jsonFiles.length === 0) {
|
|
4721
|
+
return {
|
|
4722
|
+
handled: true,
|
|
4723
|
+
output: pc5.dim("No post-mortems found. Run a session that triggers a post-mortem first.")
|
|
4724
|
+
};
|
|
4725
|
+
}
|
|
4726
|
+
const latest = jsonFiles[0];
|
|
4727
|
+
const content = fs16.readFileSync(path16.join(pmDir, latest), "utf-8");
|
|
4728
|
+
const report = JSON.parse(content);
|
|
4729
|
+
if (!report.crystallizationCandidates || report.crystallizationCandidates.length === 0) {
|
|
4730
|
+
return {
|
|
4731
|
+
handled: true,
|
|
4732
|
+
output: pc5.dim(`No crystallization candidates in the most recent post-mortem (${latest}). Run a longer session or wait for the next auto-postmortem.`)
|
|
4733
|
+
};
|
|
4734
|
+
}
|
|
4735
|
+
const skillsMdPath = path16.join(os15.homedir(), ".askill", "skills.md");
|
|
4736
|
+
const logPath = path16.join(os15.homedir(), ".aman-agent", "crystallization-log.json");
|
|
4737
|
+
const postmortemFilename = latest.replace(/\.json$/, ".md");
|
|
4738
|
+
const lines = [
|
|
4739
|
+
pc5.bold(`Found ${report.crystallizationCandidates.length} candidate(s) in ${latest}:`)
|
|
4740
|
+
];
|
|
4741
|
+
let written = 0;
|
|
4742
|
+
for (const raw of report.crystallizationCandidates) {
|
|
4743
|
+
const candidate = validateCandidate(raw);
|
|
4744
|
+
if (!candidate) {
|
|
4745
|
+
const rawName = raw.name ?? "unknown";
|
|
4746
|
+
lines.push(pc5.dim(` \u2298 ${rawName} \u2014 failed validation`));
|
|
4747
|
+
continue;
|
|
4748
|
+
}
|
|
4749
|
+
const result = await writeSkillToFile(candidate, skillsMdPath, postmortemFilename);
|
|
4750
|
+
if (result.written) {
|
|
4751
|
+
written++;
|
|
4752
|
+
lines.push(pc5.green(` \u2713 Crystallized: ${candidate.name}`));
|
|
4753
|
+
await appendCrystallizationLog(
|
|
4754
|
+
{
|
|
4755
|
+
name: candidate.name,
|
|
4756
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4757
|
+
fromPostmortem: postmortemFilename,
|
|
4758
|
+
confidence: candidate.confidence,
|
|
4759
|
+
triggers: candidate.triggers
|
|
4760
|
+
},
|
|
4761
|
+
logPath
|
|
4762
|
+
);
|
|
4763
|
+
} else {
|
|
4764
|
+
lines.push(pc5.yellow(` \u2298 ${candidate.name} \u2014 ${result.reason}`));
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
if (written > 0) {
|
|
4768
|
+
lines.push("");
|
|
4769
|
+
lines.push(pc5.dim(`Crystallized skills will auto-activate in your next session.`));
|
|
4770
|
+
}
|
|
4771
|
+
return { handled: true, output: lines.join("\n") };
|
|
4772
|
+
} catch (err) {
|
|
4773
|
+
return {
|
|
4774
|
+
handled: true,
|
|
4775
|
+
output: pc5.red(`Failed to load post-mortems: ${err instanceof Error ? err.message : String(err)}`)
|
|
4776
|
+
};
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4333
4779
|
if (action === "help") {
|
|
4334
4780
|
return { handled: true, output: [
|
|
4335
4781
|
pc5.bold("Skills commands:"),
|
|
4336
|
-
` ${pc5.cyan("/skills")}
|
|
4337
|
-
` ${pc5.cyan("/skills install")} <name>
|
|
4338
|
-
` ${pc5.cyan("/skills uninstall")} <name>
|
|
4339
|
-
` ${pc5.cyan("/skills search")} <query>
|
|
4782
|
+
` ${pc5.cyan("/skills")} View installed skills`,
|
|
4783
|
+
` ${pc5.cyan("/skills install")} <name> Install a skill`,
|
|
4784
|
+
` ${pc5.cyan("/skills uninstall")} <name> Uninstall a skill`,
|
|
4785
|
+
` ${pc5.cyan("/skills search")} <query> Search skills by name/description`,
|
|
4786
|
+
` ${pc5.cyan("/skills crystallize")} Crystallize skills from most recent post-mortem`,
|
|
4787
|
+
` ${pc5.cyan("/skills list --auto")} List crystallized (auto-created) skills`
|
|
4340
4788
|
].join("\n") };
|
|
4341
4789
|
}
|
|
4342
4790
|
return { handled: true, output: pc5.yellow(`Unknown action: /skills ${action}. Try /skills --help`) };
|
|
4343
4791
|
}
|
|
4344
4792
|
async function handleEvalCommand(action, args, ctx) {
|
|
4345
|
-
const home2 =
|
|
4793
|
+
const home2 = os15.homedir();
|
|
4346
4794
|
if (!action) {
|
|
4347
|
-
const content = readEcosystemFile(
|
|
4795
|
+
const content = readEcosystemFile(path16.join(home2, ".aeval", "eval.md"), "evaluation (aeval)");
|
|
4348
4796
|
return { handled: true, output: content };
|
|
4349
4797
|
}
|
|
4350
4798
|
if (action === "milestone") {
|
|
@@ -4356,11 +4804,11 @@ async function handleEvalCommand(action, args, ctx) {
|
|
|
4356
4804
|
return { handled: true, output };
|
|
4357
4805
|
}
|
|
4358
4806
|
if (action === "report") {
|
|
4359
|
-
const evalFile =
|
|
4360
|
-
if (!
|
|
4807
|
+
const evalFile = path16.join(home2, ".aeval", "eval.md");
|
|
4808
|
+
if (!fs16.existsSync(evalFile)) {
|
|
4361
4809
|
return { handled: true, output: pc5.dim("No eval report found. Log milestones with /eval milestone <text>.") };
|
|
4362
4810
|
}
|
|
4363
|
-
const content =
|
|
4811
|
+
const content = fs16.readFileSync(evalFile, "utf-8").trim();
|
|
4364
4812
|
return { handled: true, output: [pc5.bold("Eval Report"), "", content].join("\n") };
|
|
4365
4813
|
}
|
|
4366
4814
|
return { handled: true, output: pc5.yellow(`Unknown action: /eval ${action}. Use /eval, /eval report, or /eval milestone <text>.`) };
|
|
@@ -4852,7 +5300,7 @@ function handleHelp() {
|
|
|
4852
5300
|
` ${pc5.cyan("/rules")} View rules [add|remove|toggle ...]`,
|
|
4853
5301
|
` ${pc5.cyan("/workflows")} View workflows [add|remove ...]`,
|
|
4854
5302
|
` ${pc5.cyan("/akit")} Manage tools [add|remove <tool>]`,
|
|
4855
|
-
` ${pc5.cyan("/skills")} View skills [install|uninstall
|
|
5303
|
+
` ${pc5.cyan("/skills")} View skills [install|uninstall|crystallize|list --auto]`,
|
|
4856
5304
|
` ${pc5.cyan("/eval")} View evaluation [milestone ...]`,
|
|
4857
5305
|
` ${pc5.cyan("/memory")} View recent memories [search|fts|since|stats|export|clear|timeline]`,
|
|
4858
5306
|
` ${pc5.cyan("/reminder")} Manage reminders [set|check|done]`,
|
|
@@ -4884,10 +5332,10 @@ function handleSave() {
|
|
|
4884
5332
|
}
|
|
4885
5333
|
function handleReset(action) {
|
|
4886
5334
|
const dirs = {
|
|
4887
|
-
config:
|
|
4888
|
-
memory:
|
|
4889
|
-
identity:
|
|
4890
|
-
rules:
|
|
5335
|
+
config: path16.join(os15.homedir(), ".aman-agent"),
|
|
5336
|
+
memory: path16.join(os15.homedir(), ".amem"),
|
|
5337
|
+
identity: path16.join(os15.homedir(), ".acore"),
|
|
5338
|
+
rules: path16.join(os15.homedir(), ".arules")
|
|
4891
5339
|
};
|
|
4892
5340
|
if (action === "help" || !action) {
|
|
4893
5341
|
return {
|
|
@@ -4912,15 +5360,15 @@ function handleReset(action) {
|
|
|
4912
5360
|
const removed = [];
|
|
4913
5361
|
for (const target of targets) {
|
|
4914
5362
|
const dir = dirs[target];
|
|
4915
|
-
if (
|
|
4916
|
-
|
|
5363
|
+
if (fs16.existsSync(dir)) {
|
|
5364
|
+
fs16.rmSync(dir, { recursive: true, force: true });
|
|
4917
5365
|
removed.push(target);
|
|
4918
5366
|
}
|
|
4919
5367
|
}
|
|
4920
5368
|
if (targets.includes("config")) {
|
|
4921
5369
|
const configDir = dirs.config;
|
|
4922
|
-
|
|
4923
|
-
|
|
5370
|
+
fs16.mkdirSync(configDir, { recursive: true });
|
|
5371
|
+
fs16.writeFileSync(path16.join(configDir, ".reconfig"), "", "utf-8");
|
|
4924
5372
|
}
|
|
4925
5373
|
if (removed.length === 0) {
|
|
4926
5374
|
return { handled: true, output: pc5.dim("Nothing to reset \u2014 directories don't exist.") };
|
|
@@ -4937,7 +5385,7 @@ function handleReset(action) {
|
|
|
4937
5385
|
function handleUpdate() {
|
|
4938
5386
|
try {
|
|
4939
5387
|
const current = execFileSync3("npm", ["view", "@aman_asmuei/aman-agent", "version"], { encoding: "utf-8" }).trim();
|
|
4940
|
-
const local = true ? "0.
|
|
5388
|
+
const local = true ? "0.26.0" : "unknown";
|
|
4941
5389
|
if (current === local) {
|
|
4942
5390
|
return { handled: true, output: `${pc5.green("Up to date")} \u2014 v${local}` };
|
|
4943
5391
|
}
|
|
@@ -4981,11 +5429,11 @@ function handleExportCommand() {
|
|
|
4981
5429
|
return { handled: true, exportConversation: true };
|
|
4982
5430
|
}
|
|
4983
5431
|
function handleDebugCommand() {
|
|
4984
|
-
const logPath =
|
|
4985
|
-
if (!
|
|
5432
|
+
const logPath = path16.join(os15.homedir(), ".aman-agent", "debug.log");
|
|
5433
|
+
if (!fs16.existsSync(logPath)) {
|
|
4986
5434
|
return { handled: true, output: pc5.dim("No debug log found.") };
|
|
4987
5435
|
}
|
|
4988
|
-
const content =
|
|
5436
|
+
const content = fs16.readFileSync(logPath, "utf-8");
|
|
4989
5437
|
const lines = content.trim().split("\n");
|
|
4990
5438
|
const last20 = lines.slice(-20).join("\n");
|
|
4991
5439
|
return { handled: true, output: pc5.bold("Debug Log (last 20 entries):\n") + pc5.dim(last20) };
|
|
@@ -5183,7 +5631,7 @@ ${result.response}`
|
|
|
5183
5631
|
};
|
|
5184
5632
|
}
|
|
5185
5633
|
function handleProfileCommand(action, args) {
|
|
5186
|
-
const profilesDir =
|
|
5634
|
+
const profilesDir = path16.join(os15.homedir(), ".acore", "profiles");
|
|
5187
5635
|
if (action === "me") {
|
|
5188
5636
|
const user = loadUserIdentity();
|
|
5189
5637
|
if (!user) {
|
|
@@ -5251,8 +5699,8 @@ ${pc5.dim("Edit with: /profile edit")}` };
|
|
|
5251
5699
|
};
|
|
5252
5700
|
}
|
|
5253
5701
|
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
5254
|
-
const profileDir =
|
|
5255
|
-
if (
|
|
5702
|
+
const profileDir = path16.join(profilesDir, slug);
|
|
5703
|
+
if (fs16.existsSync(profileDir)) {
|
|
5256
5704
|
return { handled: true, output: pc5.yellow(`Profile already exists: ${slug}`) };
|
|
5257
5705
|
}
|
|
5258
5706
|
const builtIn = BUILT_IN_PROFILES.find((t) => t.name === slug);
|
|
@@ -5268,16 +5716,16 @@ ${pc5.dim("Edit with: /profile edit")}` };
|
|
|
5268
5716
|
Use: aman-agent --profile ${slug}`
|
|
5269
5717
|
};
|
|
5270
5718
|
}
|
|
5271
|
-
|
|
5272
|
-
const globalCore =
|
|
5273
|
-
if (
|
|
5274
|
-
let content =
|
|
5719
|
+
fs16.mkdirSync(profileDir, { recursive: true });
|
|
5720
|
+
const globalCore = path16.join(os15.homedir(), ".acore", "core.md");
|
|
5721
|
+
if (fs16.existsSync(globalCore)) {
|
|
5722
|
+
let content = fs16.readFileSync(globalCore, "utf-8");
|
|
5275
5723
|
const aiName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
5276
5724
|
content = content.replace(/^# .+$/m, `# ${aiName}`);
|
|
5277
|
-
|
|
5725
|
+
fs16.writeFileSync(path16.join(profileDir, "core.md"), content, "utf-8");
|
|
5278
5726
|
} else {
|
|
5279
5727
|
const aiName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
5280
|
-
|
|
5728
|
+
fs16.writeFileSync(path16.join(profileDir, "core.md"), `# ${aiName}
|
|
5281
5729
|
|
|
5282
5730
|
## Identity
|
|
5283
5731
|
- Role: ${aiName} is your AI companion
|
|
@@ -5290,7 +5738,7 @@ ${pc5.dim("Edit with: /profile edit")}` };
|
|
|
5290
5738
|
return {
|
|
5291
5739
|
handled: true,
|
|
5292
5740
|
output: pc5.green(`Profile created: ${slug}`) + `
|
|
5293
|
-
Edit: ${
|
|
5741
|
+
Edit: ${path16.join(profileDir, "core.md")}
|
|
5294
5742
|
Use: aman-agent --profile ${slug}
|
|
5295
5743
|
|
|
5296
5744
|
${pc5.dim("Add rules.md or skills.md for profile-specific overrides.")}`
|
|
@@ -5299,9 +5747,9 @@ ${pc5.dim("Edit with: /profile edit")}` };
|
|
|
5299
5747
|
case "show": {
|
|
5300
5748
|
const name = args[0];
|
|
5301
5749
|
if (!name) return { handled: true, output: pc5.yellow("Usage: /profile show <name>") };
|
|
5302
|
-
const profileDir =
|
|
5303
|
-
if (!
|
|
5304
|
-
const files =
|
|
5750
|
+
const profileDir = path16.join(profilesDir, name);
|
|
5751
|
+
if (!fs16.existsSync(profileDir)) return { handled: true, output: pc5.red(`Profile not found: ${name}`) };
|
|
5752
|
+
const files = fs16.readdirSync(profileDir).filter((f) => f.endsWith(".md"));
|
|
5305
5753
|
const lines = files.map((f) => ` ${f}`);
|
|
5306
5754
|
return { handled: true, output: `Profile: ${pc5.bold(name)}
|
|
5307
5755
|
Files:
|
|
@@ -5310,9 +5758,9 @@ ${lines.join("\n")}` };
|
|
|
5310
5758
|
case "delete": {
|
|
5311
5759
|
const name = args[0];
|
|
5312
5760
|
if (!name) return { handled: true, output: pc5.yellow("Usage: /profile delete <name>") };
|
|
5313
|
-
const profileDir =
|
|
5314
|
-
if (!
|
|
5315
|
-
|
|
5761
|
+
const profileDir = path16.join(profilesDir, name);
|
|
5762
|
+
if (!fs16.existsSync(profileDir)) return { handled: true, output: pc5.red(`Profile not found: ${name}`) };
|
|
5763
|
+
fs16.rmSync(profileDir, { recursive: true });
|
|
5316
5764
|
return { handled: true, output: pc5.dim(`Profile deleted: ${name}`) };
|
|
5317
5765
|
}
|
|
5318
5766
|
case "help":
|
|
@@ -5530,10 +5978,10 @@ function handleShowcaseCommand(action, args) {
|
|
|
5530
5978
|
Or place it as a sibling directory to aman-agent.`
|
|
5531
5979
|
};
|
|
5532
5980
|
}
|
|
5533
|
-
const corePath =
|
|
5981
|
+
const corePath = path16.join(os15.homedir(), ".acore", "core.md");
|
|
5534
5982
|
let currentShowcase = null;
|
|
5535
|
-
if (
|
|
5536
|
-
const content =
|
|
5983
|
+
if (fs16.existsSync(corePath)) {
|
|
5984
|
+
const content = fs16.readFileSync(corePath, "utf-8");
|
|
5537
5985
|
const nameMatch = content.match(/^# (.+)/m);
|
|
5538
5986
|
if (nameMatch) {
|
|
5539
5987
|
const coreName = nameMatch[1].trim().toLowerCase();
|
|
@@ -5920,9 +6368,10 @@ ${summaryParts.slice(0, 20).join("\n")}
|
|
|
5920
6368
|
}
|
|
5921
6369
|
|
|
5922
6370
|
// src/skill-engine.ts
|
|
5923
|
-
import
|
|
5924
|
-
import
|
|
5925
|
-
import
|
|
6371
|
+
import fs17 from "fs";
|
|
6372
|
+
import fsp from "fs/promises";
|
|
6373
|
+
import path17 from "path";
|
|
6374
|
+
import os16 from "os";
|
|
5926
6375
|
var SKILL_TRIGGERS = {
|
|
5927
6376
|
testing: ["test", "spec", "coverage", "tdd", "jest", "vitest", "mocha", "assert", "mock", "stub", "fixture", "e2e", "integration test", "unit test"],
|
|
5928
6377
|
"api-design": ["api", "endpoint", "rest", "graphql", "route", "controller", "middleware", "http", "request", "response", "status code", "pagination"],
|
|
@@ -5937,20 +6386,34 @@ var SKILL_TRIGGERS = {
|
|
|
5937
6386
|
typescript: ["typescript", "type", "interface", "generic", "infer", "utility type", "zod", "discriminated union", "type guard", "as const"],
|
|
5938
6387
|
accessibility: ["accessibility", "a11y", "aria", "screen reader", "wcag", "semantic html", "tab order", "focus", "contrast"]
|
|
5939
6388
|
};
|
|
5940
|
-
|
|
6389
|
+
async function loadRuntimeTriggers(skillsMdPath) {
|
|
6390
|
+
try {
|
|
6391
|
+
const content = await fsp.readFile(skillsMdPath, "utf-8");
|
|
6392
|
+
const skills = extractSkillsWithMarkers(content);
|
|
6393
|
+
const result = /* @__PURE__ */ new Map();
|
|
6394
|
+
for (const [name, marker] of skills) {
|
|
6395
|
+
result.set(name, marker.triggers);
|
|
6396
|
+
}
|
|
6397
|
+
return result;
|
|
6398
|
+
} catch (err) {
|
|
6399
|
+
log.debug("skill-engine", "loadRuntimeTriggers failed", err);
|
|
6400
|
+
return /* @__PURE__ */ new Map();
|
|
6401
|
+
}
|
|
6402
|
+
}
|
|
6403
|
+
var LEVEL_FILE = path17.join(os16.homedir(), ".aman-agent", "skill-levels.json");
|
|
5941
6404
|
function loadSkillLevels() {
|
|
5942
6405
|
try {
|
|
5943
|
-
if (
|
|
5944
|
-
return JSON.parse(
|
|
6406
|
+
if (fs17.existsSync(LEVEL_FILE)) {
|
|
6407
|
+
return JSON.parse(fs17.readFileSync(LEVEL_FILE, "utf-8"));
|
|
5945
6408
|
}
|
|
5946
6409
|
} catch {
|
|
5947
6410
|
}
|
|
5948
6411
|
return {};
|
|
5949
6412
|
}
|
|
5950
6413
|
function saveSkillLevels(levels) {
|
|
5951
|
-
const dir =
|
|
5952
|
-
if (!
|
|
5953
|
-
|
|
6414
|
+
const dir = path17.dirname(LEVEL_FILE);
|
|
6415
|
+
if (!fs17.existsSync(dir)) fs17.mkdirSync(dir, { recursive: true });
|
|
6416
|
+
fs17.writeFileSync(LEVEL_FILE, JSON.stringify(levels, null, 2), "utf-8");
|
|
5954
6417
|
}
|
|
5955
6418
|
function computeLevel(activations) {
|
|
5956
6419
|
if (activations >= 50) return { level: 5, label: "Expert" };
|
|
@@ -5969,20 +6432,28 @@ function recordActivation(skillName) {
|
|
|
5969
6432
|
saveSkillLevels(levels);
|
|
5970
6433
|
return computeLevel(levels[skillName].activations);
|
|
5971
6434
|
}
|
|
5972
|
-
function matchSkills(userInput, installedSkillNames) {
|
|
6435
|
+
function matchSkills(userInput, installedSkillNames, runtimeTriggers = /* @__PURE__ */ new Map()) {
|
|
5973
6436
|
const input = userInput.toLowerCase();
|
|
5974
|
-
const matched =
|
|
6437
|
+
const matched = /* @__PURE__ */ new Set();
|
|
5975
6438
|
for (const skillName of installedSkillNames) {
|
|
5976
6439
|
const triggers = SKILL_TRIGGERS[skillName];
|
|
5977
6440
|
if (!triggers) continue;
|
|
5978
6441
|
for (const trigger of triggers) {
|
|
5979
6442
|
if (input.includes(trigger)) {
|
|
5980
|
-
matched.
|
|
6443
|
+
matched.add(skillName);
|
|
5981
6444
|
break;
|
|
5982
6445
|
}
|
|
5983
6446
|
}
|
|
5984
6447
|
}
|
|
5985
|
-
|
|
6448
|
+
for (const [skillName, triggers] of runtimeTriggers) {
|
|
6449
|
+
for (const trigger of triggers) {
|
|
6450
|
+
if (input.includes(trigger)) {
|
|
6451
|
+
matched.add(skillName);
|
|
6452
|
+
break;
|
|
6453
|
+
}
|
|
6454
|
+
}
|
|
6455
|
+
}
|
|
6456
|
+
return Array.from(matched);
|
|
5986
6457
|
}
|
|
5987
6458
|
function formatSkillContext(skillName, skillContent, level) {
|
|
5988
6459
|
let depthHint;
|
|
@@ -6006,8 +6477,10 @@ async function autoTriggerSkills(userInput, mcpManager) {
|
|
|
6006
6477
|
const result = await mcpManager.callTool("skill_list", {});
|
|
6007
6478
|
const skills = JSON.parse(result);
|
|
6008
6479
|
const installed = skills.filter((s) => s.installed).map((s) => s.name);
|
|
6009
|
-
|
|
6010
|
-
const
|
|
6480
|
+
const skillsMdPath = path17.join(os16.homedir(), ".askill", "skills.md");
|
|
6481
|
+
const runtimeTriggers = await loadRuntimeTriggers(skillsMdPath);
|
|
6482
|
+
if (installed.length === 0 && runtimeTriggers.size === 0) return "";
|
|
6483
|
+
const matched = matchSkills(userInput, installed, runtimeTriggers);
|
|
6011
6484
|
if (matched.length === 0) return "";
|
|
6012
6485
|
const blocks = [];
|
|
6013
6486
|
for (const skillName of matched.slice(0, 2)) {
|
|
@@ -6563,9 +7036,9 @@ function humanizeError(message) {
|
|
|
6563
7036
|
}
|
|
6564
7037
|
|
|
6565
7038
|
// src/hints.ts
|
|
6566
|
-
import
|
|
6567
|
-
import
|
|
6568
|
-
import
|
|
7039
|
+
import fs18 from "fs";
|
|
7040
|
+
import path18 from "path";
|
|
7041
|
+
import os17 from "os";
|
|
6569
7042
|
var HINTS = [
|
|
6570
7043
|
{
|
|
6571
7044
|
id: "eval",
|
|
@@ -6603,11 +7076,11 @@ function getHint(state, ctx) {
|
|
|
6603
7076
|
}
|
|
6604
7077
|
return null;
|
|
6605
7078
|
}
|
|
6606
|
-
var HINTS_FILE =
|
|
7079
|
+
var HINTS_FILE = path18.join(os17.homedir(), ".aman-agent", "hints-seen.json");
|
|
6607
7080
|
function loadShownHints() {
|
|
6608
7081
|
try {
|
|
6609
|
-
if (
|
|
6610
|
-
const data = JSON.parse(
|
|
7082
|
+
if (fs18.existsSync(HINTS_FILE)) {
|
|
7083
|
+
const data = JSON.parse(fs18.readFileSync(HINTS_FILE, "utf-8"));
|
|
6611
7084
|
return new Set(Array.isArray(data) ? data : []);
|
|
6612
7085
|
}
|
|
6613
7086
|
} catch {
|
|
@@ -6616,9 +7089,9 @@ function loadShownHints() {
|
|
|
6616
7089
|
}
|
|
6617
7090
|
function saveShownHints(shown) {
|
|
6618
7091
|
try {
|
|
6619
|
-
const dir =
|
|
6620
|
-
|
|
6621
|
-
|
|
7092
|
+
const dir = path18.dirname(HINTS_FILE);
|
|
7093
|
+
fs18.mkdirSync(dir, { recursive: true });
|
|
7094
|
+
fs18.writeFileSync(HINTS_FILE, JSON.stringify([...shown]), "utf-8");
|
|
6622
7095
|
} catch {
|
|
6623
7096
|
}
|
|
6624
7097
|
}
|
|
@@ -6885,9 +7358,9 @@ ${task.result}`
|
|
|
6885
7358
|
}
|
|
6886
7359
|
if (cmdResult.exportConversation) {
|
|
6887
7360
|
try {
|
|
6888
|
-
const exportDir =
|
|
6889
|
-
|
|
6890
|
-
const exportPath =
|
|
7361
|
+
const exportDir = path19.join(os18.homedir(), ".aman-agent", "exports");
|
|
7362
|
+
fs19.mkdirSync(exportDir, { recursive: true });
|
|
7363
|
+
const exportPath = path19.join(exportDir, `${sessionId}.md`);
|
|
6891
7364
|
const lines = [
|
|
6892
7365
|
`# Conversation \u2014 ${(/* @__PURE__ */ new Date()).toLocaleString()}`,
|
|
6893
7366
|
`**Model:** ${model}`,
|
|
@@ -6901,7 +7374,7 @@ ${task.result}`
|
|
|
6901
7374
|
lines.push(`${label} ${msg.content}`, "");
|
|
6902
7375
|
}
|
|
6903
7376
|
}
|
|
6904
|
-
|
|
7377
|
+
fs19.writeFileSync(exportPath, lines.join("\n"), "utf-8");
|
|
6905
7378
|
console.log(pc7.green(`Exported to ${exportPath}`));
|
|
6906
7379
|
} catch {
|
|
6907
7380
|
console.log(pc7.red("Failed to export conversation."));
|
|
@@ -7027,25 +7500,25 @@ ${knowledgeItem.content}
|
|
|
7027
7500
|
for (const match of filePathMatches) {
|
|
7028
7501
|
let filePath = match[1];
|
|
7029
7502
|
if (filePath.startsWith("~/")) {
|
|
7030
|
-
filePath =
|
|
7503
|
+
filePath = path19.join(os18.homedir(), filePath.slice(2));
|
|
7031
7504
|
}
|
|
7032
|
-
if (!
|
|
7033
|
-
const ext =
|
|
7505
|
+
if (!fs19.existsSync(filePath) || !fs19.statSync(filePath).isFile()) continue;
|
|
7506
|
+
const ext = path19.extname(filePath).toLowerCase();
|
|
7034
7507
|
if (imageExts.has(ext)) {
|
|
7035
7508
|
try {
|
|
7036
|
-
const stat =
|
|
7509
|
+
const stat = fs19.statSync(filePath);
|
|
7037
7510
|
if (stat.size > maxImageBytes) {
|
|
7038
|
-
process.stdout.write(pc7.yellow(` [skipped: ${
|
|
7511
|
+
process.stdout.write(pc7.yellow(` [skipped: ${path19.basename(filePath)} \u2014 exceeds 20MB limit]
|
|
7039
7512
|
`));
|
|
7040
7513
|
continue;
|
|
7041
7514
|
}
|
|
7042
|
-
const data =
|
|
7515
|
+
const data = fs19.readFileSync(filePath).toString("base64");
|
|
7043
7516
|
const mediaType = mimeMap[ext] || "image/png";
|
|
7044
7517
|
imageBlocks.push({
|
|
7045
7518
|
type: "image",
|
|
7046
7519
|
source: { type: "base64", media_type: mediaType, data }
|
|
7047
7520
|
});
|
|
7048
|
-
process.stdout.write(pc7.dim(` [attached image: ${
|
|
7521
|
+
process.stdout.write(pc7.dim(` [attached image: ${path19.basename(filePath)} (${(stat.size / 1024).toFixed(1)}KB)]
|
|
7049
7522
|
`));
|
|
7050
7523
|
} catch {
|
|
7051
7524
|
process.stdout.write(pc7.dim(` [could not read image: ${filePath}]
|
|
@@ -7053,7 +7526,7 @@ ${knowledgeItem.content}
|
|
|
7053
7526
|
}
|
|
7054
7527
|
} else if (textExts.has(ext) || ext === "") {
|
|
7055
7528
|
try {
|
|
7056
|
-
const content =
|
|
7529
|
+
const content = fs19.readFileSync(filePath, "utf-8");
|
|
7057
7530
|
const maxChars = 5e4;
|
|
7058
7531
|
const trimmed = content.length > maxChars ? content.slice(0, maxChars) + `
|
|
7059
7532
|
|
|
@@ -7063,7 +7536,7 @@ ${knowledgeItem.content}
|
|
|
7063
7536
|
<file path="${filePath}" size="${content.length} chars">
|
|
7064
7537
|
${trimmed}
|
|
7065
7538
|
</file>`;
|
|
7066
|
-
process.stdout.write(pc7.dim(` [attached: ${
|
|
7539
|
+
process.stdout.write(pc7.dim(` [attached: ${path19.basename(filePath)} (${(content.length / 1024).toFixed(1)}KB)]
|
|
7067
7540
|
`));
|
|
7068
7541
|
} catch {
|
|
7069
7542
|
process.stdout.write(pc7.dim(` [could not read: ${filePath}]
|
|
@@ -7072,7 +7545,7 @@ ${trimmed}
|
|
|
7072
7545
|
} else if (docExts.has(ext)) {
|
|
7073
7546
|
if (mcpManager) {
|
|
7074
7547
|
try {
|
|
7075
|
-
process.stdout.write(pc7.dim(` [converting: ${
|
|
7548
|
+
process.stdout.write(pc7.dim(` [converting: ${path19.basename(filePath)}...]
|
|
7076
7549
|
`));
|
|
7077
7550
|
const converted = await mcpManager.callTool("doc_convert", { path: filePath });
|
|
7078
7551
|
if (converted && !converted.startsWith("Error") && !converted.includes("Could not convert")) {
|
|
@@ -7081,7 +7554,7 @@ ${trimmed}
|
|
|
7081
7554
|
<file path="${filePath}" format="${ext}">
|
|
7082
7555
|
${converted.slice(0, 5e4)}
|
|
7083
7556
|
</file>`;
|
|
7084
|
-
process.stdout.write(pc7.dim(` [attached: ${
|
|
7557
|
+
process.stdout.write(pc7.dim(` [attached: ${path19.basename(filePath)} (converted from ${ext})]
|
|
7085
7558
|
`));
|
|
7086
7559
|
} else {
|
|
7087
7560
|
textContent += `
|
|
@@ -7093,7 +7566,7 @@ ${converted}
|
|
|
7093
7566
|
`));
|
|
7094
7567
|
}
|
|
7095
7568
|
} catch {
|
|
7096
|
-
process.stdout.write(pc7.dim(` [could not convert: ${
|
|
7569
|
+
process.stdout.write(pc7.dim(` [could not convert: ${path19.basename(filePath)}]
|
|
7097
7570
|
`));
|
|
7098
7571
|
}
|
|
7099
7572
|
} else {
|
|
@@ -7397,7 +7870,7 @@ ${result2.response}` : `[${input2.profile}] failed: ${result2.error}`;
|
|
|
7397
7870
|
}
|
|
7398
7871
|
if (hooksConfig?.featureHints) {
|
|
7399
7872
|
hintState.turnCount++;
|
|
7400
|
-
const hasWorkflows =
|
|
7873
|
+
const hasWorkflows = fs19.existsSync(path19.join(os18.homedir(), ".aflow", "flow.md"));
|
|
7401
7874
|
const memoryCount = memoryTokens > 0 ? Math.floor(memoryTokens / 5) : 0;
|
|
7402
7875
|
const hint = getHint(hintState, { hasWorkflows, memoryCount });
|
|
7403
7876
|
if (hint) {
|
|
@@ -7443,9 +7916,9 @@ async function saveConversationToMemory(messages, sessionId) {
|
|
|
7443
7916
|
}
|
|
7444
7917
|
|
|
7445
7918
|
// src/index.ts
|
|
7446
|
-
import
|
|
7447
|
-
import
|
|
7448
|
-
import
|
|
7919
|
+
import fs20 from "fs";
|
|
7920
|
+
import path20 from "path";
|
|
7921
|
+
import os19 from "os";
|
|
7449
7922
|
|
|
7450
7923
|
// src/presets.ts
|
|
7451
7924
|
var PRESETS = {
|
|
@@ -7554,9 +8027,9 @@ ${wfSections}`;
|
|
|
7554
8027
|
|
|
7555
8028
|
// src/index.ts
|
|
7556
8029
|
async function autoDetectConfig() {
|
|
7557
|
-
const reconfigMarker =
|
|
7558
|
-
if (
|
|
7559
|
-
|
|
8030
|
+
const reconfigMarker = path20.join(os19.homedir(), ".aman-agent", ".reconfig");
|
|
8031
|
+
if (fs20.existsSync(reconfigMarker)) {
|
|
8032
|
+
fs20.unlinkSync(reconfigMarker);
|
|
7560
8033
|
return null;
|
|
7561
8034
|
}
|
|
7562
8035
|
const anthropicKey = process.env.ANTHROPIC_API_KEY;
|
|
@@ -7585,11 +8058,11 @@ async function autoDetectConfig() {
|
|
|
7585
8058
|
return null;
|
|
7586
8059
|
}
|
|
7587
8060
|
function bootstrapEcosystem() {
|
|
7588
|
-
const home2 =
|
|
7589
|
-
const corePath =
|
|
7590
|
-
if (
|
|
7591
|
-
|
|
7592
|
-
|
|
8061
|
+
const home2 = os19.homedir();
|
|
8062
|
+
const corePath = path20.join(home2, ".acore", "core.md");
|
|
8063
|
+
if (fs20.existsSync(corePath)) return false;
|
|
8064
|
+
fs20.mkdirSync(path20.join(home2, ".acore"), { recursive: true });
|
|
8065
|
+
fs20.writeFileSync(corePath, [
|
|
7593
8066
|
"# Aman",
|
|
7594
8067
|
"",
|
|
7595
8068
|
"## Personality",
|
|
@@ -7601,11 +8074,11 @@ function bootstrapEcosystem() {
|
|
|
7601
8074
|
"## Session",
|
|
7602
8075
|
"_New companion \u2014 no prior sessions._"
|
|
7603
8076
|
].join("\n"), "utf-8");
|
|
7604
|
-
const rulesDir =
|
|
7605
|
-
const rulesPath =
|
|
7606
|
-
if (!
|
|
7607
|
-
|
|
7608
|
-
|
|
8077
|
+
const rulesDir = path20.join(home2, ".arules");
|
|
8078
|
+
const rulesPath = path20.join(rulesDir, "rules.md");
|
|
8079
|
+
if (!fs20.existsSync(rulesPath)) {
|
|
8080
|
+
fs20.mkdirSync(rulesDir, { recursive: true });
|
|
8081
|
+
fs20.writeFileSync(rulesPath, [
|
|
7609
8082
|
"# Guardrails",
|
|
7610
8083
|
"",
|
|
7611
8084
|
"## safety",
|
|
@@ -7617,22 +8090,22 @@ function bootstrapEcosystem() {
|
|
|
7617
8090
|
"- Respect the user's preferences stored in memory"
|
|
7618
8091
|
].join("\n"), "utf-8");
|
|
7619
8092
|
}
|
|
7620
|
-
const flowDir =
|
|
7621
|
-
const flowPath =
|
|
7622
|
-
if (!
|
|
7623
|
-
|
|
7624
|
-
|
|
8093
|
+
const flowDir = path20.join(home2, ".aflow");
|
|
8094
|
+
const flowPath = path20.join(flowDir, "flow.md");
|
|
8095
|
+
if (!fs20.existsSync(flowPath)) {
|
|
8096
|
+
fs20.mkdirSync(flowDir, { recursive: true });
|
|
8097
|
+
fs20.writeFileSync(flowPath, "# Workflows\n\n_No workflows defined yet. Use /workflows add to create one._\n", "utf-8");
|
|
7625
8098
|
}
|
|
7626
|
-
const skillDir =
|
|
7627
|
-
const skillPath =
|
|
7628
|
-
if (!
|
|
7629
|
-
|
|
7630
|
-
|
|
8099
|
+
const skillDir = path20.join(home2, ".askill");
|
|
8100
|
+
const skillPath = path20.join(skillDir, "skills.md");
|
|
8101
|
+
if (!fs20.existsSync(skillPath)) {
|
|
8102
|
+
fs20.mkdirSync(skillDir, { recursive: true });
|
|
8103
|
+
fs20.writeFileSync(skillPath, "# Skills\n\n_No skills installed yet. Use /skills install to add domain expertise._\n", "utf-8");
|
|
7631
8104
|
}
|
|
7632
8105
|
return true;
|
|
7633
8106
|
}
|
|
7634
8107
|
var program = new Command();
|
|
7635
|
-
program.name("aman-agent").description("Your AI companion, running locally").version("0.
|
|
8108
|
+
program.name("aman-agent").description("Your AI companion, running locally").version("0.26.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) => {
|
|
7636
8109
|
p3.intro(pc8.bold("aman agent") + pc8.dim(" \u2014 your AI companion"));
|
|
7637
8110
|
let config = loadConfig();
|
|
7638
8111
|
if (!config) {
|
|
@@ -7984,19 +8457,19 @@ program.command("init").description("Set up your AI companion with a guided wiza
|
|
|
7984
8457
|
});
|
|
7985
8458
|
if (p3.isCancel(preset)) process.exit(0);
|
|
7986
8459
|
const result = applyPreset(preset, name || "Aman");
|
|
7987
|
-
const home2 =
|
|
7988
|
-
|
|
7989
|
-
|
|
8460
|
+
const home2 = os19.homedir();
|
|
8461
|
+
fs20.mkdirSync(path20.join(home2, ".acore"), { recursive: true });
|
|
8462
|
+
fs20.writeFileSync(path20.join(home2, ".acore", "core.md"), result.coreMd, "utf-8");
|
|
7990
8463
|
p3.log.success(`Identity created \u2014 ${PRESETS[preset].identity.personality.split(".")[0].toLowerCase()}`);
|
|
7991
8464
|
if (result.rulesMd) {
|
|
7992
|
-
|
|
7993
|
-
|
|
8465
|
+
fs20.mkdirSync(path20.join(home2, ".arules"), { recursive: true });
|
|
8466
|
+
fs20.writeFileSync(path20.join(home2, ".arules", "rules.md"), result.rulesMd, "utf-8");
|
|
7994
8467
|
const ruleCount = (result.rulesMd.match(/^- /gm) || []).length;
|
|
7995
8468
|
p3.log.success(`${ruleCount} rules set`);
|
|
7996
8469
|
}
|
|
7997
8470
|
if (result.flowMd) {
|
|
7998
|
-
|
|
7999
|
-
|
|
8471
|
+
fs20.mkdirSync(path20.join(home2, ".aflow"), { recursive: true });
|
|
8472
|
+
fs20.writeFileSync(path20.join(home2, ".aflow", "flow.md"), result.flowMd, "utf-8");
|
|
8000
8473
|
const wfCount = (result.flowMd.match(/^## /gm) || []).length;
|
|
8001
8474
|
p3.log.success(`${wfCount} workflow${wfCount > 1 ? "s" : ""} added`);
|
|
8002
8475
|
}
|