@aman_asmuei/aman 0.5.2 → 0.5.4

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.
Files changed (2) hide show
  1. package/dist/index.js +140 -90
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ import { Command } from "commander";
4
4
  // src/commands/setup.ts
5
5
  import * as p from "@clack/prompts";
6
6
  import pc from "picocolors";
7
- import fs4 from "fs";
8
- import path3 from "path";
9
- import os2 from "os";
7
+ import fs5 from "fs";
8
+ import path4 from "path";
9
+ import os3 from "os";
10
10
 
11
11
  // src/lib/detect.ts
12
12
  import fs from "fs";
@@ -266,6 +266,75 @@ function getPlatformFile(platform) {
266
266
  }
267
267
  }
268
268
 
269
+ // src/lib/heal.ts
270
+ import fs4 from "fs";
271
+ import path3 from "path";
272
+ import os2 from "os";
273
+ var TEMPLATE_MARKERS = /\[your name\]|\[YOUR_NAME\]|\[AI_NAME\]/;
274
+ function healLayerScope(layerDir, filename, home) {
275
+ const homeDir = home ?? os2.homedir();
276
+ const scopedPath = path3.join(homeDir, layerDir, "dev", "plugin", filename);
277
+ const legacyPath = path3.join(homeDir, layerDir, filename);
278
+ const scopedExists = fs4.existsSync(scopedPath);
279
+ const legacyExists = fs4.existsSync(legacyPath);
280
+ if (!legacyExists) {
281
+ return scopedExists ? "ok" : "no-op";
282
+ }
283
+ const legacyContent = fs4.readFileSync(legacyPath, "utf-8");
284
+ if (TEMPLATE_MARKERS.test(legacyContent)) {
285
+ return "no-op";
286
+ }
287
+ if (!scopedExists) {
288
+ fs4.mkdirSync(path3.dirname(scopedPath), { recursive: true });
289
+ fs4.writeFileSync(scopedPath, legacyContent, "utf-8");
290
+ return "migrated";
291
+ }
292
+ const scopedContent = fs4.readFileSync(scopedPath, "utf-8");
293
+ if (TEMPLATE_MARKERS.test(scopedContent)) {
294
+ fs4.writeFileSync(scopedPath, legacyContent, "utf-8");
295
+ return "repaired";
296
+ }
297
+ if (scopedContent === legacyContent) {
298
+ return "ok";
299
+ }
300
+ const legacyMtime = fs4.statSync(legacyPath).mtimeMs;
301
+ const scopedMtime = fs4.statSync(scopedPath).mtimeMs;
302
+ if (legacyMtime > scopedMtime) {
303
+ fs4.writeFileSync(scopedPath, legacyContent, "utf-8");
304
+ return "synced";
305
+ }
306
+ if (scopedMtime > legacyMtime) {
307
+ fs4.writeFileSync(legacyPath, scopedContent, "utf-8");
308
+ return "synced";
309
+ }
310
+ fs4.writeFileSync(legacyPath, scopedContent, "utf-8");
311
+ return "synced";
312
+ }
313
+ function healResultMessage(layerDir, filename, result) {
314
+ if (result === "migrated") {
315
+ return `${layerDir.replace(/^\./, "")}: migrated ~/${layerDir}/${filename} \u2192 ~/${layerDir}/dev/plugin/${filename}`;
316
+ }
317
+ if (result === "repaired") {
318
+ return `${layerDir.replace(/^\./, "")}: replaced unfilled template at ~/${layerDir}/dev/plugin/${filename} with your personalised content`;
319
+ }
320
+ if (result === "synced") {
321
+ return `${layerDir.replace(/^\./, "")}: synced diverged content between ~/${layerDir}/${filename} and ~/${layerDir}/dev/plugin/${filename} (newer wins)`;
322
+ }
323
+ return null;
324
+ }
325
+ function runAutoHeal(home) {
326
+ const messages = [];
327
+ for (const [layerDir, filename] of [
328
+ [".acore", "core.md"],
329
+ [".arules", "rules.md"]
330
+ ]) {
331
+ const result = healLayerScope(layerDir, filename, home);
332
+ const msg = healResultMessage(layerDir, filename, result);
333
+ if (msg) messages.push(msg);
334
+ }
335
+ return messages;
336
+ }
337
+
269
338
  // src/templates.ts
