@lark-project/meegle 1.0.10 → 1.0.12

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/CHANGELOG.md CHANGED
@@ -8,6 +8,22 @@ versioned section on each npm release.
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [v1.0.12] - 2026-06-29
12
+
13
+ ### Fixed
14
+
15
+ - The install wizard now always installs the AI Agent Skill instead of trying to detect a prior install. The previous name-substring check matched sibling skills such as `meegle-plugin` and silently skipped installing the core `meegle` skill. `skills add` is idempotent, so re-running it on an already-installed skill simply upgrades or no-ops.
16
+
17
+ ## [v1.0.11] - 2026-06-26
18
+
19
+ ### Added
20
+
21
+ - `meegle install` now runs a one-stop setup wizard for npm users: install or upgrade the CLI globally, install the AI Agent Skill, configure the host, and start browser or Device Code login. This enables `npx @lark-project/meegle@latest install` as the primary quick-start command.
22
+
23
+ ### Fixed
24
+
25
+ - The install wizard now invokes the `skills` CLI through `npm exec --package=skills` so `skills add` and `skills ls` are parsed reliably across npm/npx versions. A real isolated install smoke test previously exposed that `npx -y skills add ...` could be misparsed as `add@latest`.
26
+
11
27
  ## [v1.0.10] - 2026-06-25
12
28
 
13
29
  ### Fixed
