@leviyuan/lodestar 0.2.4 → 0.2.5

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
@@ -50,9 +50,9 @@ AI 不是帮手,是倍率。它放大的不是体力,是你——你的直觉、
50
50
  | --- | --- |
51
51
  | `hi` | 未运行时启动;运行中弹一张**状态卡片** |
52
52
  | `stop` | 软打断当前 turn + 清空 type-ahead 排队;子进程保活,刚排队中的消息会被打 `CrossMark` 反应表示取消 |
53
- | `kill` | 优雅关闭 Claude 进程;记住 `sessionId`,下次 `restart` 还能 resume |
54
- | `restart` | 用上一次的 `sessionId` 重启会话(保留上下文) |
55
- | `clear` | 杀掉进程并启动一个全新 session(等价于 Claude Code 的 `/clear`) |
53
+ | `kill` | 优雅关闭 Claude 进程;`sessionId` 仍记在磁盘,下次 `restart` 还能 resume |
54
+ | `restart` | 用上一次的 `sessionId` 重启会话(保留上下文);无进程时也能用,等于"恢复上一会话" |
55
+ | `clear` | 杀掉当前进程并启动一个全新 session(等价于 Claude Code 的 `/clear`);**无进程时无效** |
56
56
 
57
57
  > 这五个词被全局保留:在群里发 "hi" 当问候也会触发控制台卡片,不会到 Claude 那边。换来的是手机上单手打字的便利。
58
58
 
package/daemon.ts CHANGED
@@ -192,9 +192,6 @@ async function handleCardAction(data: any): Promise<any> {
192
192
  case 'permission':
193
193
  await session.onPermissionDecision(value.request_id, value.decision, userId)
194
194
  return { toast: { type: value.decision === 'deny' ? 'error' : 'success', content: '已处理' } }
195
- case 'console':
196
- await session.onConsoleAction(value.action)
197
- return { toast: { type: 'info', content: value.action } }
198
195
  case 'menu':
199
196
  await session.onUserMessage(`(menu choice ${value.choice + 1})`)
200
197
  return { toast: { type: 'success', content: 'OK' } }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leviyuan/lodestar",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/src/cards.ts CHANGED
@@ -550,7 +550,6 @@ interface ConsoleOpts {
550
550
  cumStats?: { tokens: number; costUsd: number; turns: number }
551
551
  lastTurn?: { tokens: number; costUsd: number; durationMs: number }
552
552
  sessionId?: string | null
553
- hasSession: boolean
554
553
  }
555
554
 
556
555
  /** Format token counts as a compact human-readable string: 1,234 → 1.2K. */
@@ -656,7 +655,7 @@ export function consoleUsageContent(
656
655
  export function consoleCard(opts: ConsoleOpts): object {
657
656
  const {
658
657
  sessionName, status, model, effort, uptimeMs, peers, usage,
659
- contextTokens, contextLimit, cumStats, lastTurn, sessionId, hasSession,
658
+ contextTokens, contextLimit, cumStats, lastTurn, sessionId,
660
659
  } = opts
661
660
  const statusEmoji = {
662
661
  idle: '🟢 闲', working: '⚙️ 工作中', awaiting_permission: '🔐 等审批',
@@ -698,12 +697,6 @@ export function consoleCard(opts: ConsoleOpts): object {
698
697
  lines.push(`**🆔 session** \`${sessionId.slice(0, 8)}…\``)
699
698
  }
700
699
 
701
- void hasSession // accept the field for caller compat; lifecycle is now
702
- // driven by bare-word commands (`hi` / `kill` / `restart` / `clear`),
703
- // not buttons — keeps the panel pure-readout and one-handed mobile-
704
- // friendly. The 'refresh' / 'ls' actions stay in onConsoleAction for
705
- // backward compat with any still-floating older cards in chat history.
706
-
707
700
  const template = status === 'working' ? 'blue'
708
701
  : status === 'awaiting_permission' ? 'orange'
709
702
  : status === 'stopped' ? 'grey'
package/src/session.ts CHANGED
@@ -398,9 +398,23 @@ export class Session {
398
398
  await this.stop()
399
399
  return true
400
400
  case 'restart':
401
+ // resume the prior conversation — kills the current proc (if
402
+ // any) and spawns a new one with `--resume <lastSessionId>`.
403
+ // If no process is running, this is how the user gets back the
404
+ // previous conversation after a `kill` or a daemon crash.
401
405
  await this.restart(true)
402
406
  return true
403
407
  case 'clear':
408
+ // "throw away current conversation, start a new one". By design
409
+ // this only makes sense when there IS a current conversation:
410
+ // calling clear from stopped state is a no-op (user-confirmed
411
+ // 2026-05-16) — we don't want a stray `clear` to silently spawn
412
+ // a fresh session the user didn't ask for. To start from cold,
413
+ // use `hi`.
414
+ if (!this.isRunning()) {
415
+ await feishu.sendText(this.chatId, `⚪ session "${this.sessionName}" 当前未运行,clear 无效;用 \`hi\` 启动或 \`restart\` 恢复上一会话`)
416
+ return true
417
+ }
404
418
  await this.restart(false)
405
419
  return true
406
420
  }
@@ -438,7 +452,6 @@ export class Session {
438
452
  }
439
453
  : undefined,
440
454
  sessionId: this.proc?.sessionId ?? this.lastSessionId,
441
- hasSession: this.isRunning(),
442
455
  })
443
456
  const messageId = await feishu.sendCard(this.chatId, card)
444
457
  if (!messageId) return
@@ -777,19 +790,6 @@ export class Session {
777
790
  }
778
791
  }
779
792
 
780
- async onConsoleAction(action: string): Promise<void> {
781
- log(`session "${this.sessionName}": console action=${action}`)
782
- switch (action) {
783
- case 'interrupt': this.interrupt(); break
784
- case 'clear': await this.restart(false); break
785
- case 'stop': await this.stop(); break
786
- case 'start': await this.start(); break
787
- case 'resume': await this.restart(true); break
788
- case 'refresh': await this.showConsole(); break
789
- case 'ls': await feishu.sendText(this.chatId, `📁 ${this.workDir}`); break
790
- }
791
- }
792
-
793
793
  // ── Wiring Claude → Feishu ─────────────────────────────────────────
794
794
  private wireProc(p: ClaudeProcess): void {
795
795
  p.on('init', () => {