dsh-working-activity 0.2.6 → 0.3.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/LICENSE +28 -28
- package/README.i18n.yaml +6 -6
- package/README.md +122 -122
- package/README.zh.md +113 -113
- package/cordis.patch.yml +19 -19
- package/lib/client.js +6 -6
- package/lib/client.js.map +1 -1
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +11 -4
- package/lib/types/lang.d.ts +90 -0
- package/lib/types/lang.d.ts.map +1 -0
- package/lib/types/lang.js +128 -0
- package/lib/types/phrases.d.ts +39 -5
- package/lib/types/phrases.d.ts.map +1 -1
- package/lib/types/phrases.js +115 -11
- package/lib/types/status.d.ts.map +1 -1
- package/lib/types/status.js +26 -16
- package/package.json +119 -117
- package/src/client/WorkingLine.module.css +74 -74
- package/src/client/WorkingLine.tsx +42 -42
- package/src/client/activity.ts +50 -50
- package/src/client/css-modules.d.ts +6 -6
- package/src/client/index.ts +42 -42
- package/src/events.ts +45 -45
- package/src/index.ts +237 -228
- package/src/invariant.ts +69 -69
- package/src/lang.ts +141 -0
- package/src/phrases.ts +306 -180
- package/src/registration.ts +99 -99
- package/src/status.ts +511 -501
package/src/lang.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language resolution + copy dictionary for the working-activity plugin.
|
|
3
|
+
*
|
|
4
|
+
* The plugin follows the dsh-tui UI language without importing it: the same
|
|
5
|
+
* chain dsh-tui resolves (`DSH_TUI_LANG` env → `~/.dsh-tui/lang.json` → OS
|
|
6
|
+
* locale → zh) is read here directly, so a `/lang en|zh` switch (or the
|
|
7
|
+
* /settings language pick) hot-swaps this plugin's narration and status-line
|
|
8
|
+
* copy on the next render tick.
|
|
9
|
+
*
|
|
10
|
+
* Resolution order:
|
|
11
|
+
* 1. `setLangOverride()` — a plugin-level `lang: zh|en` config key
|
|
12
|
+
* (cordis.yml) or an explicit test pin.
|
|
13
|
+
* 2. `DSH_TUI_LANG` env var — pinned at process start.
|
|
14
|
+
* 3. `~/.dsh-tui/lang.json` — the persisted dsh-tui choice, mtime-cached
|
|
15
|
+
* so the per-tick status render never re-reads an unchanged file.
|
|
16
|
+
* 4. OS locale guess (`LC_ALL` / `LC_MESSAGES` / `LANG`); POSIX/C means
|
|
17
|
+
* "no locale selected" and maps to English.
|
|
18
|
+
* 5. `zh` — the original hard-coded language.
|
|
19
|
+
*
|
|
20
|
+
* The dictionary is a flat key → per-language string map; `t(key, params)`
|
|
21
|
+
* substitutes `{{name}}` placeholders. Missing keys render the key itself so
|
|
22
|
+
* a typo is visible instead of silently blank.
|
|
23
|
+
* @module @deepseek-ai/dsh-working-activity/lang
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { readFileSync, statSync } from 'node:fs'
|
|
27
|
+
import { homedir } from 'node:os'
|
|
28
|
+
import { join } from 'node:path'
|
|
29
|
+
|
|
30
|
+
export type Lang = 'zh' | 'en'
|
|
31
|
+
|
|
32
|
+
/** The languages shipped with the plugin, in display order. */
|
|
33
|
+
export const LANGS = ['zh', 'en'] as const
|
|
34
|
+
|
|
35
|
+
/** The dsh-tui prefs file this plugin mirrors (shared language contract). */
|
|
36
|
+
const LANG_FILE = join(homedir(), '.dsh-tui', 'lang.json')
|
|
37
|
+
|
|
38
|
+
const dict = {
|
|
39
|
+
// ── ⏵ self-narration contract (system prompt, /lang aware) ──────────
|
|
40
|
+
'narrate-instruction': {
|
|
41
|
+
zh: '[状态栏] 你有一个状态栏展示给用户。【必须】在每个步骤/子任务开始时(不只是调用工具前),在回复正文的最前面单独写一行:⏵ 你在做的具体事情(不超过20字),然后换行继续正常回复。整轮回复只写一行 ⏵,不要重复。信息为主——让人一眼知道你在干什么,风格自然、可以带点俏皮。例:⏵ 修复登录页样式、⏵ 查一下报错原因、⏵ 给补丁跑个验证。切换任务时必须更新。',
|
|
42
|
+
en: '[Status line] You have a status line visible to the user. [Required] At the start of each step or subtask (not only before tool calls), write exactly one standalone line at the very beginning of your response: ⏵ a concrete description of what you are doing (20 words max), then continue with the normal response on the next line. Write only one ⏵ line per response and do not repeat it. Prioritize information so the user can understand the current work at a glance; keep the style natural and optionally playful. Examples: ⏵ Fixing the login page styles, ⏵ Investigating the error, ⏵ Running validation for the patch. Update it when the task changes.',
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// ── status-line structural copy ─────────────────────────────────────
|
|
46
|
+
/** Plain (non-playful) phase labels. */
|
|
47
|
+
'waiting-label': { zh: '等待模型响应', en: 'Waiting for model' },
|
|
48
|
+
'thinking-label': { zh: '思考中', en: 'Thinking' },
|
|
49
|
+
/** Elapsed suffix: zh glues the char, en keeps a space. */
|
|
50
|
+
'line-elapsed': { zh: '总{{elapsed}}', en: 'total {{elapsed}}' },
|
|
51
|
+
/** Plain-mode completion prefix (playful pools pick their own). */
|
|
52
|
+
'done-prefix': { zh: '搞定 ✓', en: 'Finished' },
|
|
53
|
+
/** Turn-completion summary. */
|
|
54
|
+
'done-summary': {
|
|
55
|
+
zh: '{{tools}} · 想{{thinking}} 干{{tooling}}',
|
|
56
|
+
en: '{{tools}} · thought {{thinking}} worked {{tooling}}',
|
|
57
|
+
},
|
|
58
|
+
'tool-count-one': { zh: '{{count}} 工具', en: '{{count}} tool' },
|
|
59
|
+
'tool-count-many': { zh: '{{count}} 工具', en: '{{count}} tools' },
|
|
60
|
+
} as const
|
|
61
|
+
|
|
62
|
+
export type I18nKey = keyof typeof dict
|
|
63
|
+
export type I18nParams = Record<string, string | number>
|
|
64
|
+
|
|
65
|
+
/** Explicit pin set by plugin config or tests; `auto` restores the chain. */
|
|
66
|
+
let override: Lang | 'auto' = 'auto'
|
|
67
|
+
|
|
68
|
+
/** Force (or release) the active language; `auto` re-enables the chain. */
|
|
69
|
+
export function setLangOverride(lang: Lang | 'auto'): void {
|
|
70
|
+
override = lang
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The currently active language. */
|
|
74
|
+
export function langNow(): Lang {
|
|
75
|
+
if (override !== 'auto') return override
|
|
76
|
+
const env = process.env.DSH_TUI_LANG
|
|
77
|
+
if (env === 'zh' || env === 'en') return env
|
|
78
|
+
return readLangFile() ?? detectLocaleLang()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Translate a dictionary key, substituting `{{name}}` placeholders. */
|
|
82
|
+
export function t(key: I18nKey, params: I18nParams = {}): string {
|
|
83
|
+
const entry = dict[key] as { zh: string; en: string } | undefined
|
|
84
|
+
const template = entry?.[langNow()] ?? key
|
|
85
|
+
return template.replace(/\{\{(\w+)\}\}/g, (match, name: string) =>
|
|
86
|
+
name in params ? String(params[name]) : match,
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Is a value a valid shipped language code? */
|
|
91
|
+
export function isLang(value: unknown): value is Lang {
|
|
92
|
+
return value === 'zh' || value === 'en'
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Read the persisted dsh-tui language choice, mtime-cached so the per-tick
|
|
97
|
+
* status render never re-reads an unchanged file. `undefined` when the file
|
|
98
|
+
* is absent or holds no valid `{ lang }` value.
|
|
99
|
+
*/
|
|
100
|
+
export function readLangFile(): Lang | undefined {
|
|
101
|
+
let mtimeMs: number
|
|
102
|
+
try {
|
|
103
|
+
mtimeMs = statSync(LANG_FILE).mtimeMs
|
|
104
|
+
} catch {
|
|
105
|
+
return undefined
|
|
106
|
+
}
|
|
107
|
+
if (cachedMtime === mtimeMs) return cachedLang
|
|
108
|
+
try {
|
|
109
|
+
const parsed: unknown = JSON.parse(readFileSync(LANG_FILE, 'utf8'))
|
|
110
|
+
const lang = parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
111
|
+
? (parsed as Record<string, unknown>).lang
|
|
112
|
+
: undefined
|
|
113
|
+
cachedLang = isLang(lang) ? lang : undefined
|
|
114
|
+
} catch {
|
|
115
|
+
cachedLang = undefined
|
|
116
|
+
}
|
|
117
|
+
cachedMtime = mtimeMs
|
|
118
|
+
return cachedLang
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let cachedMtime = -1
|
|
122
|
+
let cachedLang: Lang | undefined
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Guess the language from the OS locale (`LC_ALL`, `LC_MESSAGES`, `LANG`),
|
|
126
|
+
* defaulting to `zh`. POSIX/C means "no locale selected" and conventionally
|
|
127
|
+
* maps to English (what CI runners report). An absent locale variable
|
|
128
|
+
* (typical on Windows) defaults to `zh`.
|
|
129
|
+
*/
|
|
130
|
+
export function detectLocaleLang(): Lang {
|
|
131
|
+
const raw =
|
|
132
|
+
process.env.LC_ALL ||
|
|
133
|
+
process.env.LC_MESSAGES ||
|
|
134
|
+
process.env.LANG ||
|
|
135
|
+
''
|
|
136
|
+
const locale = raw.split('.')[0]?.toLowerCase() ?? ''
|
|
137
|
+
if (locale.startsWith('zh')) return 'zh'
|
|
138
|
+
if (locale.startsWith('en')) return 'en'
|
|
139
|
+
if (locale === 'c' || locale === 'posix') return 'en'
|
|
140
|
+
return 'zh'
|
|
141
|
+
}
|