dsh-code 1.3.0 → 1.4.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.
Files changed (92) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +2 -2
  3. package/lib/index.mjs +5115 -4390
  4. package/lib/types/app.d.ts +18 -51
  5. package/lib/types/completion.d.ts +29 -0
  6. package/lib/types/composer.d.ts +150 -0
  7. package/lib/types/git-workflow.d.ts +6 -0
  8. package/lib/types/index.d.ts +6 -188
  9. package/lib/types/locales/en.d.ts +16 -4
  10. package/lib/types/{authorization-panel.d.ts → panels/authorization-panel.d.ts} +6 -1
  11. package/lib/types/panels/completion-panel.d.ts +13 -0
  12. package/lib/types/panels/interaction-bars.d.ts +38 -0
  13. package/lib/types/{kernel-panels.d.ts → panels/kernel-panels.d.ts} +16 -37
  14. package/lib/types/{language-panel.d.ts → panels/language-panel.d.ts} +1 -1
  15. package/lib/types/panels/model-panels.d.ts +86 -0
  16. package/lib/types/{theme-panel.d.ts → panels/theme-panel.d.ts} +1 -1
  17. package/lib/types/{update-panel.d.ts → panels/update-panel.d.ts} +1 -1
  18. package/lib/types/provider-settings.d.ts +11 -0
  19. package/lib/types/render/inspector.d.ts +8 -0
  20. package/lib/types/render/text.d.ts +4 -0
  21. package/lib/types/runner/harness-gate.d.ts +83 -0
  22. package/lib/types/runner/input-history.d.ts +31 -0
  23. package/lib/types/runner/mode-cycle.d.ts +44 -0
  24. package/lib/types/runner/preferences.d.ts +40 -0
  25. package/lib/types/runner/quit.d.ts +27 -0
  26. package/lib/types/runner/search-rows.d.ts +38 -0
  27. package/lib/types/runner/session-io.d.ts +46 -0
  28. package/lib/types/runner/session-target.d.ts +42 -0
  29. package/lib/types/runner/startup-config.d.ts +33 -0
  30. package/lib/types/runner/submissions.d.ts +87 -0
  31. package/lib/types/session/attach.d.ts +39 -0
  32. package/lib/types/{store.d.ts → session/store.d.ts} +1 -1
  33. package/lib/types/{subagents.d.ts → session/subagents.d.ts} +8 -1
  34. package/lib/types/settings-file.d.ts +10 -0
  35. package/lib/types/{panel-accent.d.ts → ui/panel-accent.d.ts} +1 -1
  36. package/lib/types/ui/panel-gap.d.ts +6 -0
  37. package/lib/types/ui/query-editor.d.ts +10 -0
  38. package/lib/types/ui/styled-rows.d.ts +8 -0
  39. package/lib/types/{terminal-title.d.ts → ui/terminal-title.d.ts} +1 -1
  40. package/lib/types/ui/ui-contract.d.ts +12 -0
  41. package/lib/types/ui/use-frames.d.ts +6 -0
  42. package/lib/types/ui/use-stable-input.d.ts +7 -0
  43. package/package.json +1 -1
  44. package/src/app.ts +543 -4066
  45. package/src/completion.ts +117 -0
  46. package/src/composer.ts +1956 -0
  47. package/src/git-workflow.ts +18 -0
  48. package/src/index.ts +113 -633
  49. package/src/internals.ts +1 -1
  50. package/src/locales/en.ts +16 -4
  51. package/src/locales/zh.ts +16 -4
  52. package/src/{authorization-panel.ts → panels/authorization-panel.ts} +25 -6
  53. package/src/panels/completion-panel.ts +79 -0
  54. package/src/panels/interaction-bars.ts +567 -0
  55. package/src/{kernel-panels.ts → panels/kernel-panels.ts} +84 -65
  56. package/src/{language-panel.ts → panels/language-panel.ts} +4 -4
  57. package/src/panels/model-panels.ts +1021 -0
  58. package/src/{theme-panel.ts → panels/theme-panel.ts} +5 -5
  59. package/src/{update-panel.ts → panels/update-panel.ts} +6 -6
  60. package/src/provider-settings.ts +38 -0
  61. package/src/render/inspector.ts +23 -0
  62. package/src/render/text.ts +8 -0
  63. package/src/runner/harness-gate.ts +168 -0
  64. package/src/runner/input-history.ts +77 -0
  65. package/src/runner/mode-cycle.ts +49 -0
  66. package/src/runner/preferences.ts +67 -0
  67. package/src/runner/quit.ts +53 -0
  68. package/src/runner/search-rows.ts +55 -0
  69. package/src/runner/session-io.ts +206 -0
  70. package/src/runner/session-target.ts +81 -0
  71. package/src/runner/startup-config.ts +54 -0
  72. package/src/runner/submissions.ts +157 -0
  73. package/src/session/attach.ts +87 -0
  74. package/src/{session-directory.ts → session/session-directory.ts} +1 -1
  75. package/src/{store.ts → session/store.ts} +1 -1
  76. package/src/{subagents.ts → session/subagents.ts} +12 -1
  77. package/src/settings-file.ts +19 -1
  78. package/src/{panel-accent.ts → ui/panel-accent.ts} +1 -1
  79. package/src/ui/panel-gap.ts +9 -0
  80. package/src/ui/query-editor.ts +16 -0
  81. package/src/ui/styled-rows.ts +124 -0
  82. package/src/{terminal-title.ts → ui/terminal-title.ts} +1 -1
  83. package/src/ui/ui-contract.ts +10 -0
  84. package/src/ui/use-frames.ts +23 -0
  85. package/src/ui/use-stable-input.ts +17 -0
  86. /package/lib/types/{fork.d.ts → session/fork.d.ts} +0 -0
  87. /package/lib/types/{history.d.ts → session/history.d.ts} +0 -0
  88. /package/lib/types/{session-directory.d.ts → session/session-directory.d.ts} +0 -0
  89. /package/lib/types/{session-switch.d.ts → session/session-switch.d.ts} +0 -0
  90. /package/src/{fork.ts → session/fork.ts} +0 -0
  91. /package/src/{history.ts → session/history.ts} +0 -0
  92. /package/src/{session-switch.ts → session/session-switch.ts} +0 -0
