@hirohsu/user-web-feedback 2.8.1 → 2.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -3302,6 +3302,7 @@ __export(database_exports, {
3302
3302
  getMCPServerById: () => getMCPServerById,
3303
3303
  getPinnedPrompts: () => getPinnedPrompts,
3304
3304
  getPromptById: () => getPromptById,
3305
+ getPromptConfigs: () => getPromptConfigs,
3305
3306
  getRecentMCPServerErrors: () => getRecentMCPServerErrors,
3306
3307
  getSelfProbeSettings: () => getSelfProbeSettings,
3307
3308
  getToolEnableConfigs: () => getToolEnableConfigs,
@@ -3319,6 +3320,7 @@ __export(database_exports, {
3319
3320
  queryLogs: () => queryLogs,
3320
3321
  queryMCPServerLogs: () => queryMCPServerLogs,
3321
3322
  reorderPrompts: () => reorderPrompts,
3323
+ resetPromptConfigs: () => resetPromptConfigs,
3322
3324
  saveSelfProbeSettings: () => saveSelfProbeSettings,
3323
3325
  setToolEnabled: () => setToolEnabled,
3324
3326
  toggleMCPServerEnabled: () => toggleMCPServerEnabled,
@@ -3329,6 +3331,7 @@ __export(database_exports, {
3329
3331
  updateCLITerminalActivity: () => updateCLITerminalActivity,
3330
3332
  updateMCPServer: () => updateMCPServer,
3331
3333
  updatePrompt: () => updatePrompt,
3334
+ updatePromptConfigs: () => updatePromptConfigs,
3332
3335
  updateUserPreferences: () => updateUserPreferences
3333
3336
  });
3334
3337
  function hashPrompt(prompt) {
@@ -3626,6 +3629,21 @@ function createTables() {
3626
3629
  updated_at TEXT DEFAULT CURRENT_TIMESTAMP
3627
3630
  )
3628
3631
  `);
3632
+ db.exec(`
3633
+ CREATE TABLE IF NOT EXISTS prompt_configs (
3634
+ id TEXT PRIMARY KEY,
3635
+ name TEXT NOT NULL,
3636
+ display_name TEXT NOT NULL,
3637
+ content TEXT,
3638
+ first_order INTEGER DEFAULT 0,
3639
+ second_order INTEGER DEFAULT 0,
3640
+ enabled INTEGER DEFAULT 1,
3641
+ editable INTEGER DEFAULT 1,
3642
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
3643
+ updated_at TEXT DEFAULT CURRENT_TIMESTAMP
3644
+ )
3645
+ `);
3646
+ initDefaultPromptConfigs();
3629
3647
  }
3630
3648
  function initDefaultSettings() {
3631
3649
  if (!db) throw new Error("Database not initialized");
@@ -5020,7 +5038,104 @@ function saveSelfProbeSettings(settings) {
5020
5038
  }
5021
5039
  return getSelfProbeSettings();
5022
5040
  }
5023
- var import_better_sqlite3, import_path2, import_fs2, import_crypto2, DB_DIR, DB_PATH, db, SYSTEM_PROMPT_VERSIONS, CURRENT_PROMPT_VERSION;
5041
+ function initDefaultPromptConfigs() {
5042
+ const db2 = tryGetDb();
5043
+ if (!db2) return;
5044
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5045
+ const existingIds = db2.prepare("SELECT id FROM prompt_configs").all().map((row) => row.id);
5046
+ const stmt = db2.prepare(`
5047
+ INSERT INTO prompt_configs (id, name, display_name, content, first_order, second_order, enabled, editable, created_at, updated_at)
5048
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
5049
+ `);
5050
+ for (const config2 of DEFAULT_PROMPT_CONFIGS) {
5051
+ if (!existingIds.includes(config2.id)) {
5052
+ stmt.run(
5053
+ config2.id,
5054
+ config2.name,
5055
+ config2.displayName,
5056
+ config2.content,
5057
+ config2.firstOrder,
5058
+ config2.secondOrder,
5059
+ config2.enabled ? 1 : 0,
5060
+ config2.editable ? 1 : 0,
5061
+ now,
5062
+ now
5063
+ );
5064
+ logger.info(`Added missing prompt config: ${config2.id}`);
5065
+ }
5066
+ }
5067
+ }
5068
+ function getPromptConfigs() {
5069
+ const db2 = tryGetDb();
5070
+ if (!db2) return [];
5071
+ const rows = db2.prepare(`
5072
+ SELECT id, name, display_name as displayName, content,
5073
+ first_order as firstOrder, second_order as secondOrder,
5074
+ enabled, editable, created_at as createdAt, updated_at as updatedAt
5075
+ FROM prompt_configs
5076
+ ORDER BY first_order ASC
5077
+ `).all();
5078
+ const aiSettings = getAISettings();
5079
+ return rows.map((row) => {
5080
+ let content = row.content;
5081
+ if (row.id === "system_prompt" && !content && aiSettings.systemPrompt) {
5082
+ content = aiSettings.systemPrompt;
5083
+ } else if (row.id === "mcp_tools" && !content && aiSettings.mcpToolsPrompt) {
5084
+ content = aiSettings.mcpToolsPrompt;
5085
+ }
5086
+ return {
5087
+ ...row,
5088
+ content,
5089
+ enabled: row.enabled === 1,
5090
+ editable: row.editable === 1
5091
+ };
5092
+ });
5093
+ }
5094
+ function updatePromptConfigs(request) {
5095
+ const db2 = tryGetDb();
5096
+ if (!db2) return false;
5097
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5098
+ for (const prompt of request.prompts) {
5099
+ const updates = [];
5100
+ const values = [];
5101
+ if (prompt.firstOrder !== void 0) {
5102
+ updates.push("first_order = ?");
5103
+ values.push(prompt.firstOrder);
5104
+ }
5105
+ if (prompt.secondOrder !== void 0) {
5106
+ updates.push("second_order = ?");
5107
+ values.push(prompt.secondOrder);
5108
+ }
5109
+ if (prompt.enabled !== void 0) {
5110
+ updates.push("enabled = ?");
5111
+ values.push(prompt.enabled ? 1 : 0);
5112
+ }
5113
+ if (prompt.content !== void 0) {
5114
+ updates.push("content = ?");
5115
+ values.push(prompt.content);
5116
+ if (prompt.id === "system_prompt") {
5117
+ db2.prepare("UPDATE ai_settings SET system_prompt = ?, updated_at = ? WHERE id = 1").run(prompt.content || "", now);
5118
+ } else if (prompt.id === "mcp_tools") {
5119
+ db2.prepare("UPDATE ai_settings SET mcp_tools_prompt = ?, updated_at = ? WHERE id = 1").run(prompt.content || "", now);
5120
+ }
5121
+ }
5122
+ if (updates.length > 0) {
5123
+ updates.push("updated_at = ?");
5124
+ values.push(now);
5125
+ values.push(prompt.id);
5126
+ db2.prepare(`UPDATE prompt_configs SET ${updates.join(", ")} WHERE id = ?`).run(...values);
5127
+ }
5128
+ }
5129
+ return true;
5130
+ }
5131
+ function resetPromptConfigs() {
5132
+ const db2 = tryGetDb();
5133
+ if (!db2) return [];
5134
+ db2.prepare("DELETE FROM prompt_configs").run();
5135
+ initDefaultPromptConfigs();
5136
+ return getPromptConfigs();
5137
+ }
5138
+ var import_better_sqlite3, import_path2, import_fs2, import_crypto2, DB_DIR, DB_PATH, db, SYSTEM_PROMPT_VERSIONS, CURRENT_PROMPT_VERSION, DEFAULT_PROMPT_CONFIGS;
5024
5139
  var init_database = __esm({
5025
5140
  "src/utils/database.ts"() {
5026
5141
  "use strict";
@@ -5067,6 +5182,14 @@ var init_database = __esm({
5067
5182
  \u4FDD\u6301\u56DE\u61C9\u7C21\u77ED\uFF082-3\u53E5\u8A71\uFF09\uFF0C\u9664\u975E\u9700\u8981\u66F4\u8A73\u7D30\u7684\u8AAA\u660E\u3002`
5068
5183
  };
5069
5184
  CURRENT_PROMPT_VERSION = "v2";
5185
+ DEFAULT_PROMPT_CONFIGS = [
5186
+ { id: "system_prompt", name: "System Prompt", displayName: "\u7CFB\u7D71\u63D0\u793A\u8A5E", content: null, firstOrder: 10, secondOrder: 10, enabled: true, editable: true },
5187
+ { id: "mcp_tools", name: "MCP Tools", displayName: "MCP \u5DE5\u5177\u8AAA\u660E", content: null, firstOrder: 20, secondOrder: 0, enabled: true, editable: true },
5188
+ { id: "mcp_tools_detailed", name: "MCP Tools Detailed", displayName: "MCP \u5DE5\u5177\u8A73\u7D30\u5217\u8868", content: null, firstOrder: 25, secondOrder: 15, enabled: true, editable: false },
5189
+ { id: "user_context", name: "User Context", displayName: "\u7528\u6236\u4E0A\u4E0B\u6587", content: null, firstOrder: 30, secondOrder: 20, enabled: true, editable: false },
5190
+ { id: "tool_results", name: "Tool Results", displayName: "\u5DE5\u5177\u57F7\u884C\u7D50\u679C", content: null, firstOrder: 0, secondOrder: 30, enabled: true, editable: false },
5191
+ { id: "closing", name: "Closing", displayName: "\u7D50\u5C3E\u63D0\u793A", content: null, firstOrder: 100, secondOrder: 100, enabled: true, editable: true }
5192
+ ];
5070
5193
  }
5071
5194
  });
5072
5195
 
@@ -77793,6 +77916,107 @@ ${mcpPrompt}`;
77793
77916
  }
77794
77917
  });
77795
77918
 
77919
+ // src/utils/prompt-aggregator/components/mcp-tools-detailed.ts
77920
+ var MCPToolsDetailedComponent;
77921
+ var init_mcp_tools_detailed = __esm({
77922
+ "src/utils/prompt-aggregator/components/mcp-tools-detailed.ts"() {
77923
+ "use strict";
77924
+ init_cjs_shims();
77925
+ init_base_component();
77926
+ init_mcp_client_manager();
77927
+ MCPToolsDetailedComponent = class extends BasePromptComponent {
77928
+ constructor() {
77929
+ super("MCPToolsDetailed", 25);
77930
+ }
77931
+ build(context) {
77932
+ const { request } = context;
77933
+ if (!request.includeMCPTools) {
77934
+ return null;
77935
+ }
77936
+ try {
77937
+ const allTools = mcpClientManager.getAllTools();
77938
+ if (!allTools || allTools.length === 0) {
77939
+ return null;
77940
+ }
77941
+ let result = "## MCP \u5DE5\u5177\u8A73\u7D30\u4F7F\u7528\u8AAA\u660E\n\n";
77942
+ result += "\u4EE5\u4E0B\u662F\u6240\u6709\u53EF\u7528\u7684 MCP \u5DE5\u5177\u53CA\u5176\u8A73\u7D30\u4F7F\u7528\u65B9\u5F0F\uFF1A\n\n";
77943
+ for (const tool of allTools) {
77944
+ result += `### ${tool.name}
77945
+
77946
+ `;
77947
+ if (tool.description) {
77948
+ result += `**\u8AAA\u660E**: ${tool.description}
77949
+
77950
+ `;
77951
+ }
77952
+ if (tool.inputSchema) {
77953
+ result += "**\u53C3\u6578\u683C\u5F0F**:\n```json\n";
77954
+ result += JSON.stringify(tool.inputSchema, null, 2);
77955
+ result += "\n```\n\n";
77956
+ const schema = tool.inputSchema;
77957
+ if (schema.properties && typeof schema.properties === "object") {
77958
+ result += "**\u53C3\u6578\u8AAA\u660E**:\n";
77959
+ const props = schema.properties;
77960
+ const required2 = schema.required || [];
77961
+ for (const [propName, propDef] of Object.entries(props)) {
77962
+ const isRequired = required2.includes(propName);
77963
+ result += `- \`${propName}\``;
77964
+ if (propDef.type) result += ` (${propDef.type})`;
77965
+ if (isRequired) result += " **\u5FC5\u586B**";
77966
+ if (propDef.description) result += `: ${propDef.description}`;
77967
+ result += "\n";
77968
+ }
77969
+ result += "\n";
77970
+ }
77971
+ }
77972
+ result += "**\u8ABF\u7528\u7BC4\u4F8B**:\n```json\n";
77973
+ result += JSON.stringify({
77974
+ tool_calls: [{
77975
+ name: tool.name,
77976
+ arguments: this.generateExampleArgs(tool.inputSchema)
77977
+ }]
77978
+ }, null, 2);
77979
+ result += "\n```\n\n---\n\n";
77980
+ }
77981
+ return result;
77982
+ } catch {
77983
+ return null;
77984
+ }
77985
+ }
77986
+ generateExampleArgs(schema) {
77987
+ if (!schema || !schema.properties) {
77988
+ return {};
77989
+ }
77990
+ const result = {};
77991
+ const props = schema.properties;
77992
+ for (const [propName, propDef] of Object.entries(props)) {
77993
+ switch (propDef.type) {
77994
+ case "string":
77995
+ result[propName] = "<\u5B57\u4E32\u503C>";
77996
+ break;
77997
+ case "number":
77998
+ case "integer":
77999
+ result[propName] = 0;
78000
+ break;
78001
+ case "boolean":
78002
+ result[propName] = true;
78003
+ break;
78004
+ case "array":
78005
+ result[propName] = [];
78006
+ break;
78007
+ case "object":
78008
+ result[propName] = {};
78009
+ break;
78010
+ default:
78011
+ result[propName] = "<\u503C>";
78012
+ }
78013
+ }
78014
+ return result;
78015
+ }
78016
+ };
78017
+ }
78018
+ });
78019
+
77796
78020
  // src/utils/prompt-aggregator/components/user-context.ts
