u1s1-cli 1.11.8 → 1.12.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 (45) hide show
  1. package/dist/agent-setup.d.ts +5 -0
  2. package/dist/agent-setup.js +31 -7
  3. package/dist/api.js +42 -34
  4. package/dist/bench-report.js +12 -9
  5. package/dist/bench.js +34 -31
  6. package/dist/brand.d.ts +2 -1
  7. package/dist/brand.js +38 -12
  8. package/dist/config.d.ts +11 -1
  9. package/dist/config.js +25 -8
  10. package/dist/context-usage.js +12 -9
  11. package/dist/deploy.js +49 -45
  12. package/dist/device-auth.js +7 -5
  13. package/dist/error-humanize.d.ts +1 -0
  14. package/dist/error-humanize.js +18 -6
  15. package/dist/feedback.js +48 -31
  16. package/dist/i18n.d.ts +21 -0
  17. package/dist/i18n.js +46 -0
  18. package/dist/import/index.js +64 -28
  19. package/dist/import/skills.js +72 -41
  20. package/dist/import/util.js +3 -2
  21. package/dist/import/write.js +2 -1
  22. package/dist/index.js +139 -48
  23. package/dist/login.js +39 -28
  24. package/dist/loop.js +18 -12
  25. package/dist/mcp/client.js +15 -14
  26. package/dist/mcp/command.js +76 -48
  27. package/dist/mcp/extension.js +5 -3
  28. package/dist/mcp/tools.js +6 -5
  29. package/dist/model.js +11 -10
  30. package/dist/nudges.d.ts +8 -2
  31. package/dist/nudges.js +22 -3
  32. package/dist/search-tools.js +7 -6
  33. package/dist/shell-doctor.js +9 -8
  34. package/dist/subagent.js +5 -4
  35. package/dist/tools.js +53 -37
  36. package/dist/update.js +29 -28
  37. package/dist/usage.d.ts +1 -1
  38. package/dist/usage.js +96 -43
  39. package/dist/web.js +2 -1
  40. package/dist/workflow/runner.js +33 -29
  41. package/dist/workflow/templates.js +70 -45
  42. package/dist/workflow/tool.js +15 -12
  43. package/package.json +2 -2
  44. package/scripts/patch-pi.js +243 -278
  45. package/scripts/render-regression.mjs +55 -4
@@ -1,4 +1,5 @@
1
1
  import { execFileSync } from "node:child_process";
2
+ import { tr } from "../i18n.js";
2
3
  import { validateScript } from "./runner.js";
