@ai-setting/roy-agent-core 1.5.41 → 1.5.43

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.
@@ -1,3 +1,6 @@
1
+ import {
2
+ getXDGPath
3
+ } from "./roy-agent-core-qxnbvgwe.js";
1
4
  import {
2
5
  envKeyToConfigKey,
3
6
  toEnvKey
@@ -236,6 +239,33 @@ roy-agent mcp list|tools|reload
236
239
  roy-agent config|cfg list|export|import
237
240
  \`\`\`
238
241
 
242
+ - \`roy-agent config list [component]\` - 查看组件配置 (tool/session/agent/prompt/all)
243
+ - \`roy-agent config export <component>\` - 导出组件配置
244
+ - \`roy-agent config import <component>\` - 导入组件配置
245
+
246
+ #### Agents Command
247
+
248
+ \`\`\`bash
249
+ roy-agent agents list|get|add|delete|config-dir
250
+ \`\`\`
251
+
252
+ - \`roy-agent agents list\` - 列出所有可用 agents
253
+ - \`roy-agent agents get <name>\` - 获取指定 agent 的详细信息和 resolved system prompt
254
+ - \`roy-agent agents add <name>\` - 添加 agent 配置(写入 YAML 文件)
255
+ - \`roy-agent agents delete <name>\` - 删除 agent 配置文件
256
+ - \`roy-agent agents config-dir\` - 显示 agent 配置目录路径
257
+
258
+ #### Prompt Command
259
+
260
+ \`\`\`bash
261
+ roy-agent prompt list|get|add|config-dir
262
+ \`\`\`
263
+
264
+ - \`roy-agent prompt list\` - 列出所有可用 prompts (built-in/directory/file/all)
265
+ - \`roy-agent prompt get <name>\` - 获取指定 prompt 的内容
266
+ - \`roy-agent prompt add <name>\` - 添加 prompt 并持久化到 ~/.local/share/roy-agent/prompts/
267
+ - \`roy-agent prompt config-dir\` - 显示 prompt 持久化目录路径
268
+
239
269
  #### Debug Command
240
270
 
241
271
  \`\`\`bash
@@ -557,16 +587,95 @@ function getBuiltInPrompt(name) {
557
587
  return builtInPrompts[name];
558
588
  }
559
589
 
590
+ // src/env/prompt/prompt-store.ts
591
+ import { mkdir, readFile, writeFile, unlink, readdir } from "fs/promises";
592
+ import { existsSync } from "fs";
593
+ import { join, dirname } from "path";
594
+ init_logger();
595
+ var logger = createLogger("prompt:store");
596
+
597
+ class PromptStore {
598
+ configDir;
599
+ constructor(options = {}) {
600
+ if (options.configDir && options.configDir.length > 0) {
601
+ this.configDir = options.configDir;
602
+ } else if (options.baseDir) {
603
+ this.configDir = join(options.baseDir, "prompts");
604
+ } else {
605
+ this.configDir = join(getXDGPath("data"), "prompts");
606
+ }
607
+ }
608
+ getConfigDir() {
609
+ return this.configDir;
610
+ }
611
+ configDirExists() {
612
+ return existsSync(this.configDir);
613
+ }
614
+ getPromptFilePath(name) {
615
+ return join(this.configDir, `${name}.md`);
616
+ }
617
+ async savePrompt(name, content) {
618
+ await this.ensureConfigDir();
619
+ const filePath = this.getPromptFilePath(name);
620
+ await mkdir(dirname(filePath), { recursive: true });
621
+ await writeFile(filePath, content, "utf-8");
622
+ logger.info(`[PromptStore] Saved prompt: ${name} -> ${filePath}`);
623
+ return filePath;
624
+ }
625
+ async deletePrompt(name) {
626
+ const filePath = this.getPromptFilePath(name);
627
+ if (!existsSync(filePath)) {
628
+ return false;
629
+ }
630
+ await unlink(filePath);
631
+ logger.info(`[PromptStore] Deleted prompt file: ${filePath}`);
632
+ return true;
633
+ }
634
+ async loadAll() {
635
+ if (!existsSync(this.configDir)) {
636
+ return [];
637
+ }
638
+ const prompts = [];
639
+ await this.collectPrompts(this.configDir, "", prompts);
640
+ return prompts;
641
+ }
642
+ async collectPrompts(directory, prefix, out) {
643
+ const entries = await readdir(directory, { withFileTypes: true });
644
+ for (const entry of entries) {
645
+ const fullPath = join(directory, entry.name);
646
+ if (entry.isDirectory()) {
647
+ const nextPrefix = prefix ? `${prefix}/${entry.name}` : entry.name;
648
+ await this.collectPrompts(fullPath, nextPrefix, out);
649
+ continue;
650
+ }
651
+ if (!entry.isFile() || !entry.name.endsWith(".md")) {
652
+ continue;
653
+ }
654
+ const baseName = entry.name.slice(0, -3);
655
+ const name = prefix ? `${prefix}/${baseName}` : baseName;
656
+ const content = (await readFile(fullPath, "utf-8")).trim();
657
+ out.push({ name, content, filePath: fullPath });
658
+ }
659
+ }
660
+ async ensureConfigDir() {
661
+ if (!existsSync(this.configDir)) {
662
+ await mkdir(this.configDir, { recursive: true });
663
+ logger.debug(`[PromptStore] Created config directory: ${this.configDir}`);
664
+ }
665
+ }
666
+ }
667
+
560
668
  // src/env/prompt/prompt-component.ts
561
669
  init_logger();
562
- import { readFile, readdir } from "fs/promises";
563
- import { join, basename, extname } from "path";
670
+ import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
671
+ import { join as join2, basename, extname } from "path";
564
672
 
565
673
  // src/env/prompt/prompt-config-registration.ts
566
674
  var PROMPT_DEFAULTS = {
567
675
  "prompt.defaultName": "default",
568
676
  "prompt.variablePrefix": "{{",
569
- "prompt.variableSuffix": "}}"
677
+ "prompt.variableSuffix": "}}",
678
+ "prompt.configDir": ""
570
679
  };
571
680
  var PROMPT_CONFIG_REGISTRATION = {
572
681
  name: "prompt",
@@ -575,6 +684,7 @@ var PROMPT_CONFIG_REGISTRATION = {
575
684
  ],
576
685
  keys: [
577
686
  { key: "prompt.promptPaths", sources: ["env", "file"] },
687
+ { key: "prompt.configDir", sources: ["env", "file"] },
578
688
  { key: "prompt.defaultName", sources: ["env", "file"] },
579
689
  { key: "prompt.variablePrefix", sources: ["env", "file"] },
580
690
  { key: "prompt.variableSuffix", sources: ["env", "file"] }
@@ -582,7 +692,7 @@ var PROMPT_CONFIG_REGISTRATION = {
582
692
  };
583
693
 
584
694
  // src/env/prompt/prompt-component.ts
585
- var logger = createLogger("prompt");
695
+ var logger2 = createLogger("prompt");
586
696
 
587
697
  class PromptComponent extends BaseComponent {
588
698
  name = "prompt";
@@ -591,6 +701,7 @@ class PromptComponent extends BaseComponent {
591
701
  config;
592
702
  configComponent;
593
703
  renderer;
704
+ promptStore;
594
705
  configWatcher;
595
706
  constructor() {
596
707
  super();
@@ -604,9 +715,10 @@ class PromptComponent extends BaseComponent {
604
715
  this.configComponent = options.configComponent;
605
716
  await this.registerConfig(options);
606
717
  this.initRenderer();
718
+ this.initPromptStore();
607
719
  await this.loadPrompts();
608
720
  this.setStatus("running");
609
- logger.info(`[PromptComponent] Initialized with ${this.prompts.size} prompts`);
721
+ logger2.info(`[PromptComponent] Initialized with ${this.prompts.size} prompts`);
610
722
  }
611
723
  async registerConfig(options) {
612
724
  const configComponent = options.configComponent;
@@ -700,7 +812,7 @@ class PromptComponent extends BaseComponent {
700
812
  const value = event.newValue;
701
813
  if (value === undefined)
702
814
  return;
703
- logger.debug(`[PromptComponent] Config updated: ${key} = ${JSON.stringify(value)}`);
815
+ logger2.debug(`[PromptComponent] Config updated: ${key} = ${JSON.stringify(value)}`);
704
816
  switch (key) {
705
817
  case "prompt.defaultName":
706
818
  if (this.config)
@@ -741,18 +853,20 @@ class PromptComponent extends BaseComponent {
741
853
  suffix: config.variableSuffix,
742
854
  strict: config.strictMode,
743
855
  onMissingVariable: (name) => {
744
- logger.warn(`Undefined variable: ${name}`);
856
+ logger2.warn(`Undefined variable: ${name}`);
745
857
  return `{{${name}}}`;
746
858
  }
747
859
  });
748
860
  }
749
861
  async onStart() {
750
- logger.info("[PromptComponent] Started");
862
+ logger2.info("[PromptComponent] Started");
751
863
  }
752
864
  async onStop() {
865
+ this.configWatcher?.();
866
+ this.configWatcher = undefined;
753
867
  this.prompts.clear();
754
868
  this.setStatus("stopped");
755
- logger.info("[PromptComponent] Stopped");
869
+ logger2.info("[PromptComponent] Stopped");
756
870
  }
757
871
  getPromptConfig(key, defaultValue) {
758
872
  if (this.config && key in this.config) {
@@ -770,11 +884,11 @@ class PromptComponent extends BaseComponent {
770
884
  };
771
885
  const existing = this.prompts.get(name);
772
886
  if (existing && !existing.overridable) {
773
- logger.debug(`[PromptComponent] Skip override for non-overridable prompt: ${name}`);
887
+ logger2.debug(`[PromptComponent] Skip override for non-overridable prompt: ${name}`);
774
888
  return;
775
889
  }
776
890
  this.prompts.set(name, entry);
777
- logger.debug(`[PromptComponent] Added prompt: ${name} (${source})`);
891
+ logger2.debug(`[PromptComponent] Added prompt: ${name} (${source})`);
778
892
  }
779
893
  get(name) {
780
894
  return this.prompts.get(name)?.content;
@@ -791,6 +905,34 @@ class PromptComponent extends BaseComponent {
791
905
  list() {
792
906
  return Array.from(this.prompts.keys());
793
907
  }
908
+ listEntries() {
909
+ return Array.from(this.prompts.values());
910
+ }
911
+ getPromptStore() {
912
+ if (!this.promptStore) {
913
+ this.initPromptStore();
914
+ }
915
+ return this.promptStore;
916
+ }
917
+ getPromptConfigDir() {
918
+ return this.getPromptStore().getConfigDir();
919
+ }
920
+ async savePrompt(name, content) {
921
+ const filePath = await this.getPromptStore().savePrompt(name, content.trim());
922
+ this.add(name, content.trim(), "file");
923
+ const entry = this.prompts.get(name);
924
+ if (entry) {
925
+ entry.filePath = filePath;
926
+ }
927
+ return filePath;
928
+ }
929
+ async deletePromptFile(name) {
930
+ const deleted = await this.getPromptStore().deletePrompt(name);
931
+ if (deleted) {
932
+ this.delete(name);
933
+ }
934
+ return deleted;
935
+ }
794
936
  size() {
795
937
  return this.prompts.size;
796
938
  }
@@ -799,7 +941,7 @@ class PromptComponent extends BaseComponent {
799
941
  const targetName = this.has(name) ? name : defaultName;
800
942
  const entry = this.prompts.get(targetName);
801
943
  if (!entry) {
802
- logger.warn(`[PromptComponent] Prompt not found: ${name}, using fallback`);
944
+ logger2.warn(`[PromptComponent] Prompt not found: ${name}, using fallback`);
803
945
  return "You are a helpful assistant.";
804
946
  }
805
947
  return this.render(entry.content, variables, { name: targetName });
@@ -819,10 +961,31 @@ class PromptComponent extends BaseComponent {
819
961
  extractVariables(content) {
820
962
  return this.renderer.extractVariables(content);
821
963
  }
964
+ initPromptStore() {
965
+ const customDir = this.configComponent?.get?.("prompt.configDir");
966
+ this.promptStore = new PromptStore({
967
+ configDir: typeof customDir === "string" && customDir.length > 0 ? customDir : undefined
968
+ });
969
+ }
822
970
  async loadPrompts() {
823
971
  await this.loadBuiltInPrompts();
972
+ await this.loadUserPrompts();
824
973
  await this.loadExternalPrompts();
825
974
  }
975
+ async loadUserPrompts() {
976
+ const store = this.getPromptStore();
977
+ const prompts = await store.loadAll();
978
+ for (const prompt of prompts) {
979
+ this.add(prompt.name, prompt.content, "file");
980
+ const entry = this.prompts.get(prompt.name);
981
+ if (entry) {
982
+ entry.filePath = prompt.filePath;
983
+ }
984
+ }
985
+ if (prompts.length > 0) {
986
+ logger2.info(`[PromptComponent] Loaded ${prompts.length} prompts from ${store.getConfigDir()}`);
987
+ }
988
+ }
826
989
  async loadBuiltInPrompts() {
827
990
  for (const [name, content] of Object.entries(builtInPrompts)) {
828
991
  if (content) {
@@ -833,10 +996,10 @@ class PromptComponent extends BaseComponent {
833
996
  overridable: false,
834
997
  loadedAt: Date.now()
835
998
  });
836
- logger.debug(`[PromptComponent] Loaded built-in prompt: ${name}`);
999
+ logger2.debug(`[PromptComponent] Loaded built-in prompt: ${name}`);
837
1000
  }
838
1001
  }
839
- logger.info(`[PromptComponent] Loaded ${this.prompts.size} built-in prompts`);
1002
+ logger2.info(`[PromptComponent] Loaded ${this.prompts.size} built-in prompts`);
840
1003
  }
841
1004
  async loadExternalPrompts() {
842
1005
  const promptPaths = this.getPromptConfig("promptPaths", []);
@@ -848,22 +1011,22 @@ class PromptComponent extends BaseComponent {
848
1011
  await this.loadFromDirectory(pathConfig.path, pathConfig.extension || ".md", pathConfig.recursive !== false);
849
1012
  }
850
1013
  } catch (error) {
851
- logger.error(`[PromptComponent] Failed to load from ${pathConfig.path}:`, error);
1014
+ logger2.error(`[PromptComponent] Failed to load from ${pathConfig.path}:`, error);
852
1015
  }
853
1016
  }
854
1017
  }
855
1018
  async loadFromFile(filePath, name) {
856
1019
  try {
857
- const content = await readFile(filePath, "utf-8");
1020
+ const content = await readFile2(filePath, "utf-8");
858
1021
  const promptName = name || basename(filePath, extname(filePath));
859
1022
  this.add(promptName, content.trim(), "file");
860
1023
  const entry = this.prompts.get(promptName);
861
1024
  if (entry)
862
1025
  entry.filePath = filePath;
863
- logger.debug(`[PromptComponent] Loaded prompt from file: ${filePath}`);
1026
+ logger2.debug(`[PromptComponent] Loaded prompt from file: ${filePath}`);
864
1027
  return true;
865
1028
  } catch (error) {
866
- logger.error(`[PromptComponent] Failed to load file ${filePath}:`, error);
1029
+ logger2.error(`[PromptComponent] Failed to load file ${filePath}:`, error);
867
1030
  return false;
868
1031
  }
869
1032
  }
@@ -874,25 +1037,25 @@ class PromptComponent extends BaseComponent {
874
1037
  for (const filePath of files) {
875
1038
  const relativePath = filePath.replace(directory + "/", "");
876
1039
  const promptName = relativePath.replace(/\\/g, "/").replace(new RegExp(escapeRegex2(extension) + "$"), "").replace(/\//g, "-");
877
- const content = await readFile(filePath, "utf-8");
1040
+ const content = await readFile2(filePath, "utf-8");
878
1041
  this.add(promptName, content.trim(), "directory");
879
1042
  const entry = this.prompts.get(promptName);
880
1043
  if (entry)
881
1044
  entry.filePath = filePath;
882
1045
  loaded++;
883
1046
  }
884
- logger.info(`[PromptComponent] Loaded ${loaded} prompts from directory: ${directory}`);
1047
+ logger2.info(`[PromptComponent] Loaded ${loaded} prompts from directory: ${directory}`);
885
1048
  } catch (error) {
886
- logger.error(`[PromptComponent] Failed to load directory ${directory}:`, error);
1049
+ logger2.error(`[PromptComponent] Failed to load directory ${directory}:`, error);
887
1050
  }
888
1051
  return loaded;
889
1052
  }
890
1053
  async findFiles(dir, extension, recursive) {
891
1054
  const files = [];
892
1055
  try {
893
- const entries = await readdir(dir, { withFileTypes: true });
1056
+ const entries = await readdir2(dir, { withFileTypes: true });
894
1057
  for (const entry of entries) {
895
- const fullPath = join(dir, entry.name);
1058
+ const fullPath = join2(dir, entry.name);
896
1059
  if (entry.isDirectory() && recursive) {
897
1060
  const subFiles = await this.findFiles(fullPath, extension, true);
898
1061
  files.push(...subFiles);
@@ -901,7 +1064,7 @@ class PromptComponent extends BaseComponent {
901
1064
  }
902
1065
  }
903
1066
  } catch (error) {
904
- logger.warn(`[PromptComponent] Cannot read directory ${dir}:`, error);
1067
+ logger2.warn(`[PromptComponent] Cannot read directory ${dir}:`, error);
905
1068
  }
906
1069
  return files;
907
1070
  }
@@ -919,4 +1082,4 @@ function escapeRegex2(str) {
919
1082
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
920
1083
  }
921
1084
 
922
- export { PromptPathSchema, PromptConfigSchema, PromptRenderer, getBuiltInPromptNames, getBuiltInPrompt, PromptComponent };
1085
+ export { PromptPathSchema, PromptConfigSchema, PromptRenderer, getBuiltInPromptNames, getBuiltInPrompt, PromptStore, PromptComponent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-setting/roy-agent-core",
3
- "version": "1.5.41",
3
+ "version": "1.5.43",
4
4
  "type": "module",
5
5
  "description": "Core SDK for roy-agent - Environment, Components, Tools, Sessions, Tasks",
6
6
  "main": "./dist/index.js",