77797
78021
  var UserContextComponent;
77798
78022
  var init_user_context = __esm({
@@ -77905,6 +78129,7 @@ var init_components = __esm({
77905
78129
  init_system_prompt();
77906
78130
  init_pinned_prompts();
77907
78131
  init_mcp_tools();
78132
+ init_mcp_tools_detailed();
77908
78133
  init_user_context();
77909
78134
  init_tool_results();
77910
78135
  init_ai_message();
@@ -77916,7 +78141,7 @@ var init_components = __esm({
77916
78141
  function getPromptAggregator() {
77917
78142
  return PromptAggregator.getInstance();
77918
78143
  }
77919
- var PromptAggregator;
78144
+ var COMPONENT_NAME_MAP, PromptAggregator;
77920
78145
  var init_prompt_aggregator = __esm({
77921
78146
  "src/utils/prompt-aggregator/prompt-aggregator.ts"() {
77922
78147
  "use strict";
@@ -77925,6 +78150,14 @@ var init_prompt_aggregator = __esm({
77925
78150
  init_database();
77926
78151
  init_mcp_client_manager();
77927
78152
  init_logger();
78153
+ COMPONENT_NAME_MAP = {
78154
+ "system_prompt": "SystemPrompt",
78155
+ "mcp_tools": "MCPToolsPrompt",
78156
+ "mcp_tools_detailed": "MCPToolsDetailed",
78157
+ "user_context": "UserContext",
78158
+ "tool_results": "ToolResults",
78159
+ "closing": "ClosingPrompt"
78160
+ };
77928
78161
  PromptAggregator = class _PromptAggregator {
77929
78162
  static instance;
77930
78163
  components = [];
@@ -77943,6 +78176,7 @@ var init_prompt_aggregator = __esm({
77943
78176
  this.register(new SystemPromptComponent());
77944
78177
  this.register(new PinnedPromptsComponent());
77945
78178
  this.register(new MCPToolsPromptComponent());
78179
+ this.register(new MCPToolsDetailedComponent());
77946
78180
  this.register(new UserContextComponent());
77947
78181
  this.register(new ToolResultsComponent());
77948
78182
  this.register(new AIMessageComponent());
@@ -77958,14 +78192,21 @@ var init_prompt_aggregator = __esm({
77958
78192
  aggregate(context) {
77959
78193
  const sections = [];
77960
78194
  const promptParts = [];
77961
- for (const component of this.components) {
78195
+ const configs = this.getPromptConfigsWithDefaults();
78196
+ const isFirstCall = context.isFirstCall !== false;
78197
+ const configuredComponents = this.components.map((component) => {
78198
+ const configId = Object.entries(COMPONENT_NAME_MAP).find(
78199
+ ([, name]) => name === component.getName()
78200
+ )?.[0];
78201
+ const config2 = configId ? configs.find((c) => c.id === configId) : null;
78202
+ const order = config2 ? isFirstCall ? config2.firstOrder : config2.secondOrder : component.getOrder();
78203
+ const enabled = config2 ? config2.enabled : true;
78204
+ return { component, order, enabled };
78205
+ }).filter((item) => item.enabled && item.order > 0).sort((a, b) => a.order - b.order);
78206
+ for (const { component, order } of configuredComponents) {
77962
78207
  const content = component.build(context);
77963
78208
  if (content) {
77964
- sections.push({
77965
- name: component.getName(),
77966
- content,
77967
- order: component.getOrder()
77968
- });
78209
+ sections.push({ name: component.getName(), content, order });
77969
78210
  promptParts.push(content);
77970
78211
  }
77971
78212
  }
@@ -78056,6 +78297,21 @@ var init_prompt_aggregator = __esm({
78056
78297
  getComponentNames() {
78057
78298
  return this.components.map((c) => c.getName());
78058
78299
  }
78300
+ getPromptConfigsWithDefaults() {
78301
+ try {
78302
+ const configs = getPromptConfigs();
78303
+ if (configs.length > 0) return configs;
78304
+ } catch {
78305
+ logger.warn("[PromptAggregator] \u7121\u6CD5\u5F9E\u8CC7\u6599\u5EAB\u7372\u53D6\u63D0\u793A\u8A5E\u914D\u7F6E\uFF0C\u4F7F\u7528\u9810\u8A2D\u503C");
78306
+ }
78307
+ return [
78308
+ { id: "system_prompt", name: "System Prompt", displayName: "\u7CFB\u7D71\u63D0\u793A\u8A5E", content: null, firstOrder: 10, secondOrder: 10, enabled: true, editable: false, createdAt: "", updatedAt: "" },
78309
+ { id: "mcp_tools", name: "MCP Tools", displayName: "MCP \u5DE5\u5177\u8AAA\u660E", content: null, firstOrder: 20, secondOrder: 0, enabled: true, editable: true, createdAt: "", updatedAt: "" },
78310
+ { id: "user_context", name: "User Context", displayName: "\u7528\u6236\u4E0A\u4E0B\u6587", content: null, firstOrder: 30, secondOrder: 20, enabled: true, editable: true, createdAt: "", updatedAt: "" },
78311
+ { id: "tool_results", name: "Tool Results", displayName: "\u5DE5\u5177\u57F7\u884C\u7D50\u679C", content: null, firstOrder: 0, secondOrder: 30, enabled: true, editable: false, createdAt: "", updatedAt: "" },
78312
+ { id: "closing", name: "Closing", displayName: "\u7D50\u5C3E\u63D0\u793A", content: null, firstOrder: 40, secondOrder: 40, enabled: true, editable: true, createdAt: "", updatedAt: "" }
78313
+ ];
78314
+ }
78059
78315
  };
78060
78316
  }
78061
78317
  });
@@ -93591,6 +93847,68 @@ var WebServer = class {
93591
93847
  });
93592
93848
  }
93593
93849
  });
93850
+ this.app.get("/api/settings/prompts", (req, res) => {
93851
+ try {
93852
+ const prompts = getPromptConfigs();
93853
+ res.json({ success: true, prompts });
93854
+ } catch (error2) {
93855
+ logger.error("\u7372\u53D6\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557:", error2);
93856
+ res.status(500).json({
93857
+ success: false,
93858
+ error: error2 instanceof Error ? error2.message : "\u7372\u53D6\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557"
93859
+ });
93860
+ }
93861
+ });
93862
+ this.app.put("/api/settings/prompts", (req, res) => {
93863
+ try {
93864
+ const { prompts } = req.body;
93865
+ if (!prompts || !Array.isArray(prompts)) {
93866
+ res.status(400).json({ success: false, error: "prompts \u5FC5\u9808\u662F\u9663\u5217" });
93867
+ return;
93868
+ }
93869
+ for (const prompt of prompts) {
93870
+ if (!prompt.id) {
93871
+ res.status(400).json({ success: false, error: "\u6BCF\u500B\u914D\u7F6E\u5FC5\u9808\u5305\u542B id" });
93872
+ return;
93873
+ }
93874
+ if (prompt.firstOrder !== void 0 && (prompt.firstOrder < 0 || prompt.firstOrder > 1e3)) {
93875
+ res.status(400).json({ success: false, error: "firstOrder \u5FC5\u9808\u5728 0-1000 \u4E4B\u9593" });
93876
+ return;
93877
+ }
93878
+ if (prompt.secondOrder !== void 0 && (prompt.secondOrder < 0 || prompt.secondOrder > 1e3)) {
93879
+ res.status(400).json({ success: false, error: "secondOrder \u5FC5\u9808\u5728 0-1000 \u4E4B\u9593" });
93880
+ return;
93881
+ }
93882
+ }
93883
+ const success = updatePromptConfigs({ prompts });
93884
+ if (success) {
93885
+ const updatedPrompts = getPromptConfigs();
93886
+ logger.info(`\u63D0\u793A\u8A5E\u914D\u7F6E\u5DF2\u66F4\u65B0: ${prompts.length} \u9805`);
93887
+ res.json({ success: true, prompts: updatedPrompts });
93888
+ } else {
93889
+ res.status(500).json({ success: false, error: "\u66F4\u65B0\u5931\u6557" });
93890
+ }
93891
+ } catch (error2) {
93892
+ logger.error("\u66F4\u65B0\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557:", error2);
93893
+ res.status(500).json({
93894
+ success: false,
93895
+ error: error2 instanceof Error ? error2.message : "\u66F4\u65B0\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557"
93896
+ });
93897
+ }
93898
+ });
93899
+ this.app.post("/api/settings/prompts/reset", (req, res) => {
93900
+ try {
93901
+ const prompts = resetPromptConfigs();
93902
+ logger.info("\u63D0\u793A\u8A5E\u914D\u7F6E\u5DF2\u91CD\u7F6E\u70BA\u9810\u8A2D\u503C");
93903
+ res.json({ success: true, prompts });
93904
+ } catch (error2) {
93905
+ logger.error("\u91CD\u7F6E\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557:", error2);
93906
+ res.status(500).json({
93907
+ success: false,
93908
+ error: error2 instanceof Error ? error2.message : "\u91CD\u7F6E\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557"
93909
+ });
93910
+ }
93911
+ });
93594
93912
  this.app.get("/api/logs", (req, res) => {
93595
93913
  try {
93596
93914
  const options = {};
package/dist/index.cjs CHANGED
@@ -20660,6 +20660,7 @@ __export(database_exports, {
20660
20660
  getMCPServerById: () => getMCPServerById,
20661
20661
  getPinnedPrompts: () => getPinnedPrompts,
20662
20662
  getPromptById: () => getPromptById,
20663
+ getPromptConfigs: () => getPromptConfigs,
20663
20664
  getRecentMCPServerErrors: () => getRecentMCPServerErrors,
20664
20665
  getSelfProbeSettings: () => getSelfProbeSettings,
20665
20666
  getToolEnableConfigs: () => getToolEnableConfigs,
@@ -20677,6 +20678,7 @@ __export(database_exports, {
20677
20678
  queryLogs: () => queryLogs,
20678
20679
  queryMCPServerLogs: () => queryMCPServerLogs,
20679
20680
  reorderPrompts: () => reorderPrompts,
20681
+ resetPromptConfigs: () => resetPromptConfigs,
20680
20682
  saveSelfProbeSettings: () => saveSelfProbeSettings,
20681
20683
  setToolEnabled: () => setToolEnabled,
20682
20684
  toggleMCPServerEnabled: () => toggleMCPServerEnabled,
@@ -20687,6 +20689,7 @@ __export(database_exports, {
20687
20689
  updateCLITerminalActivity: () => updateCLITerminalActivity,
20688
20690
  updateMCPServer: () => updateMCPServer,
20689
20691
  updatePrompt: () => updatePrompt,
20692
+ updatePromptConfigs: () => updatePromptConfigs,
20690
20693
  updateUserPreferences: () => updateUserPreferences
20691
20694
  });
20692
20695
  function hashPrompt(prompt) {
@@ -20984,6 +20987,21 @@ function createTables() {
20984
20987
  updated_at TEXT DEFAULT CURRENT_TIMESTAMP
20985
20988
  )
20986
20989
  `);
20990
+ db.exec(`
20991
+ CREATE TABLE IF NOT EXISTS prompt_configs (
20992
+ id TEXT PRIMARY KEY,
20993
+ name TEXT NOT NULL,
20994
+ display_name TEXT NOT NULL,
20995
+ content TEXT,
20996
+ first_order INTEGER DEFAULT 0,
20997
+ second_order INTEGER DEFAULT 0,
20998
+ enabled INTEGER DEFAULT 1,
20999
+ editable INTEGER DEFAULT 1,
21000
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
21001
+ updated_at TEXT DEFAULT CURRENT_TIMESTAMP
21002
+ )
21003
+ `);
21004
+ initDefaultPromptConfigs();
20987
21005
  }
20988
21006
  function initDefaultSettings() {
20989
21007
  if (!db) throw new Error("Database not initialized");
@@ -22378,7 +22396,104 @@ function saveSelfProbeSettings(settings) {
22378
22396
  }
22379
22397
  return getSelfProbeSettings();
22380
22398
  }
22381
- var import_better_sqlite3, import_path2, import_fs2, import_crypto2, DB_DIR, DB_PATH, db, SYSTEM_PROMPT_VERSIONS, CURRENT_PROMPT_VERSION;
22399
+ function initDefaultPromptConfigs() {
22400
+ const db2 = tryGetDb();
22401
+ if (!db2) return;
22402
+ const now = (/* @__PURE__ */ new Date()).toISOString();
22403
+ const existingIds = db2.prepare("SELECT id FROM prompt_configs").all().map((row) => row.id);
22404
+ const stmt = db2.prepare(`
22405
+ INSERT INTO prompt_configs (id, name, display_name, content, first_order, second_order, enabled, editable, created_at, updated_at)
22406
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
22407
+ `);
22408
+ for (const config2 of DEFAULT_PROMPT_CONFIGS) {
22409
+ if (!existingIds.includes(config2.id)) {
22410
+ stmt.run(
22411
+ config2.id,
22412
+ config2.name,
22413
+ config2.displayName,
22414
+ config2.content,
22415
+ config2.firstOrder,
22416
+ config2.secondOrder,
22417
+ config2.enabled ? 1 : 0,
22418
+ config2.editable ? 1 : 0,
22419
+ now,
22420
+ now
22421
+ );
22422
+ logger.info(`Added missing prompt config: ${config2.id}`);
22423
+ }
22424
+ }
22425
+ }
22426
+ function getPromptConfigs() {
22427
+ const db2 = tryGetDb();
22428
+ if (!db2) return [];
22429
+ const rows = db2.prepare(`
22430
+ SELECT id, name, display_name as displayName, content,
22431
+ first_order as firstOrder, second_order as secondOrder,
22432
+ enabled, editable, created_at as createdAt, updated_at as updatedAt
22433
+ FROM prompt_configs
22434
+ ORDER BY first_order ASC
22435
+ `).all();
22436
+ const aiSettings = getAISettings();
22437
+ return rows.map((row) => {
22438
+ let content = row.content;
22439
+ if (row.id === "system_prompt" && !content && aiSettings.systemPrompt) {
22440
+ content = aiSettings.systemPrompt;
22441
+ } else if (row.id === "mcp_tools" && !content && aiSettings.mcpToolsPrompt) {
22442
+ content = aiSettings.mcpToolsPrompt;
22443
+ }
22444
+ return {
22445
+ ...row,
22446
+ content,
22447
+ enabled: row.enabled === 1,
22448
+ editable: row.editable === 1
22449
+ };
22450
+ });
22451
+ }
22452
+ function updatePromptConfigs(request) {
22453
+ const db2 = tryGetDb();
22454
+ if (!db2) return false;
22455
+ const now = (/* @__PURE__ */ new Date()).toISOString();
22456
+ for (const prompt of request.prompts) {
22457
+ const updates = [];
22458
+ const values = [];
22459
+ if (prompt.firstOrder !== void 0) {
22460
+ updates.push("first_order = ?");
22461
+ values.push(prompt.firstOrder);
22462
+ }
22463
+ if (prompt.secondOrder !== void 0) {
22464
+ updates.push("second_order = ?");
22465
+ values.push(prompt.secondOrder);
22466
+ }
22467
+ if (prompt.enabled !== void 0) {
22468
+ updates.push("enabled = ?");
22469
+ values.push(prompt.enabled ? 1 : 0);
22470
+ }
22471
+ if (prompt.content !== void 0) {
22472
+ updates.push("content = ?");
22473
+ values.push(prompt.content);
22474
+ if (prompt.id === "system_prompt") {
22475
+ db2.prepare("UPDATE ai_settings SET system_prompt = ?, updated_at = ? WHERE id = 1").run(prompt.content || "", now);
22476
+ } else if (prompt.id === "mcp_tools") {
22477
+ db2.prepare("UPDATE ai_settings SET mcp_tools_prompt = ?, updated_at = ? WHERE id = 1").run(prompt.content || "", now);
22478
+ }
22479
+ }
22480
+ if (updates.length > 0) {
22481
+ updates.push("updated_at = ?");
22482
+ values.push(now);
22483
+ values.push(prompt.id);
22484
+ db2.prepare(`UPDATE prompt_configs SET ${updates.join(", ")} WHERE id = ?`).run(...values);
22485
+ }
22486
+ }
22487
+ return true;
22488
+ }
22489
+ function resetPromptConfigs() {
22490
+ const db2 = tryGetDb();
22491
+ if (!db2) return [];
22492
+ db2.prepare("DELETE FROM prompt_configs").run();
22493
+ initDefaultPromptConfigs();
22494
+ return getPromptConfigs();
22495
+ }
22496
+ var import_better_sqlite3, import_path2, import_fs2, import_crypto2, DB_DIR, DB_PATH, db, SYSTEM_PROMPT_VERSIONS, CURRENT_PROMPT_VERSION, DEFAULT_PROMPT_CONFIGS;
22382
22497
  var init_database = __esm({
22383
22498
  "src/utils/database.ts"() {
22384
22499
  "use strict";
@@ -22425,6 +22540,14 @@ var init_database = __esm({
22425
22540
  \u4FDD\u6301\u56DE\u61C9\u7C21\u77ED\uFF082-3\u53E5\u8A71\uFF09\uFF0C\u9664\u975E\u9700\u8981\u66F4\u8A73\u7D30\u7684\u8AAA\u660E\u3002`
22426
22541
  };
22427
22542
  CURRENT_PROMPT_VERSION = "v2";
22543
+ DEFAULT_PROMPT_CONFIGS = [
22544
+ { id: "system_prompt", name: "System Prompt", displayName: "\u7CFB\u7D71\u63D0\u793A\u8A5E", content: null, firstOrder: 10, secondOrder: 10, enabled: true, editable: true },
22545
+ { id: "mcp_tools", name: "MCP Tools", displayName: "MCP \u5DE5\u5177\u8AAA\u660E", content: null, firstOrder: 20, secondOrder: 0, enabled: true, editable: true },
22546
+ { id: "mcp_tools_detailed", name: "MCP Tools Detailed", displayName: "MCP \u5DE5\u5177\u8A73\u7D30\u5217\u8868", content: null, firstOrder: 25, secondOrder: 15, enabled: true, editable: false },
22547
+ { id: "user_context", name: "User Context", displayName: "\u7528\u6236\u4E0A\u4E0B\u6587", content: null, firstOrder: 30, secondOrder: 20, enabled: true, editable: false },
22548
+ { id: "tool_results", name: "Tool Results", displayName: "\u5DE5\u5177\u57F7\u884C\u7D50\u679C", content: null, firstOrder: 0, secondOrder: 30, enabled: true, editable: false },
22549
+ { id: "closing", name: "Closing", displayName: "\u7D50\u5C3E\u63D0\u793A", content: null, firstOrder: 100, secondOrder: 100, enabled: true, editable: true }
22550
+ ];
22428
22551
  }
22429
22552
  });
22430
22553
 
@@ -74680,6 +74803,107 @@ ${mcpPrompt}`;
74680
74803
  }
74681
74804
  });
74682
74805
 
74806
+ // src/utils/prompt-aggregator/components/mcp-tools-detailed.ts
74807
+ var MCPToolsDetailedComponent;
74808
+ var init_mcp_tools_detailed = __esm({
74809
+ "src/utils/prompt-aggregator/components/mcp-tools-detailed.ts"() {
74810
+ "use strict";
74811
+ init_cjs_shims();
74812
+ init_base_component();
74813
+ init_mcp_client_manager();
74814
+ MCPToolsDetailedComponent = class extends BasePromptComponent {
74815
+ constructor() {
74816
+ super("MCPToolsDetailed", 25);
74817
+ }
74818
+ build(context) {
74819
+ const { request } = context;
74820
+ if (!request.includeMCPTools) {
74821
+ return null;
74822
+ }
74823
+ try {
74824
+ const allTools = mcpClientManager.getAllTools();
74825
+ if (!allTools || allTools.length === 0) {
74826
+ return null;
74827
+ }
74828
+ let result = "## MCP \u5DE5\u5177\u8A73\u7D30\u4F7F\u7528\u8AAA\u660E\n\n";
74829
+ result += "\u4EE5\u4E0B\u662F\u6240\u6709\u53EF\u7528\u7684 MCP \u5DE5\u5177\u53CA\u5176\u8A73\u7D30\u4F7F\u7528\u65B9\u5F0F\uFF1A\n\n";
74830
+ for (const tool of allTools) {
74831
+ result += `### ${tool.name}
74832
+
74833
+ `;
74834
+ if (tool.description) {
74835
+ result += `**\u8AAA\u660E**: ${tool.description}
74836
+
74837
+ `;
74838
+ }
74839
+ if (tool.inputSchema) {
74840
+ result += "**\u53C3\u6578\u683C\u5F0F**:\n```json\n";
74841
+ result += JSON.stringify(tool.inputSchema, null, 2);
74842
+ result += "\n```\n\n";
74843
+ const schema = tool.inputSchema;
74844
+ if (schema.properties && typeof schema.properties === "object") {
74845
+ result += "**\u53C3\u6578\u8AAA\u660E**:\n";
74846
+ const props = schema.properties;
74847
+ const required2 = schema.required || [];
74848
+ for (const [propName, propDef] of Object.entries(props)) {
74849
+ const isRequired = required2.includes(propName);
74850
+ result += `- \`${propName}\``;
74851
+ if (propDef.type) result += ` (${propDef.type})`;
74852
+ if (isRequired) result += " **\u5FC5\u586B**";
74853
+ if (propDef.description) result += `: ${propDef.description}`;
74854
+ result += "\n";
74855
+ }
74856
+ result += "\n";
74857
+ }
74858
+ }
74859
+ result += "**\u8ABF\u7528\u7BC4\u4F8B**:\n```json\n";
74860
+ result += JSON.stringify({
74861
+ tool_calls: [{
74862
+ name: tool.name,
74863
+ arguments: this.generateExampleArgs(tool.inputSchema)
74864
+ }]
74865
+ }, null, 2);
74866
+ result += "\n```\n\n---\n\n";
74867
+ }
74868
+ return result;
74869
+ } catch {
74870
+ return null;
74871
+ }
74872
+ }
74873
+ generateExampleArgs(schema) {
74874
+ if (!schema || !schema.properties) {
74875
+ return {};
74876
+ }
74877
+ const result = {};
74878
+ const props = schema.properties;
74879
+ for (const [propName, propDef] of Object.entries(props)) {
74880
+ switch (propDef.type) {
74881
+ case "string":
74882
+ result[propName] = "<\u5B57\u4E32\u503C>";
74883
+ break;
74884
+ case "number":
74885
+ case "integer":
74886
+ result[propName] = 0;
74887
+ break;
74888
+ case "boolean":
74889
+ result[propName] = true;
74890
+ break;
74891
+ case "array":
74892
+ result[propName] = [];
74893
+ break;
74894
+ case "object":
74895
+ result[propName] = {};
74896
+ break;
74897
+ default:
74898
+ result[propName] = "<\u503C>";
74899
+ }
74900
+ }
74901
+ return result;
74902
+ }
74903
+ };
74904
+ }
74905
+ });
74906
+
74683
74907
  // src/utils/prompt-aggregator/components/user-context.ts
74684
74908
  var UserContextComponent;
74685
74909
  var init_user_context = __esm({
@@ -74792,6 +75016,7 @@ var init_components = __esm({
74792
75016
  init_system_prompt();
74793
75017
  init_pinned_prompts();
74794
75018
  init_mcp_tools();
75019
+ init_mcp_tools_detailed();
74795
75020
  init_user_context();
74796
75021
  init_tool_results();
74797
75022
  init_ai_message();
@@ -74803,7 +75028,7 @@ var init_components = __esm({
74803
75028
  function getPromptAggregator() {
74804
75029
  return PromptAggregator.getInstance();
74805
75030
  }
74806
- var PromptAggregator;
75031
+ var COMPONENT_NAME_MAP, PromptAggregator;
74807
75032
  var init_prompt_aggregator = __esm({
74808
75033
  "src/utils/prompt-aggregator/prompt-aggregator.ts"() {
74809
75034
  "use strict";
@@ -74812,6 +75037,14 @@ var init_prompt_aggregator = __esm({
74812
75037
  init_database();
74813
75038
  init_mcp_client_manager();
74814
75039
  init_logger();
75040
+ COMPONENT_NAME_MAP = {
75041
+ "system_prompt": "SystemPrompt",
75042
+ "mcp_tools": "MCPToolsPrompt",
75043
+ "mcp_tools_detailed": "MCPToolsDetailed",
75044
+ "user_context": "UserContext",
75045
+ "tool_results": "ToolResults",
75046
+ "closing": "ClosingPrompt"
75047
+ };
74815
75048
  PromptAggregator = class _PromptAggregator {
74816
75049
  static instance;
74817
75050
  components = [];
@@ -74830,6 +75063,7 @@ var init_prompt_aggregator = __esm({
74830
75063
  this.register(new SystemPromptComponent());
74831
75064
  this.register(new PinnedPromptsComponent());
74832
75065
  this.register(new MCPToolsPromptComponent());
75066
+ this.register(new MCPToolsDetailedComponent());
74833
75067
  this.register(new UserContextComponent());
74834
75068
  this.register(new ToolResultsComponent());
74835
75069
  this.register(new AIMessageComponent());
@@ -74845,14 +75079,21 @@ var init_prompt_aggregator = __esm({
74845
75079
  aggregate(context) {
74846
75080
  const sections = [];
74847
75081
  const promptParts = [];
74848
- for (const component of this.components) {
75082
+ const configs = this.getPromptConfigsWithDefaults();
75083
+ const isFirstCall = context.isFirstCall !== false;
75084
+ const configuredComponents = this.components.map((component) => {
75085
+ const configId = Object.entries(COMPONENT_NAME_MAP).find(
75086
+ ([, name]) => name === component.getName()
75087
+ )?.[0];
75088
+ const config2 = configId ? configs.find((c) => c.id === configId) : null;
75089
+ const order = config2 ? isFirstCall ? config2.firstOrder : config2.secondOrder : component.getOrder();
75090
+ const enabled = config2 ? config2.enabled : true;
75091
+ return { component, order, enabled };
75092
+ }).filter((item) => item.enabled && item.order > 0).sort((a, b) => a.order - b.order);
75093
+ for (const { component, order } of configuredComponents) {
74849
75094
  const content = component.build(context);
74850
75095
  if (content) {
74851
- sections.push({
74852
- name: component.getName(),
74853
- content,
74854
- order: component.getOrder()
74855
- });
75096
+ sections.push({ name: component.getName(), content, order });
74856
75097
  promptParts.push(content);
74857
75098
  }
74858
75099
  }
@@ -74943,6 +75184,21 @@ var init_prompt_aggregator = __esm({
74943
75184
  getComponentNames() {
74944
75185
  return this.components.map((c) => c.getName());
74945
75186
  }
75187
+ getPromptConfigsWithDefaults() {
75188
+ try {
75189
+ const configs = getPromptConfigs();
75190
+ if (configs.length > 0) return configs;
75191
+ } catch {
75192
+ logger.warn("[PromptAggregator] \u7121\u6CD5\u5F9E\u8CC7\u6599\u5EAB\u7372\u53D6\u63D0\u793A\u8A5E\u914D\u7F6E\uFF0C\u4F7F\u7528\u9810\u8A2D\u503C");
75193
+ }
75194
+ return [
75195
+ { id: "system_prompt", name: "System Prompt", displayName: "\u7CFB\u7D71\u63D0\u793A\u8A5E", content: null, firstOrder: 10, secondOrder: 10, enabled: true, editable: false, createdAt: "", updatedAt: "" },
75196
+ { id: "mcp_tools", name: "MCP Tools", displayName: "MCP \u5DE5\u5177\u8AAA\u660E", content: null, firstOrder: 20, secondOrder: 0, enabled: true, editable: true, createdAt: "", updatedAt: "" },
75197
+ { id: "user_context", name: "User Context", displayName: "\u7528\u6236\u4E0A\u4E0B\u6587", content: null, firstOrder: 30, secondOrder: 20, enabled: true, editable: true, createdAt: "", updatedAt: "" },
75198
+ { id: "tool_results", name: "Tool Results", displayName: "\u5DE5\u5177\u57F7\u884C\u7D50\u679C", content: null, firstOrder: 0, secondOrder: 30, enabled: true, editable: false, createdAt: "", updatedAt: "" },
75199
+ { id: "closing", name: "Closing", displayName: "\u7D50\u5C3E\u63D0\u793A", content: null, firstOrder: 40, secondOrder: 40, enabled: true, editable: true, createdAt: "", updatedAt: "" }
75200
+ ];
75201
+ }
74946
75202
  };
74947
75203
  }
74948
75204
  });
@@ -90606,6 +90862,68 @@ var WebServer = class {
90606
90862
  });
90607
90863
  }
90608
90864
  });
90865
+ this.app.get("/api/settings/prompts", (req, res) => {
90866
+ try {
90867
+ const prompts = getPromptConfigs();
90868
+ res.json({ success: true, prompts });
90869
+ } catch (error2) {
90870
+ logger.error("\u7372\u53D6\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557:", error2);
90871
+ res.status(500).json({
90872
+ success: false,
90873
+ error: error2 instanceof Error ? error2.message : "\u7372\u53D6\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557"
90874
+ });
90875
+ }
90876
+ });
90877
+ this.app.put("/api/settings/prompts", (req, res) => {
90878
+ try {
90879
+ const { prompts } = req.body;
90880
+ if (!prompts || !Array.isArray(prompts)) {
90881
+ res.status(400).json({ success: false, error: "prompts \u5FC5\u9808\u662F\u9663\u5217" });
90882
+ return;
90883
+ }
90884
+ for (const prompt of prompts) {
90885
+ if (!prompt.id) {
90886
+ res.status(400).json({ success: false, error: "\u6BCF\u500B\u914D\u7F6E\u5FC5\u9808\u5305\u542B id" });
90887
+ return;
90888
+ }
90889
+ if (prompt.firstOrder !== void 0 && (prompt.firstOrder < 0 || prompt.firstOrder > 1e3)) {
90890
+ res.status(400).json({ success: false, error: "firstOrder \u5FC5\u9808\u5728 0-1000 \u4E4B\u9593" });
90891
+ return;
90892
+ }
90893
+ if (prompt.secondOrder !== void 0 && (prompt.secondOrder < 0 || prompt.secondOrder > 1e3)) {
90894
+ res.status(400).json({ success: false, error: "secondOrder \u5FC5\u9808\u5728 0-1000 \u4E4B\u9593" });
90895
+ return;
90896
+ }
90897
+ }
90898
+ const success = updatePromptConfigs({ prompts });
90899
+ if (success) {
90900
+ const updatedPrompts = getPromptConfigs();
90901
+ logger.info(`\u63D0\u793A\u8A5E\u914D\u7F6E\u5DF2\u66F4\u65B0: ${prompts.length} \u9805`);
90902
+ res.json({ success: true, prompts: updatedPrompts });
90903
+ } else {
90904
+ res.status(500).json({ success: false, error: "\u66F4\u65B0\u5931\u6557" });
90905
+ }
90906
+ } catch (error2) {
90907
+ logger.error("\u66F4\u65B0\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557:", error2);
90908
+ res.status(500).json({
90909
+ success: false,
90910
+ error: error2 instanceof Error ? error2.message : "\u66F4\u65B0\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557"
90911
+ });
90912
+ }
90913
+ });
90914
+ this.app.post("/api/settings/prompts/reset", (req, res) => {
90915
+ try {
90916
+ const prompts = resetPromptConfigs();
90917
+ logger.info("\u63D0\u793A\u8A5E\u914D\u7F6E\u5DF2\u91CD\u7F6E\u70BA\u9810\u8A2D\u503C");
90918
+ res.json({ success: true, prompts });
90919
+ } catch (error2) {
90920
+ logger.error("\u91CD\u7F6E\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557:", error2);
90921
+ res.status(500).json({
90922
+ success: false,
90923
+ error: error2 instanceof Error ? error2.message : "\u91CD\u7F6E\u63D0\u793A\u8A5E\u914D\u7F6E\u5931\u6557"
90924
+ });
90925
+ }
90926
+ });
90609
90927
  this.app.get("/api/logs", (req, res) => {
90610
90928
  try {
90611
90929
  const options = {};
@@ -53,9 +53,7 @@
53
53
 
54
54
  .logs-table-container {
55
55
  background: var(--bg-primary);
56
- border: 1px solid var(--border-color);
57
56
  border-radius: var(--radius-md);
58
- overflow: hidden;
59
57
  }
60
58
 
61
59
  .logs-table {
@@ -178,15 +176,17 @@
178
176
  display: flex;
179
177
  flex-direction: column;
180
178
  min-height: 0;
181
- overflow: hidden;
179
+ overflow: visible;
182
180
  }
183
181
 
184
182
  .logs-table-wrapper {
185
183
  flex: 1;
186
184
  overflow-y: auto;
185
+ overflow-x: hidden;
187
186
  border: 1px solid var(--border-color);
188
187
  border-radius: 8px;
189
188
  margin-bottom: 16px;
189
+ max-height: calc(100vh - 350px);
190
190
  }
191
191
 
192
192
  .logs-table-wrapper::-webkit-scrollbar {
@@ -187,6 +187,22 @@
187
187
  <option value="openai">OpenAI</option>
188
188
  <option value="anthropic">Anthropic (Claude)</option>
189
189
  <option value="google">Google (Gemini)</option>
190
+ <option value="nvidia">NVIDIA</option>
191
+ <option value="zai">Z.AI (Zhipu AI)</option>
192
+ </select>
193
+ </div>
194
+
195
+ <!-- 擴展提供商專用設定 -->
196
+ <div id="nvidiaExtSettings" class="form-group" style="display: none;">
197
+ <label class="form-label">API Endpoint</label>
198
+ <input type="url" class="form-input" value="https://integrate.api.nvidia.com/v1" readonly>
199
+ </div>
200
+
201
+ <div id="zaiExtSettings" class="form-group" style="display: none;">
202
+ <label class="form-label" for="zaiRegion">地區</label>
203
+ <select id="zaiRegion" class="form-select">
204
+ <option value="international">國際版 (api.z.ai)</option>
205
+ <option value="china">中國版 (bigmodel.cn)</option>
190
206
  </select>
191
207
  </div>
192
208
 
@@ -204,17 +220,6 @@
204
220
  <input type="text" id="aiModel" class="form-input" placeholder="例如: gpt-4-turbo, claude-3-opus">
205
221
  </div>
206
222
 
207
- <div class="form-group">
208
- <label class="form-label" for="systemPrompt">系統提示詞</label>
209
- <textarea id="systemPrompt" class="form-textarea" placeholder="自定義系統提示詞..."></textarea>
210
- <p class="form-help">留空將使用預設提示詞</p>
211
- </div>
212
-
213
- <div class="form-group">
214
- <label class="form-label" for="mcpToolsPrompt">MCP 工具提示詞</label>
215
- <textarea id="mcpToolsPrompt" class="form-textarea" placeholder="設定 MCP 工具的使用說明..."></textarea>
216
- <p class="form-help">定義 AI 如何使用 MCP 工具。{project_name} 和 {project_path} 會被替換為實際值</p>
217
- </div>
218
223
 
219
224
  <div class="form-row" style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
220
225
  <div class="form-group">
@@ -378,6 +383,27 @@
378
383
  <button id="saveSelfProbeBtn" class="btn btn-primary">儲存設定</button>
379
384
  </div>
380
385
  </section>
386
+
387
+ <!-- AI 提示詞設定 -->
388
+ <section class="settings-section">
389
+ <h2 class="section-title">
390
+ <span class="icon">📝</span>
391
+ AI 提示詞設定
392
+ </h2>
393
+ <p class="form-help" style="margin-bottom: 16px;">
394
+ 自定義 AI 回覆時使用的提示詞順序。第一次順序用於首次呼叫,第二次順序用於後續呼叫。順序為 0 表示不使用該組件。
395
+ </p>
396
+
397
+ <div id="promptConfigList" style="display: flex; flex-direction: column; gap: 16px;">
398
+ <div style="text-align: center; padding: 20px; color: var(--text-muted);">正在載入...</div>
399
+ </div>
400
+
401
+ <div class="form-actions" style="margin-top: 16px;">
402
+ <button id="resetPromptsBtn" class="btn btn-secondary">恢復預設</button>
403
+ <button id="savePromptsBtn" class="btn btn-primary">儲存提示詞設定</button>
404
+ </div>
405
+ </section>
406
+
381
407
  </div>
382
408
 
383
409
  <!-- Toast Container -->
@@ -37,8 +37,6 @@
37
37
  apiKey: document.getElementById("apiKey"),
38
38
  toggleApiKey: document.getElementById("toggleApiKey"),
39
39
  aiModel: document.getElementById("aiModel"),
40
- systemPrompt: document.getElementById("systemPrompt"),
41
- mcpToolsPrompt: document.getElementById("mcpToolsPrompt"),
42
40
  temperature: document.getElementById("temperature"),
43
41
  maxTokens: document.getElementById("maxTokens"),
44
42
  autoReplyTimerSeconds: document.getElementById("autoReplyTimerSeconds"),
@@ -72,6 +70,14 @@
72
70
  selfProbeCount: document.getElementById("selfProbeCount"),
73
71
  selfProbeLastTime: document.getElementById("selfProbeLastTime"),
74
72
  saveSelfProbeBtn: document.getElementById("saveSelfProbeBtn"),
73
+ // Prompt Config Settings
74
+ promptConfigList: document.getElementById("promptConfigList"),
75
+ resetPromptsBtn: document.getElementById("resetPromptsBtn"),
76
+ savePromptsBtn: document.getElementById("savePromptsBtn"),
77
+ // Extended Provider Settings (integrated into AI settings)
78
+ nvidiaExtSettings: document.getElementById("nvidiaExtSettings"),
79
+ zaiExtSettings: document.getElementById("zaiExtSettings"),
80
+ zaiRegion: document.getElementById("zaiRegion"),
75
81
  toastContainer: document.getElementById("toastContainer"),
76
82
  };
77
83
 
@@ -86,6 +92,7 @@
86
92
  loadCLISettings();
87
93
  loadPreferences();
88
94
  loadSelfProbeSettings();
95
+ loadPromptConfigs();
89
96
  }
90
97
 
91
98
  function setupEventListeners() {
@@ -93,6 +100,9 @@
93
100
  elements.toggleApiKey.addEventListener("click", toggleApiKeyVisibility);
94
101
  elements.testAiBtn.addEventListener("click", testAIConnection);
95
102
  elements.saveAiBtn.addEventListener("click", saveAISettings);
103
+ if (elements.aiProvider) {
104
+ elements.aiProvider.addEventListener("change", handleAIProviderChange);
105
+ }
96
106
 
97
107
  // CLI Settings
98
108
  elements.aiModeApi.addEventListener("change", handleAIModeChange);
@@ -110,6 +120,33 @@
110
120
  if (elements.saveSelfProbeBtn) {
111
121
  elements.saveSelfProbeBtn.addEventListener("click", saveSelfProbeSettings);
112
122
  }
123
+
124
+ // Prompt Config Settings
125
+ if (elements.resetPromptsBtn) {
126
+ elements.resetPromptsBtn.addEventListener("click", resetPromptConfigs);
127
+ }
128
+ if (elements.savePromptsBtn) {
129
+ elements.savePromptsBtn.addEventListener("click", savePromptConfigs);
130
+ }
131
+ }
132
+
133
+ function handleAIProviderChange() {
134
+ const provider = elements.aiProvider?.value || 'google';
135
+
136
+ // 隱藏所有擴展設定
137
+ if (elements.nvidiaExtSettings) {
138
+ elements.nvidiaExtSettings.style.display = 'none';
139
+ }
140
+ if (elements.zaiExtSettings) {
141
+ elements.zaiExtSettings.style.display = 'none';
142
+ }
143
+
144
+ // 顯示對應的擴展設定
145
+ if (provider === 'nvidia' && elements.nvidiaExtSettings) {
146
+ elements.nvidiaExtSettings.style.display = 'block';
147
+ } else if (provider === 'zai' && elements.zaiExtSettings) {
148
+ elements.zaiExtSettings.style.display = 'block';
149
+ }
113
150
  }
114
151
 
115
152
  function handleSelfProbeToggle() {
@@ -150,8 +187,6 @@
150
187
  elements.apiKey.value = originalApiKeyMasked;
151
188
  elements.apiKey.placeholder = originalApiKeyMasked ? "輸入新的 API Key 以更換" : "請輸入 API Key";
152
189
  elements.aiModel.value = data.settings.model || "";
153
- elements.systemPrompt.value = data.settings.systemPrompt || "";
154
- elements.mcpToolsPrompt.value = data.settings.mcpToolsPrompt || "";
155
190
  elements.temperature.value = data.settings.temperature ?? 0.7;
156
191
  elements.maxTokens.value = data.settings.maxTokens ?? 1000;
157
192
  elements.autoReplyTimerSeconds.value = data.settings.autoReplyTimerSeconds ?? 300;
@@ -347,8 +382,6 @@
347
382
  const settings = {
348
383
  apiUrl: getApiUrlFromProvider(provider),
349
384
  model: elements.aiModel.value,
350
- systemPrompt: elements.systemPrompt.value,
351
- mcpToolsPrompt: elements.mcpToolsPrompt.value,
352
385
  temperature: parseFloat(elements.temperature.value) || 0.7,
353
386
  maxTokens: parseInt(elements.maxTokens.value) || 1000,
354
387
  autoReplyTimerSeconds: parseInt(elements.autoReplyTimerSeconds.value) || 300,
@@ -515,6 +548,135 @@
515
548
  }
516
549
  }
517
550
 
551
+ // ============ Prompt Config Functions ============
552
+
553
+ let promptConfigs = [];
554
+
555
+ async function loadPromptConfigs() {
556
+ if (!elements.promptConfigList) return;
557
+
558
+ try {
559
+ const response = await fetch(`${API_BASE}/api/settings/prompts`);
560
+ const data = await response.json();
561
+
562
+ if (data.success && data.prompts) {
563
+ promptConfigs = data.prompts;
564
+ renderPromptConfigs();
565
+ } else {
566
+ elements.promptConfigList.innerHTML = '<div style="text-align: center; padding: 20px; color: var(--text-muted);">無法載入配置</div>';
567
+ }
568
+ } catch (error) {
569
+ console.error("Load prompt configs failed:", error);
570
+ elements.promptConfigList.innerHTML = '<div style="text-align: center; padding: 20px; color: var(--text-muted);">載入失敗</div>';
571
+ }
572
+ }
573
+
574
+ function renderPromptConfigs() {
575
+ if (!elements.promptConfigList || !promptConfigs.length) return;
576
+
577
+ const showEditor = (id) => id !== 'user_context' && id !== 'tool_results';
578
+
579
+ elements.promptConfigList.innerHTML = promptConfigs.map(config => `
580
+ <div class="prompt-config-item" data-id="${config.id}" style="background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: var(--radius-sm); padding: 16px;">
581
+ <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-bottom: ${showEditor(config.id) ? '12px' : '0'};">
582
+ <span style="font-weight: 600; color: var(--text-primary); font-size: 14px;">${config.displayName}</span>
583
+ <div style="display: flex; align-items: center; gap: 16px; flex-wrap: wrap;">
584
+ <label style="display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-secondary);">
585
+ 第一次:
586
+ <input type="number" class="first-order form-input" value="${config.firstOrder}" min="0" max="1000" step="10" style="width: 60px; padding: 4px 8px;">
587
+ </label>
588
+ <label style="display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-secondary);">
589
+ 第二次:
590
+ <input type="number" class="second-order form-input" value="${config.secondOrder}" min="0" max="1000" step="10" style="width: 60px; padding: 4px 8px;">
591
+ </label>
592
+ <label style="display: flex; align-items: center; gap: 6px; font-size: 13px;">
593
+ <input type="checkbox" class="prompt-enabled" ${config.enabled ? 'checked' : ''}>
594
+ 啟用
595
+ </label>
596
+ </div>
597
+ </div>
598
+ ${showEditor(config.id) ? `
599
+ <textarea class="prompt-content form-textarea" style="min-height: 100px;">${config.content || ''}</textarea>
600
+ ` : ''}
601
+ </div>
602
+ `).join('');
603
+ }
604
+
605
+ async function savePromptConfigs() {
606
+ if (!elements.savePromptsBtn) return;
607
+
608
+ elements.savePromptsBtn.disabled = true;
609
+ elements.savePromptsBtn.textContent = "儲存中...";
610
+
611
+ try {
612
+ const items = document.querySelectorAll('.prompt-config-item');
613
+ const updates = [];
614
+
615
+ items.forEach(item => {
616
+ const id = item.dataset.id;
617
+ const firstOrder = parseInt(item.querySelector('.first-order').value) || 0;
618
+ const secondOrder = parseInt(item.querySelector('.second-order').value) || 0;
619
+ const enabled = item.querySelector('.prompt-enabled').checked;
620
+ const contentEl = item.querySelector('.prompt-content');
621
+ const content = contentEl ? contentEl.value || null : null;
622
+
623
+ updates.push({ id, firstOrder, secondOrder, enabled, content });
624
+ });
625
+
626
+ const response = await fetch(`${API_BASE}/api/settings/prompts`, {
627
+ method: 'PUT',
628
+ headers: { 'Content-Type': 'application/json' },
629
+ body: JSON.stringify({ prompts: updates })
630
+ });
631
+
632
+ const data = await response.json();
633
+
634
+ if (data.success) {
635
+ showToast("提示詞配置已儲存", "success");
636
+ if (data.prompts) {
637
+ promptConfigs = data.prompts;
638
+ }
639
+ } else {
640
+ showToast(`儲存失敗: ${data.error || "未知錯誤"}`, "error");
641
+ }
642
+ } catch (error) {
643
+ console.error("Save prompt configs failed:", error);
644
+ showToast("儲存失敗", "error");
645
+ } finally {
646
+ elements.savePromptsBtn.disabled = false;
647
+ elements.savePromptsBtn.textContent = "儲存提示詞設定";
648
+ }
649
+ }
650
+
651
+ async function resetPromptConfigs() {
652
+ if (!confirm("確定要重置為預設配置?")) return;
653
+ if (!elements.resetPromptsBtn) return;
654
+
655
+ elements.resetPromptsBtn.disabled = true;
656
+ elements.resetPromptsBtn.textContent = "重置中...";
657
+
658
+ try {
659
+ const response = await fetch(`${API_BASE}/api/settings/prompts/reset`, { method: 'POST' });
660
+ const data = await response.json();
661
+
662
+ if (data.success) {
663
+ showToast("已重置為預設配置", "success");
664
+ if (data.prompts) {
665
+ promptConfigs = data.prompts;
666
+ renderPromptConfigs();
667
+ }
668
+ } else {
669
+ showToast(`重置失敗: ${data.error || "未知錯誤"}`, "error");
670
+ }
671
+ } catch (error) {
672
+ console.error("Reset prompt configs failed:", error);
673
+ showToast("重置失敗", "error");
674
+ } finally {
675
+ elements.resetPromptsBtn.disabled = false;
676
+ elements.resetPromptsBtn.textContent = "恢復預設";
677
+ }
678
+ }
679
+
518
680
  function showToast(message, type = "info") {
519
681
  const toast = document.createElement("div");
520
682
  toast.className = `toast toast-${type}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hirohsu/user-web-feedback",
3
- "version": "2.8.1",
3
+ "version": "2.8.2",
4
4
  "description": "基於Node.js的MCP回饋收集器 - 支持AI工作彙報和用戶回饋收集",
5
5
  "main": "dist/index.cjs",
6
6
  "bin": {
@@ -45,6 +45,7 @@
45
45
  "prepublishOnly": "npm run clean && npm run build && node scripts/remove-sourcemaps.cjs"
46
46
  },
47
47
  "dependencies": {
48
+ "@hirohsu/user-web-feedback": "^2.8.1",
48
49
  "better-sqlite3": "^12.4.1"
49
50
  },
50
51
  "optionalDependencies": {