yingclaw 2.3.0 → 2.4.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 CHANGED
@@ -72,6 +72,7 @@ claw config # 配置 API 连接
72
72
  claw code # 接入 Claude Code 终端
73
73
  claw desktop # 接入 Claude 桌面应用
74
74
  claw switch # 快速切换厂商或模型
75
+ claw prompt # 配置系统提示词(按模型独立保存)
75
76
  claw status # 查看当前配置,验证 Key 是否有效
76
77
  claw update # 检查并升级到最新版本
77
78
 
package/bin/cli.js CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const { Command } = require('commander');
4
- const { select, input, confirm } = require('@inquirer/prompts');
4
+ const { select, input, confirm, editor } = require('@inquirer/prompts');
5
5
  const {
6
6
  loadConfig,
7
7
  saveConfig,
8
+ getSystemPrompt,
9
+ setSystemPrompt,
8
10
  writeEnvToZshrc,
9
11
  clearClaudeCodeEnv,
10
12
  fetchModels,
@@ -354,7 +356,7 @@ async function runConfigFlow({ writeCodeEnv = false } = {}) {
354
356
  cfg = customConfig || { provider: providerKey, model, fastModel, apiKey, baseUrl: provider.baseUrl, availableModels };
355
357
  saveConfig(cfg);
356
358
  if (writeCodeEnv) {
357
- ({ file } = writeEnvToZshrc(cfg.baseUrl, cfg.apiKey, cfg.model, cfg.fastModel));
359
+ ({ file } = writeEnvToZshrc(cfg.baseUrl, cfg.apiKey, cfg.model, cfg.fastModel, { systemPrompt: getSystemPrompt(cfg) }));
358
360
  }
359
361
  spinner.succeed(chalk.green(writeCodeEnv ? 'API 连接已保存,Claude Code 终端已接入' : 'API 连接已保存'));
360
362
  } catch (e) {
@@ -482,7 +484,7 @@ program
482
484
  const spinner = ora('写入 Claude Code 终端环境变量...').start();
483
485
  let file;
484
486
  try {
485
- ({ file } = writeEnvToZshrc(config.baseUrl, config.apiKey, config.model, config.fastModel));
487
+ ({ file } = writeEnvToZshrc(config.baseUrl, config.apiKey, config.model, config.fastModel, { systemPrompt: getSystemPrompt(config) }));
486
488
  spinner.succeed(chalk.green(`Claude Code 终端已接入 → ${file}`));
487
489
  } catch (e) {
488
490
  spinner.fail(chalk.red(`写入失败: ${e.message}`));
@@ -633,6 +635,82 @@ program
633
635
  await offerDesktopSync(chalk, ora, newConfig);
634
636
  });
635
637
 
638
+ program
639
+ .command('prompt')
640
+ .description('配置系统提示词')
641
+ .action(async () => {
642
+ const chalk = (await import('chalk')).default;
643
+ const ora = (await import('ora')).default;
644
+ const boxen = (await import('boxen')).default;
645
+
646
+ console.log(await getBanner());
647
+
648
+ const config = loadConfig();
649
+ if (!config) {
650
+ console.log(chalk.red('\n未配置 API 连接,请先运行: claw config\n'));
651
+ return;
652
+ }
653
+
654
+ const provider = PROVIDERS[config.provider];
655
+ const providerName = config.providerName || provider?.name || config.provider;
656
+ const currentPrompt = getSystemPrompt(config);
657
+
658
+ console.log(chalk.dim(` 当前模型:${providerName} · ${config.model}\n`));
659
+
660
+ if (currentPrompt) {
661
+ const preview = currentPrompt.length > 300 ? currentPrompt.slice(0, 300) + '\n...' : currentPrompt;
662
+ console.log(boxen(
663
+ chalk.bold('当前系统提示词\n\n') + chalk.dim(preview),
664
+ { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'cyan', margin: { top: 1, bottom: 1 } }
665
+ ));
666
+ } else {
667
+ console.log(chalk.dim(' 当前模型未配置系统提示词\n'));
668
+ }
669
+
670
+ const action = await select({ loop: false,
671
+ message: chalk.cyan('操作'),
672
+ choices: [
673
+ { name: currentPrompt ? '✏️ 编辑系统提示词' : '✏️ 设置系统提示词', value: 'edit' },
674
+ ...(currentPrompt ? [{ name: '🗑 清除当前模型提示词', value: 'clear' }] : []),
675
+ { name: chalk.dim('↩ 返回'), value: '__BACK__' },
676
+ ],
677
+ });
678
+
679
+ if (action === '__BACK__') return;
680
+
681
+ if (action === 'clear') {
682
+ const yes = await confirm({ message: '确定清除该模型的系统提示词?', default: false });
683
+ if (!yes) { console.log(chalk.dim('已取消')); return; }
684
+ const spinner = ora('清除中...').start();
685
+ setSystemPrompt(config, null);
686
+ saveConfig(config);
687
+ spinner.succeed(chalk.green('系统提示词已清除'));
688
+ return;
689
+ }
690
+
691
+ const newPrompt = await editor({
692
+ message: chalk.cyan('编辑系统提示词(保存并关闭编辑器后生效)'),
693
+ default: currentPrompt || '',
694
+ waitForUseInput: false,
695
+ });
696
+
697
+ if (!newPrompt || !newPrompt.trim()) {
698
+ console.log(chalk.dim('内容为空,已取消'));
699
+ return;
700
+ }
701
+
702
+ const spinner = ora('保存中...').start();
703
+ setSystemPrompt(config, newPrompt);
704
+ saveConfig(config);
705
+ spinner.succeed(chalk.green('系统提示词已保存'));
706
+
707
+ console.log(boxen(
708
+ chalk.dim('• 通过菜单"启动 Claude Code"时自动应用\n') +
709
+ chalk.dim('• 终端直接使用 claude 命令请重新运行 ') + chalk.cyan('claw code'),
710
+ { padding: { top: 0, bottom: 0, left: 2, right: 2 }, borderStyle: 'round', borderColor: 'green', margin: { top: 1, bottom: 1 } }
711
+ ));
712
+ });
713
+
636
714
  program
637
715
  .command('status')
638
716
  .description('查看当前配置和 Key 有效性')
@@ -1065,6 +1143,7 @@ async function runMenu() {
1065
1143
  { name: '📦 安装 Claude Code', value: 'install' },
1066
1144
  { name: config ? '🔑 重新配置 API 连接' : '🔑 配置 API 连接', value: 'config' },
1067
1145
  { name: '🔄 切换厂商或模型', value: 'switch', disabled: disabledHint },
1146
+ { name: getSystemPrompt(config) ? '📝 系统提示词(已配置)' : '📝 配置系统提示词', value: 'prompt', disabled: !config && '需先配置 API 连接' },
1068
1147
  { name: '💻 接入 Claude Code 终端', value: 'code', disabled: disabledHint },
1069
1148
  { name: '🖥 接入 Claude 桌面应用', value: 'desktop', disabled: disabledHint },
1070
1149
  { name: '📊 查看当前配置', value: 'status', disabled: !config && '需先配置 API 连接' },
@@ -1091,8 +1170,9 @@ async function runMenu() {
1091
1170
  if (resolvedAction === 'launch') {
1092
1171
  const cfg = loadConfig();
1093
1172
  if (!cfg || getConfigValidationMessage(cfg)) continue;
1173
+ const launchArgs = getSystemPrompt(cfg) ? ['--append-system-prompt', getSystemPrompt(cfg)] : [];
1094
1174
  await new Promise((resolve) => {
1095
- const child = spawn('claude', [], {
1175
+ const child = spawn('claude', launchArgs, {
1096
1176
  stdio: 'inherit',
1097
1177
  env: { ...process.env, ...buildClaudeEnv(cfg) },
1098
1178
  shell: process.platform === 'win32',
@@ -1112,6 +1192,7 @@ async function runMenu() {
1112
1192
  code: 'code',
1113
1193
  'code-reset': 'code-reset',
1114
1194
  switch: 'switch',
1195
+ prompt: 'prompt',
1115
1196
  desktop: 'desktop',
1116
1197
  'desktop-reset': 'desktop-reset',
1117
1198
  status: 'status',
package/lib/config.js CHANGED
@@ -128,6 +128,26 @@ function normalizeAnthropicBaseUrl(baseUrl) {
128
128
  return url.toString().replace(/\/+$/, '');
129
129
  }
130
130
 
131
+ function systemPromptKey(config) {
132
+ return `${config.provider}::${config.model}`;
133
+ }
134
+
135
+ function getSystemPrompt(config) {
136
+ if (!config?.systemPrompts) return null;
137
+ return config.systemPrompts[systemPromptKey(config)] || null;
138
+ }
139
+
140
+ function setSystemPrompt(config, prompt) {
141
+ const key = systemPromptKey(config);
142
+ if (!config.systemPrompts) config.systemPrompts = {};
143
+ if (prompt && prompt.trim()) {
144
+ config.systemPrompts[key] = prompt.trim();
145
+ } else {
146
+ delete config.systemPrompts[key];
147
+ if (Object.keys(config.systemPrompts).length === 0) delete config.systemPrompts;
148
+ }
149
+ }
150
+
131
151
  function buildModelUrlCandidates(baseUrl) {
132
152
  let url;
133
153
  try { url = new URL(normalizeAnthropicBaseUrl(baseUrl)); } catch { return []; }
@@ -324,10 +344,10 @@ function shellQuote(value) {
324
344
  }
325
345
 
326
346
  // 构造完整的 clawai 环境变量块
327
- function buildEnvBlock(baseUrl, apiKey, model, fastModel) {
347
+ function buildEnvBlock(baseUrl, apiKey, model, fastModel, systemPrompt) {
328
348
  const provider = providerKeyFromBaseUrl(baseUrl);
329
349
  const env = buildClaudeEnv({ provider, baseUrl, apiKey, model, fastModel });
330
- return [
350
+ const lines = [
331
351
  '',
332
352
  '# clawai-start',
333
353
  `export ANTHROPIC_BASE_URL=${shellQuote(env.ANTHROPIC_BASE_URL)}`,
@@ -339,9 +359,15 @@ function buildEnvBlock(baseUrl, apiKey, model, fastModel) {
339
359
  `export ANTHROPIC_DEFAULT_HAIKU_MODEL=${shellQuote(env.ANTHROPIC_DEFAULT_HAIKU_MODEL)}`,
340
360
  `export CLAUDE_CODE_SUBAGENT_MODEL=${shellQuote(env.CLAUDE_CODE_SUBAGENT_MODEL)}`,
341
361
  `export CLAUDE_CODE_EFFORT_LEVEL=${shellQuote(env.CLAUDE_CODE_EFFORT_LEVEL)}`,
342
- '# clawai-end',
343
- '',
344
- ].join('\n');
362
+ ];
363
+
364
+ if (systemPrompt && systemPrompt.trim()) {
365
+ lines.push(`_claw_system_prompt=${shellQuote(systemPrompt.trim())}`);
366
+ lines.push(`claude() { command claude --append-system-prompt "$_claw_system_prompt" "$@"; }`);
367
+ }
368
+
369
+ lines.push('# clawai-end', '');
370
+ return lines.join('\n');
345
371
  }
346
372
 
347
373
  // 写入或更新 shell 配置文件中的环境变量块
@@ -364,7 +390,7 @@ function writeEnvToZshrc(baseUrl, apiKey, model, fastModel, options = {}) {
364
390
  rcFile = path.join(os.homedir(), '.zshrc');
365
391
  }
366
392
 
367
- const block = buildEnvBlock(baseUrl, apiKey, model, fastModel);
393
+ const block = buildEnvBlock(baseUrl, apiKey, model, fastModel, options.systemPrompt);
368
394
  const current = fs.existsSync(rcFile) ? fs.readFileSync(rcFile, 'utf8') : '';
369
395
 
370
396
  // 兼容旧版(# clawai 单行块)和新版(# clawai-start...# clawai-end 多行块)
@@ -424,6 +450,8 @@ function resetConfig(options = {}) {
424
450
  module.exports = {
425
451
  loadConfig,
426
452
  saveConfig,
453
+ getSystemPrompt,
454
+ setSystemPrompt,
427
455
  writeEnvToZshrc,
428
456
  buildWindowsSetEnvCommands,
429
457
  buildWindowsClearEnvCommands,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yingclaw",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Claude Code × 国产大模型一键接入:DeepSeek、Kimi、Qwen、MiniMax、GLM、MiMo",
5
5
  "main": "index.js",
6
6
  "bin": {