package/src/internals.ts CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  shouldEnableKeyboardEnhancement,
20
20
  } from './keyboard.ts'
21
21
  import { createSplitStdin } from './input-split.ts'
22
- import { ensureVsCodeTabTitleSetting } from './terminal-title.ts'
22
+ import { ensureVsCodeTabTitleSetting } from './ui/terminal-title.ts'
23
23
 
24
24
  /** A mounted terminal app instance; the runner owns unmount ordering. */
25
25
  export interface TuiMount {
package/src/locales/en.ts CHANGED
@@ -103,8 +103,15 @@ export const en = {
103
103
 
104
104
  // Transcript document panel (/resume details)
105
105
  'document.loading': 'loading transcript…',
106
+ 'attach.bar': 'observing · {label} · r reload · esc/ctrl+d back',
107
+ 'attach.loading': 'loading {label}\'s conversation…',
108
+ 'attach.status': 'subagent {label} · {id} · {turns} turns',
109
+ 'notice.attachUnavailable': 'subagent attachment is unavailable in this profile',
106
110
  'document.compact': 'transcript · esc close',
107
- 'document.footer': 'lines {from}-{to}/{total} · ↑↓/pg/g/G · t/esc close',
111
+ 'document.expanded': 'expanded',
112
+ 'document.refreshHint': 'r refresh',
113
+ 'document.footerExpanded': 'lines {from}-{to}/{total} · ↑↓/pg/g/G{refresh} · esc/ctrl+d back',
114
+ 'document.footer': 'lines {from}-{to}/{total} · ↑↓/pg/g/G · enter/f expand · t/esc close',
108
115
 
109
116
  // /review picker
110
117
  'review.picker.title': '/review · choose a target',
@@ -456,7 +463,6 @@ export const en = {
456
463
  'panel.provider.notConfiguredDivider': '── not configured ──',
457
464
  'panel.provider.title': '/model — providers',
458
465
  'panel.provider.titleCount': '/model — providers · {index}/{total}',
459
- 'panel.provider.state': '{route} · {value}',
460
466
  'panel.provider.key': 'key {value}',
461
467
  'panel.provider.authConfigured': 'provider auth',
462
468
  'panel.provider.noKey': 'key missing',
@@ -471,7 +477,7 @@ export const en = {
471
477
  'panel.provider.readOnlyLogin': 'this login record is read-only',
472
478
  'panel.provider.notManaged': 'this provider is not managed by Harness settings',
473
479
  'panel.providers.compact': '/model providers · enter configure · d remove key · esc back',
474
- 'panel.providers.footer': '↑↓ move · enter configure · l login · o logout · d remove key · x remove provider · r retry · esc back',
480
+ 'panel.providers.footer': '↑↓ move · enter configure · d remove key · x remove provider · r retry · esc back',
475
481
  'panel.update.compact': '/update · {summary}',
476
482
  'panel.update.checking': 'checking…',
477
483
  'panel.update.probeFailed': 'probe failed',
@@ -499,8 +505,14 @@ export const en = {
499
505
  'panel.setup.copyInto': 'copy verbatim into {id}',
500
506
  'panel.setup.copyTitle': '/model — copy efforts',
501
507
  'panel.setup.copyFooter': '↑↓ move · enter copy · esc back',
508
+ 'panel.setup.mode.subscribe': 'Subscription',
509
+ 'panel.setup.mode.key': 'Key',
510
+ 'panel.setup.mode.login.plan': 'signed in · plan',
511
+ 'panel.setup.mode.login.none': 'not signed in',
512
+ 'panel.setup.mode.login.running': 'signing in…',
513
+ 'panel.setup.mode.overridden': '⚠ the settings key overrides the plan sign-in',
502
514
  'panel.setup.title': '/model — configure {provider}',
503
- 'panel.setup.footer': '↑↓ move · ←→ in/out · space remove · e efforts · c copy efforts · tab discover · enter save · esc back',
515
+ 'panel.setup.footer': '↑↓ move · ←→ in/out · space remove · e efforts · c copy efforts · tab per channel (subscription = web sign-in, key = discover) · enter save · esc back',
504
516
  'panel.discovery.compact': 'model discovery · terminal too small · esc back',
505
517
  'panel.discovery.loading': 'discovering models…',
506
518
  'panel.discovery.empty': 'the endpoint advertised no models; add ids by hand on the setup page',
package/src/locales/zh.ts CHANGED
@@ -105,8 +105,15 @@ export const zh: MessageCatalog = {
105
105
 
106
106
  // Transcript document panel (/resume details)
107
107
  'document.loading': '转录加载中…',
108
+ 'attach.bar': '观察模式 · {label} · r 重新载入 · esc/ctrl+d 返回',
109
+ 'attach.loading': '正在载入 {label} 的子会话…',
110
+ 'attach.status': '子代理 {label} · {id} · {turns} 回合',
111
+ 'notice.attachUnavailable': '当前配置无法附加子代理会话',
108
112
  'document.compact': '转录 · esc 关闭',
109
- 'document.footer': '行 {from}-{to}/{total} · ↑↓/pg/g/G · t/esc 关闭',
113
+ 'document.expanded': '全屏',
114
+ 'document.refreshHint': 'r 刷新',
115
+ 'document.footerExpanded': '行 {from}-{to}/{total} · ↑↓/pg/g/G{refresh} · esc/ctrl+d 返回',
116
+ 'document.footer': '行 {from}-{to}/{total} · ↑↓/pg/g/G · enter/f 全屏 · t/esc 关闭',
110
117
 
111
118
  // /review picker
112
119
  'review.picker.title': '/review · 选择审查目标',
@@ -458,7 +465,6 @@ export const zh: MessageCatalog = {
458
465
  'panel.provider.notConfiguredDivider': '── 未配置 ──',
459
466
  'panel.provider.title': '/model — 供应商',
460
467
  'panel.provider.titleCount': '/model — 供应商 · {index}/{total}',
461
- 'panel.provider.state': '{route} · {value}',
462
468
  'panel.provider.key': '密钥 {value}',
463
469
  'panel.provider.authConfigured': '供应商认证',
464
470
  'panel.provider.noKey': '缺少密钥',
@@ -473,7 +479,7 @@ export const zh: MessageCatalog = {
473
479
  'panel.provider.readOnlyLogin': '该登录记录为只读',
474
480
  'panel.provider.notManaged': '该供应商不受 Harness settings 管理',
475
481
  'panel.providers.compact': '/model providers · 回车配置 · d 删除密钥 · esc 返回',
476
- 'panel.providers.footer': '↑↓ 移动 · 回车配置 · l 登录 · o 登出 · d 删除密钥 · x 删除供应商 · r 重试 · esc 返回',
482
+ 'panel.providers.footer': '↑↓ 移动 · 回车配置 · d 删除密钥 · x 删除供应商 · r 重试 · esc 返回',
477
483
  'panel.update.compact': '/update · {summary}',
478
484
  'panel.update.checking': '检查中…',
479
485
  'panel.update.probeFailed': '检查失败',
@@ -501,8 +507,14 @@ export const zh: MessageCatalog = {
501
507
  'panel.setup.copyInto': '原样复制到 {id}',
502
508
  'panel.setup.copyTitle': '/model — 复制推理档位',
503
509
  'panel.setup.copyFooter': '↑↓ 移动 · 回车复制 · esc 返回',
510
+ 'panel.setup.mode.subscribe': '订阅',
511
+ 'panel.setup.mode.key': 'Key',
512
+ 'panel.setup.mode.login.plan': '已登录 · 套餐',
513
+ 'panel.setup.mode.login.none': '未登录',
514
+ 'panel.setup.mode.login.running': '登录中…',
515
+ 'panel.setup.mode.overridden': '⚠ 设置中的 Key 覆盖订阅登录',
504
516
  'panel.setup.title': '/model — 配置 {provider}',
505
- 'panel.setup.footer': '↑↓ 移动 · ←→ 输入/输出 · 空格移除 · e 推理档位 · c 复制推理档位 · tab 发现 · 回车保存 · esc 返回',
517
+ 'panel.setup.footer': '↑↓ 移动 · ←→ 输入/输出 · 空格移除 · e 推理档位 · c 复制推理档位 · tab 按当前方式(订阅=网页登录 / Key=发现) · 回车保存 · esc 返回',
506
518
  'panel.discovery.compact': '模型发现 · 终端过窄 · esc 返回',
507
519
  'panel.discovery.loading': '正在发现模型…',
508
520
  'panel.discovery.empty': '端点未提供模型;请在配置页手动添加 ID',
@@ -10,12 +10,12 @@ import {
10
10
  type AuthorizationStatus,
11
11
  } from '@deepseek-ai/dsh-authorization'
12
12
  import type { CredentialKey } from '@deepseek-ai/dsh-credentials'
13
- import type { ProviderAuthorizationRow } from './authorization.ts'
14
- import { panelViewport } from './render/inspector.ts'
15
- import { displayText, singleLineText, truncateColumns } from './render/text.ts'
16
- import { panelAccent } from './panel-accent.ts'
17
- import { getPalette, inkColor } from './theme.ts'
18
- import { t } from './i18n.ts'
13
+ import type { ProviderAuthorizationRow } from '../authorization.ts'
14
+ import { panelViewport } from '../render/inspector.ts'
15
+ import { displayText, singleLineText, truncateColumns } from '../render/text.ts'
16
+ import { panelAccent } from '../ui/panel-accent.ts'
17
+ import { getPalette, inkColor } from '../theme.ts'
18
+ import { t } from '../i18n.ts'
19
19
 
20
20
  interface PromptReply {
21
21
  readonly resolve: (value: string) => void
@@ -25,6 +25,11 @@ interface PromptReply {
25
25
 
26
26
  export interface ProviderAuthorizationPanelProps {
27
27
  readonly row: ProviderAuthorizationRow
28
+ /**
29
+ * Start this method immediately instead of showing the picker: a
30
+ * single-method provider signs in with no extra step.
31
+ */
32
+ readonly autoStartMethod?: string
28
33
  begin(
29
34
  row: ProviderAuthorizationRow,
30
35
  method: string,
@@ -78,6 +83,20 @@ export function ProviderAuthorizationPanel(props: ProviderAuthorizationPanelProp
78
83
  const propsRef = useRef(props)
79
84
  propsRef.current = props
80
85
  const rowKey = props.row.key
86
+ // One-shot auto start (single-method providers): guarded by a ref so a
87
+ // remount of the same row cannot start a second attempt.
88
+ const autoStarted = useRef(false)
89
+ useEffect(() => {
90
+ const method = props.autoStartMethod
91
+ if (method === undefined || autoStarted.current) return
92
+ autoStarted.current = true
93
+ if (phase !== 'methods') return
94
+ if (props.row.methods.some(entry => entry.id === method)) start(method)
95
+ // One mount-time decision: the auto method, the method list, and the
96
+ // phase are fixed for this mount, so re-running on their identity would
97
+ // risk starting a second attempt.
98
+ // eslint-disable-next-line react-hooks/exhaustive-deps
99
+ }, [props.autoStartMethod])
81
100
  useEffect(() => () => {
82
101
  controllerRef.current?.abort()
83
102
  propsRef.current.cancel(rowKey)
@@ -0,0 +1,79 @@
1
+ /** Bounded slash-command and mention completion menu. */
2
+
3
+ import { createElement, type ReactElement } from 'react'
4
+ import { Box, Text, useStdout } from 'ink'
5
+ import type { CompletionCandidate } from '../completion.ts'
6
+ import { selectionWindow } from '../render/inspector.ts'
7
+ import { visibleColumns } from '../render/markdown.ts'
8
+ import { displayText, padColumns, singleLineText, truncateColumns } from '../render/text.ts'
9
+ import { dim, getPalette, inkColor } from '../theme.ts'
10
+
11
+ /** Shared geometry used by both the menu view and the App dynamic-row budget. */
12
+ function completionMenuMetrics(terminalRows: number): { limit: number; showFooter: boolean; verticalPadding: number } {
13
+ const showFooter = terminalRows >= 12
14
+ const verticalPadding = terminalRows >= 14 ? 1 : 0
15
+ const limit = Math.max(1, Math.min(6, terminalRows - (showFooter ? 11 : 10) - verticalPadding * 2))
16
+ return { limit, showFooter, verticalPadding }
17
+ }
18
+
19
+ /** Total physical rows occupied by the completion menu at this terminal height. */
20
+ export function completionMenuRowCount(terminalRows: number, rowCount: number): number {
21
+ const { limit, showFooter, verticalPadding } = completionMenuMetrics(terminalRows)
22
+ const visible = rowCount === 0 ? 1 : Math.min(rowCount, limit)
23
+ const hidden = rowCount === 0 ? 0 : rowCount - visible
24
+ return visible + (hidden > 0 ? 1 : 0) + (showFooter ? 1 : 0) + verticalPadding * 2
25
+ }
26
+
27
+ /** Completion view anchored directly above the composer band. */
28
+ export function CompletionMenu({ active, mention, index, rows, error }: {
29
+ active: boolean
30
+ mention: boolean
31
+ index: number
32
+ rows: readonly CompletionCandidate[]
33
+ error?: string
34
+ }): ReactElement | undefined {
35
+ const stdout = useStdout().stdout
36
+ const columns = stdout?.columns ?? 80
37
+ const terminalRows = stdout?.rows ?? 30
38
+ if (!active) return undefined
39
+ const contentColumns = Math.max(1, columns - 4)
40
+ const nameWidth = mention
41
+ ? Math.max(1, contentColumns - 2)
42
+ : Math.min(18, Math.max(1, contentColumns - 2), Math.max(0, ...rows.map(row => visibleColumns(row.label))) + 2)
43
+ const descBudget = Math.max(0, contentColumns - nameWidth - 2)
44
+ const { limit, showFooter, verticalPadding } = completionMenuMetrics(terminalRows)
45
+ const selected = rows.length === 0 ? 0 : index % rows.length
46
+ const first = selectionWindow(selected, rows.length, limit)
47
+ const visible = rows.slice(first, first + limit)
48
+ const hidden = rows.length - visible.length
49
+ return createElement(
50
+ Box,
51
+ { flexDirection: 'column', marginLeft: 2, paddingY: verticalPadding },
52
+ ...(rows.length === 0
53
+ ? [error === undefined
54
+ ? createElement(Text, { key: 'loading', dimColor: true }, 'searching…')
55
+ : createElement(
56
+ Text,
57
+ { key: 'error', color: inkColor(getPalette().error), wrap: 'truncate-end' },
58
+ truncateColumns(`workspace search unavailable: ${singleLineText(error)} · keep typing to retry`, contentColumns),
59
+ )]
60
+ : visible.map((candidate, at) => {
61
+ const absolute = first + at
62
+ return createElement(
63
+ Text,
64
+ {
65
+ key: candidate.label,
66
+ color: absolute === selected ? inkColor(getPalette().brandBright) : inkColor(getPalette().dim),
67
+ wrap: 'truncate-end',
68
+ },
69
+ `${absolute === selected ? '❯ ' : ' '}${padColumns(candidate.label, nameWidth)}${dim(truncateColumns(displayText(candidate.description), descBudget))}`,
70
+ )
71
+ })),
72
+ hidden > 0
73
+ ? createElement(Text, { key: 'more', color: inkColor(getPalette().dim), wrap: 'truncate-end' }, ` … +${hidden} more`)
74
+ : undefined,
75
+ showFooter
76
+ ? createElement(Text, { color: inkColor(getPalette().dim), wrap: 'truncate-end' }, dim(mention ? `↑↓ choose · ${rows.length} items · tab insert` : `↑↓ choose · ${rows.length} items · tab complete`))
77
+ : undefined,
78
+ )
79
+ }