@zlnap/astrolabe 0.16.0 → 0.17.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
@@ -6,14 +6,22 @@ Astrolabe AI:面向 AI 编码助手的本地代码检索与导航 MCP server
6
6
  npx -y @zlnap/astrolabe /path/to/repo
7
7
  ```
8
8
 
9
- MCP 客户端:
9
+ MCP 客户端(推荐一键配置):
10
+
11
+ ```bash
12
+ npx -y @zlnap/astrolabe setup
13
+ ```
14
+
15
+ Detects Claude Code, Cursor, Codex, Windsurf, Cline, Roo, Continue, OpenCode, DeepSeek Harness, Codebuddy, Grok Build, Kimi Code, and ZCode. Merges MCP config (OpenCode V1/V2/jsonc; Grok `[mcp_servers.*]` TOML; ZCode `mcp.servers`; DSH installs the native guard plugin + `cordis.patch.yml` merge). Optional `--with-hooks` installs OpenCode's guard plugin and hook recipes for Codebuddy / Grok / Kimi / ZCode (ZCode: no `SessionEnd`, do not hang cleanup on `Stop`). Flags: `--yes`, `--clients=…`, `--dry-run`, `--with-hooks`, `--list`.
16
+
17
+ 探测本机 Claude Code / Cursor / Codex 等,交互勾选后合并写入 MCP 配置(`--yes`、`--dry-run`、`--list` 可用)。手动配置示例:
10
18
 
11
19
  ```json
12
20
  {
13
21
  "mcpServers": {
14
22
  "astrolabe": {
15
23
  "command": "npx",
16
- "args": ["-y", "@zlnap/astrolabe", "."]
24
+ "args": ["-y", "@zlnap/astrolabe", ".", "--context=cursor"]
17
25
  }
18
26
  }
19
27
  }
package/bin/astrolabe.js CHANGED
@@ -1,14 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- const { resolveBinary } = require('../lib/resolve');
5
- const { runBinary } = require('../lib/run');
4
+ const argv = process.argv.slice(2);
6
5
 
7
- resolveBinary()
8
- .then((binPath) => {
9
- runBinary(binPath, process.argv.slice(2));
10
- })
11
- .catch((error) => {
12
- console.error(`[astrolabe] ${error.message || error}`);
13
- process.exit(1);
14
- });
6
+ // `astrolabe setup` is implemented in Node (detect agents + merge MCP configs).
7
+ // Intercept before resolving / spawning the native binary.
8
+ if (argv[0] === 'setup') {
9
+ const { run } = require('../lib/setup');
10
+ try {
11
+ process.exitCode = run(argv.slice(1));
12
+ } catch (error) {
13
+ console.error(`[astrolabe setup] ${error.message || error}`);
14
+ process.exitCode = 1;
15
+ }
16
+ } else {
17
+ const { resolveBinary } = require('../lib/resolve');
18
+ const { runBinary } = require('../lib/run');
19
+
20
+ resolveBinary()
21
+ .then((binPath) => {
22
+ runBinary(binPath, argv);
23
+ })
24
+ .catch((error) => {
25
+ console.error(`[astrolabe] ${error.message || error}`);
26
+ process.exit(1);
27
+ });
28
+ }
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ const USAGE = `Configure Astrolabe as an MCP server for local AI coding agents.
4
+
5
+ Usage:
6
+ astrolabe setup [options]
7
+
8
+ Options:
9
+ --yes, -y Configure all detected clients without prompts
10
+ --clients=a,b,c Configure only these client ids (comma-separated)
11
+ --list Print detection results and exit
12
+ --dry-run Preview writes; do not modify files
13
+ --native Use command "astrolabe" on PATH instead of npx
14
+ --command=<bin> Override MCP command (default: npx, or astrolabe with --native)
15
+ --with-hooks Also install drift-guard hooks/plugins where supported
16
+ (OpenCode guard plugin; Codebuddy/Grok/Kimi/ZCode hook recipes;
17
+ DSH always installs its native guard)
18
+ --no-cursor-permissions
19
+ Skip merging Cursor mcpAllowlist
20
+ --cursor-permissions Force Cursor allowlist merge when Cursor is selected (default on)
21
+ --home <dir> Override HOME (tests)
22
+ -h, --help Show this help
23
+
24
+ Client ids:
25
+ claude-code, cursor, codex, windsurf, cline, roo, continue, opencode,
26
+ deepseek-harness (alias: dsh), codebuddy, grok (aliases: grokbuild, grok-build),
27
+ kimicode (aliases: kimi-code, kimi_code, kimi), zcode (aliases: z-code, zai)
28
+ `;
29
+
30
+ function parseArgs(argv) {
31
+ const opts = {
32
+ yes: false,
33
+ list: false,
34
+ dryRun: false,
35
+ native: false,
36
+ help: false,
37
+ home: null,
38
+ clients: null,
39
+ command: null,
40
+ cursorPermissions: true,
41
+ withHooks: false,
42
+ };
43
+ for (let i = 0; i < argv.length; i += 1) {
44
+ const arg = argv[i];
45
+ if (arg === '--yes' || arg === '-y') opts.yes = true;
46
+ else if (arg === '--list') opts.list = true;
47
+ else if (arg === '--dry-run') opts.dryRun = true;
48
+ else if (arg === '--native') opts.native = true;
49
+ else if (arg === '--help' || arg === '-h') opts.help = true;
50
+ else if (arg === '--with-hooks') opts.withHooks = true;
51
+ else if (arg === '--no-cursor-permissions') opts.cursorPermissions = false;
52
+ else if (arg === '--cursor-permissions') opts.cursorPermissions = true;
53
+ else if (arg === '--home') {
54
+ const value = argv[i + 1];
55
+ if (!value || value.startsWith('-')) throw new Error('--home needs a directory');
56
+ opts.home = value;
57
+ i += 1;
58
+ } else if (arg.startsWith('--home=')) {
59
+ opts.home = arg.slice('--home='.length);
60
+ if (!opts.home) throw new Error('--home needs a directory');
61
+ } else if (arg.startsWith('--clients=')) {
62
+ const raw = arg.slice('--clients='.length).trim();
63
+ if (!raw) throw new Error('--clients needs a comma-separated list');
64
+ opts.clients = raw.split(',').map((s) => s.trim()).filter(Boolean);
65
+ } else if (arg === '--clients') {
66
+ const value = argv[i + 1];
67
+ if (!value || value.startsWith('-')) throw new Error('--clients needs a comma-separated list');
68
+ opts.clients = value.split(',').map((s) => s.trim()).filter(Boolean);
69
+ i += 1;
70
+ } else if (arg.startsWith('--command=')) {
71
+ opts.command = arg.slice('--command='.length);
72
+ if (!opts.command) throw new Error('--command needs a value');
73
+ } else if (arg === '--command') {
74
+ const value = argv[i + 1];
75
+ if (!value || value.startsWith('-')) throw new Error('--command needs a value');
76
+ opts.command = value;
77
+ i += 1;
78
+ } else {
79
+ throw new Error(`unknown argument: ${arg}`);
80
+ }
81
+ }
82
+ return opts;
83
+ }
84
+
85
+ module.exports = { parseArgs, USAGE };