@lyra-ai/toolkit 0.2.4 → 0.2.6

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/src/uninstall.mjs CHANGED
@@ -1,16 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * uninstall.mjs — `toolkit uninstall`
4
- * 删插件目录 + 移除 settings.json enabledPlugins 条目 + 可选保留 ~/.toolkit/ 数据。
3
+ * uninstall.mjs — `toolkit uninstall [--purge]`
4
+ *
5
+ * 删除 @skills-dir 插件目录(~/.claude/skills/toolkit)+ 旧版裸目录残留
6
+ * + settings.json 里失效的 toolkit@local 条目。可选 --purge 清除 ~/.toolkit/ 数据。
7
+ *
8
+ * 注:@skills-dir plugin 没有「卸载步骤」(按官方文档,删文件夹即停用)。
5
9
  */
6
10
  import fs from 'node:fs';
7
11
  import path from 'node:path';
8
12
  import os from 'node:os';
9
13
 
10
14
  const HOME = os.homedir();
11
- const PLUGIN_DIR = path.join(HOME, '.claude', 'plugins', 'toolkit');
15
+ const PLUGIN_DIR = path.join(HOME, '.claude', 'skills', 'toolkit');
16
+ const OLD_PLUGIN_DIR = path.join(HOME, '.claude', 'plugins', 'toolkit');
12
17
  const SETTINGS_PATH = path.join(HOME, '.claude', 'settings.json');
13
- const PLUGIN_KEY = 'toolkit@local';
18
+ const OLD_PLUGIN_KEY = 'toolkit@local';
19
+
20
+ // 数据目录与 shared/config.mjs 的 TOOLKIT_DIR 同逻辑,但在调用时求值,
21
+ // 以便测试用 TOOLKIT_HOME / HOME 隔离(避免模块加载时绑定的陈旧值)。
22
+ function dataDir() {
23
+ return process.env.TOOLKIT_HOME || path.join(os.homedir(), '.toolkit');
24
+ }
14
25
 
15
26
  function rmrf(dir) {
16
27
  if (!fs.existsSync(dir)) return;
@@ -25,24 +36,26 @@ function rmrf(dir) {
25
36
  }
26
37
 
27
38
  export function uninstall(opts = {}) {
28
- // 1. 删插件目录
39
+ // 1. 删 @skills-dir 插件目录 + 旧版裸目录残留
29
40
  rmrf(PLUGIN_DIR);
30
- console.log('✅ 插件目录已删除');
41
+ rmrf(OLD_PLUGIN_DIR);
42
+ console.log(`✅ 插件目录已删除(${PLUGIN_DIR})`);
31
43
 
32
- // 2. 移除 settings.json enabledPlugins
44
+ // 2. 移除 settings.json 里失效的 toolkit@local(若有)
33
45
  try {
34
46
  const settings = JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf-8'));
35
- if (settings.enabledPlugins?.[PLUGIN_KEY] !== undefined) {
36
- delete settings.enabledPlugins[PLUGIN_KEY];
47
+ if (settings.enabledPlugins?.[OLD_PLUGIN_KEY] !== undefined) {
48
+ delete settings.enabledPlugins[OLD_PLUGIN_KEY];
37
49
  fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
38
- console.log('✅ settings.json enabledPlugins 已移除');
50
+ console.log('✅ settings.json 失效条目已移除');
39
51
  }
40
52
  } catch {}
41
53
 
42
- // 3. 数据(~/.toolkit/)
54
+ // 3. 数据(~/.toolkit/,走 TOOLKIT_HOME 或 ~/.toolkit,与 install 一致)
43
55
  if (opts.purge) {
44
- rmrf(path.join(HOME, '.toolkit'));
45
- console.log('✅ ~/.toolkit/ 数据已清除');
56
+ const dir = dataDir();
57
+ rmrf(dir);
58
+ console.log(`✅ ${dir} 数据已清除`);
46
59
  } else {
47
60
  console.log('ℹ️ ~/.toolkit/ 数据保留(含 UID + 上传记录)。');
48
61
  console.log(' 彻底清除: toolkit uninstall --purge');
package/src/zentao.mjs CHANGED
@@ -595,24 +595,29 @@ async function maskedPrompt(question) {
595
595
  return new Promise((resolve) => {
596
596
  process.stdout.write(question);
597
597
  let buf = '';
598
+ // Windows 终端回车是 \r\n:上一个 rl.question 按 \r 返回后,残留 \n 会被下一个 raw read 读到。
599
+ // 用 started 标志忽略"开头还没输入真字符时"收到的 \r/\n,避免空密码直接提交(向导秒退)。
600
+ let started = false;
601
+ const finish = () => {
602
+ process.stdout.write('\n');
603
+ process.stdin.removeListener('data', onData);
604
+ process.stdin.setRawMode(false);
605
+ resolve(buf);
606
+ };
598
607
  const onData = (c) => {
599
608
  const ch = c.toString();
600
- if (ch === '\r' || ch === '\n') {
601
- process.stdout.write('\n');
602
- process.stdin.removeListener('data', onData);
603
- process.stdin.setRawMode(false);
604
- resolve(buf);
605
- } else if (ch === '') {
606
- process.stdout.write('\n');
607
- process.exit(0);
608
- } else if (ch === '\x7f' || ch === '\b') {
609
- if (buf.length > 0) { buf = buf.slice(0, -1); process.stdout.write('\b \b'); }
610
- } else {
611
- buf += ch;
612
- process.stdout.write('*');
609
+ if (ch === '\x03') { process.stdout.write('\n'); process.exit(0); } // Ctrl+C
610
+ if (ch === '\r' || ch === '\n') { if (started) finish(); return; } // 开头换行 = 残留,忽略
611
+ if (ch === '\x7f' || ch === '\b') {
612
+ if (buf.length > 0) { buf = buf.slice(0, -1); process.stdout.write('\b \b'); started = true; }
613
+ return;
613
614
  }
615
+ buf += ch;
616
+ started = true;
617
+ process.stdout.write('*');
614
618
  };
615
619
  process.stdin.setRawMode(true);
620
+ process.stdin.resume();
616
621
  process.stdin.on('data', onData);
617
622
  });
618
623
  }
@@ -661,14 +666,10 @@ async function cmdInit(parsed, config, deps) {
661
666
 
662
667
  const u = await ask('禅道地址 (ZENTAO_URL)', config.zentaoUrl || '');
663
668
  const un = await ask('用户名 (ZENTAO_USERNAME)', config.username || '');
664
- const pw = await (async () => {
665
- rl.pause();
666
- const p = await maskedPrompt('密码 (ZENTAO_PASSWORD): ');
667
- rl.resume();
668
- return p;
669
- })();
670
669
 
670
+ // 先关掉 readline,彻底交出 stdin 控制权,再用裸 raw-mode 收密码(避免与 readline 冲突)
671
671
  rl.close();
672
+ const pw = await maskedPrompt('密码 (ZENTAO_PASSWORD): ');
672
673
 
673
674
  setConfigKey('ZENTAO_URL', u, deps.fs);
674
675
  setConfigKey('ZENTAO_USERNAME', un, deps.fs);