@buaa_smat/hometrans 0.1.9 → 0.1.10

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
@@ -1,6 +1,6 @@
1
1
  # hometrans
2
2
 
3
- > Skill + agent installer for the **HomeTrans** Android-to-HarmonyOS conversion toolkit. One command (`ht init`) distributes **7 skills**, **9 subagents** (with a script bundle + on-device test tooling), and an **MCP server** into Claude Code, Cursor, OpenCode, and Codex.
3
+ > Skill + agent installer for the **HomeTrans** Android-to-HarmonyOS conversion toolkit. One command (`ht init`) distributes **7 skills**, **9 subagents** (with a script bundle + on-device test tooling), and an **MCP server** into Claude Code, Cursor, OpenCode, Codex, and CodeBuddy.
4
4
 
5
5
  ---
6
6
 
@@ -23,7 +23,7 @@ After running `ht init`, follow these steps to complete the installation:
23
23
 
24
24
  ### Step 1: Choose your target editor
25
25
 
26
- `ht init` automatically detects supported editors (Claude Code, Cursor, OpenCode, Codex) and presents an interactive selection prompt. Use **Space** to toggle and **Enter** to confirm.
26
+ `ht init` automatically detects supported editors (Claude Code, Cursor, OpenCode, Codex, CodeBuddy) and presents an interactive selection prompt. Use **Space** to toggle and **Enter** to confirm.
27
27
 
28
28
  ![Choose target editor](resource/choose_editor.png)
29
29
 
@@ -167,12 +167,13 @@ For more details, see the official GitNexus repository: <https://github.com/abhi
167
167
 
168
168
  ### Target directories
169
169
 
170
- | Editor | Marker | Skills target | Agents target |
171
- |--------|--------|---------------|---------------|
172
- | Claude Code | `~/.claude/` | `~/.claude/skills/` | `~/.claude/agents/` |
173
- | Cursor | `~/.cursor/` | `~/.cursor/skills/` | `~/.cursor/agents/` |
174
- | OpenCode | `~/.config/opencode/` | `~/.config/opencode/skills/` | `~/.config/opencode/agents/` |
175
- | Codex | `~/.codex/` | `~/.agents/skills/` | `~/.codex/agents/` |
170
+ | Editor | Marker | Skills target | Agents target | MCP config |
171
+ |--------|--------|---------------|---------------|------------|
172
+ | Claude Code | `~/.claude/` | `~/.claude/skills/` | `~/.claude/agents/` | `~/.claude.json` → `mcpServers.hometrans` |
173
+ | Cursor | `~/.cursor/` | `~/.cursor/skills/` | `~/.cursor/agents/` | `~/.cursor/mcp.json` → `mcpServers.hometrans` |
174
+ | OpenCode | `~/.config/opencode/` | `~/.config/opencode/skills/` | `~/.config/opencode/agents/` | `~/.config/opencode/opencode.json` → `mcp.hometrans` |
175
+ | Codex | `~/.codex/` | `~/.agents/skills/` | `~/.codex/agents/` | `codex mcp add` → `~/.codex/config.toml` `[mcp_servers.hometrans]` |
176
+ | CodeBuddy | `~/.codebuddy/` | `~/.codebuddy/skills/` | `~/.codebuddy/agents/` | `~/.codebuddy/mcp.json` → `mcpServers.hometrans` |
176
177
 
177
178
  ---
178
179
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 全局配置存储:~/.hometrans/config.json
3
3
  *
4
- * 当前包含 editors 配置(Claude Code / Cursor / OpenCode / Codex),后续可扩展
4
+ * 当前包含 editors 配置(Claude Code / Cursor / OpenCode / Codex / CodeBuddy),后续可扩展
5
5
  * 其它顶层字段。首次 setup 时若文件不存在则写入默认值;之后用户可手动编辑。
6
6
  * `ht config` 只读不改。
7
7
  */
@@ -76,6 +76,19 @@ export function defaultEditors() {
76
76
  section: 'mcp_servers.hometrans',
77
77
  },
78
78
  },
79
+ {
80
+ // CodeBuddy(腾讯):与 Claude Code 同构的 .codebuddy/ 目录,
81
+ // 全局安装映射到 ~/.codebuddy/{skills,agents},MCP 走 mcpServers JSON。
82
+ name: 'CodeBuddy',
83
+ markerDir: '~/.codebuddy',
84
+ skillsDir: '~/.codebuddy/skills',
85
+ agentsDir: '~/.codebuddy/agents',
86
+ mcp: {
87
+ format: 'jsonc-object',
88
+ path: '~/.codebuddy/mcp.json',
89
+ keyPath: ['mcpServers', 'hometrans'],
90
+ },
91
+ },
79
92
  ];
80
93
  }