package/README.md CHANGED
@@ -12,7 +12,7 @@ Command-line tool for [Meegle](https://meegle.com?utm_source=github&utm_medium=r
12
12
 
13
13
  ## Why Meegle CLI?
14
14
 
15
- - **Agent-Native** — Ships a bundled [AI Agent Skill](#ai-agent-skill) that teaches Trae, Claude Code, Cursor, Windsurf, Gemini CLI and other agents how to drive Meegle with one command. Every CLI command is designed for both humans and agents, with structured JSON output, `--dry-run` previews, and `--device-code` flows for non-TTY environments
15
+ - **Agent-Native** — The setup wizard installs the bundled AI Agent Skill for Trae, Claude Code, Cursor, Windsurf, Gemini CLI and other agents. Every CLI command is designed for both humans and agents, with structured JSON output, `--dry-run` previews, and `--device-code` flows for non-TTY environments
16
16
  - **Broad Coverage** — 16 business domains (work items, workflow, subtasks, comments, work hours, relations, my-work, views, charts, team, user, project, attachments, deliverables, resource library, WBS plan tables) and 50+ commands mapping to Meegle's core capabilities
17
17
  - **Two-Layer Parameters** — Ergonomic `--flag-name` for everyday use, fallback `--params <json>` for complex payloads like `fields[]` — pick the right granularity per call
18
18
  - **Flexible Output** — `json` / `table` / `ndjson` / `raw`, with `--select` dot-path projection for piping to other tools
@@ -47,22 +47,21 @@ Command-line tool for [Meegle](https://meegle.com?utm_source=github&utm_medium=r
47
47
 
48
48
  - Node.js >= 16 (ships with `npm` / `npx`)
49
49
 
50
- ### Install
50
+ Run the setup wizard:
51
51
 
52
52
  ```bash
53
- npm install -g @lark-project/meegle
53
+ npx @lark-project/meegle@latest install
54
54
  ```
55
55
 
56
+ The wizard installs or upgrades the CLI globally, installs the AI Agent Skill, configures the Meegle host, and starts login.
57
+
56
58
  ## Quick Start (Human Users)
57
59
 
58
- > **Note for AI assistants:** if you are an AI Agent helping the user set this up, jump directly to [Quick Start (AI Agent)](#quick-start-ai-agent--ci--headless) — it contains the non-interactive steps you need.
60
+ > **Note for AI assistants:** if you are an AI Agent helping the user set this up, jump directly to [Quick Start (AI Agent)](#quick-start-ai-agent--ci--headless) — it contains the non-interactive command you need.
59
61
 
60
62
  ```bash
61
- # (Optional) Persist the host so future logins skip the arrow-key picker
62
- meegle config set host <host>
63
-
64
- # 1. Log in (arrow-key host picker + browser OAuth)
65
- meegle auth login
63
+ # 1. Install CLI + Skill, configure host, and log in
64
+ npx @lark-project/meegle@latest install
66
65
 
67
66
  # 2. View this week's to-dos
68
67
  meegle mywork todo --action this_week --page-num 1
@@ -77,45 +76,15 @@ meegle inspect workitem.create
77
76
 
78
77
  ## Quick Start (AI Agent / CI / Headless)
79
78
 
80
- The default `meegle auth login` uses an arrow-key host picker plus a browser OAuth callback both require a real TTY, so they will hang or fail in CI runners, pipes, and agent shells like Claude Code. Use the Device Code flow instead: it prints an authorization URL that the user opens in any browser.
81
-
82
- **Step 1 — Install the CLI**
83
-
84
- ```bash
85
- npm install -g @lark-project/meegle
86
- ```
87
-
88
- **Step 2 — Install the Agent Skill**
89
-
90
- > Recommended for AI-agent workflows, optional for direct CLI use. The skill teaches agents how to choose and run `meegle` commands correctly.
91
-
92
- ```bash
93
- npx skills add larksuite/meegle-cli -y -g
94
- ```
95
-
96
- **Step 3 — Persist the host**
79
+ The default browser OAuth flow requires a real TTY. In CI runners, pipes, and agent shells like Claude Code, run the same setup wizard with an explicit host and Device Code login:
97
80
 
98
81
  ```bash
99
- meegle config set host <host>
82
+ npx -y @lark-project/meegle@latest install --host <host> --device-code --lang en
100
83
  ```
101
84
 
102
- Examples of `<host>`: `project.feishu.cn`, `meegle.com`, or your self-hosted tenant domain such as `your-tenant.example.com`.
103
-
104
- **Step 4 — Log in with Device Code**
85
+ Examples of `<host>`: `project.feishu.cn`, `meegle.com`, or your self-hosted tenant domain such as `your-tenant.example.com`. The Device Code flow prints an authorization URL; send it to the user and keep the command running until authorization completes.
105
86
 
106
- > Run this command in the background. It prints an authorization URL — extract it and send it to the user. The command exits automatically once the user completes authorization in the browser.
107
-
108
- ```bash
109
- meegle auth login --device-code
110
- ```
111
-
112
- Alternatively, pass the host inline each time without persisting it:
113
-
114
- ```bash
115
- meegle auth login --device-code --host <host>
116
- ```
117
-
118
- **Step 5 — Verify**
87
+ Verify:
119
88
 
120
89
  ```bash
121
90
  meegle auth status
@@ -125,19 +94,7 @@ For fully unattended CI (no human-in-the-loop), inject a token via environment v
125
94
 
126
95
  ## AI Agent Skill
127
96
 
128
- `skills/meegle/` is a drop-in **skill** that teaches AI Agents — Trae, Claude Code, Cursor, Windsurf, Gemini CLI, GitHub Copilot CLI how to operate Meegle through this CLI. It bundles the command catalog, MQL syntax, field-value conventions, rich-text Markdown rules, and standard operating procedures for common write flows.
129
-
130
- ### Install
131
-
132
- ```bash
133
- # Install the CLI first (the skill calls `meegle` under the hood)
134
- npm install -g @lark-project/meegle
135
-
136
- # Then add the skill — auto-detects installed agents and registers in each
137
- npx skills add larksuite/meegle-cli -y -g
138
- ```
139
-
140
- `npx skills add` reads `skills/meegle/SKILL.md` from the repo and drops it into the skill directory of every agent CLI it finds on the machine. Re-run any time to pick up updates.
97
+ The setup wizard installs `skills/meegle/`, a drop-in skill for Trae, Claude Code, Cursor, Windsurf, Gemini CLI, GitHub Copilot CLI, and other agents. It teaches agents how to operate Meegle through this CLI instead of guessing command shapes from prose.
141
98
 
142
99
  ### What it covers
143
100
 
@@ -148,11 +105,9 @@ npx skills add larksuite/meegle-cli -y -g
148
105
  - **SOPs** — step-by-step playbooks for creating work items, transitioning nodes, transitioning states, and updating fields
149
106
  - **Auth guard** — the skill refuses to run business commands until `meegle auth status` succeeds
150
107
 
151
- See [skills/meegle/SKILL.md](./skills/meegle/SKILL.md) and [skills/meegle/references/](./skills/meegle/references/) for the full contents.
152
-
153
108
  ### Usage
154
109
 
155
- Once installed, just ask the agent in natural language. For example, in Trae:
110
+ Once the setup wizard has run, ask the agent in natural language. For example:
156
111
 
157
112
  ```
158
113
  Show me this week's P0 stories in the PROJ space.
@@ -160,8 +115,6 @@ Show me this week's P0 stories in the PROJ space.
160
115
 
161
116
  The agent consults the skill, picks the right `meegle` commands, and runs them for you. Pair with `--dry-run` (see [Security](#security--risk-warnings)) to preview side-effectful operations before the agent commits them.
162
117
 
163
- > Heads up: the skill name `meegle` is the same string as the CLI binary. When documentation refers to "the **`meegle` skill**" it means the files in `skills/meegle/`; when it refers to "the **`meegle` CLI**" it means the `meegle` command on your PATH.
164
-
165
118
  ## Commands
166
119
 
167
120
  ### workitem — Work Items
package/README.zh-CN.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  ## 为什么选择 Meegle CLI?
14
14
 
15
- - **Agent 友好** — 附带一份 [AI Agent Skill](#ai-agent-skill),一条命令即可把 Meegle 操作手册喂给 Trae、Claude Code、Cursor、Windsurf、Gemini CLI 等主流 Agent。CLI 命令同时面向人类和 Agent 设计,结构化 JSON 输出、`--dry-run` 预览、`--device-code` 无 TTY 流程
15
+ - **Agent 友好** — 安装向导会安装内置 AI Agent Skill,把 Meegle 操作手册喂给 Trae、Claude Code、Cursor、Windsurf、Gemini CLI 等主流 Agent。CLI 命令同时面向人类和 Agent 设计,结构化 JSON 输出、`--dry-run` 预览、`--device-code` 无 TTY 流程
16
16
  - **覆盖完整** — 16 个业务域(工作项、工作流、子任务、评论、工时、关联、我的工作、视图、图表、团队、用户、空间、附件、交付物、资源库、WBS 计划表),50+ 命令映射到 Meegle 核心能力
17
17
  - **两层参数模型** — 日常用 `--flag-name` 轻便直接,复杂载荷(如 `fields[]`)用 `--params <json>` 兜底 —— 按场景选择合适粒度
18
18
  - **输出格式灵活** — 支持 `json` / `table` / `ndjson` / `raw`,配合 `--select` 点路径投影可直接 pipe 给其他工具
@@ -47,22 +47,21 @@
47
47
 
48
48
  - Node.js >= 16(自带 `npm` / `npx`)
49
49
 
50
- ### 安装命令
50
+ 运行安装向导:
51
51
 
52
52
  ```bash
53
- npm install -g @lark-project/meegle
53
+ npx @lark-project/meegle@latest install
54
54
  ```
55
55
 
56
+ 向导会全局安装或升级 CLI、安装 AI Agent Skill、配置 Meegle host,并启动登录。
57
+
56
58
  ## 快速开始(人类用户)
57
59
 
58
- > **给 AI Agent 的提示:** 如果你是在替用户完成这套安装的 AI Agent,请直接跳到 [快速开始(AI Agent)](#快速开始ai-agent--ci--无头环境) —— 那里有你需要的非交互步骤。
60
+ > **给 AI Agent 的提示:** 如果你是在替用户完成这套安装的 AI Agent,请直接跳到 [快速开始(AI Agent)](#快速开始ai-agent--ci--无头环境) —— 那里有你需要的非交互命令。
59
61
 
60
62
  ```bash
61
- # (可选)持久化 host,后续登录就不用再选菜单
62
- meegle config set host <host>
63
-
64
- # 1. 登录(上下选择 host + 浏览器 OAuth)
65
- meegle auth login
63
+ # 1. 安装 CLI + Skill,配置 host,并登录
64
+ npx @lark-project/meegle@latest install
66
65
 
67
66
  # 2. 查看本周待办
68
67
  meegle mywork todo --action this_week --page-num 1
@@ -77,45 +76,17 @@ meegle inspect workitem.create
77
76
 
78
77
  ## 快速开始(AI Agent / CI / 无头环境)
79
78
 
80
- `meegle auth login` 默认走上下选择菜单 + 浏览器 OAuth 回调,依赖真实 TTY,在 CI Runner、管道、Claude Code 这类没有 TTY 的环境里会挂起或报错。这些场景请改用 Device Code 流程 —— 命令会输出授权 URL,让用户在浏览器里完成授权。
81
-
82
- **Step 1 — 安装 CLI**
79
+ 默认浏览器 OAuth 流程依赖真实 TTY。在 CI Runner、管道、Claude Code 这类环境里,使用同一个安装向导,但显式传入 host 并启用 Device Code 登录:
83
80
 
84
81
  ```bash
85
- npm install -g @lark-project/meegle
86
- ```
87
-
88
- **Step 2 — 安装 Agent Skill**
89
-
90
- > 推荐用于 AI Agent 场景;如果只是手动使用 CLI,则是可选步骤。Skill 会教 Agent 正确选择和执行 `meegle` 命令。
91
-
92
- ```bash
93
- npx skills add larksuite/meegle-cli -y -g
94
- ```
95
-
96
- **Step 3 — 持久化 host**
97
-
98
- ```bash
99
- meegle config set host <host>
82
+ npx -y @lark-project/meegle@latest install --host <host> --device-code --lang zh
100
83
  ```
101
84
 
102
85
  `<host>` 示例:`project.feishu.cn`、`meegle.com`,或自建租户域名(如 `your-tenant.example.com`)。
103
86
 
104
- **Step 4 — Device Code 登录**
105
-
106
- > 建议后台执行。命令会输出授权 URL —— 提取后发给用户,用户在浏览器完成授权后命令自动退出。
107
-
108
- ```bash
109
- meegle auth login --device-code
110
- ```
111
-
112
- 如不想持久化 host,也可以每次显式传入:
87
+ Device Code 流程会输出授权 URL;把链接发给用户,并保持命令运行直到用户在浏览器完成授权。
113
88
 
114
- ```bash
115
- meegle auth login --device-code --host <host>
116
- ```
117
-
118
- **Step 5 — 验证**
89
+ 验证:
119
90
 
120
91
  ```bash
121
92
  meegle auth status
@@ -125,19 +96,7 @@ meegle auth status
125
96
 
126
97
  ## AI Agent Skill
127
98
 
128
- `skills/meegle/` 是一份可直接加载的 **skill**,用来教 AI Agent —— Trae、Claude Code、Cursor、Windsurf、Gemini CLI、GitHub Copilot CLI —— 通过本 CLI 操作 Meegle。它把命令目录、MQL 语法、字段值写法、富文本 Markdown 规则,以及常见写入流程的 SOP 全部打包好。
129
-
130
- ### 安装
131
-
132
- ```bash
133
- # 先装 CLI(skill 底层会调用 meegle 命令)
134
- npm install -g @lark-project/meegle
135
-
136
- # 再加载 skill —— 会自动探测已装的 Agent,并在各自的 skill 目录里登记
137
- npx skills add larksuite/meegle-cli -y -g
138
- ```
139
-
140
- `npx skills add` 会从仓库读取 `skills/meegle/SKILL.md`,写入本机所有 Agent CLI 的 skill 目录。每次升级重跑一遍即可拉取最新内容。
99
+ 安装向导会安装 `skills/meegle/`,这是一份可直接加载到 Trae、Claude Code、Cursor、Windsurf、Gemini CLI、GitHub Copilot CLI Agent 的 skill。它会教 Agent 通过本 CLI 操作 Meegle,而不是从自然语言说明里猜命令形状。
141
100
 
142
101
  ### 覆盖内容
143
102
 
@@ -148,11 +107,9 @@ npx skills add larksuite/meegle-cli -y -g
148
107
  - **SOP** — 创建工作项、节点流转、状态流转、更新字段等场景的分步剧本
149
108
  - **授权守护** — 所有业务命令前强制先过 `meegle auth status`
150
109
 
151
- 完整内容见 [skills/meegle/SKILL.md](./skills/meegle/SKILL.md) 和 [skills/meegle/references/](./skills/meegle/references/)。
152
-
153
110
  ### 使用方式
154
111
 
155
- 安装后直接用自然语言和 Agent 对话。例如 Trae:
112
+ 安装向导执行完成后,直接用自然语言和 Agent 对话。例如:
156
113
 
157
114
  ```
158
115
  帮我看一下 PROJ 空间本周待办里的 P0 需求
@@ -160,8 +117,6 @@ npx skills add larksuite/meegle-cli -y -g
160
117
 
161
118
  Agent 会参考 skill,自动选择合适的 `meegle` 命令执行。配合 `--dry-run`(见 [安全](#安全与风险提示))可以在 Agent 真正执行副作用前先预览请求。
162
119
 
163
- > 小提示:skill 的名字 `meegle` 和 CLI 二进制同名。文档里提到 **`meegle` skill** 时指 `skills/meegle/` 里的文件;提到 **`meegle` CLI** 时指你 PATH 上的 `meegle` 命令。
164
-
165
120
  ## 命令一览
166
121
 
167
122
  ### workitem — 工作项域
@@ -0,0 +1,378 @@
1
+ #!/usr/bin/env node
2
+ const { execFileSync, spawnSync } = require("child_process");
3
+ const fs = require("fs");
4
+ const os = require("os");
5
+ const path = require("path");
6
+ const readline = require("readline");
7
+
8
+ const PKG = "@lark-project/meegle";
9
+ const SKILLS_REPO = "larksuite/meegle-cli";
10
+ const DEFAULT_HOST = "meegle.com";
11
+ const PRESET_HOSTS = [
12
+ { label: "Feishu Project (project.feishu.cn)", value: "project.feishu.cn" },
13
+ { label: "Meegle (meegle.com)", value: "meegle.com" },
14
+ ];
15
+ const isWindows = process.platform === "win32";
16
+
17
+ const messages = {
18
+ en: {
19
+ setup: "Setting up Meegle CLI...",
20
+ language: "Select language",
21
+ installing: "Installing %s globally...",
22
+ upgrading: "Upgrading %s (v%s -> v%s)...",
23
+ installedSkip: "Already installed globally (v%s). Skipped",
24
+ installed: "CLI installed globally",
25
+ skills: "Installing AI Agent Skill...",
26
+ skillsDone: "AI Agent Skill installed",
27
+ hostExisting: "Found existing host: %s. Use it?",
28
+ hostSelect: "Select Meegle site",
29
+ hostCustom: "Custom domain",
30
+ hostInput: "Enter custom domain, for example project.feishu.cn: ",
31
+ hostConfigured: "Host configured: %s",
32
+ authSkip: "Skipped authorization. Run later: meegle auth login",
33
+ authAlready: "Already authenticated. Skipped authorization",
34
+ authConfirm: "Log in now?",
35
+ authBrowser: "Starting browser OAuth login...",
36
+ authDevice: "Starting Device Code login...",
37
+ done: "Setup complete. Try: meegle mywork todo --action this_week --page-num 1",
38
+ nonTtyHost: "No TTY detected and no --host was provided. Using default host: %s",
39
+ },
40
+ zh: {
41
+ setup: "正在设置 Meegle CLI...",
42
+ language: "请选择语言",
43
+ installing: "正在全局安装 %s...",
44
+ upgrading: "正在升级 %s (v%s -> v%s)...",
45
+ installedSkip: "已全局安装 (v%s),跳过",
46
+ installed: "CLI 已全局安装",
47
+ skills: "正在安装 AI Agent Skill...",
48
+ skillsDone: "AI Agent Skill 已安装",
49
+ hostExisting: "发现已有 host: %s,继续使用?",
50
+ hostSelect: "请选择 Meegle 站点",
51
+ hostCustom: "自定义域名",
52
+ hostInput: "请输入自定义域名,例如 project.feishu.cn: ",
53
+ hostConfigured: "Host 已配置: %s",
54
+ authSkip: "已跳过授权。后续可运行: meegle auth login",
55
+ authAlready: "已登录,跳过授权",
56
+ authConfirm: "现在登录?",
57
+ authBrowser: "正在启动浏览器 OAuth 登录...",
58
+ authDevice: "正在启动 Device Code 登录...",
59
+ done: "安装完成。可以试试: meegle mywork todo --action this_week --page-num 1",
60
+ nonTtyHost: "当前不是交互终端且未传 --host,将使用默认 host: %s",
61
+ },
62
+ };
63
+
64
+ function fmt(template, ...values) {
65
+ let i = 0;
66
+ return template.replace(/%s/g, () => String(values[i++] ?? ""));
67
+ }
68
+
69
+ function parseArgs(argv) {
70
+ const opts = {
71
+ auth: true,
72
+ skills: true,
73
+ deviceCode: false,
74
+ yes: false,
75
+ host: "",
76
+ lang: "",
77
+ };
78
+ for (let i = 0; i < argv.length; i += 1) {
79
+ const arg = argv[i];
80
+ if (arg === "--host" && argv[i + 1]) {
81
+ opts.host = argv[++i];
82
+ } else if (arg.startsWith("--host=")) {
83
+ opts.host = arg.slice("--host=".length);
84
+ } else if (arg === "--lang" && argv[i + 1]) {
85
+ opts.lang = argv[++i];
86
+ } else if (arg.startsWith("--lang=")) {
87
+ opts.lang = arg.slice("--lang=".length);
88
+ } else if (arg === "--device-code") {
89
+ opts.deviceCode = true;
90
+ } else if (arg === "--no-auth") {
91
+ opts.auth = false;
92
+ } else if (arg === "--no-skills") {
93
+ opts.skills = false;
94
+ } else if (arg === "-y" || arg === "--yes") {
95
+ opts.yes = true;
96
+ } else if (arg === "-h" || arg === "--help") {
97
+ printHelp();
98
+ process.exit(0);
99
+ }
100
+ }
101
+ if (opts.lang !== "zh" && opts.lang !== "en") {
102
+ opts.lang = "";
103
+ }
104
+ return opts;
105
+ }
106
+
107
+ function printHelp() {
108
+ console.log(`Usage: meegle install [options]
109
+
110
+ Set up Meegle CLI, AI Agent Skill, host configuration, and login.
111
+
112
+ Options:
113
+ --host <host> Configure a site host, e.g. project.feishu.cn
114
+ --device-code Use Device Code login instead of browser OAuth
115
+ --no-auth Skip login
116
+ --no-skills Skip AI Agent Skill installation
117
+ --lang <en|zh> Select wizard language
118
+ -y, --yes Use defaults for non-essential prompts
119
+ -h, --help Show help`);
120
+ }
121
+
122
+ function cmdParts(cmd, args) {
123
+ if (!isWindows) return { cmd, args };
124
+ return { cmd: "cmd.exe", args: ["/c", cmd, ...args] };
125
+ }
126
+
127
+ function run(cmd, args, opts = {}) {
128
+ const actual = cmdParts(cmd, args);
129
+ const result = spawnSync(actual.cmd, actual.args, {
130
+ stdio: "inherit",
131
+ env: process.env,
132
+ ...opts,
133
+ });
134
+ if (result.signal) process.kill(process.pid, result.signal);
135
+ if (result.status !== 0) {
136
+ throw new Error(`${cmd} ${args.join(" ")} failed with exit code ${result.status}`);
137
+ }
138
+ }
139
+
140
+ function runSilent(cmd, args, opts = {}) {
141
+ const actual = cmdParts(cmd, args);
142
+ return execFileSync(actual.cmd, actual.args, {
143
+ stdio: ["ignore", "pipe", "pipe"],
144
+ encoding: "utf8",
145
+ env: process.env,
146
+ ...opts,
147
+ });
148
+ }
149
+
150
+ function runCapture(cmd, args, opts = {}) {
151
+ const actual = cmdParts(cmd, args);
152
+ return spawnSync(actual.cmd, actual.args, {
153
+ stdio: ["ignore", "pipe", "pipe"],
154
+ encoding: "utf8",
155
+ env: process.env,
156
+ ...opts,
157
+ });
158
+ }
159
+
160
+ function question(prompt) {
161
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
162
+ return new Promise((resolve) => {
163
+ rl.question(prompt, (answer) => {
164
+ rl.close();
165
+ resolve(answer.trim());
166
+ });
167
+ });
168
+ }
169
+
170
+ async function select(message, options) {
171
+ console.log(message);
172
+ options.forEach((opt, idx) => {
173
+ console.log(` ${idx + 1}. ${opt.label}`);
174
+ });
175
+ for (;;) {
176
+ const answer = await question("> ");
177
+ const idx = Number(answer);
178
+ if (Number.isInteger(idx) && idx >= 1 && idx <= options.length) {
179
+ return options[idx - 1].value;
180
+ }
181
+ }
182
+ }
183
+
184
+ async function confirm(message, defaultValue) {
185
+ const suffix = defaultValue ? " [Y/n] " : " [y/N] ";
186
+ const answer = (await question(message + suffix)).toLowerCase();
187
+ if (!answer) return defaultValue;
188
+ return answer === "y" || answer === "yes";
189
+ }
190
+
191
+ function semverLessThan(a, b) {
192
+ const pa = String(a).replace(/-.*/, "").split(".").map(Number);
193
+ const pb = String(b).replace(/-.*/, "").split(".").map(Number);
194
+ for (let i = 0; i < 3; i += 1) {
195
+ if ((pa[i] || 0) < (pb[i] || 0)) return true;
196
+ if ((pa[i] || 0) > (pb[i] || 0)) return false;
197
+ }
198
+ return false;
199
+ }
200
+
201
+ function getGloballyInstalledVersion() {
202
+ try {
203
+ const out = runSilent("npm", ["list", "-g", PKG, "--depth=0"], { timeout: 15000 });
204
+ const match = out.match(/@lark-project\/meegle@([^\s]+)/);
205
+ return match ? match[1] : "unknown";
206
+ } catch (_) {
207
+ return null;
208
+ }
209
+ }
210
+
211
+ function getLatestVersion() {
212
+ try {
213
+ const out = runSilent("npm", ["view", PKG, "version"], { timeout: 15000 }).trim();
214
+ return /^\d+\.\d+\.\d+/.test(out) ? out : null;
215
+ } catch (_) {
216
+ return null;
217
+ }
218
+ }
219
+
220
+ function whichMeegle() {
221
+ try {
222
+ const prefix = runSilent("npm", ["prefix", "-g"], { timeout: 15000 }).trim();
223
+ const bin = isWindows ? path.join(prefix, "meegle.cmd") : path.join(prefix, "bin", "meegle");
224
+ if (fs.existsSync(bin)) return bin;
225
+ } catch (_) {
226
+ // fall through
227
+ }
228
+ try {
229
+ const cmd = isWindows ? "where" : "which";
230
+ const out = runSilent(cmd, ["meegle"], { timeout: 15000 });
231
+ return out.split(/\r?\n/).map((s) => s.trim()).find(Boolean) || null;
232
+ } catch (_) {
233
+ return null;
234
+ }
235
+ }
236
+
237
+ function normalizeHost(raw) {
238
+ const trimmed = String(raw || "").trim();
239
+ if (!trimmed) return "";
240
+ try {
241
+ const withScheme = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
242
+ return new URL(withScheme).host;
243
+ } catch (_) {
244
+ return trimmed.replace(/^https?:\/\//, "").replace(/\/.*$/, "");
245
+ }
246
+ }
247
+
248
+ async function selectLanguage(opts) {
249
+ if (opts.lang) return opts.lang;
250
+ const locale = (process.env.LANG || process.env.LANGUAGE || "").toLowerCase();
251
+ if (!process.stdin.isTTY || opts.yes) return locale.includes("zh") ? "zh" : "en";
252
+ return select("Select language / 请选择语言", [
253
+ { label: "English", value: "en" },
254
+ { label: "中文", value: "zh" },
255
+ ]);
256
+ }
257
+
258
+ async function stepInstallGlobally(msg) {
259
+ const installed = getGloballyInstalledVersion();
260
+ const latest = getLatestVersion();
261
+ const needsUpgrade = installed && latest && semverLessThan(installed, latest);
262
+ if (installed && !needsUpgrade) {
263
+ console.log(fmt(msg.installedSkip, installed));
264
+ return;
265
+ }
266
+ console.log(needsUpgrade ? fmt(msg.upgrading, PKG, installed, latest) : fmt(msg.installing, PKG));
267
+ run("npm", ["install", "-g", PKG], { timeout: 120000 });
268
+ console.log(msg.installed);
269
+ }
270
+
271
+ async function stepInstallSkills(msg) {
272
+ console.log(msg.skills);
273
+ // `skills add` is idempotent (with `-y`): re-running on an already-installed
274
+ // skill just upgrades / no-ops. We always run it instead of trying to detect
275
+ // prior installs — name-substring detection mis-fired on sibling skills like
276
+ // `meegle-plugin` and silently skipped the core skill.
277
+ run("npm", ["exec", "--yes", "--package=skills", "--", "skills", "add", SKILLS_REPO, "-y", "-g"], { timeout: 120000 });
278
+ console.log(msg.skillsDone);
279
+ }
280
+
281
+ function getConfiguredHost(meegleBin) {
282
+ try {
283
+ const out = runSilent(meegleBin, ["config", "get", "host"], { timeout: 15000 }).trim();
284
+ if (!out || out === "(not set)") return "";
285
+ return normalizeHost(out);
286
+ } catch (_) {
287
+ return "";
288
+ }
289
+ }
290
+
291
+ async function resolveHost(opts, msg, meegleBin) {
292
+ if (opts.host) return normalizeHost(opts.host);
293
+ const existing = getConfiguredHost(meegleBin);
294
+ if (existing) {
295
+ if (!process.stdin.isTTY || opts.yes) return existing;
296
+ if (await confirm(fmt(msg.hostExisting, existing), true)) return existing;
297
+ }
298
+ if (!process.stdin.isTTY || opts.yes) {
299
+ console.log(fmt(msg.nonTtyHost, DEFAULT_HOST));
300
+ return DEFAULT_HOST;
301
+ }
302
+ const selected = await select(msg.hostSelect, [
303
+ ...PRESET_HOSTS,
304
+ { label: msg.hostCustom, value: "__custom__" },
305
+ ]);
306
+ if (selected !== "__custom__") return selected;
307
+ return normalizeHost(await question(msg.hostInput));
308
+ }
309
+
310
+ function configureHost(meegleBin, host, msg) {
311
+ run(meegleBin, ["config", "set", "host", host], { timeout: 30000 });
312
+ console.log(fmt(msg.hostConfigured, host));
313
+ }
314
+
315
+ function isAuthenticated(meegleBin) {
316
+ const result = runCapture(meegleBin, ["auth", "status", "--format", "json"], { timeout: 15000 });
317
+ const text = result.stdout || "";
318
+ try {
319
+ const parsed = JSON.parse(text);
320
+ return !!parsed.authenticated;
321
+ } catch (_) {
322
+ return result.status === 0;
323
+ }
324
+ }
325
+
326
+ async function stepAuth(meegleBin, host, opts, msg) {
327
+ if (!opts.auth) {
328
+ console.log(msg.authSkip);
329
+ return;
330
+ }
331
+ if (isAuthenticated(meegleBin)) {
332
+ console.log(msg.authAlready);
333
+ return;
334
+ }
335
+ const useDeviceCode = opts.deviceCode || !process.stdin.isTTY;
336
+ if (!useDeviceCode && !opts.yes) {
337
+ const shouldLogin = await confirm(msg.authConfirm, true);
338
+ if (!shouldLogin) {
339
+ console.log(msg.authSkip);
340
+ return;
341
+ }
342
+ }
343
+ console.log(useDeviceCode ? msg.authDevice : msg.authBrowser);
344
+ const args = ["auth", "login", "--host", host];
345
+ if (useDeviceCode) args.push("--device-code");
346
+ run(meegleBin, args, { timeout: 900000 });
347
+ }
348
+
349
+ async function main(argv = process.argv.slice(2)) {
350
+ const opts = parseArgs(argv);
351
+ const lang = await selectLanguage(opts);
352
+ const msg = messages[lang];
353
+ console.log(msg.setup);
354
+
355
+ await stepInstallGlobally(msg);
356
+ if (opts.skills) {
357
+ await stepInstallSkills(msg);
358
+ }
359
+
360
+ const meegleBin = whichMeegle();
361
+ if (!meegleBin) {
362
+ throw new Error("meegle was installed but no global executable was found in PATH");
363
+ }
364
+ const host = await resolveHost(opts, msg, meegleBin);
365
+ configureHost(meegleBin, host, msg);
366
+ await stepAuth(meegleBin, host, opts, msg);
367
+
368
+ console.log(msg.done);
369
+ }
370
+
371
+ if (require.main === module) {
372
+ main().catch((err) => {
373
+ console.error(err && err.message ? err.message : err);
374
+ process.exit(1);
375
+ });
376
+ }
377
+
378
+ module.exports = { main };
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/bin/meegle.js CHANGED
@@ -15,30 +15,38 @@ const arch = os.arch();
15
15
  const ext = platform === "win32" ? ".exe" : "";
16
16
  const binName = `meegle-${platform}-${arch}${ext}`;
17
17
  const binPath = path.join(__dirname, binName);
18
+ const args = process.argv.slice(2);
18
19
 
19
- try {
20
- fs.accessSync(binPath, fs.constants.X_OK);
21
- } catch {
22
- const detected = `${platform}-${arch}`;
23
- const isSupported = SUPPORTED.includes(detected);
24
- console.error(
25
- isSupported
26
- ? `meegle binary is missing or not executable.\n` +
27
- `Detected platform: ${detected}\n` +
28
- `Expected binary at: ${binPath}\n` +
29
- `Try reinstalling: npm i -g @lark-project/meegle\n` +
30
- `If the problem persists, please file an issue with the output of: node -v && npm -v`
31
- : `Unsupported platform: ${detected}\n` +
32
- `Supported platforms: ${SUPPORTED.join(", ")}`
33
- );
34
- process.exit(1);
35
- }
20
+ if (args[0] === "install") {
21
+ Promise.resolve(require("./install-wizard.js").main(args.slice(1))).catch((err) => {
22
+ console.error("Unexpected install error:", err && err.message ? err.message : err);
23
+ process.exit(1);
24
+ });
25
+ } else {
26
+ try {
27
+ fs.accessSync(binPath, fs.constants.X_OK);
28
+ } catch {
29
+ const detected = `${platform}-${arch}`;
30
+ const isSupported = SUPPORTED.includes(detected);
31
+ console.error(
32
+ isSupported
33
+ ? `meegle binary is missing or not executable.\n` +
34
+ `Detected platform: ${detected}\n` +
35
+ `Expected binary at: ${binPath}\n` +
36
+ `Try reinstalling: npm i -g @lark-project/meegle\n` +
37
+ `If the problem persists, please file an issue with the output of: node -v && npm -v`
38
+ : `Unsupported platform: ${detected}\n` +
39
+ `Supported platforms: ${SUPPORTED.join(", ")}`
40
+ );
41
+ process.exit(1);
42
+ }
36
43
 
37
- const result = spawnSync(binPath, process.argv.slice(2), { stdio: "inherit" });
44
+ const result = spawnSync(binPath, args, { stdio: "inherit" });
38
45
 
39
- // Re-raise the signal so parent shells see the real cause (e.g. 130 for SIGINT)
40
- // instead of a generic exit 1.
41
- if (result.signal) {
42
- process.kill(process.pid, result.signal);
46
+ // Re-raise the signal so parent shells see the real cause (e.g. 130 for SIGINT)
47
+ // instead of a generic exit 1.
48
+ if (result.signal) {
49
+ process.kill(process.pid, result.signal);
50
+ }
51
+ process.exit(result.status ?? 1);
43
52
  }
44
- process.exit(result.status ?? 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-project/meegle",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "Agent-First CLI for Meegle (Lark Project)",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/larksuite/meegle-cli#readme",
@@ -28,6 +28,7 @@
28
28
  ],
29
29
  "files": [
30
30
  "bin/meegle.js",
31
+ "bin/install-wizard.js",
31
32
  "bin/meegle-*",
32
33
  "README.md",
33
34
  "README.zh-CN.md",