270
339
  var STARTER_FLOW = `# My Workflows
271
340
 
@@ -383,40 +452,12 @@ var ARCHETYPES = {
383
452
  };
384
453
  async function setupCommand() {
385
454
  p.intro(pc.bold("aman") + " \u2014 your complete AI companion");
455
+ for (const msg of runAutoHeal()) {
456
+ p.log.success(msg);
457
+ }
386
458
  const ecosystem = detectEcosystem();
387
- const scopedCorePath = path3.join(os2.homedir(), ".acore", "dev", "plugin", "core.md");
388
- const legacyCorePath = path3.join(os2.homedir(), ".acore", "core.md");
389
459
  if (ecosystem.acore.installed) {
390
- const scopedExists = fs4.existsSync(scopedCorePath);
391
- const legacyExists = fs4.existsSync(legacyCorePath);
392
- if (legacyExists) {
393
- const legacyContent = fs4.readFileSync(legacyCorePath, "utf-8");
394
- const legacyIsTemplate = /\[your name\]|\[YOUR_NAME\]|\[AI_NAME\]/.test(legacyContent);
395
- if (!legacyIsTemplate) {
396
- if (!scopedExists) {
397
- fs4.mkdirSync(path3.dirname(scopedCorePath), { recursive: true });
398
- fs4.writeFileSync(scopedCorePath, legacyContent, "utf-8");
399
- p.log.success(
400
- `Identity: migrated ${pc.dim("~/.acore/core.md")} \u2192 ${pc.dim("~/.acore/dev/plugin/core.md")}`
401
- );
402
- } else {
403
- const scopedContent = fs4.readFileSync(scopedCorePath, "utf-8");
404
- const scopedIsTemplate = /\[your name\]|\[YOUR_NAME\]|\[AI_NAME\]/.test(scopedContent);
405
- if (scopedIsTemplate) {
406
- fs4.writeFileSync(scopedCorePath, legacyContent, "utf-8");
407
- p.log.success(
408
- `Identity: replaced unfilled template at ${pc.dim("~/.acore/dev/plugin/core.md")} with your personalized identity`
409
- );
410
- } else {
411
- p.log.success(`Identity: ${pc.dim(ecosystem.acore.path)} already exists`);
412
- }
413
- }
414
- } else {
415
- p.log.success(`Identity: ${pc.dim(ecosystem.acore.path)} already exists`);
416
- }
417
- } else {
418
- p.log.success(`Identity: ${pc.dim(ecosystem.acore.path)} already exists`);
419
- }
460
+ p.log.success(`Identity: ${pc.dim(ecosystem.acore.path)} already exists`);
420
461
  } else {
421
462
  p.log.step("Setting up your AI identity...");
422
463
  let userName = detectUserName();
@@ -462,13 +503,13 @@ async function setupCommand() {
462
503
  DATE: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
463
504
  UPDATE_INSTRUCTIONS: getUpdateInstructions(platform)
464
505
  });
465
- const scopedAcoreDir = path3.join(os2.homedir(), ".acore", "dev", "plugin");
466
- fs4.mkdirSync(scopedAcoreDir, { recursive: true });
467
- fs4.writeFileSync(path3.join(scopedAcoreDir, "core.md"), content, "utf-8");
506
+ const scopedAcoreDir = path4.join(os3.homedir(), ".acore", "dev", "plugin");
507
+ fs5.mkdirSync(scopedAcoreDir, { recursive: true });
508
+ fs5.writeFileSync(path4.join(scopedAcoreDir, "core.md"), content, "utf-8");
468
509
  p.log.success(`Created ${pc.dim("~/.acore/dev/plugin/core.md")} (identity)`);
469
510
  if (stack) {
470
- const localDir = path3.join(process.cwd(), ".acore");
471
- fs4.mkdirSync(localDir, { recursive: true });
511
+ const localDir = path4.join(process.cwd(), ".acore");
512
+ fs5.mkdirSync(localDir, { recursive: true });
472
513
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
473
514
  const contextContent = `## Work