81
94
  async function fileExists(p) {
@@ -139,6 +152,16 @@ export async function loadHomeTransConfig() {
139
152
  delete anyParsed.sdkPaths;
140
153
  delete anyParsed.params;
141
154
  delete anyParsed.tool_path;
155
+ // Append any default editors that this (older) config predates, matched by
156
+ // name. Existing entries are never modified, so manual edits are preserved;
157
+ // we only add editors the user's file is missing (e.g. CodeBuddy shipped
158
+ // after they first ran `ht init`). Persist so the new editor shows up.
159
+ const existingNames = new Set(config.editors.map((e) => e.name));
160
+ const missingEditors = defaultEditors().filter((e) => !existingNames.has(e.name));
161
+ if (missingEditors.length > 0) {
162
+ config.editors.push(...missingEditors);
163
+ await saveHomeTransConfig(config);
164
+ }
142
165
  return config;
143
166
  }
144
167
  export async function saveHomeTransConfig(config) {
@@ -2,7 +2,7 @@
2
2
  * `ht config` — 打印 editors.json 路径与内容。
3
3
  *
4
4
  * 该命令只读:用户若要修改 editor 配置,直接编辑这个 JSON 文件即可。
5
- * 文件不存在时自动写入默认 4 个 editor。
5
+ * 文件不存在时自动写入默认 5 个 editor。
6
6
  */
7
7
  import fs from 'node:fs/promises';
8
8
  import { getConfigPath, loadHomeTransConfig } from './config-store.js';
package/dist/cli/index.js CHANGED
@@ -1,12 +1,34 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
3
  import { createRequire } from 'node:module';
4
+ import chalk from 'chalk';
4
5
  import { initCommand } from './init.js';
5
6
  import { runMcpServer } from './mcp.js';
6
7
  import { configCommand } from './config.js';
7
8
  import { uninstallCommand } from './uninstall.js';
8
9
  const _require = createRequire(import.meta.url);
9
10
  const pkg = _require('../../package.json');
11
+ /**
12
+ * Global Ctrl-C safety net.
13
+ *
14
+ * inquirer intercepts Ctrl-C as a keypress *inside* a prompt (raw mode) and the
15
+ * per-command catches print a tailored "Cancelled" notice there. But Ctrl-C
16
+ * pressed *outside* a prompt — e.g. while `init` is copying files or `uninstall`
17
+ * is deleting — arrives as an OS SIGINT, which Node would otherwise terminate on
18
+ * silently. This handler guarantees a cancel notice in that case too.
19
+ *
20
+ * The MCP server (`ht mcp`) runs over stdio and must keep its own shutdown
21
+ * semantics, so it removes this handler on startup.
22
+ */
23
+ let interrupted = false;
24
+ function onSigint() {
25
+ if (interrupted)
26
+ process.exit(130);
27
+ interrupted = true;
28
+ console.log(chalk.yellow('\n Cancelled by Ctrl-C. No further changes will be made.\n'));
29
+ process.exit(130);
30
+ }
31
+ process.on('SIGINT', onSigint);
10
32
  const program = new Command();
11
33
  program
12
34
  .name('hometrans')
@@ -23,6 +45,8 @@ program
23
45
  .command('mcp')
24
46
  .description('Run the HomeTrans MCP server (stdio)')
25
47
  .action(async () => {
48
+ // The MCP server owns its own SIGINT/shutdown handling; drop the CLI net.
49
+ process.off('SIGINT', onSigint);
26
50
  await runMcpServer();
27
51
  });
28
52
  program
package/dist/cli/init.js CHANGED
@@ -19,6 +19,20 @@ function ensureChalkColor() {
19
19
  chalk.level = 1;
20
20
  }
21
21
  }
22
+ /**
23
+ * Did the user abort an inquirer prompt with Ctrl-C / Esc?
24
+ * inquirer v14 (@inquirer/core) rejects with an `ExitPromptError` in that case.
25
+ * We must distinguish this from a genuine non-interactive failure (no TTY),
26
+ * otherwise Ctrl-C silently gets swallowed and the install proceeds anyway.
27
+ */
28
+ export function isPromptAbort(err) {
29
+ return err instanceof Error && err.name === 'ExitPromptError';
30
+ }
31
+ /** Print a cancellation notice and exit with the conventional SIGINT code. */
32
+ function abortInit() {
33
+ console.log(chalk.yellow('\n Cancelled. No changes were made beyond this point.\n'));
34
+ process.exit(130);
35
+ }
22
36
  const __filename = fileURLToPath(import.meta.url);
23
37
  const __dirname = path.dirname(__filename);
24
38
  export async function dirExists(dirPath) {
@@ -256,13 +270,26 @@ export async function initCommand(options = {}) {
256
270
  {
257
271
  type: 'checkbox',
258
272
  name: 'selectedEditors',
259
- message: 'Select editors to configure (arrow keys + space to toggle, enter to confirm):',
273
+ message: 'Select editors to configure:',
260
274
  choices,
275
+ // Append a "ctrl-c cancel" entry to the built-in key help line, reusing
276
+ // @inquirer/checkbox's default formatting (bold key · dim action · dim
277
+ // " • " separator):
278
+ // ↑↓ navigate • space select • a all • i invert • ⏎ submit • ctrl-c cancel
279
+ theme: {
280
+ style: {
281
+ keysHelpTip: (keys) => [...keys, ['ctrl-c', 'cancel']]
282
+ .map(([key, action]) => `${chalk.bold(key)} ${chalk.dim(action)}`)
283
+ .join(chalk.dim(' • ')),
284
+ },
285
+ },
261
286
  },
262
287
  ]);
263
288
  selectedEditors = answers.selectedEditors;
264
289
  }
