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/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["css"],"sources":["../src/client/WorkingLine.tsx","../src/client/index.ts"],"sourcesContent":["// Working-line dock entry: one dim full-width row above the composer card\n// showing the live working-activity snapshot — phase-colored breathing\n// marker, the host-composed status line, and the turn's tool count badge.\n// Renders for every live phase (waiting/thinking/tool) and the done summary;\n// hides while idle or before the first publish.\n//\n// The 'conversation.input.dock' SlotMap declaration lives in\n// @deepseek-ai/dsh-client-ui-conversation/client (contract/slots.ts); this\n// entry contributes into it without owning it.\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport css from './WorkingLine.module.css'\n\n/** Full props of the dock entry: the input-zone runtime share (session standard kit). */\nexport type WorkingLineProps = PropsRuntime<'conversation.input.dock'>\n\n/** Tool-count badge copy (no locale seat: the line text itself is host-composed). */\nconst TOOLS_LABEL = 'tools this turn'\n\n/**\n * Working-line dock entry: reads the latest activity snapshot off the\n * conversation snapshot and renders the row, or nothing when idle/absent.\n */\nexport function WorkingLine({ useSession }: WorkingLineProps) {\n // Defensive read: the runtime patch that puts `activity` onto the\n // conversation snapshot ships separately — on an unpatched host the field\n // is absent (undefined), not null, and this component must render nothing\n // instead of crashing into the slot error boundary. Same never-throw\n // discipline as the node side's registration.js.\n const activity = useSession(s => s.activity ?? null)\n if (activity === null || activity.phase === 'idle' || activity.line === '') return null\n return (\n <div className={css.line} data-activity-phase={activity.phase}>\n <span className={css.marker} aria-hidden=\"true\" />\n <span className={css.text}>{activity.line}</span>\n {activity.toolCount > 0 && (\n <span className={css.tools} title={`${activity.toolCount} ${TOOLS_LABEL}`}>\n {activity.toolCount}\n </span>\n )}\n </div>\n )\n}\n","/**\n * Working-activity surface plugin, browser half: the working-line entry in\n * the conversation.input.dock strip. This package's node half emits\n * `activity/status` session events; the web runtime patch narrows them into\n * the conversation snapshot's `activity` member, so this dock entry reads\n * the live frame through the standard session kit and owns no store, no\n * refresh chain, and no event listener.\n *\n * Mount contract (see the root README's \"Web UI 集成\" section): the web\n * client's client-modules host scans loader entries for `dsh.client`\n * declarations and serves this package's `./client` bundle at\n * /plugins/dsh-working-activity/client.js. The entry contributes into the\n * input dock — no official-source patch is involved.\n */\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\n// Type-only: pulls the ui-conversation SlotMap merge (the input.dock entry)\n// through the Client assembly boundary.\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport { WorkingLine } from './WorkingLine.tsx'\nimport type { ActivityPhase, ActivitySnapshot } from './activity.ts'\n\nexport { WorkingLine, type WorkingLineProps } from './WorkingLine.tsx'\nexport type { ActivityPhase, ActivitySnapshot } from './activity.ts'\n\n/** Required services for the dock registration. */\nexport const inject = ['slots']\n\n/**\n * Client plugin body: the working-line dock entry.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({\n name: 'conversation.input.dock',\n // The pre-slots patch used the same id in ui-conversation; entries with\n // equal order render in registration order, and goal (10) / queue (20)\n // keep their seats — this row sits between them.\n id: 'activity',\n order: 15,\n registrant: 'dsh-working-activity',\n }, WorkingLine))\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBA,MAAM,cAAc;;;;;EAMpB,SAAgB,YAAY,EAAE,cAAgC;GAM5D,MAAM,WAAW,YAAW,MAAK,EAAE,YAAY,IAAI;GACnD,IAAI,aAAa,QAAQ,SAAS,UAAU,UAAU,SAAS,SAAS,IAAI,OAAO;GACnF,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAWA,+BAAI;IAAM,uBAAqB,SAAS;IAAxD,UAAA;KACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,+BAAI;MAAQ,eAAY;KAAQ,CAAA;KACjD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,+BAAI;MAAO,UAAA,SAAS;KAAW,CAAA;KAC/C,SAAS,YAAY,KACpB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,+BAAI;MAAO,OAAO,GAAG,SAAS,UAAU,GAAG;MACzD,UAAA,SAAS;KACN,CAAA;IAEL;;EAET;;;;EChBA,MAAa,SAAS,CAAC,OAAO;;;;;EAM9B,SAAgB,MAAM,KAA0B;GAC9C,IAAI,MAAM,OAAO,iCAAiC,IAAI,MAAM,SAAS;IACnE,MAAM;IAIN,IAAI;IACJ,OAAO;IACP,YAAY;GACd,GAAG,WAAW,CAAC;EACjB"}
1
+ {"version":3,"file":"client.js","names":["css"],"sources":["../src/client/WorkingLine.tsx","../src/client/index.ts"],"sourcesContent":["// Working-line dock entry: one dim full-width row above the composer card\r\n// showing the live working-activity snapshot — phase-colored breathing\r\n// marker, the host-composed status line, and the turn's tool count badge.\r\n// Renders for every live phase (waiting/thinking/tool) and the done summary;\r\n// hides while idle or before the first publish.\r\n//\r\n// The 'conversation.input.dock' SlotMap declaration lives in\r\n// @deepseek-ai/dsh-client-ui-conversation/client (contract/slots.ts); this\r\n// entry contributes into it without owning it.\r\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\r\nimport css from './WorkingLine.module.css'\r\n\r\n/** Full props of the dock entry: the input-zone runtime share (session standard kit). */\r\nexport type WorkingLineProps = PropsRuntime<'conversation.input.dock'>\r\n\r\n/** Tool-count badge copy (no locale seat: the line text itself is host-composed). */\r\nconst TOOLS_LABEL = 'tools this turn'\r\n\r\n/**\r\n * Working-line dock entry: reads the latest activity snapshot off the\r\n * conversation snapshot and renders the row, or nothing when idle/absent.\r\n */\r\nexport function WorkingLine({ useSession }: WorkingLineProps) {\r\n // Defensive read: the runtime patch that puts `activity` onto the\r\n // conversation snapshot ships separately — on an unpatched host the field\r\n // is absent (undefined), not null, and this component must render nothing\r\n // instead of crashing into the slot error boundary. Same never-throw\r\n // discipline as the node side's registration.js.\r\n const activity = useSession(s => s.activity ?? null)\r\n if (activity === null || activity.phase === 'idle' || activity.line === '') return null\r\n return (\r\n <div className={css.line} data-activity-phase={activity.phase}>\r\n <span className={css.marker} aria-hidden=\"true\" />\r\n <span className={css.text}>{activity.line}</span>\r\n {activity.toolCount > 0 && (\r\n <span className={css.tools} title={`${activity.toolCount} ${TOOLS_LABEL}`}>\r\n {activity.toolCount}\r\n </span>\r\n )}\r\n </div>\r\n )\r\n}\r\n","/**\r\n * Working-activity surface plugin, browser half: the working-line entry in\r\n * the conversation.input.dock strip. This package's node half emits\r\n * `activity/status` session events; the web runtime patch narrows them into\r\n * the conversation snapshot's `activity` member, so this dock entry reads\r\n * the live frame through the standard session kit and owns no store, no\r\n * refresh chain, and no event listener.\r\n *\r\n * Mount contract (see the root README's \"Web UI 集成\" section): the web\r\n * client's client-modules host scans loader entries for `dsh.client`\r\n * declarations and serves this package's `./client` bundle at\r\n * /plugins/dsh-working-activity/client.js. The entry contributes into the\r\n * input dock — no official-source patch is involved.\r\n */\r\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\r\n// Type-only: pulls the ui-conversation SlotMap merge (the input.dock entry)\r\n// through the Client assembly boundary.\r\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\r\nimport { WorkingLine } from './WorkingLine.tsx'\r\nimport type { ActivityPhase, ActivitySnapshot } from './activity.ts'\r\n\r\nexport { WorkingLine, type WorkingLineProps } from './WorkingLine.tsx'\r\nexport type { ActivityPhase, ActivitySnapshot } from './activity.ts'\r\n\r\n/** Required services for the dock registration. */\r\nexport const inject = ['slots']\r\n\r\n/**\r\n * Client plugin body: the working-line dock entry.\r\n * @param ctx - client root context.\r\n */\r\nexport function apply(ctx: ClientContext): void {\r\n ctx.slots.inject('conversation.input.dock', () => ctx.slots.register({\r\n name: 'conversation.input.dock',\r\n // The pre-slots patch used the same id in ui-conversation; entries with\r\n // equal order render in registration order, and goal (10) / queue (20)\r\n // keep their seats — this row sits between them.\r\n id: 'activity',\r\n order: 15,\r\n registrant: 'dsh-working-activity',\r\n }, WorkingLine))\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBA,MAAM,cAAc;;;;;EAMpB,SAAgB,YAAY,EAAE,cAAgC;GAM5D,MAAM,WAAW,YAAW,MAAK,EAAE,YAAY,IAAI;GACnD,IAAI,aAAa,QAAQ,SAAS,UAAU,UAAU,SAAS,SAAS,IAAI,OAAO;GACnF,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAWA,+BAAI;IAAM,uBAAqB,SAAS;IAAxD,UAAA;KACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,+BAAI;MAAQ,eAAY;KAAQ,CAAA;KACjD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,+BAAI;MAAO,UAAA,SAAS;KAAW,CAAA;KAC/C,SAAS,YAAY,KACpB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAWA,+BAAI;MAAO,OAAO,GAAG,SAAS,UAAU,GAAG;MACzD,UAAA,SAAS;KACN,CAAA;IAEL;;EAET;;;;EChBA,MAAa,SAAS,CAAC,OAAO;;;;;EAM9B,SAAgB,MAAM,KAA0B;GAC9C,IAAI,MAAM,OAAO,iCAAiC,IAAI,MAAM,SAAS;IACnE,MAAM;IAIN,IAAI;IACJ,OAAO;IACP,YAAY;GACd,GAAG,WAAW,CAAC;EACjB"}
@@ -39,6 +39,9 @@ export type Config = {
39
39
  customActions?: Record<string, string[]>;
40
40
  /** Inject the `⏵` self-narration contract into the system prompt and surface it. */
41
41
  narrate?: boolean;
42
+ /** UI language: `auto` follows `DSH_TUI_LANG` → `~/.dsh-tui/lang.json` →
43
+ * OS locale → zh; `zh`/`en` pin the copy directly. */
44
+ lang?: 'auto' | 'zh' | 'en';
42
45
  };
43
46
  export declare const Config: Schemastery<Config>;
44
47
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAalD,mBAAmB,aAAa,CAAA;AAEhC,eAAO,MAAM,IAAI,qBAAqB,CAAA;AAEtC,wDAAwD;AACxD,MAAM,MAAM,MAAM,GAAG;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;;mCAM+B;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACxC,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAMD,eAAO,MAAM,MAAM,EAAE,WAAW,CAAC,MAAM,CAQrC,CAAA;AAyBF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CA+H7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAclD,mBAAmB,aAAa,CAAA;AAEhC,eAAO,MAAM,IAAI,qBAAqB,CAAA;AAEtC,wDAAwD;AACxD,MAAM,MAAM,MAAM,GAAG;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;;mCAM+B;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACxC,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;2DACuD;IACvD,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;CAC5B,CAAA;AAMD,eAAO,MAAM,MAAM,EAAE,WAAW,CAAC,MAAM,CASrC,CAAA;AAsBF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CAsI7D"}
@@ -17,6 +17,7 @@
17
17
  import z from '@deepseek-ai/schemastery';
18
18
  import { ActivityTracker } from './status.js';
19
19
  import { registerActivityEventType } from './registration.js';
20
+ import { setLangOverride, t } from './lang.js';
20
21
  export const name = 'working-activity';
21
22
  // Explicit annotation: the inferred z.dict output references cosmokit's
22
23
  // Dict through a pnpm-virtual path, which is not portable in declaration
@@ -30,9 +31,8 @@ export const Config = z.object({
30
31
  detailLimit: z.number().step(1).min(8).max(120).default(40),
31
32
  customActions: z.dict(z.array(z.string())).default({}),
32
33
  narrate: z.boolean().default(true),
34
+ lang: z.union(['auto', 'zh', 'en']).default('auto'),
33
35
  });
34
- /** The self-narration contract injected into the system prompt (narrate on). */
35
- const NARRATE_INSTRUCTION = '[状态栏] 你有一个状态栏展示给用户。【必须】在每个步骤/子任务开始时(不只是调用工具前),在回复正文的最前面单独写一行:⏵ 你在做的具体事情(不超过20字),然后换行继续正常回复。整轮回复只写一行 ⏵,不要重复。信息为主——让人一眼知道你在干什么,风格自然、可以带点俏皮。例:⏵ 修复登录页样式、⏵ 查一下报错原因、⏵ 给补丁跑个验证。切换任务时必须更新。';
36
36
  /**
37
37
  * Wire the working-activity plugin.
38
38
  * @param ctx - Cordis context (agent loop + session services composed).
@@ -52,8 +52,13 @@ export function apply(ctx, config = {}) {
52
52
  publishIntervalMs: config.publishIntervalMs ?? 2000,
53
53
  detailLimit: config.detailLimit ?? 40,
54
54
  narrate: config.narrate ?? true,
55
+ lang: config.lang ?? 'auto',
55
56
  customActions: config.customActions ?? {},
56
57
  };
58
+ // A pinned plugin-level language beats the env/file chain; releasing it on
59
+ // dispose restores `auto` for any other composition in the process.
60
+ setLangOverride(resolved.lang);
61
+ ctx.effect(() => () => setLangOverride('auto'), 'working-activity lang override');
57
62
  const trackers = new Map();
58
63
  let activeSession;
59
64
  let lastPublishedLine;
@@ -65,13 +70,15 @@ export function apply(ctx, config = {}) {
65
70
  const promptHandle = prompt?.register('activity', undefined);
66
71
  // The `⏵` self-narration contract rides the stable system-prompt sections:
67
72
  // injected when the systemPrompt service is composed (agent assemblies
68
- // always mount it), removed with this fiber.
73
+ // always mount it), removed with this fiber. The text is resolved at every
74
+ // assembly in the live language, so a `/lang` switch applies to the next
75
+ // turn without rebuilding the agent.
69
76
  if (resolved.narrate) {
70
77
  ctx.inject(['systemPrompt'], (promptCtx) => {
71
78
  promptCtx.systemPrompt.section({
72
79
  name: 'working-activity:narrate',
73
80
  order: 60,
74
- text: NARRATE_INSTRUCTION,
81
+ text: () => t('narrate-instruction'),
75
82
  });
76
83
  });
77
84
  }
@@ -0,0 +1,90 @@
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
+ export type Lang = 'zh' | 'en';
26
+ /** The languages shipped with the plugin, in display order. */
27
+ export declare const LANGS: readonly ["zh", "en"];
28
+ declare const dict: {
29
+ readonly 'narrate-instruction': {
30
+ readonly zh: "[状态栏] 你有一个状态栏展示给用户。【必须】在每个步骤/子任务开始时(不只是调用工具前),在回复正文的最前面单独写一行:⏵ 你在做的具体事情(不超过20字),然后换行继续正常回复。整轮回复只写一行 ⏵,不要重复。信息为主——让人一眼知道你在干什么,风格自然、可以带点俏皮。例:⏵ 修复登录页样式、⏵ 查一下报错原因、⏵ 给补丁跑个验证。切换任务时必须更新。";
31
+ readonly 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.";
32
+ };
33
+ /** Plain (non-playful) phase labels. */
34
+ readonly 'waiting-label': {
35
+ readonly zh: "等待模型响应";
36
+ readonly en: "Waiting for model";
37
+ };
38
+ readonly 'thinking-label': {
39
+ readonly zh: "思考中";
40
+ readonly en: "Thinking";
41
+ };
42
+ /** Elapsed suffix: zh glues the char, en keeps a space. */
43
+ readonly 'line-elapsed': {
44
+ readonly zh: "总{{elapsed}}";
45
+ readonly en: "total {{elapsed}}";
46
+ };
47
+ /** Plain-mode completion prefix (playful pools pick their own). */
48
+ readonly 'done-prefix': {
49
+ readonly zh: "搞定 ✓";
50
+ readonly en: "Finished";
51
+ };
52
+ /** Turn-completion summary. */
53
+ readonly 'done-summary': {
54
+ readonly zh: "{{tools}} · 想{{thinking}} 干{{tooling}}";
55
+ readonly en: "{{tools}} · thought {{thinking}} worked {{tooling}}";
56
+ };
57
+ readonly 'tool-count-one': {
58
+ readonly zh: "{{count}} 工具";
59
+ readonly en: "{{count}} tool";
60
+ };
61
+ readonly 'tool-count-many': {
62
+ readonly zh: "{{count}} 工具";
63
+ readonly en: "{{count}} tools";
64
+ };
65
+ };
66
+ export type I18nKey = keyof typeof dict;
67
+ export type I18nParams = Record<string, string | number>;
68
+ /** Force (or release) the active language; `auto` re-enables the chain. */
69
+ export declare function setLangOverride(lang: Lang | 'auto'): void;
70
+ /** The currently active language. */
71
+ export declare function langNow(): Lang;
72
+ /** Translate a dictionary key, substituting `{{name}}` placeholders. */
73
+ export declare function t(key: I18nKey, params?: I18nParams): string;
74
+ /** Is a value a valid shipped language code? */
75
+ export declare function isLang(value: unknown): value is Lang;
76
+ /**
77
+ * Read the persisted dsh-tui language choice, mtime-cached so the per-tick
78
+ * status render never re-reads an unchanged file. `undefined` when the file
79
+ * is absent or holds no valid `{ lang }` value.
80
+ */
81
+ export declare function readLangFile(): Lang | undefined;
82
+ /**
83
+ * Guess the language from the OS locale (`LC_ALL`, `LC_MESSAGES`, `LANG`),
84
+ * defaulting to `zh`. POSIX/C means "no locale selected" and conventionally
85
+ * maps to English (what CI runners report). An absent locale variable
86
+ * (typical on Windows) defaults to `zh`.
87
+ */
88
+ export declare function detectLocaleLang(): Lang;
89
+ export {};
90
+ //# sourceMappingURL=lang.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lang.d.ts","sourceRoot":"","sources":["../../src/lang.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAE9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAK1C,QAAA,MAAM,IAAI;;;;;IAQR,wCAAwC;;;;;;;;;IAGxC,2DAA2D;;;;;IAE3D,mEAAmE;;;;;IAEnE,+BAA+B;;;;;;;;;;;;;CAOvB,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,IAAI,CAAA;AACvC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAKxD,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAEzD;AAED,qCAAqC;AACrC,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED,wEAAwE;AACxE,wBAAgB,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,UAAe,GAAG,MAAM,CAM/D;AAED,gDAAgD;AAChD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,IAAI,GAAG,SAAS,CAmB/C;AAKD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAWvC"}
@@ -0,0 +1,128 @@
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
+ import { readFileSync, statSync } from 'node:fs';
26
+ import { homedir } from 'node:os';
27
+ import { join } from 'node:path';
28
+ /** The languages shipped with the plugin, in display order. */
29
+ export const LANGS = ['zh', 'en'];
30
+ /** The dsh-tui prefs file this plugin mirrors (shared language contract). */
31
+ const LANG_FILE = join(homedir(), '.dsh-tui', 'lang.json');
32
+ const dict = {
33
+ // ── ⏵ self-narration contract (system prompt, /lang aware) ──────────
34
+ 'narrate-instruction': {
35
+ zh: '[状态栏] 你有一个状态栏展示给用户。【必须】在每个步骤/子任务开始时(不只是调用工具前),在回复正文的最前面单独写一行:⏵ 你在做的具体事情(不超过20字),然后换行继续正常回复。整轮回复只写一行 ⏵,不要重复。信息为主——让人一眼知道你在干什么,风格自然、可以带点俏皮。例:⏵ 修复登录页样式、⏵ 查一下报错原因、⏵ 给补丁跑个验证。切换任务时必须更新。',
36
+ 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.',
37
+ },
38
+ // ── status-line structural copy ─────────────────────────────────────
39
+ /** Plain (non-playful) phase labels. */
40
+ 'waiting-label': { zh: '等待模型响应', en: 'Waiting for model' },
41
+ 'thinking-label': { zh: '思考中', en: 'Thinking' },
42
+ /** Elapsed suffix: zh glues the char, en keeps a space. */
43
+ 'line-elapsed': { zh: '总{{elapsed}}', en: 'total {{elapsed}}' },
44
+ /** Plain-mode completion prefix (playful pools pick their own). */
45
+ 'done-prefix': { zh: '搞定 ✓', en: 'Finished' },
46
+ /** Turn-completion summary. */
47
+ 'done-summary': {
48
+ zh: '{{tools}} · 想{{thinking}} 干{{tooling}}',
49
+ en: '{{tools}} · thought {{thinking}} worked {{tooling}}',
50
+ },
51
+ 'tool-count-one': { zh: '{{count}} 工具', en: '{{count}} tool' },
52
+ 'tool-count-many': { zh: '{{count}} 工具', en: '{{count}} tools' },
53
+ };
54
+ /** Explicit pin set by plugin config or tests; `auto` restores the chain. */
55
+ let override = 'auto';
56
+ /** Force (or release) the active language; `auto` re-enables the chain. */
57
+ export function setLangOverride(lang) {
58
+ override = lang;
59
+ }
60
+ /** The currently active language. */
61
+ export function langNow() {
62
+ if (override !== 'auto')
63
+ return override;
64
+ const env = process.env.DSH_TUI_LANG;
65
+ if (env === 'zh' || env === 'en')
66
+ return env;
67
+ return readLangFile() ?? detectLocaleLang();
68
+ }
69
+ /** Translate a dictionary key, substituting `{{name}}` placeholders. */
70
+ export function t(key, params = {}) {
71
+ const entry = dict[key];
72
+ const template = entry?.[langNow()] ?? key;
73
+ return template.replace(/\{\{(\w+)\}\}/g, (match, name) => name in params ? String(params[name]) : match);
74
+ }
75
+ /** Is a value a valid shipped language code? */
76
+ export function isLang(value) {
77
+ return value === 'zh' || value === 'en';
78
+ }
79
+ /**
80
+ * Read the persisted dsh-tui language choice, mtime-cached so the per-tick
81
+ * status render never re-reads an unchanged file. `undefined` when the file
82
+ * is absent or holds no valid `{ lang }` value.
83
+ */
84
+ export function readLangFile() {
85
+ let mtimeMs;
86
+ try {
87
+ mtimeMs = statSync(LANG_FILE).mtimeMs;
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
92
+ if (cachedMtime === mtimeMs)
93
+ return cachedLang;
94
+ try {
95
+ const parsed = JSON.parse(readFileSync(LANG_FILE, 'utf8'));
96
+ const lang = parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)
97
+ ? parsed.lang
98
+ : undefined;
99
+ cachedLang = isLang(lang) ? lang : undefined;
100
+ }
101
+ catch {
102
+ cachedLang = undefined;
103
+ }
104
+ cachedMtime = mtimeMs;
105
+ return cachedLang;
106
+ }
107
+ let cachedMtime = -1;
108
+ let cachedLang;
109
+ /**
110
+ * Guess the language from the OS locale (`LC_ALL`, `LC_MESSAGES`, `LANG`),
111
+ * defaulting to `zh`. POSIX/C means "no locale selected" and conventionally
112
+ * maps to English (what CI runners report). An absent locale variable
113
+ * (typical on Windows) defaults to `zh`.
114
+ */
115
+ export function detectLocaleLang() {
116
+ const raw = process.env.LC_ALL ||
117
+ process.env.LC_MESSAGES ||
118
+ process.env.LANG ||
119
+ '';
120
+ const locale = raw.split('.')[0]?.toLowerCase() ?? '';
121
+ if (locale.startsWith('zh'))
122
+ return 'zh';
123
+ if (locale.startsWith('en'))
124
+ return 'en';
125
+ if (locale === 'c' || locale === 'posix')
126
+ return 'en';
127
+ return 'zh';
128
+ }
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * Copy pools for the working-activity status line: short, colloquial, playful
3
- * Chinese fragments with deadpan English one-liners mixed in, matching the
4
- * pi-working-activity tone. Everything here is pure data + pure pickers.
3
+ * fragments with a "live person" vibe, in both Chinese and English. The zh
4
+ * pools are the original copy; the en pools mirror the same tone — deadpan,
5
+ * meme-y, occasionally unhinged. Everything here is pure data + pure pickers;
6
+ * the active language is resolved per pick via `langNow()`.
5
7
  * @module @deepseek-ai/dsh-working-activity/phrases
6
8
  */
7
9
  /** A pool of copy fragments. */
@@ -31,21 +33,53 @@ export declare const FAIL_PHRASES: readonly string[];
31
33
  export declare const DONE_PHRASES: readonly string[];
32
34
  /** Night-owl phrases mixed in between 00:00 and 06:00 local time. */
33
35
  export declare const NIGHT_PHRASES: readonly string[];
36
+ /** English thinking phrases — casual, meme-y, alive. */
37
+ export declare const EN_THINKING_PHRASES: readonly string[];
38
+ /** English tiered phrases when thinking runs long. */
39
+ export declare const EN_THINKING_TIERS: readonly {
40
+ readonly atMs: number;
41
+ readonly pool: readonly string[];
42
+ }[];
43
+ /** English phrases while waiting for the first token. */
44
+ export declare const EN_WAITING_PHRASES: readonly string[];
45
+ /** English tool-name → action verbs. */
46
+ export declare const EN_ACTION_MAP: readonly {
47
+ readonly test: RegExp;
48
+ readonly actions: readonly string[];
49
+ }[];
50
+ /** English fallback verbs for unknown tools. */
51
+ export declare const EN_FALLBACK_ACTIONS: readonly string[];
52
+ /** English tool failure phrases. */
53
+ export declare const EN_FAIL_PHRASES: readonly string[];
54
+ /** English turn-completion phrases. */
55
+ export declare const EN_DONE_PHRASES: readonly string[];
56
+ /** English night-owl phrases. */
57
+ export declare const EN_NIGHT_PHRASES: readonly string[];
34
58
  /** Common git tool names / bash commands containing `git `. */
35
59
  export declare const GIT_TOOL_RE: RegExp;
36
60
  /** Detect the 00:00–06:00 night window (local time). */
37
61
  export declare function isNight(hour: number): boolean;
62
+ /** The zh or en base waiting pool by the active language. */
63
+ export declare function waitingPool(): readonly string[];
38
64
  /**
39
- * Pick a thinking phrase appropriate for the elapsed thinking time.
65
+ * Pick a thinking phrase appropriate for the elapsed thinking time, in the
66
+ * active language.
40
67
  * @param elapsedMs - Milliseconds spent thinking in the current phase.
41
68
  * @param previous - Previously shown phrase, to avoid repeats.
42
69
  * @param night - Mix night-owl copy into the pool.
43
70
  */
44
71
  export declare function thinkingPhrase(elapsedMs: number, previous?: string, night?: boolean): string;
72
+ /** Pick a waiting phrase in the active language. */
73
+ export declare function waitingPhrase(previous?: string): string;
74
+ /** Pick a tool-failure phrase in the active language. */
75
+ export declare function failPhrase(): string;
76
+ /** Pick a turn-completion phrase in the active language. */
77
+ export declare function donePhrase(): string;
45
78
  /**
46
- * Map a tool name to a playful action verb.
79
+ * Map a tool name to a playful action verb, in the active language.
47
80
  * @param toolName - Registry tool name (unqualified).
48
- * @param custom - Exact-name custom action pools, matched case-insensitively.
81
+ * @param custom - Exact-name custom action pools, matched case-insensitively
82
+ * (user-owned copy, used verbatim in any language).
49
83
  */
50
84
  export declare function actionFor(toolName: string, custom?: Readonly<Record<string, readonly string[]>>): string;
51
85
  /** Whether a tool is a git operation (name match, or a shell command containing `git `). */
@@ -1 +1 @@
1
- {"version":3,"file":"phrases.d.ts","sourceRoot":"","sources":["../../src/phrases.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,gCAAgC;AAChC,MAAM,MAAM,UAAU,GAAG,SAAS,MAAM,EAAE,CAAA;AAE1C,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CASzE;AAED,6DAA6D;AAC7D,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAiB7C,CAAA;AAED,qEAAqE;AACrE,eAAO,MAAM,cAAc,EAAE,SAAS;IACpC,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CACjC,EAIA,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAQ5C,CAAA;AAED,yDAAyD;AACzD,eAAO,MAAM,UAAU,EAAE,SAAS;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC,EAkBA,CAAA;AAED,wCAAwC;AACxC,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAAqD,CAAA;AAEnG,gDAAgD;AAChD,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAMzC,CAAA;AAED,+BAA+B;AAC/B,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAMzC,CAAA;AAED,qEAAqE;AACrE,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAI1C,CAAA;AAED,+DAA+D;AAC/D,eAAO,MAAM,WAAW,QAA4G,CAAA;AAEpI,wDAAwD;AACxD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,MAAM,CAY1F;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,GAAG,MAAM,CAQxG;AAED,4FAA4F;AAC5F,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAO7F;AAED,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAS9C"}
1
+ {"version":3,"file":"phrases.d.ts","sourceRoot":"","sources":["../../src/phrases.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * Copy pools for the working-activity status line: short, colloquial, playful
3
- * Chinese fragments with deadpan English one-liners mixed in, matching the
4
- * pi-working-activity tone. Everything here is pure data + pure pickers.
3
+ * fragments with a "live person" vibe, in both Chinese and English. The zh
4
+ * pools are the original copy; the en pools mirror the same tone — deadpan,
5
+ * meme-y, occasionally unhinged. Everything here is pure data + pure pickers;
6
+ * the active language is resolved per pick via `langNow()`.
5
7
  * @module @deepseek-ai/dsh-working-activity/phrases
6
8
  */
9
+ import { langNow } from './lang.js';
7
10
  /** Pick one random entry; repeated draws avoid the previous entry when possible. */
8
11
  export function pickPhrase(entries, previous) {
9
12
  if (entries.length === 0)
@@ -17,6 +20,7 @@ export function pickPhrase(entries, previous) {
17
20
  }
18
21
  return next;
19
22
  }
23
+ // ── zh pools (original copy) ─────────────────────────────────────────────
20
24
  /** Thinking phrases while the model works without a tool. */
21
25
  export const THINKING_PHRASES = [
22
26
  '嗯…让我捋捋', '盘一下盘一下', '大脑转起来了', '思考.gif', '给我一秒', '脑子在冒烟',
@@ -96,46 +100,146 @@ export const NIGHT_PHRASES = [
96
100
  '深夜盘东西', '熬夜冠军上线', '困了,但能行', '过了零点照样肝', '夜猫子出没', '深夜档营业',
97
101
  '星星都睡了', '凌晨还在盘', '深夜上线', '凌晨部署', '通宵了',
98
102
  ];
103
+ // ── en pools (same vibe, different language) ─────────────────────────────
104
+ /** English thinking phrases — casual, meme-y, alive. */
105
+ export const EN_THINKING_PHRASES = [
106
+ 'Thinking…', 'Pondering…', 'Mulling it over', 'Brain.exe running', 'Loading thoughts…',
107
+ 'Deep in thought', 'Hmm…', 'Cogitating', 'Reasoning intensifies', 'On it', 'Working it out',
108
+ 'Connecting the dots', 'Crunching ideas', 'Chewing on it', 'Neurons firing',
109
+ 'Reticulating splines', 'Thinking cap on', 'Give me a sec', 'Halfway there',
110
+ 'Still thinking', 'Turning the crank', 'Ideas brewing', 'Thoughts loading…',
111
+ 'Calculating life choices', 'Let me cook', 'Cooking…', 'Brain cells: engaged',
112
+ 'Thinking thoughts', 'Mmm…', 'Processing…', 'Almost', 'Just vibing with the problem',
113
+ 'Mystery math happening', 'Gathering thoughts', 'In the zone', 'Distracted by a pigeon',
114
+ 'Have you tried thinking harder?', 'Summoning wisdom', 'rrrr', 'um', 'ok ok', 'wait…',
115
+ ];
116
+ /** English tiered phrases when thinking runs long. */
117
+ export const EN_THINKING_TIERS = [
118
+ { atMs: 30_000, pool: ['30s in, still thinking', "This one's a thinker", 'Deeper than it looks', 'Getting warmer…', 'Still cooking', 'Brain on overtime', 'Not done yet', 'Almost there…', 'The gears are turning', 'One more sec', 'Loading 99%… again'] },
119
+ { atMs: 60_000, pool: ['1m in, still going', "This is a tough one", 'Full brain power', '1m, stay with me', 'Still grinding', 'Brain at full tilt', 'Almost…', 'Taking the scenic route', "It's a marathon, not a sprint", 'The plot thickens'] },
120
+ { atMs: 300_000, pool: ['5m in, big brain energy', 'This is a marathon', 'Seriously deep now', 'Meditating on it', 'Slow and steady', 'Getting there', 'Worth the wait', 'One song later…', '5 minutes of pure thought', "I've seen things", 'Ascending to another plane of thought'] },
121
+ ];
122
+ /** English phrases while waiting for the first token. */
123
+ export const EN_WAITING_PHRASES = [
124
+ 'Pinging the model…', 'Model inbound…', 'Waiting on the muse', 'Wake up, model',
125
+ "It's warming up", 'Model: almost there', 'Holding for a token…', 'Brewing a response…',
126
+ 'Model is stretching', 'Just a sec', 'One moment, please', 'Loading…', 'Connecting…',
127
+ 'Model said "be right back"', "It's yawning", 'First token incoming…', 'Hmm, still waiting',
128
+ 'Model is putting on its glasses', 'Tick tock…', 'Summoning tokens…', 'Patiently waiting',
129
+ 'Model is thinking of a hello', 'Booting brain…', 'Is it plugged in?', 'Gently poking the model',
130
+ 'Waiting for the magic words…', 'A wild token appears… soon', 'Reticulating the request',
131
+ ];
132
+ /** English tool-name → action verbs. */
133
+ export const EN_ACTION_MAP = [
134
+ { test: /^(read|read_file|cat)$/i, actions: ['Reading', 'Peeking at', 'Snooping through', 'Skimming', 'Checking out', 'Eyes on', 'Giving it a read'] },
135
+ { test: /^(write|write_file|create_file)$/i, actions: ['Writing', 'Typing it out', 'Crafting', 'Saving progress', 'Pen to paper (virtually)', 'Putting words down'] },
136
+ { test: /^(edit|edit_file|str_replace|apply_patch|search_replace)$/i, actions: ['Editing', 'Patching', 'Tweaking', 'Polishing', 'Fixing a typo (probably)', 'Adjusting', 'Giving it a touch-up'] },
137
+ { test: /^(bash|shell|run|exec|powershell|cmd)$/i, actions: ['Running', 'Executing', 'Shelling out', 'Firing a command', 'Terminal time', 'Doing terminal things', 'Making the computer do stuff'] },
138
+ { test: /^(grep|rg|search|search_in_files)$/i, actions: ['Searching', 'Grepping', 'Hunting for matches', 'Looking for a needle', 'Sifting through', 'Diving into the haystack'] },
139
+ { test: /^(find|glob)$/i, actions: ['Finding files', 'Looking for files', 'File hunt', 'Hunting down a path'] },
140
+ { test: /^(ls|list_dir|list)$/i, actions: ['Listing', 'Peeking at the dir', "What's in here?", 'Taking inventory'] },
141
+ { test: /^(web_search|search_web|brave|tavily|exa)$/i, actions: ['Searching the web', 'Googling', 'Researching', 'Web diving (the safe kind)', 'Looking it up'] },
142
+ { test: /^(web_fetch|fetch|fetch_content)$/i, actions: ['Fetching a page', 'Grabbing content', 'Pulling the page', 'Downloading knowledge'] },
143
+ { test: /^(mcp)/i, actions: ['Calling MCP', 'Hitting a service', 'MCP time', 'Talking to a server'] },
144
+ { test: /^(subagent|agent|task)$/i, actions: ['Delegating', 'Sending a subagent', 'Calling a helper', 'Outsourcing', 'Drafting a minion'] },
145
+ { test: /^(todo|manage_todo_list)$/i, actions: ['Updating todos', 'Checking the list', 'Todo time', 'Adding a checkbox'] },
146
+ { test: /^(browser|chrome|playwright)/i, actions: ['Driving the browser', 'Clicking around', 'Browsing', 'Puppeteering a browser'] },
147
+ { test: /^(git|gh|github)/i, actions: ['Git-ing', 'Committing', 'Version control dance', 'Git things', 'Saving the timeline'] },
148
+ { test: /^(ask_user_question|ask)$/i, actions: ['Asking you', 'Checking with you', 'Quick question', 'Pinging the human'] },
149
+ { test: /^(goal_complete|goal_blocked)$/i, actions: ['Updating goals', 'Tracking progress', 'Checking the objective'] },
150
+ { test: /^(todo_write)$/i, actions: ['Writing todos', 'Making a list', 'Adding a checkbox'] },
151
+ ];
152
+ /** English fallback verbs for unknown tools. */
153
+ export const EN_FALLBACK_ACTIONS = [
154
+ 'Working on it', 'Doing a thing', 'Handling it', 'Taking care of it',
155
+ 'Something productive', 'Figuring it out', 'Winging it',
156
+ ];
157
+ /** English tool failure phrases. */
158
+ export const EN_FAIL_PHRASES = [
159
+ 'That failed', 'Oops', 'No dice', "Didn't work", 'Broke it (it was already broken)',
160
+ "It's not a bug, it's a feature", '404: success not found', 'Retry?', "Hmm, that's odd",
161
+ 'RIP', 'Sigh…', 'One more time', 'Classic', 'Works on my machine',
162
+ 'Have you tried turning it off and on?', 'Someone unplugged the internet',
163
+ 'Out of ideas, trying again', 'The computer said no', 'Error: user error', 'So close…',
164
+ ];
165
+ /** English turn-completion phrases. */
166
+ export const EN_DONE_PHRASES = [
167
+ 'Done!', 'All set', 'Finished', "That's that", 'Done and dusted', 'Mission complete',
168
+ 'Sorted', 'Wrapped up', 'Ship it!', 'Clean run', 'Nice', 'One and done', 'All green',
169
+ 'Knocked out', 'Closed out', 'Task: destroyed', 'EZ', 'GG', 'Another one bites the dust',
170
+ 'Victory lap', 'Boom', 'Tada!', 'Smooth sailing', 'No notes', 'Crushed it',
171
+ ];
172
+ /** English night-owl phrases. */
173
+ export const EN_NIGHT_PHRASES = [
174
+ 'Night owl shift', 'Burning midnight oil', 'Past midnight, still going', "The moon's out",
175
+ 'Night grind', 'Up late', 'Almost dawn', '2AM thoughts', 'Red-eye shift',
176
+ 'Who needs sleep anyway?', 'The stars are my witness',
177
+ ];
99
178
  /** Common git tool names / bash commands containing `git `. */
100
179
  export const GIT_TOOL_RE = /^(?:git|git_diff|git_commit|git_push|git_pull|git_checkout|git_branch|git_merge|git_rebase|github|gh)$/i;
101
180
  /** Detect the 00:00–06:00 night window (local time). */
102
181
  export function isNight(hour) {
103
182
  return hour >= 0 && hour < 6;
104
183
  }
184
+ /** The zh or en base thinking pool by the active language. */
185
+ function thinkingPools() {
186
+ return langNow() === 'en' ? EN_THINKING_TIERS : THINKING_TIERS;
187
+ }
188
+ /** The zh or en base waiting pool by the active language. */
189
+ export function waitingPool() {
190
+ return langNow() === 'en' ? EN_WAITING_PHRASES : WAITING_PHRASES;
191
+ }
105
192
  /**
106
- * Pick a thinking phrase appropriate for the elapsed thinking time.
193
+ * Pick a thinking phrase appropriate for the elapsed thinking time, in the
194
+ * active language.
107
195
  * @param elapsedMs - Milliseconds spent thinking in the current phase.
108
196
  * @param previous - Previously shown phrase, to avoid repeats.
109
197
  * @param night - Mix night-owl copy into the pool.
110
198
  */
111
199
  export function thinkingPhrase(elapsedMs, previous, night = false) {
112
- let pool = THINKING_PHRASES;
113
- for (const tier of THINKING_TIERS) {
200
+ let pool = langNow() === 'en' ? EN_THINKING_PHRASES : THINKING_PHRASES;
201
+ for (const tier of thinkingPools()) {
114
202
  if (elapsedMs >= tier.atMs) {
115
203
  pool = tier.pool;
116
204
  break;
117
205
  }
118
206
  }
119
- if (night && pool === THINKING_PHRASES) {
120
- return pickPhrase([...pool, ...NIGHT_PHRASES], previous);
207
+ if (night && pool === (langNow() === 'en' ? EN_THINKING_PHRASES : THINKING_PHRASES)) {
208
+ const nightPool = langNow() === 'en' ? EN_NIGHT_PHRASES : NIGHT_PHRASES;
209
+ return pickPhrase([...pool, ...nightPool], previous);
121
210
  }
122
211
  return pickPhrase(pool, previous);
123
212
  }
213
+ /** Pick a waiting phrase in the active language. */
214
+ export function waitingPhrase(previous) {
215
+ return pickPhrase(waitingPool(), previous);
216
+ }
217
+ /** Pick a tool-failure phrase in the active language. */
218
+ export function failPhrase() {
219
+ return pickPhrase(langNow() === 'en' ? EN_FAIL_PHRASES : FAIL_PHRASES);
220
+ }
221
+ /** Pick a turn-completion phrase in the active language. */
222
+ export function donePhrase() {
223
+ return pickPhrase(langNow() === 'en' ? EN_DONE_PHRASES : DONE_PHRASES);
224
+ }
124
225
  /**
125
- * Map a tool name to a playful action verb.
226
+ * Map a tool name to a playful action verb, in the active language.
126
227
  * @param toolName - Registry tool name (unqualified).
127
- * @param custom - Exact-name custom action pools, matched case-insensitively.
228
+ * @param custom - Exact-name custom action pools, matched case-insensitively
229
+ * (user-owned copy, used verbatim in any language).
128
230
  */
129
231
  export function actionFor(toolName, custom) {
130
232
  const normalized = toolName.trim().toLowerCase();
131
233
  const customPool = custom?.[normalized];
132
234
  if (customPool !== undefined && customPool.length > 0)
133
235
  return pickPhrase(customPool);
134
- for (const { test, actions } of ACTION_MAP) {
236
+ const map = langNow() === 'en' ? EN_ACTION_MAP : ACTION_MAP;
237
+ const fallback = langNow() === 'en' ? EN_FALLBACK_ACTIONS : FALLBACK_ACTIONS;
238
+ for (const { test, actions } of map) {
135
239
  if (test.test(normalized))
136
240
  return pickPhrase(actions);
137
241
  }
138
- return pickPhrase(FALLBACK_ACTIONS);
242
+ return pickPhrase(fallback);
139
243
  }
140
244
  /** Whether a tool is a git operation (name match, or a shell command containing `git `). */
141
245
  export function isGitTool(toolName, args) {
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/status.ts"],"names":[],"mappings":"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"}