474
515
  - Stack: ${stack}
@@ -485,9 +526,9 @@ async function setupCommand() {
485
526
  ## Project Patterns
486
527
  - [observations specific to this project \u2014 built over time]
487
528
  `;
488
- fs4.writeFileSync(path3.join(localDir, "context.md"), contextContent, "utf-8");
529
+ fs5.writeFileSync(path4.join(localDir, "context.md"), contextContent, "utf-8");
489
530
  const configContent = JSON.stringify({ platform: platform || "other" }, null, 2) + "\n";
490
- fs4.writeFileSync(path3.join(localDir, "config.json"), configContent, "utf-8");
531
+ fs5.writeFileSync(path4.join(localDir, "config.json"), configContent, "utf-8");
491
532
  } else {
492
533
  p.log.info(
493
534
  `No project detected here \u2014 run ${pc.bold("aman here")} later from inside any repo to add per-project context.`
@@ -495,7 +536,7 @@ async function setupCommand() {
495
536
  }
496
537
  const platformFile = getPlatformFile(platform);
497
538
  if (platformFile) {
498
- const filePath = path3.join(process.cwd(), platformFile);
539
+ const filePath = path4.join(process.cwd(), platformFile);
499
540
  const result = injectIntoFile(filePath, content);
500
541
  if (result.created) {
501
542
  p.log.success(`Created ${pc.dim(platformFile)} with identity`);
@@ -507,7 +548,7 @@ async function setupCommand() {
507
548
  if (stack) inferredParts.push(stack);
508
549
  p.log.info(`Inferred: ${pc.dim(inferredParts.join(" \xB7 "))}`);
509
550
  }
510
- const home = os2.homedir();
551
+ const home = os3.homedir();
511
552
  const aflowExists = ecosystem.aflow.installed;
512
553
  const arulesExists = ecosystem.arules.installed;
513
554
  const aevalExists = ecosystem.aeval.installed;
@@ -550,26 +591,26 @@ async function setupCommand() {
550
591
  doEval = selected.includes("eval");
551
592
  }
552
593
  if (doFlow) {
553
- const aflowDir = path3.join(home, ".aflow");
554
- fs4.mkdirSync(aflowDir, { recursive: true });
555
- fs4.writeFileSync(path3.join(aflowDir, "flow.md"), STARTER_FLOW, "utf-8");
594
+ const aflowDir = path4.join(home, ".aflow");
595
+ fs5.mkdirSync(aflowDir, { recursive: true });
596
+ fs5.writeFileSync(path4.join(aflowDir, "flow.md"), STARTER_FLOW, "utf-8");
556
597
  p.log.success(`Workflows: created ${pc.dim("~/.aflow/flow.md")} (4 starter workflows)`);
557
598
  } else if (aflowExists) {
558
599
  p.log.success(`Workflows: ${ecosystem.aflow.workflowCount} defined`);
559
600
  }
560
601
  if (doRules) {
561
- const arulesDir = path3.join(home, ".arules", "dev", "plugin");
562
- fs4.mkdirSync(arulesDir, { recursive: true });
563
- fs4.writeFileSync(path3.join(arulesDir, "rules.md"), STARTER_RULES, "utf-8");
602
+ const arulesDir = path4.join(home, ".arules", "dev", "plugin");
603
+ fs5.mkdirSync(arulesDir, { recursive: true });
604
+ fs5.writeFileSync(path4.join(arulesDir, "rules.md"), STARTER_RULES, "utf-8");
564
605
  p.log.success(`Guardrails: created ${pc.dim("~/.arules/dev/plugin/rules.md")} (24 rules)`);
565
606
  } else if (arulesExists) {
566
607
  p.log.success(`Guardrails: ${ecosystem.arules.ruleCount} rules`);
567
608
  }
568
609
  if (doEval) {
569
- const aevalDir = path3.join(home, ".aeval");
570
- fs4.mkdirSync(aevalDir, { recursive: true });
610
+ const aevalDir = path4.join(home, ".aeval");
611
+ fs5.mkdirSync(aevalDir, { recursive: true });
571
612
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
572
- fs4.writeFileSync(path3.join(aevalDir, "eval.md"), STARTER_EVAL.replace("{{DATE}}", today), "utf-8");
613
+ fs5.writeFileSync(path4.join(aevalDir, "eval.md"), STARTER_EVAL.replace("{{DATE}}", today), "utf-8");
573
614
  p.log.success(`Evaluation: created ${pc.dim("~/.aeval/eval.md")}`);
574
615
  } else if (aevalExists) {
575
616
  p.log.success("Evaluation: already configured");
@@ -603,13 +644,13 @@ async function setupCommand() {
603
644
  // src/commands/status.ts
604
645
  import * as p2 from "@clack/prompts";
605
646
  import pc2 from "picocolors";
606
- import fs5 from "fs";
647
+ import fs6 from "fs";
607
648
  function statusCommand() {
608
649
  p2.intro(pc2.bold("aman") + " \u2014 ecosystem status");
609
650
  const ecosystem = detectEcosystem();
610
651
  const platform = detectPlatform();
611
652
  if (ecosystem.acore.installed) {
612
- const content = fs5.readFileSync(ecosystem.acore.path, "utf-8");
653
+ const content = fs6.readFileSync(ecosystem.acore.path, "utf-8");
613
654
  const aiName = content.match(/^# (.+)$/m)?.[1] || "Companion";
614
655
  const userName = content.match(/- Name: (.+)$/m)?.[1] || "Unknown";
615
656
  p2.log.success(`Identity: ${pc2.bold(aiName)} + ${pc2.bold(userName)}`);
@@ -674,8 +715,8 @@ function statusCommand() {
674
715
  }
675
716
 
676
717
  // src/commands/deploy.ts
677
- import fs6 from "fs";
678
- import path4 from "path";
718
+ import fs7 from "fs";
719
+ import path5 from "path";
679
720
  import * as p3 from "@clack/prompts";
680
721
  import pc3 from "picocolors";
681
722
  async function deployCommand() {
@@ -732,15 +773,15 @@ AMAN_MODEL=${isAnthropic ? "claude-sonnet-4-6" : "gpt-4o"}
732
773
  # TELEGRAM_BOT_TOKEN=
733
774
  # DISCORD_BOT_TOKEN=
734
775
  `;
735
- const envPath = path4.join(cwd, ".env");
736
- fs6.writeFileSync(envPath, envContent, "utf-8");
776
+ const envPath = path5.join(cwd, ".env");
777
+ fs7.writeFileSync(envPath, envContent, "utf-8");
737
778
  p3.log.success(`Created ${pc3.bold(".env")} with API key`);
738
779
  const pkgDir = findPackageDir();
739
780
  copyDeployFile(pkgDir, cwd, "Dockerfile");
740
781
  copyDeployFile(pkgDir, cwd, "docker-entrypoint.sh");
741
782
  copyDeployFile(pkgDir, cwd, "docker-compose.yml");
742
783
  try {
743
- fs6.chmodSync(path4.join(cwd, "docker-entrypoint.sh"), 493);
784
+ fs7.chmodSync(path5.join(cwd, "docker-entrypoint.sh"), 493);
744
785
  } catch {
745
786
  }
746
787
  p3.log.success(`Created ${pc3.bold("Dockerfile")} + ${pc3.bold("docker-compose.yml")}`);
@@ -776,20 +817,20 @@ AMAN_MODEL=${model}
776
817
  # TELEGRAM_BOT_TOKEN=
777
818
  # DISCORD_BOT_TOKEN=
778
819
  `;
779
- const envPath = path4.join(cwd, ".env");
780
- fs6.writeFileSync(envPath, envContent, "utf-8");
820
+ const envPath = path5.join(cwd, ".env");
821
+ fs7.writeFileSync(envPath, envContent, "utf-8");
781
822
  p3.log.success(`Created ${pc3.bold(".env")} with Ollama config`);
782
823
  const pkgDir = findPackageDir();
783
824
  copyDeployFile(pkgDir, cwd, "Dockerfile");
784
825
  copyDeployFile(pkgDir, cwd, "docker-entrypoint.sh");
785
826
  copyDeployFile(pkgDir, cwd, "docker-compose.ollama.yml");
786
- const src = path4.join(cwd, "docker-compose.ollama.yml");
787
- const dest = path4.join(cwd, "docker-compose.yml");
788
- if (fs6.existsSync(src) && !fs6.existsSync(dest)) {
789
- fs6.renameSync(src, dest);
827
+ const src = path5.join(cwd, "docker-compose.ollama.yml");
828
+ const dest = path5.join(cwd, "docker-compose.yml");
829
+ if (fs7.existsSync(src) && !fs7.existsSync(dest)) {
830
+ fs7.renameSync(src, dest);
790
831
  }
791
832
  try {
792
- fs6.chmodSync(path4.join(cwd, "docker-entrypoint.sh"), 493);
833
+ fs7.chmodSync(path5.join(cwd, "docker-entrypoint.sh"), 493);
793
834
  } catch {
794
835
  }
795
836
  p3.log.success(`Created ${pc3.bold("Dockerfile")} + ${pc3.bold("docker-compose.yml")} (with Ollama)`);
@@ -866,18 +907,18 @@ ${pc3.bold("Raspberry Pi:")}
866
907
  function findPackageDir() {
867
908
  let dir = new URL(".", import.meta.url).pathname;
868
909
  for (let i = 0; i < 5; i++) {
869
- if (fs6.existsSync(path4.join(dir, "Dockerfile"))) return dir;
870
- dir = path4.dirname(dir);
910
+ if (fs7.existsSync(path5.join(dir, "Dockerfile"))) return dir;
911
+ dir = path5.dirname(dir);
871
912
  }
872
- const globalDir = path4.join(process.env.npm_config_prefix || "/usr/local", "lib/node_modules/@aman_asmuei/aman");
873
- if (fs6.existsSync(path4.join(globalDir, "Dockerfile"))) return globalDir;
913
+ const globalDir = path5.join(process.env.npm_config_prefix || "/usr/local", "lib/node_modules/@aman_asmuei/aman");
914
+ if (fs7.existsSync(path5.join(globalDir, "Dockerfile"))) return globalDir;
874
915
  return process.cwd();
875
916
  }
876
917
  function copyDeployFile(pkgDir, destDir, filename) {
877
- const src = path4.join(pkgDir, filename);
878
- const dest = path4.join(destDir, filename);
879
- if (fs6.existsSync(src)) {
880
- fs6.copyFileSync(src, dest);
918
+ const src = path5.join(pkgDir, filename);
919
+ const dest = path5.join(destDir, filename);
920
+ if (fs7.existsSync(src)) {
921
+ fs7.copyFileSync(src, dest);
881
922
  }
882
923
  }
883
924
 
@@ -1007,14 +1048,14 @@ async function showcaseCommand(nameArg, opts = {}) {
1007
1048
  // src/commands/here.ts
1008
1049
  import * as p5 from "@clack/prompts";
1009
1050
  import pc5 from "picocolors";
1010
- import fs7 from "fs";
1011
- import path5 from "path";
1051
+ import fs8 from "fs";
1052
+ import path6 from "path";
1012
1053
  async function hereCommand(opts = {}) {
1013
1054
  const cwd = process.cwd();
1014
- const acoreDir = path5.join(cwd, ".acore");
1015
- const contextPath = path5.join(acoreDir, "context.md");
1016
- const configPath = path5.join(acoreDir, "config.json");
1017
- if (fs7.existsSync(contextPath) && !opts.force) {
1055
+ const acoreDir = path6.join(cwd, ".acore");
1056
+ const contextPath = path6.join(acoreDir, "context.md");
1057
+ const configPath = path6.join(acoreDir, "config.json");
1058
+ if (fs8.existsSync(contextPath) && !opts.force) {
1018
1059
  p5.intro(pc5.bold("aman here") + " \u2014 project context card");
1019
1060
  const overwrite = await p5.confirm({
1020
1061
  message: `${pc5.dim(".acore/context.md")} already exists. Overwrite?`,
@@ -1028,7 +1069,7 @@ async function hereCommand(opts = {}) {
1028
1069
  const stack = detectStack() || "unknown";
1029
1070
  const platform = detectPlatform();
1030
1071
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1031
- fs7.mkdirSync(acoreDir, { recursive: true });
1072
+ fs8.mkdirSync(acoreDir, { recursive: true });
1032
1073
  const contextContent = `## Work
1033
1074
  - Stack: ${stack}
1034
1075
  - Domain:
@@ -1044,21 +1085,21 @@ async function hereCommand(opts = {}) {
1044
1085
  ## Project Patterns
1045
1086
  - [observations specific to this project \u2014 built over time]
1046
1087
  `;
1047
- fs7.writeFileSync(contextPath, contextContent, "utf-8");
1048
- if (fs7.existsSync(configPath)) {
1088
+ fs8.writeFileSync(contextPath, contextContent, "utf-8");
1089
+ if (fs8.existsSync(configPath)) {
1049
1090
  try {
1050
- const existing = JSON.parse(fs7.readFileSync(configPath, "utf-8"));
1091
+ const existing = JSON.parse(fs8.readFileSync(configPath, "utf-8"));
1051
1092
  if (typeof existing.platform !== "string" || existing.platform === "") {
1052
1093
  existing.platform = platform || "other";
1053
- fs7.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
1094
+ fs8.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
1054
1095
  }
1055
1096
  } catch {
1056
1097
  const fresh = { platform: platform || "other" };
1057
- fs7.writeFileSync(configPath, JSON.stringify(fresh, null, 2) + "\n", "utf-8");
1098
+ fs8.writeFileSync(configPath, JSON.stringify(fresh, null, 2) + "\n", "utf-8");
1058
1099
  }
1059
1100
  } else {
1060
1101
  const fresh = { platform: platform || "other" };
1061
- fs7.writeFileSync(configPath, JSON.stringify(fresh, null, 2) + "\n", "utf-8");
1102
+ fs8.writeFileSync(configPath, JSON.stringify(fresh, null, 2) + "\n", "utf-8");
1062
1103
  }
1063
1104
  if (opts.force) {
1064
1105
  return;
@@ -1071,8 +1112,12 @@ async function hereCommand(opts = {}) {
1071
1112
  }
1072
1113
 
1073
1114
  // src/index.ts
1115
+ import pc6 from "picocolors";
1074
1116
  var program = new Command();
1075
- program.name("aman").description("Your complete AI companion \u2014 identity, memory, and tools in one command").version("0.5.2").action(() => {
1117
+ program.name("aman").description("Your complete AI companion \u2014 identity, memory, and tools in one command").version("0.5.4").action(() => {
1118
+ for (const msg of runAutoHeal()) {
1119
+ console.log(` ${pc6.green("\u2714")} ${msg}`);
1120
+ }
1076
1121
  const ecosystem = detectEcosystem();
1077
1122
  if (ecosystem.acore.installed) {
1078
1123
  statusCommand();
@@ -1081,7 +1126,12 @@ program.name("aman").description("Your complete AI companion \u2014 identity, me
1081
1126
  }
1082
1127
  });
1083
1128
  program.command("setup").description("Set up your AI companion (identity + memory + tools)").action(() => setupCommand());
1084
- program.command("status").description("View your full ecosystem status").action(() => statusCommand());
1129
+ program.command("status").description("View your full ecosystem status").action(() => {
1130
+ for (const msg of runAutoHeal()) {
1131
+ console.log(` ${pc6.green("\u2714")} ${msg}`);
1132
+ }
1133
+ statusCommand();
1134
+ });
1085
1135
  program.command("deploy").description("Deploy your AI companion (Docker, systemd, or cloud)").action(() => deployCommand());
1086
1136
  program.command("showcase [name]").description("Set up a showcase AI companion (rutin, kedai, monitor, ...)").option("--dry-run", "preview files without installing").option("--list", "list all available showcases").action(
1087
1137
  (name, opts) => showcaseCommand(name, opts)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aman_asmuei/aman",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Your complete AI companion — identity, memory, and tools in one command",
5
5
  "type": "module",
6
6
  "bin": {