265
- catch {
290
+ catch (err) {
291
+ if (isPromptAbort(err))
292
+ abortInit();
266
293
  console.log(chalk.yellow('\n Interactive selection failed. Auto-selecting all detected editors.'));
267
294
  console.log(chalk.gray(' Tip: use --all to skip interactive selection.\n'));
268
295
  selectedEditors = editors
@@ -304,7 +331,9 @@ export async function initCommand(options = {}) {
304
331
  config.env.TEST_API_KEY = answers.TEST_API_KEY.trim();
305
332
  await saveHomeTransConfig(config);
306
333
  }
307
- catch {
334
+ catch (err) {
335
+ if (isPromptAbort(err))
336
+ abortInit();
308
337
  console.log(chalk.yellow(' Parameter prompts skipped (non-interactive mode).'));
309
338
  }
310
339
  // Copy bundled tools/ into ~/.hometrans/tools and record env.TOOL_PATH.
@@ -17,7 +17,7 @@ import chalk from 'chalk';
17
17
  import inquirer from 'inquirer';
18
18
  import { modify, applyEdits } from 'jsonc-parser';
19
19
  import { expandHome, loadHomeTransConfig, } from './config-store.js';
20
- import { dirExists, prettyHome } from './init.js';
20
+ import { dirExists, isPromptAbort, prettyHome } from './init.js';
21
21
  const execFileAsync = promisify(execFile);
22
22
  const __filename = fileURLToPath(import.meta.url);
23
23
  const __dirname = path.dirname(__filename);
@@ -291,14 +291,25 @@ export async function uninstallCommand() {
291
291
  if (plan.deletions.length === 0 && plan.modifications.length === 0) {
292
292
  return;
293
293
  }
294
- const { proceed } = await inquirer.prompt([
295
- {
296
- type: 'confirm',
297
- name: 'proceed',
298
- message: 'Continue?',
299
- default: false,
300
- },
301
- ]);
294
+ let proceed;
295
+ try {
296
+ ({ proceed } = await inquirer.prompt([
297
+ {
298
+ type: 'confirm',
299
+ name: 'proceed',
300
+ message: 'Continue?',
301
+ default: false,
302
+ },
303
+ ]));
304
+ }
305
+ catch (err) {
306
+ // Ctrl-C / Esc at the confirm prompt — cancel cleanly, no stack trace.
307
+ if (isPromptAbort(err)) {
308
+ console.log(chalk.yellow('\n Uninstall cancelled. No files modified.\n'));
309
+ process.exit(130);
310
+ }
311
+ throw err;
312
+ }
302
313
  if (!proceed) {
303
314
  console.log(chalk.yellow('\n Uninstall cancelled. No files modified.\n'));
304
315
  return;
@@ -73,6 +73,19 @@ function defaultEditors() {
73
73
  path: "~/.codex/config.toml",
74
74
  section: "mcp_servers.hometrans"
75
75
  }
76
+ },
77
+ {
78
+ // CodeBuddy(腾讯):与 Claude Code 同构的 .codebuddy/ 目录,
79
+ // 全局安装映射到 ~/.codebuddy/{skills,agents},MCP 走 mcpServers JSON。
80
+ name: "CodeBuddy",
81
+ markerDir: "~/.codebuddy",
82
+ skillsDir: "~/.codebuddy/skills",
83
+ agentsDir: "~/.codebuddy/agents",
84
+ mcp: {
85
+ format: "jsonc-object",
86
+ path: "~/.codebuddy/mcp.json",
87
+ keyPath: ["mcpServers", "hometrans"]
88
+ }
76
89
  }
77
90
  ];
78
91
  }
@@ -132,8 +145,21 @@ async function loadHomeTransConfig() {
132
145
  delete anyParsed.sdkPaths;
133
146
  delete anyParsed.params;
134
147
  delete anyParsed.tool_path;
148
+ const existingNames = new Set(config.editors.map((e) => e.name));
149
+ const missingEditors = defaultEditors().filter(
150
+ (e) => !existingNames.has(e.name)
151
+ );
152
+ if (missingEditors.length > 0) {
153
+ config.editors.push(...missingEditors);
154
+ await saveHomeTransConfig(config);
155
+ }
135
156
  return config;
136
157
  }
158
+ async function saveHomeTransConfig(config) {
159
+ const configPath = getConfigPath();
160
+ await fs.mkdir(getConfigDir(), { recursive: true });
161
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
162
+ }
137
163
 
138
164
  // src/context/analysis/ArkTsGitInfoAnalysis.ts
139
165
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buaa_smat/hometrans",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "HomeTrans (Android-to-HarmonyOS) skill + agent installer. Run `ht init` to distribute conversion skills and subagents into AI editors.",
5
5
  "license": "MIT",
6
6
  "repository": {