3
4
  function str(v, fallback = "") {
4
5
  return typeof v === "string" && v.trim() ? v.trim() : fallback;
@@ -12,6 +13,10 @@ function strArray(v) {
12
13
  function lit(v) {
13
14
  return JSON.stringify(v ?? null);
14
15
  }
16
+ /** 脚本里给子 agent 的任务文案:按界面语言取中/英,再经 lit() 成为脚本里的字符串字面量。 */
17
+ function q(zh, en) {
18
+ return lit(tr(zh, en));
19
+ }
15
20
  /** git 改动文件列表(模板构建期在主进程执行,不进沙箱)。
16
21
  * 剔除已删除文件(评审/重构 agent 没法打开),并入未跟踪的新文件。 */
17
22
  function gitDiffFiles() {
@@ -31,12 +36,17 @@ function gitDiffFiles() {
31
36
  export const TEMPLATES = {
32
37
  i18n: {
33
38
  name: "i18n",
34
- description: "批量多语言翻译:每个语言一个子 agent 并行翻译,完成后统一校验 key 对齐并自动修复",
35
- inputHint: '{ "langs": ["en", "ja", "de"], "source": "src/locales/zh.json", "dir": "src/locales" } — source/dir 可省略(默认 src/locales/zh.json 与同目录)',
39
+ get description() {
40
+ return tr("批量多语言翻译:每个语言一个子 agent 并行翻译,完成后统一校验 key 对齐并自动修复", "Batch translation: one sub-agent per language translates in parallel, then a final pass checks key alignment and fixes issues");
41
+ },
42
+ get inputHint() {
43
+ return tr('{ "langs": ["en", "ja", "de"], "source": "src/locales/zh.json", "dir": "src/locales" } — source/dir 可省略(默认 src/locales/zh.json 与同目录)', '{ "langs": ["en", "ja", "de"], "source": "src/locales/zh.json", "dir": "src/locales" } — source/dir are optional (default: src/locales/zh.json and its directory)');
44
+ },
36
45
  build(input) {
37
46
  const langs = strArray(input.langs);
38
- if (langs.length === 0)
39
- throw new Error('i18n 模板需要 langs 数组,例如 { "langs": ["en", "ja"] }');
47
+ if (langs.length === 0) {
48
+ throw new Error(tr('i18n 模板需要 langs 数组,例如 { "langs": ["en", "ja"] }', 'The i18n template needs a langs array, e.g. { "langs": ["en", "ja"] }'));
49
+ }
40
50
  const source = str(input.source, "src/locales/zh.json");
41
51
  const dir = str(input.dir, source.includes("/") ? source.slice(0, source.lastIndexOf("/")) : "src/locales");
42
52
  // 用户输入一律经 lit() 进脚本,再在运行期拼接——裸内插遇到引号/反斜杠/换行会把生成的脚本弄成语法错误
@@ -44,19 +54,19 @@ export const TEMPLATES = {
44
54
  const langs = ${lit(langs)};
45
55
  const source = ${lit(source)};
46
56
  const dir = ${lit(dir)};
47
- log("i18n:翻译 " + langs.length + " 个语言,源文件 " + source);
57
+ log(${q("i18n:翻译 ", "i18n: translating ")} + langs.length + ${q(" 个语言,源文件 ", " languages, source file ")} + source);
48
58
  const results = await parallel(langs.map(lang => () =>
49
59
  subagent({
50
- task: "读取 " + source + ",把所有文案翻译成对应语言(保持所有 key 不变、输出合法 JSON、译文自然地道)," +
51
- "写入 " + dir + "/" + lang + ".json。写完后重新读一遍文件,确认是合法 JSON 且 key 与源文件完全一致。" +
52
- "目标语言代码:" + lang
60
+ task: ${q("读取 ", "Read ")} + source + ${q(",把所有文案翻译成对应语言(保持所有 key 不变、输出合法 JSON、译文自然地道),", " and translate all strings into the target language (keep every key unchanged, output valid JSON, make the translation natural and idiomatic), ")} +
61
+ ${q("写入 ", "and write it to ")} + dir + "/" + lang + ${q(".json。写完后重新读一遍文件,确认是合法 JSON 且 key 与源文件完全一致。", ".json. After writing, re-read the file to confirm it is valid JSON and its keys exactly match the source file. ")} +
62
+ ${q("目标语言代码:", "Target language code: ")} + lang
53
63
  })
54
64
  ));
55
65
  const okLangs = langs.filter((_, i) => results[i] !== null);
56
66
  if (okLangs.length > 0) {
57
67
  await subagent({
58
- task: "对照 " + source + " 检查 " + dir + "/ 目录下这些语言的翻译文件:" + okLangs.join(",") + "." +
59
- "逐个核对 key 是否与源文件完全一致、JSON 是否合法、译文是否有明显机翻痕迹,发现问题直接修复。最后汇报每个语言的状态"
68
+ task: ${q("对照 ", "Compare against ")} + source + ${q(" 检查 ", " and check the translation files under ")} + dir + ${q("/ 目录下这些语言的翻译文件:", "/ for these languages: ")} + okLangs.join(",") + "." +
69
+ ${q("逐个核对 key 是否与源文件完全一致、JSON 是否合法、译文是否有明显机翻痕迹,发现问题直接修复。最后汇报每个语言的状态", " For each one, verify the keys exactly match the source, the JSON is valid, and the translation doesn't read like obvious machine translation; fix any problems directly. Finally, report the status of each language")}
60
70
  });
61
71
  }
62
72
  return { translated: okLangs, failed: langs.filter(l => !okLangs.includes(l)) };
@@ -65,28 +75,33 @@ return { translated: okLangs, failed: langs.filter(l => !okLangs.includes(l)) };
65
75
  },
66
76
  review: {
67
77
  name: "review",
68
- description: "全面代码审查:每个文件一个评审 agent 并行审查,发现项再交独立 agent 对抗性验证,只保留核实为真的问题",
69
- inputHint: '{ "files": ["src/a.ts", "src/b.ts"] } — files 可省略,默认取 git diff --name-only HEAD 的改动文件',
78
+ get description() {
79
+ return tr("全面代码审查:每个文件一个评审 agent 并行审查,发现项再交独立 agent 对抗性验证,只保留核实为真的问题", "Thorough code review: one reviewer agent per file in parallel, then an independent agent adversarially verifies the findings and keeps only confirmed issues");
80
+ },
81
+ get inputHint() {
82
+ return tr('{ "files": ["src/a.ts", "src/b.ts"] } — files 可省略,默认取 git diff --name-only HEAD 的改动文件', '{ "files": ["src/a.ts", "src/b.ts"] } — files is optional; defaults to the changed files from git diff --name-only HEAD');
83
+ },
70
84
  build(input) {
71
85
  let files = strArray(input.files);
72
86
  if (files.length === 0)
73
87
  files = gitDiffFiles();
74
- if (files.length === 0)
75
- throw new Error("review 模板需要 files 数组,或当前仓库有未提交改动");
88
+ if (files.length === 0) {
89
+ throw new Error(tr("review 模板需要 files 数组,或当前仓库有未提交改动", "The review template needs a files array, or uncommitted changes in the current repository"));
90
+ }
76
91
  return `
77
92
  const files = ${lit(files)};
78
- log("review:评审 " + files.length + " 个文件,发现项将做对抗性验证");
93
+ log(${q("review:评审 ", "review: reviewing ")} + files.length + ${q(" 个文件,发现项将做对抗性验证", " files; findings will be adversarially verified")});
79
94
  const reports = await parallel(files.map(f => () =>
80
95
  subagent({
81
- task: "通读 " + f + " 及其直接依赖,做代码审查:正确性 bug、安全漏洞、明显的性能问题。" +
82
- "只报告有把握的问题,每条给出文件、行号、严重程度(高/中/低)、问题描述和建议修法。没有问题就明确说无发现。"
96
+ task: ${q("通读 ", "Read ")} + f + ${q(" 及其直接依赖,做代码审查:正确性 bug、安全漏洞、明显的性能问题。", " and its direct dependencies, and review the code for correctness bugs, security vulnerabilities, and obvious performance problems. ")} +
97
+ ${q("只报告有把握的问题,每条给出文件、行号、严重程度(高/中/低)、问题描述和建议修法。没有问题就明确说无发现。", "Only report issues you are confident about; for each, give the file, line number, severity (high/medium/low), a description, and a suggested fix. If there are none, say explicitly that there are no findings.")}
83
98
  })
84
99
  ));
85
100
  const findings = reports.filter(Boolean).join("\\n\\n---\\n\\n");
86
- if (!findings.trim()) return { verified: "(所有文件均无发现)" };
101
+ if (!findings.trim()) return { verified: ${q("(所有文件均无发现)", "(no findings in any file)")} };
87
102
  const verified = await subagent({
88
- task: "下面是一份代码审查发现列表。逐条对抗性验证:打开涉及的文件核对,剔除不成立、夸大或已过时的条目," +
89
- "只保留核实为真的问题,按严重程度排序输出完整清单。\\n\\n" + findings
103
+ task: ${q("下面是一份代码审查发现列表。逐条对抗性验证:打开涉及的文件核对,剔除不成立、夸大或已过时的条目,", "Below is a list of code review findings. Adversarially verify each one: open the files involved to check, drop entries that are wrong, exaggerated, or outdated, ")} +
104
+ ${q("只保留核实为真的问题,按严重程度排序输出完整清单。\n\n", "keep only issues confirmed to be real, and output the complete list sorted by severity.\n\n")} + findings
90
105
  });
91
106
  return { verified };
92
107
  `;
@@ -94,32 +109,36 @@ return { verified };
94
109
  },
95
110
  research: {
96
111
  name: "research",
97
- description: "并行研究:每个方向一个子 agent 独立调查(可联网),最后汇总成一份综合报告",
98
- inputHint: '{ "questions": ["方向一", "方向二"], "context": "可选背景" }',
112
+ get description() {
113
+ return tr("并行研究:每个方向一个子 agent 独立调查(可联网),最后汇总成一份综合报告", "Parallel research: one sub-agent investigates each question independently (with web access), then the results are merged into one report");
114
+ },
115
+ get inputHint() {
116
+ return tr('{ "questions": ["方向一", "方向二"], "context": "可选背景" }', '{ "questions": ["question 1", "question 2"], "context": "optional background" }');
117
+ },
99
118
  build(input) {
100
119
  const questions = strArray(input.questions);
101
120
  if (questions.length === 0)
102
- throw new Error('research 模板需要 questions 数组,例如 { "questions": ["竞品A的定价", "竞品B的功能"] }');
121
+ throw new Error(tr('research 模板需要 questions 数组,例如 { "questions": ["竞品A的定价", "竞品B的功能"] }', 'The research template needs a questions array, e.g. { "questions": ["Competitor A pricing", "Competitor B features"] }'));
103
122
  const context = str(input.context);
104
- const contextLine = context ? `调查背景:${context}。` : "";
123
+ const contextLine = context ? tr(`调查背景:${context}。`, `Background: ${context}. `) : "";
105
124
  return `
106
125
  const questions = ${lit(questions)};
107
126
  const contextLine = ${lit(contextLine)};
108
- log("research:并行调查 " + questions.length + " 个方向");
127
+ log(${q("research:并行调查 ", "research: investigating ")} + questions.length + ${q(" 个方向", " questions in parallel")});
109
128
  const reports = await parallel(questions.map(q => () =>
110
129
  subagent({
111
- task: "调查以下问题,可以用 web_search/web_fetch 联网,也可以读本地代码和文档。" +
112
- "输出:关键事实(带来源)、不同观点、你的结论。" + contextLine + "问题:" + q
130
+ task: ${q("调查以下问题,可以用 web_search/web_fetch 联网,也可以读本地代码和文档。", "Investigate the question below. You can use web_search/web_fetch to search the web, and you can also read local code and docs. ")} +
131
+ ${q("输出:关键事实(带来源)、不同观点、你的结论。", "Output: key facts (with sources), differing views, and your conclusion. ")} + contextLine + ${q("问题:", "Question: ")} + q
113
132
  })
114
133
  ));
115
134
  const merged = reports
116
- .map((r, i) => (r ? "## 方向 " + (i + 1) + ":" + questions[i] + "\\n\\n" + r : null))
135
+ .map((r, i) => (r ? ${q("## 方向 ", "## Question ")} + (i + 1) + ${q(":", ": ")} + questions[i] + "\\n\\n" + r : null))
117
136
  .filter(Boolean)
118
137
  .join("\\n\\n");
119
- if (!merged.trim()) return { synthesis: "(所有方向都失败了)", failed: questions.length };
138
+ if (!merged.trim()) return { synthesis: ${q("(所有方向都失败了)", "(all questions failed)")}, failed: questions.length };
120
139
  const synthesis = await subagent({
121
- task: "把以下几份调查报告综合成一份结论性报告:先给核心结论(不超过 5 条),再给分方向要点," +
122
- "最后列分歧点和待确认项。不要编造报告里没有的信息。\\n\\n" + merged
140
+ task: ${q("把以下几份调查报告综合成一份结论性报告:先给核心结论(不超过 5 条),再给分方向要点,", "Synthesize the following research reports into one conclusive report: start with the core conclusions (at most 5), then key points per question, ")} +
141
+ ${q("最后列分歧点和待确认项。不要编造报告里没有的信息。\n\n", "and finally list disagreements and open items. Do not invent information that isn't in the reports.\n\n")} + merged
123
142
  });
124
143
  return { synthesis };
125
144
  `;
@@ -127,31 +146,37 @@ return { synthesis };
127
146
  },
128
147
  refactor: {
129
148
  name: "refactor",
130
- description: "批量重构/迁移:先由一个 agent 读代表文件制定统一规则,然后每个文件一个子 agent 在当前工作区套用规则",
131
- inputHint: '{ "instruction": "把所有 var 改为 const", "files": ["src/a.ts", ...] } — files 省略时用 git diff 改动文件',
149
+ get description() {
150
+ return tr("批量重构/迁移:先由一个 agent 读代表文件制定统一规则,然后每个文件一个子 agent 在当前工作区套用规则", "Batch refactor/migration: one agent reads representative files and writes a single set of rules, then one sub-agent per file applies them in the current working tree");
151
+ },
152
+ get inputHint() {
153
+ return tr('{ "instruction": "把所有 var 改为 const", "files": ["src/a.ts", ...] } — files 省略时用 git diff 改动文件', '{ "instruction": "change every var to const", "files": ["src/a.ts", ...] } — if files is omitted, the git diff changed files are used');
154
+ },
132
155
  build(input) {
133
156
  const instruction = str(input.instruction);
134
- if (!instruction)
135
- throw new Error('refactor 模板需要 instruction,例如 { "instruction": "把 var 全部改为 const" }');
157
+ if (!instruction) {
158
+ throw new Error(tr('refactor 模板需要 instruction,例如 { "instruction": "把 var 全部改为 const" }', 'The refactor template needs an instruction, e.g. { "instruction": "change every var to const" }'));
159
+ }
136
160
  // 与 review 模板一致:构建期取 git 改动文件,不花一个 subagent 在沙箱里跑 git
137
161
  let files = [...new Set(strArray(input.files))];
138
162
  if (files.length === 0)
139
163
  files = gitDiffFiles();
140
- if (files.length === 0)
141
- throw new Error("refactor 模板需要 files 数组,或当前仓库有未提交改动");
164
+ if (files.length === 0) {
165
+ throw new Error(tr("refactor 模板需要 files 数组,或当前仓库有未提交改动", "The refactor template needs a files array, or uncommitted changes in the current repository"));
166
+ }
142
167
  return `
143
168
  const instruction = ${lit(instruction)};
144
169
  const targetFiles = ${lit(files)};
145
- log("refactor:共 " + targetFiles.length + " 个文件,先制定统一规则");
170
+ log(${q("refactor:共 ", "refactor: ")} + targetFiles.length + ${q(" 个文件,先制定统一规则", " files; drafting a common set of rules first")});
146
171
  const rule = await subagent({
147
- task: "我们要对一批文件做统一重构:" + instruction + "。" +
148
- "先读其中 1-2 个代表性文件了解现状,然后输出一份精确的重构规则清单(每条:什么模式改成什么、边界情况怎么处理)。" +
149
- "规则要具体到可以直接照做,不要空话。涉及文件:" + targetFiles.slice(0, 10).join(",")
172
+ task: ${q("我们要对一批文件做统一重构:", "We are applying the same refactor to a batch of files: ")} + instruction + ${q("。", ". ")} +
173
+ ${q("先读其中 1-2 个代表性文件了解现状,然后输出一份精确的重构规则清单(每条:什么模式改成什么、边界情况怎么处理)。", "First read 1-2 representative files to understand the current state, then output a precise list of refactoring rules (each: which pattern becomes what, and how to handle edge cases). ")} +
174
+ ${q("规则要具体到可以直接照做,不要空话。涉及文件:", "Make the rules concrete enough to follow directly; no vague advice. Files involved: ")} + targetFiles.slice(0, 10).join(",")
150
175
  });
151
176
  const results = await parallel(targetFiles.map(f => () =>
152
177
  subagent({
153
- task: "按以下重构规则处理文件 " + f + "。只修改这个目标文件,不要改动其他文件,避免与并行任务冲突。" +
154
- "改完检查该文件语法正确(必要时运行 tsc 或构建验证)。\\n\\n重构规则:\\n" + rule
178
+ task: ${q("按以下重构规则处理文件 ", "Apply the refactoring rules below to the file ")} + f + ${q("。只修改这个目标文件,不要改动其他文件,避免与并行任务冲突。", ". Only modify this target file; do not change any other files, to avoid conflicts with parallel tasks. ")} +
179
+ ${q("改完检查该文件语法正确(必要时运行 tsc 或构建验证)。\n\n重构规则:\n", "When done, check that the file's syntax is correct (run tsc or a build to verify if needed).\n\nRefactoring rules:\n")} + rule
155
180
  })
156
181
  ));
157
182
  const done = targetFiles.filter((_, i) => results[i] !== null);
@@ -164,12 +189,12 @@ return { applied: done, failed: targetFiles.filter(f => !done.includes(f)), rule
164
189
  export function buildFromTemplate(template, input) {
165
190
  const t = TEMPLATES[template];
166
191
  if (!t) {
167
- throw new Error(`未知模板 "${template}",可用:${Object.keys(TEMPLATES).join(", ")}`);
192
+ throw new Error(tr(`未知模板 "${template}",可用:${Object.keys(TEMPLATES).join(", ")}`, `Unknown template "${template}"; available: ${Object.keys(TEMPLATES).join(", ")}`));
168
193
  }
169
194
  const code = t.build(input ?? {});
170
195
  const errors = validateScript(code);
171
196
  if (errors.length > 0) {
172
- throw new Error(`模板 ${template} 生成的脚本未通过静态校验(模板 bug,请反馈):${errors.join(";")}`);
197
+ throw new Error(tr(`模板 ${template} 生成的脚本未通过静态校验(模板 bug,请反馈):${errors.join(";")}`, `The script generated by template ${template} failed static validation (template bug, please report it): ${errors.join("; ")}`));
173
198
  }
174
199
  return code;
175
200
  }
@@ -3,6 +3,7 @@ import { dirname, resolve } from "node:path";
3
3
  import { defineTool } from "@earendil-works/pi-coding-agent";
4
4
  import { Text } from "@earendil-works/pi-tui";
5
5
  import { Type } from "typebox";
6
+ import { tr } from "../i18n.js";
6
7
  import { contextProjectTrust } from "../subagent.js";
7
8
  import { compactResultRender, truncate } from "../tools.js";
8
9
  import { runWorkflow, saveWorkflowScript, validateScript, workflowsDir, WORKFLOW_DEFAULT_BUDGET_TOKENS, WORKFLOW_TIMEOUT_MS, } from "./runner.js";
@@ -10,7 +11,7 @@ import { buildFromTemplate, TEMPLATES } from "./templates.js";
10
11
  function assertValidWorkflowCode(code) {
11
12
  const errors = validateScript(code);
12
13
  if (errors.length > 0) {
13
- throw new Error(`脚本未通过静态校验,请修复后重试:\n- ${errors.join("\n- ")}`);
14
+ throw new Error(tr(`脚本未通过静态校验,请修复后重试:\n- ${errors.join("\n- ")}`, `The script failed static validation; fix it and try again:\n- ${errors.join("\n- ")}`));
14
15
  }
15
16
  }
16
17
  function buildTemplateSource(template, input) {
@@ -18,7 +19,7 @@ function buildTemplateSource(template, input) {
18
19
  return buildFromTemplate(template, input);
19
20
  }
20
21
  catch (error) {
21
- throw new Error(`${error.message}\n模板 input 说明:${TEMPLATES[template]?.inputHint ?? "—"}`, { cause: error });
22
+ throw new Error(tr(`${error.message}\n模板 input 说明:${TEMPLATES[template]?.inputHint ?? "—"}`, `${error.message}\nTemplate input: ${TEMPLATES[template]?.inputHint ?? "—"}`), { cause: error });
22
23
  }
23
24
  }
24
25
  function readWorkflowSource(scriptPath) {
@@ -26,16 +27,16 @@ function readWorkflowSource(scriptPath) {
26
27
  return readFileSync(scriptPath, "utf8");
27
28
  }
28
29
  catch {
29
- throw new Error(`读不到脚本文件:${scriptPath}`);
30
+ throw new Error(tr(`读不到脚本文件:${scriptPath}`, `Can't read the script file: ${scriptPath}`));
30
31
  }
31
32
  }
32
33
  function resolveWorkflowSource(params) {
33
34
  const saveAs = params.save_as?.trim();
34
35
  if (saveAs && !/^[a-zA-Z0-9_-]+$/.test(saveAs)) {
35
- throw new Error("save_as 只允许字母、数字、横线和下划线");
36
+ throw new Error(tr("save_as 只允许字母、数字、横线和下划线", "save_as may only contain letters, digits, hyphens, and underscores"));
36
37
  }
37
38
  if (saveAs && params.script_path?.trim()) {
38
- throw new Error("script_path 指向的脚本已经落盘,不能再配 save_as;要另存新名字请用 script 传入源码");
39
+ throw new Error(tr("script_path 指向的脚本已经落盘,不能再配 save_as;要另存新名字请用 script 传入源码", "The script at script_path is already saved, so save_as can't be used with it; to save under a new name, pass the source via script"));
39
40
  }
40
41
  const inlineCode = params.script?.trim();
41
42
  if (inlineCode) {
@@ -51,7 +52,7 @@ function resolveWorkflowSource(params) {
51
52
  const scriptPath = resolve(requestedPath);
52
53
  return { code: readWorkflowSource(scriptPath), scriptPath };
53
54
  }
54
- throw new Error("script、template、script_path 至少填一个");
55
+ throw new Error(tr("script、template、script_path 至少填一个", "Provide at least one of script, template, or script_path"));
55
56
  }
56
57
  function persistWorkflowSource(source) {
57
58
  if (source.scriptPath)
@@ -60,7 +61,7 @@ function persistWorkflowSource(source) {
60
61
  return saveWorkflowScript(source.code);
61
62
  const scriptPath = resolve(workflowsDir(), `${source.saveAs}.mjs`);
62
63
  if (existsSync(scriptPath)) {
63
- throw new Error(`工作流 "${source.saveAs}" 已存在(${scriptPath})。要重跑它请用 script_path 指向该文件(可加 resume:true 续跑);要新建请换个名字`);
64
+ throw new Error(tr(`工作流 "${source.saveAs}" 已存在(${scriptPath})。要重跑它请用 script_path 指向该文件(可加 resume:true 续跑);要新建请换个名字`, `Workflow "${source.saveAs}" already exists (${scriptPath}). To rerun it, point script_path at that file (add resume:true to continue); to create a new one, pick another name`));
64
65
  }
65
66
  mkdirSync(dirname(scriptPath), { recursive: true });
66
67
  writeFileSync(scriptPath, source.code);
@@ -73,7 +74,7 @@ function persistWorkflowSource(source) {
73
74
  export function createRunWorkflowTool(getParentModel) {
74
75
  return defineTool({
75
76
  name: "run_workflow",
76
- label: "工作流",
77
+ label: tr("工作流", "Workflow"),
77
78
  description: "Execute a JavaScript orchestration script that runs many sub-agents in structured patterns (fan-out/pipeline/loop). " +
78
79
  "The script runs in a sandbox with ONLY these APIs: `subagent(task | {task, model})` returns the sub-agent's final output; " +
79
80
  "`parallel([thunks])` fans out concurrently, a failed item becomes null (barrier); " +
@@ -125,10 +126,11 @@ export function createRunWorkflowTool(getParentModel) {
125
126
  const [result, options, theme, context] = args;
126
127
  const d = result.details;
127
128
  const summary = d && typeof d.ok === "number"
128
- ? `🧭 工作流 · ✓${d.ok}${d.failed ? ` ✗${d.failed}` : ""}` +
129
+ ? tr("🧭 工作流", "🧭 Workflow") +
130
+ ` · ✓${d.ok}${d.failed ? ` ✗${d.failed}` : ""}` +
129
131
  (typeof d.seconds === "number" ? ` · ${d.seconds}s` : "") +
130
- (d.aborted ? " · 已中止" : "")
131
- : "🧭 工作流";
132
+ (d.aborted ? tr(" · 已中止", " · aborted") : "")
133
+ : tr("🧭 工作流", "🧭 Workflow");
132
134
  return compactResultRender({ result, options, theme, context, summaryLine: summary });
133
135
  },
134
136
  async execute(...args) {
@@ -148,7 +150,8 @@ export function createRunWorkflowTool(getParentModel) {
148
150
  content: [
149
151
  {
150
152
  type: "text",
151
- text: `🧭 子任务 ✓${p.ok} ✗${p.failed}${p.cached ? ` 缓存${p.cached}` : ""} · 在跑 ${p.running} · ${(p.spentTokens / 1000).toFixed(0)}k${p.budgetTokens > 0 ? ` / ${(p.budgetTokens / 1000).toFixed(0)}k tokens` : ""}`,
153
+ text: tr(`🧭 子任务 ✓${p.ok} ✗${p.failed}${p.cached ? ` 缓存${p.cached}` : ""} · 在跑 ${p.running}`, `🧭 Subtasks ✓${p.ok} ✗${p.failed}${p.cached ? ` cached ${p.cached}` : ""} · running ${p.running}`) +
154
+ ` · ${(p.spentTokens / 1000).toFixed(0)}k${p.budgetTokens > 0 ? ` / ${(p.budgetTokens / 1000).toFixed(0)}k tokens` : ""}`,
152
155
  },
153
156
  ],
154
157
  details: p,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u1s1-cli",
3
- "version": "1.11.8",
3
+ "version": "1.12.0",
4
4
  "description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,7 +26,7 @@
26
26
  "dev": "tsx src/index.ts",
27
27
  "typecheck": "tsc --noEmit",
28
28
  "prepublishOnly": "npm run build && npm test",
29
- "test": "tsx --test test/*.test.ts && node scripts/render-regression.mjs",
29
+ "test": "U1S1_LANG=zh tsx --test test/*.test.ts && U1S1_LANG=zh node scripts/render-regression.mjs",
30
30
  "postinstall": "node scripts/patch-pi.js",
31
31
  "package": "bash ../../scripts/package.sh",
32
32
  "upload-release": "bash ../../scripts/upload-release.sh",