remote-claude 0.1.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.
@@ -0,0 +1,518 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Remote Claude - 双端共享 Claude CLI 工具
4
+
5
+ 命令:
6
+ start <name> 启动新会话(在 tmux 中)
7
+ attach <name> 连接到已有会话
8
+ list 列出所有会话
9
+ kill <name> 终止会话
10
+ lark 飞书客户端管理(start/stop/restart/status)
11
+ """
12
+
13
+ import argparse
14
+ import os
15
+ import sys
16
+ import subprocess
17
+ import time
18
+ import signal
19
+ from pathlib import Path
20
+
21
+ # 确保项目根目录在 sys.path 中,以便 import client / server 子模块
22
+ _PROJECT_ROOT = str(Path(__file__).parent)
23
+ if _PROJECT_ROOT not in sys.path:
24
+ sys.path.insert(0, _PROJECT_ROOT)
25
+
26
+ from utils.session import (
27
+ get_socket_path, get_pid_file, ensure_socket_dir,
28
+ get_tmux_session_name, tmux_session_exists, tmux_create_session,
29
+ tmux_kill_session,
30
+ list_active_sessions, is_session_active, cleanup_session,
31
+ is_lark_running, get_lark_pid, get_lark_status, get_lark_pid_file,
32
+ save_lark_status, cleanup_lark
33
+ )
34
+
35
+
36
+ # 获取脚本所在目录
37
+ SCRIPT_DIR = Path(__file__).parent.absolute()
38
+
39
+
40
+ def cmd_start(args):
41
+ """启动新会话"""
42
+ session_name = args.name
43
+
44
+ # 检查会话是否已存在
45
+ if is_session_active(session_name):
46
+ print(f"错误: 会话 '{session_name}' 已存在")
47
+ print(f"使用 'remote-claude attach {session_name}' 连接")
48
+ return 1
49
+
50
+ # 检查 tmux 会话是否存在
51
+ if tmux_session_exists(session_name):
52
+ print(f"错误: tmux 会话 'rc-{session_name}' 已存在")
53
+ print("请先使用 'remote-claude kill {session_name}' 清理")
54
+ return 1
55
+
56
+ ensure_socket_dir()
57
+
58
+ # 构建 server 命令
59
+ server_script = SCRIPT_DIR / "server" / "server.py"
60
+ claude_args = args.claude_args if args.claude_args else []
61
+ claude_args_str = " ".join(f"'{arg}'" for arg in claude_args)
62
+ debug_flag = " --debug-screen" if getattr(args, "debug_screen", False) else ""
63
+ debug_verbose_flag = " --debug-verbose" if getattr(args, "debug_verbose", False) else ""
64
+
65
+ # 捕获用户终端环境变量(tmux 会覆盖这些值,导致 Claude CLI 无法启用 kitty keyboard protocol)
66
+ env_prefix = ""
67
+ for key in ('TERM_PROGRAM', 'TERM_PROGRAM_VERSION', 'COLORTERM'):
68
+ val = os.environ.get(key)
69
+ if val:
70
+ env_prefix += f"{key}='{val}' "
71
+
72
+ server_cmd = f"{env_prefix}uv run --project '{SCRIPT_DIR}' python3 '{server_script}'{debug_flag}{debug_verbose_flag} -- '{session_name}' {claude_args_str}"
73
+
74
+ print(f"启动会话: {session_name}")
75
+
76
+ # 创建 tmux 会话,运行 server(detached,仅后台)
77
+ if not tmux_create_session(session_name, server_cmd, detached=True):
78
+ print("错误: 无法创建 tmux 会话")
79
+ return 1
80
+
81
+ # 等待 server 启动
82
+ socket_path = get_socket_path(session_name)
83
+ for _ in range(50): # 最多等待 5 秒
84
+ if socket_path.exists():
85
+ break
86
+ time.sleep(0.1)
87
+ else:
88
+ print("错误: Server 启动超时")
89
+ tmux_kill_session(session_name)
90
+ return 1
91
+
92
+ print(f"会话已启动: rc-{session_name}")
93
+ print(f"正在连接...")
94
+
95
+ # 直接在前台运行 client(不走 tmux),让终端能力协商序列
96
+ # (如 kitty keyboard protocol)直接在 Claude CLI ↔ 用户终端之间流通,
97
+ # 从而支持 Shift+Enter 等扩展键
98
+ from client.client import run_client
99
+ run_client(session_name)
100
+
101
+ return 0
102
+
103
+
104
+ def cmd_attach(args):
105
+ """连接到已有会话"""
106
+ session_name = args.name
107
+
108
+ # 检查会话是否存在
109
+ if not is_session_active(session_name):
110
+ print(f"错误: 会话 '{session_name}' 不存在")
111
+ print("使用 'remote-claude list' 查看可用会话")
112
+ return 1
113
+
114
+ print(f"连接到会话: {session_name}")
115
+
116
+ # 直接运行 client(不通过 tmux)
117
+ from client.client import run_client
118
+ run_client(session_name)
119
+
120
+ return 0
121
+
122
+
123
+ def cmd_list(args):
124
+ """列出所有会话"""
125
+ sessions = list_active_sessions()
126
+
127
+ if not sessions:
128
+ print("没有活跃的会话")
129
+ return 0
130
+
131
+ print("活跃会话:")
132
+ print("-" * 60)
133
+ print(f"{'名称':<20} {'PID':<10} {'tmux':<10} {'Socket'}")
134
+ print("-" * 60)
135
+
136
+ for s in sessions:
137
+ tmux_status = "是" if s["tmux"] else "否"
138
+ print(f"{s['name']:<20} {s['pid']:<10} {tmux_status:<10} {s['socket']}")
139
+
140
+ print("-" * 60)
141
+ print(f"共 {len(sessions)} 个会话")
142
+
143
+ return 0
144
+
145
+
146
+ def cmd_kill(args):
147
+ """终止会话"""
148
+ session_name = args.name
149
+
150
+ # 检查会话是否存在
151
+ if not is_session_active(session_name) and not tmux_session_exists(session_name):
152
+ print(f"错误: 会话 '{session_name}' 不存在")
153
+ return 1
154
+
155
+ print(f"终止会话: {session_name}")
156
+
157
+ # 终止 tmux 会话
158
+ if tmux_session_exists(session_name):
159
+ tmux_kill_session(session_name)
160
+ print(" - tmux 会话已终止")
161
+
162
+ # 清理文件
163
+ cleanup_session(session_name)
164
+ print(" - 文件已清理")
165
+
166
+ print("完成")
167
+ return 0
168
+
169
+
170
+ def cmd_status(args):
171
+ """显示会话状态(连接到会话并获取状态)"""
172
+ session_name = args.name
173
+
174
+ if not is_session_active(session_name):
175
+ print(f"错误: 会话 '{session_name}' 不存在")
176
+ return 1
177
+
178
+ # TODO: 实现状态查询
179
+ print(f"会话 '{session_name}' 状态:")
180
+ print(" (功能开发中)")
181
+ return 0
182
+
183
+
184
+ def cmd_lark_start(args):
185
+ """启动飞书客户端(守护进程)"""
186
+ if is_lark_running():
187
+ print("飞书客户端已在运行")
188
+ status = get_lark_status()
189
+ if status:
190
+ print(f"PID: {status['pid']}")
191
+ print(f"启动时间: {status['start_time']}")
192
+ print(f"运行时长: {status['uptime']}")
193
+ print("\n使用 'remote-claude lark stop' 停止")
194
+ return 1
195
+
196
+ print("正在启动飞书客户端...")
197
+
198
+ ensure_socket_dir()
199
+
200
+ # 启动守护进程(使用 -m 模块方式运行,确保相对导入正常工作)
201
+ log_file = SCRIPT_DIR / "lark_client.log"
202
+
203
+ try:
204
+ # 启动进程
205
+ process = subprocess.Popen(
206
+ ["uv", "run", "--project", str(SCRIPT_DIR), "python3", "-m", "lark_client.main"],
207
+ stdout=open(log_file, 'a'),
208
+ stderr=subprocess.STDOUT,
209
+ start_new_session=True, # 创建新的进程组
210
+ cwd=str(SCRIPT_DIR)
211
+ )
212
+
213
+ # 保存 PID
214
+ pid = process.pid
215
+ get_lark_pid_file().write_text(str(pid))
216
+ save_lark_status(pid)
217
+
218
+ # 等待一下确认启动成功
219
+ time.sleep(1)
220
+
221
+ if is_lark_running():
222
+ print(f"✓ 飞书客户端已启动")
223
+ print(f" PID: {pid}")
224
+ print(f" 日志: {log_file}")
225
+ print(f"\n使用 'python3 remote_claude.py lark status' 查看状态")
226
+ print(f"使用 'python3 remote_claude.py lark stop' 停止")
227
+ return 0
228
+ else:
229
+ print("✗ 启动失败,请查看日志:")
230
+ print(f" tail -f {log_file}")
231
+ cleanup_lark()
232
+ return 1
233
+
234
+ except Exception as e:
235
+ print(f"✗ 启动失败: {e}")
236
+ cleanup_lark()
237
+ return 1
238
+
239
+
240
+ def cmd_lark_stop(args):
241
+ """停止飞书客户端"""
242
+ if not is_lark_running():
243
+ print("飞书客户端未运行")
244
+ cleanup_lark()
245
+ return 0
246
+
247
+ pid = get_lark_pid()
248
+ if pid is None:
249
+ print("无法获取 PID,清理残留文件")
250
+ cleanup_lark()
251
+ return 1
252
+
253
+ print(f"正在停止飞书客户端 (PID: {pid})...")
254
+
255
+ try:
256
+ # 发送 SIGTERM 信号
257
+ os.kill(pid, signal.SIGTERM)
258
+
259
+ # 等待进程退出
260
+ for i in range(50): # 最多等待 5 秒
261
+ if not is_lark_running():
262
+ break
263
+ time.sleep(0.1)
264
+ else:
265
+ # 如果还没退出,强制终止
266
+ print("进程未响应,强制终止...")
267
+ os.kill(pid, signal.SIGKILL)
268
+ time.sleep(0.5)
269
+
270
+ if not is_lark_running():
271
+ print("✓ 飞书客户端已停止")
272
+ cleanup_lark()
273
+ return 0
274
+ else:
275
+ print("✗ 无法停止进程,请手动终止:")
276
+ print(f" kill -9 {pid}")
277
+ return 1
278
+
279
+ except ProcessLookupError:
280
+ print("进程已不存在,清理残留文件")
281
+ cleanup_lark()
282
+ return 0
283
+ except Exception as e:
284
+ print(f"✗ 停止失败: {e}")
285
+ return 1
286
+
287
+
288
+ def cmd_lark_restart(args):
289
+ """重启飞书客户端"""
290
+ print("正在重启飞书客户端...")
291
+
292
+ # 先停止
293
+ if is_lark_running():
294
+ cmd_lark_stop(args)
295
+ time.sleep(1)
296
+
297
+ # 再启动
298
+ return cmd_lark_start(args)
299
+
300
+
301
+ def cmd_lark_status(args):
302
+ """显示飞书客户端状态"""
303
+ if not is_lark_running():
304
+ print("飞书客户端未运行")
305
+ print("\n使用 'python3 remote_claude.py lark start' 启动")
306
+ return 0
307
+
308
+ status = get_lark_status()
309
+ if status is None:
310
+ print("无法获取状态信息")
311
+ return 1
312
+
313
+ print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
314
+ print("飞书客户端状态")
315
+ print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
316
+ print(f"状态: 运行中 ✓")
317
+ print(f"PID: {status['pid']}")
318
+ print(f"启动时间: {status['start_time']}")
319
+ print(f"运行时长: {status['uptime']}")
320
+
321
+ # 检查日志文件
322
+ log_file = SCRIPT_DIR / "lark_client.log"
323
+ if log_file.exists():
324
+ print(f"日志文件: {log_file}")
325
+ print(f"日志大小: {log_file.stat().st_size / 1024:.1f} KB")
326
+
327
+ print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
328
+
329
+ # 显示最近的日志(最后 5 行)
330
+ if log_file.exists():
331
+ print("\n最近日志:")
332
+ print("-" * 40)
333
+ try:
334
+ with open(log_file, 'r') as f:
335
+ lines = f.readlines()
336
+ for line in lines[-5:]:
337
+ print(f" {line.rstrip()}")
338
+ except Exception as e:
339
+ print(f" 无法读取日志: {e}")
340
+ print("-" * 40)
341
+
342
+ return 0
343
+
344
+
345
+ def cmd_stats(args):
346
+ """显示使用统计"""
347
+ _PROJECT_ROOT = str(Path(__file__).parent)
348
+ if _PROJECT_ROOT not in sys.path:
349
+ sys.path.insert(0, _PROJECT_ROOT)
350
+
351
+ from stats.query import query_summary, reset_stats
352
+ from stats import report_daily
353
+
354
+ if getattr(args, 'reset', False):
355
+ print(reset_stats())
356
+ return 0
357
+
358
+ if getattr(args, 'report', False):
359
+ report_daily()
360
+ return 0
361
+
362
+ range_str = getattr(args, 'range', 'today') or 'today'
363
+ session_name = getattr(args, 'session', '') or ''
364
+ detail = getattr(args, 'detail', False)
365
+
366
+ print(query_summary(range_str=range_str, session_name=session_name, detail=detail))
367
+ return 0
368
+
369
+
370
+ def cmd_lark(args):
371
+ """飞书客户端管理(兼容旧命令)"""
372
+ # 如果没有子命令,默认显示状态或启动
373
+ if is_lark_running():
374
+ return cmd_lark_status(args)
375
+ else:
376
+ print("飞书客户端未运行")
377
+ print("\n可用命令:")
378
+ print(" python3 remote_claude.py lark start - 启动客户端")
379
+ print(" python3 remote_claude.py lark stop - 停止客户端")
380
+ print(" python3 remote_claude.py lark restart - 重启客户端")
381
+ print(" python3 remote_claude.py lark status - 查看状态")
382
+ return 0
383
+
384
+
385
+ def main():
386
+ parser = argparse.ArgumentParser(
387
+ description="Remote Claude - 双端共享 Claude CLI 工具",
388
+ formatter_class=argparse.RawDescriptionHelpFormatter,
389
+ epilog="""
390
+ 示例:
391
+ %(prog)s start mywork 启动名为 mywork 的会话
392
+ %(prog)s attach mywork 连接到 mywork 会话
393
+ %(prog)s list 列出所有会话
394
+ %(prog)s kill mywork 终止 mywork 会话
395
+
396
+ 飞书客户端:
397
+ %(prog)s lark start 启动飞书客户端
398
+ %(prog)s lark stop 停止飞书客户端
399
+ %(prog)s lark restart 重启飞书客户端
400
+ %(prog)s lark status 查看飞书客户端状态
401
+
402
+ 终端控制:
403
+ Ctrl+D 断开连接
404
+
405
+ 飞书命令:
406
+ /attach <名称> 连接到会话
407
+ /detach 断开连接
408
+ /list 列出会话
409
+ /help 显示帮助
410
+
411
+ 使用统计:
412
+ %(prog)s stats 今日概览
413
+ %(prog)s stats --range 7d 最近 7 天
414
+ %(prog)s stats --detail 详细分类
415
+ %(prog)s stats --session mywork 按会话筛选
416
+ %(prog)s stats --reset 清空数据
417
+ """
418
+ )
419
+
420
+ subparsers = parser.add_subparsers(dest="command", help="命令")
421
+
422
+ # start 命令
423
+ start_parser = subparsers.add_parser("start", help="启动新会话")
424
+ start_parser.add_argument("name", help="会话名称")
425
+ start_parser.add_argument(
426
+ "claude_args",
427
+ nargs="*",
428
+ help="传递给 Claude 的参数"
429
+ )
430
+ start_parser.add_argument(
431
+ "--debug-screen",
432
+ action="store_true",
433
+ help="开启 pyte 屏幕快照调试日志(每次 flush 写入 /tmp/remote-claude/<name>_screen.log)"
434
+ )
435
+ start_parser.add_argument(
436
+ "--debug-verbose",
437
+ action="store_true",
438
+ help="debug 日志输出完整诊断信息(indicator、repr 等),默认只输出 ansi_render"
439
+ )
440
+ start_parser.set_defaults(func=cmd_start)
441
+
442
+ # attach 命令
443
+ attach_parser = subparsers.add_parser("attach", help="连接到已有会话")
444
+ attach_parser.add_argument("name", help="会话名称")
445
+ attach_parser.set_defaults(func=cmd_attach)
446
+
447
+ # list 命令
448
+ list_parser = subparsers.add_parser("list", help="列出所有会话")
449
+ list_parser.set_defaults(func=cmd_list)
450
+
451
+ # kill 命令
452
+ kill_parser = subparsers.add_parser("kill", help="终止会话")
453
+ kill_parser.add_argument("name", help="会话名称")
454
+ kill_parser.set_defaults(func=cmd_kill)
455
+
456
+ # status 命令
457
+ status_parser = subparsers.add_parser("status", help="显示会话状态")
458
+ status_parser.add_argument("name", help="会话名称")
459
+ status_parser.set_defaults(func=cmd_status)
460
+
461
+ # lark 命令(带子命令)
462
+ lark_parser = subparsers.add_parser("lark", help="飞书客户端管理")
463
+ lark_subparsers = lark_parser.add_subparsers(dest="lark_command", help="飞书客户端操作")
464
+
465
+ # lark start
466
+ lark_start_parser = lark_subparsers.add_parser("start", help="启动飞书客户端")
467
+ lark_start_parser.set_defaults(func=cmd_lark_start)
468
+
469
+ # lark stop
470
+ lark_stop_parser = lark_subparsers.add_parser("stop", help="停止飞书客户端")
471
+ lark_stop_parser.set_defaults(func=cmd_lark_stop)
472
+
473
+ # lark restart
474
+ lark_restart_parser = lark_subparsers.add_parser("restart", help="重启飞书客户端")
475
+ lark_restart_parser.set_defaults(func=cmd_lark_restart)
476
+
477
+ # lark status
478
+ lark_status_parser = lark_subparsers.add_parser("status", help="查看飞书客户端状态")
479
+ lark_status_parser.set_defaults(func=cmd_lark_status)
480
+
481
+ # 如果只输入 lark 没有子命令,使用默认处理
482
+ lark_parser.set_defaults(func=cmd_lark)
483
+
484
+ # stats 命令
485
+ stats_parser = subparsers.add_parser("stats", help="查看使用统计")
486
+ stats_parser.add_argument(
487
+ "--range", metavar="RANGE", default="today",
488
+ help="时间范围:today(默认)、7d、30d、90d"
489
+ )
490
+ stats_parser.add_argument(
491
+ "--detail", action="store_true",
492
+ help="显示详细分类"
493
+ )
494
+ stats_parser.add_argument(
495
+ "--session", metavar="NAME", default="",
496
+ help="按会话名筛选"
497
+ )
498
+ stats_parser.add_argument(
499
+ "--reset", action="store_true",
500
+ help="清空所有统计数据"
501
+ )
502
+ stats_parser.add_argument(
503
+ "--report", action="store_true",
504
+ help="立即触发 Mixpanel 聚合上报"
505
+ )
506
+ stats_parser.set_defaults(func=cmd_stats)
507
+
508
+ args = parser.parse_args()
509
+
510
+ if args.command is None:
511
+ parser.print_help()
512
+ return 0
513
+
514
+ return args.func(args)
515
+
516
+
517
+ if __name__ == "__main__":
518
+ sys.exit(main())
@@ -0,0 +1,40 @@
1
+ #!/bin/bash
2
+ # 检查 .env 中 FEISHU_APP_ID/APP_SECRET 是否已配置,未配置则交互引导
3
+ # 用法: source scripts/check-env.sh "$INSTALL_DIR"
4
+
5
+ INSTALL_DIR="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
6
+ ENV_FILE="$INSTALL_DIR/.env"
7
+ ENV_OK=false
8
+
9
+ if [ -f "$ENV_FILE" ]; then
10
+ APP_ID=$(grep -E '^FEISHU_APP_ID=' "$ENV_FILE" | cut -d= -f2)
11
+ APP_SECRET=$(grep -E '^FEISHU_APP_SECRET=' "$ENV_FILE" | cut -d= -f2)
12
+ if [ -n "$APP_ID" ] && [ "$APP_ID" != "cli_xxxxx" ] && \
13
+ [ -n "$APP_SECRET" ] && [ "$APP_SECRET" != "xxxxx" ]; then
14
+ ENV_OK=true
15
+ fi
16
+ fi
17
+
18
+ if [ "$ENV_OK" = false ]; then
19
+ echo ""
20
+ echo "飞书客户端尚未配置,需要填写应用凭证。"
21
+ echo "(在飞书开发者后台创建应用获取: https://open.feishu.cn/app)"
22
+ echo ""
23
+ read -p "FEISHU_APP_ID: " INPUT_APP_ID
24
+ read -p "FEISHU_APP_SECRET: " INPUT_APP_SECRET
25
+
26
+ if [ -z "$INPUT_APP_ID" ] || [ -z "$INPUT_APP_SECRET" ]; then
27
+ echo "错误: APP_ID 和 APP_SECRET 不能为空"
28
+ exit 1
29
+ fi
30
+
31
+ cp "$INSTALL_DIR/.env.example" "$ENV_FILE"
32
+ sed -i.bak "s/^FEISHU_APP_ID=.*/FEISHU_APP_ID=$INPUT_APP_ID/" "$ENV_FILE"
33
+ sed -i.bak "s/^FEISHU_APP_SECRET=.*/FEISHU_APP_SECRET=$INPUT_APP_SECRET/" "$ENV_FILE"
34
+ rm -f "$ENV_FILE.bak"
35
+
36
+ echo ""
37
+ echo "配置已保存到 $ENV_FILE"
38
+ echo "(可选配置如 BOT_NAME、白名单等可稍后编辑该文件)"
39
+ echo ""
40
+ fi
@@ -0,0 +1,76 @@
1
+ # remote-claude shell 自动补全(支持 bash 和 zsh)
2
+
3
+ _remote_claude_get_sessions() {
4
+ remote-claude list 2>/dev/null | awk 'NR>3 && NF>0 && !/^-/ && !/^共/ {print $1}'
5
+ }
6
+
7
+ if [[ -n "$ZSH_VERSION" ]]; then
8
+ # zsh 原生补全
9
+ # compdef 依赖 compinit 初始化过的 _comps 数组,未初始化时先调用
10
+ if ! (( ${+_comps} )); then
11
+ autoload -Uz compinit && compinit -u 2>/dev/null
12
+ fi
13
+
14
+ _remote_claude_zsh() {
15
+ local -a commands lark_cmds sessions
16
+ commands=(
17
+ 'start:启动新会话'
18
+ 'attach:连接到已有会话'
19
+ 'list:列出所有会话'
20
+ 'kill:终止会话'
21
+ 'status:显示会话状态'
22
+ 'lark:飞书客户端管理'
23
+ 'stats:查看使用统计'
24
+ )
25
+ lark_cmds=(
26
+ 'start:启动飞书客户端'
27
+ 'stop:停止飞书客户端'
28
+ 'restart:重启飞书客户端'
29
+ 'status:查看飞书客户端状态'
30
+ )
31
+
32
+ case $words[2] in
33
+ attach|kill|status)
34
+ sessions=( ${(f)"$(_remote_claude_get_sessions)"} )
35
+ _describe '会话名称' sessions
36
+ ;;
37
+ lark)
38
+ _describe 'lark 子命令' lark_cmds
39
+ ;;
40
+ *)
41
+ if [[ $CURRENT -eq 2 ]]; then
42
+ _describe '子命令' commands
43
+ fi
44
+ ;;
45
+ esac
46
+ }
47
+ compdef _remote_claude_zsh remote-claude
48
+ else
49
+ # bash 补全
50
+ _remote_claude_bash() {
51
+ local cur
52
+ cur="${COMP_WORDS[COMP_CWORD]}"
53
+
54
+ local commands="start attach list kill status lark stats"
55
+ local lark_cmds="start stop restart status"
56
+
57
+ if [[ $COMP_CWORD -eq 1 ]]; then
58
+ COMPREPLY=( $(compgen -W "$commands" -- "$cur") )
59
+ return
60
+ fi
61
+
62
+ case "${COMP_WORDS[1]}" in
63
+ attach|kill|status)
64
+ local sessions
65
+ sessions=$(_remote_claude_get_sessions)
66
+ COMPREPLY=( $(compgen -W "$sessions" -- "$cur") )
67
+ ;;
68
+ lark)
69
+ if [[ $COMP_CWORD -eq 2 ]]; then
70
+ COMPREPLY=( $(compgen -W "$lark_cmds" -- "$cur") )
71
+ fi
72
+ ;;
73
+ esac
74
+ }
75
+ complete -F _remote_claude_bash remote-claude
76
+ fi