Skip to content

Commit 991ba71

Browse files
committed
refactor(cli): CLI 输出统一为英文
1 parent 760d50c commit 991ba71

33 files changed

Lines changed: 483 additions & 322 deletions

‎internal/cli/budget.go‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ func newBudgetCmd() *cobra.Command {
2525
)
2626
cmd := &cobra.Command{
2727
Use: "budget",
28-
Short: "估算 source 与实际 target 输出的 LLM 上下文消耗",
28+
Short: "Estimate LLM context usage of sources and rendered target output",
2929
RunE: func(cmd *cobra.Command, _ []string) error {
3030
return runBudget(cmd, asJSON, rendered || len(targets) > 0, targets)
3131
},
3232
}
33-
cmd.Flags().BoolVar(&asJSON, "json", false, "结构化 JSON 输出")
34-
cmd.Flags().BoolVar(&rendered, "rendered", false, "显示启用 target 的实际 root 与 sidecar 体积")
35-
cmd.Flags().StringSliceVar(&targets, "target", nil, "限定 rendered target,可重复")
33+
cmd.Flags().BoolVar(&asJSON, "json", false, "Structured JSON output")
34+
cmd.Flags().BoolVar(&rendered, "rendered", false, "Show actual root and sidecar sizes for enabled targets")
35+
cmd.Flags().StringSliceVar(&targets, "target", nil, "Limit rendered targets, repeatable")
3636
return cmd
3737
}
3838

@@ -148,7 +148,7 @@ func runBudget(cmd *cobra.Command, asJSON, rendered bool, targets []string) erro
148148
pf := func(format string, a ...any) { _, _ = fmt.Fprintf(out, format, a...) }
149149
pl := func(s string) { _, _ = fmt.Fprintln(out, s) }
150150

151-
pf("总文档数 %d 总 rules 字节 %d 估算总 tokens %d\n\n",
151+
pf("Total docs %d total rules bytes %d estimated total tokens %d\n\n",
152152
len(r.Docs), r.TotalRulesBytes, r.TotalTokens)
153153
pf("%-50s %-12s %10s %10s\n", "PATH", "TYPE", "BYTES", "~TOKENS")
154154
pl(strings.Repeat("-", 86))
@@ -304,14 +304,15 @@ func collectBudgetSkillPackageFiles(docs []*parser.Document, files []source.File
304304
}
305305
}
306306

307-
// isMarkdownFile 判断 path 后缀是否为 markdown
307+
// isMarkdownFile reports whether the path has a markdown suffix
308308
func isMarkdownFile(p string) bool {
309309
lower := strings.ToLower(p)
310310
return strings.HasSuffix(lower, ".md") || strings.HasSuffix(lower, ".markdown")
311311
}
312312

313-
// isSkillSubdirMarkdownFile 判断是否 SKILL package 子目录的辅助 markdown
314-
// (与 runner.isSkillSubdirMarkdown 等价;cli 包独立维护避免跨包导出私有 helper)
313+
// isSkillSubdirMarkdownFile reports whether p is an auxiliary markdown file
314+
// inside a SKILL package subdirectory (same rule as runner.isSkillSubdirMarkdown;
315+
// maintained separately here to avoid exporting a private cross-package helper)
315316
func isSkillSubdirMarkdownFile(p string) bool {
316317
return strings.HasPrefix(p, "skills/") && strings.Count(p, "/") >= 3
317318
}

‎internal/cli/budget_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ func TestIsMarkdownFile(t *testing.T) {
236236

237237
func TestIsSkillSubdirMarkdownFile(t *testing.T) {
238238
cases := map[string]bool{
239-
"skills/foo/SKILL.md": false, // 顶层
239+
"skills/foo/SKILL.md": false, // top level
240240
"skills/foo/refs/x.md": true,
241241
"skills/foo/scripts/sub/y.md": true,
242242
"rules/x.md": false,

‎internal/cli/clean.go‎

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ func newCleanCmd() *cobra.Command {
1717
var keepBackups, yes bool
1818
cmd := &cobra.Command{
1919
Use: "clean",
20-
Short: "清空根目录与平台目录的生成文件,保留 .stdai/",
20+
Short: "Remove generated files from root and platform dirs, keep .stdai/",
2121
RunE: func(cmd *cobra.Command, _ []string) error {
22-
_ = keepBackups // 当前 v1.0:clean 永远保留 .stdai/backups/
22+
_ = keepBackups // v1.0: clean always keeps .stdai/backups/
2323
_, root := resolveConfigPath()
2424
st, err := state.Load(filepath.Join(root, state.StateFile))
2525
if err != nil {
@@ -68,15 +68,16 @@ func newCleanCmd() *cobra.Command {
6868
},
6969
}
7070
f := cmd.Flags()
71-
f.StringSliceVar(&targets, "target", nil, "仅清理指定 target")
72-
f.BoolVar(&keepBackups, "keep-backups", true, "保留 .stdai/backups/")
73-
f.BoolVarP(&yes, "yes", "y", false, "跳过确认")
71+
f.StringSliceVar(&targets, "target", nil, "Only clean the given target(s)")
72+
f.BoolVar(&keepBackups, "keep-backups", true, "Keep .stdai/backups/")
73+
f.BoolVarP(&yes, "yes", "y", false, "Skip confirmation")
7474
return cmd
7575
}
7676

77-
// cleanEmptyDirs 删除被清理文件的父目录(若已空)
77+
// cleanEmptyDirs removes parent dirs of cleaned files when they are empty.
7878
//
79-
// 退出条件用 filepath.Dir(dir) == dir 判定 root,跨 OS 安全:
79+
// The exit condition filepath.Dir(dir) == dir detects the filesystem root
80+
// and is safe across OSes:
8081
// Linux/macOS: filepath.Dir("/") == "/"
8182
// Windows: filepath.Dir("C:\\") == "C:\\"
8283
func cleanEmptyDirs(paths []string) {
@@ -86,7 +87,7 @@ func cleanEmptyDirs(paths []string) {
8687
for dir != "." && dir != "" {
8788
parent := filepath.Dir(dir)
8889
if parent == dir {
89-
break // 已到 filesystem root
90+
break // reached filesystem root
9091
}
9192
dirs[dir] = true
9293
dir = parent
@@ -96,11 +97,11 @@ func cleanEmptyDirs(paths []string) {
9697
for d := range dirs {
9798
dirList = append(dirList, d)
9899
}
99-
// 按深度排序,深的先删
100+
// Sort by depth so deeper dirs are removed first
100101
sort.Slice(dirList, func(i, j int) bool {
101102
return len(dirList[i]) > len(dirList[j])
102103
})
103104
for _, d := range dirList {
104-
_ = os.Remove(d) // 非空目录会自然失败,忽略
105+
_ = os.Remove(d) // non-empty dirs fail naturally, ignored
105106
}
106107
}

‎internal/cli/clean_test.go‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func TestCleanCommandRemovesGeneratedFiles(t *testing.T) {
1717
setupSyncProject(t, tmp)
1818
t.Chdir(tmp)
1919

20-
// 先 sync 生成 CLAUDE.md / AGENTS.md / .claude/rules/...
20+
// first sync to generate CLAUDE.md / AGENTS.md / .claude/rules/...
2121
syncCmd := newSyncCmd()
2222
syncCmd.SetOut(new(bytes.Buffer))
2323
syncCmd.SetErr(new(bytes.Buffer))
@@ -28,7 +28,7 @@ func TestCleanCommandRemovesGeneratedFiles(t *testing.T) {
2828
t.Fatal("CLAUDE.md should exist after sync")
2929
}
3030

31-
// clean --yes 跳过交互确认
31+
// clean --yes skips interactive confirmation
3232
cleanCmd := newCleanCmd()
3333
var out bytes.Buffer
3434
cleanCmd.SetOut(&out)
@@ -38,13 +38,13 @@ func TestCleanCommandRemovesGeneratedFiles(t *testing.T) {
3838
t.Fatalf("clean: %v", err)
3939
}
4040

41-
// 生成文件应被删除
41+
// generated files should be removed
4242
for _, gone := range []string{"CLAUDE.md", "AGENTS.md", ".claude/rules/style.md"} {
4343
if _, err := os.Stat(filepath.Join(tmp, gone)); err == nil {
4444
t.Errorf("%s should be removed after clean", gone)
4545
}
4646
}
47-
// .stdai/ 应保留
47+
// .stdai/ should be preserved
4848
if _, err := os.Stat(filepath.Join(tmp, ".stdai/config.toml")); err != nil {
4949
t.Errorf(".stdai should be preserved: %v", err)
5050
}
@@ -99,7 +99,7 @@ func TestCleanCommandTargetFilter(t *testing.T) {
9999
t.Fatal(err)
100100
}
101101

102-
// 仅 clean claude-code
102+
// only clean claude-code
103103
cleanCmd := newCleanCmd()
104104
cleanCmd.SetOut(new(bytes.Buffer))
105105
cleanCmd.SetErr(new(bytes.Buffer))
@@ -108,11 +108,11 @@ func TestCleanCommandTargetFilter(t *testing.T) {
108108
t.Fatal(err)
109109
}
110110

111-
// CLAUDE.md 被删
111+
// CLAUDE.md is removed
112112
if _, err := os.Stat(filepath.Join(tmp, "CLAUDE.md")); err == nil {
113113
t.Error("CLAUDE.md should be removed (target=claude-code)")
114114
}
115-
// AGENTS.md 保留(target 限定为 claude-code)
115+
// AGENTS.md stays (target limited to claude-code)
116116
if _, err := os.Stat(filepath.Join(tmp, "AGENTS.md")); err != nil {
117117
t.Errorf("AGENTS.md should remain when --target claude-code: %v", err)
118118
}

‎internal/cli/explain.go‎

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
//go:embed explain_text.md
1313
var explainText string
1414

15-
// explainType 是 stdagent explain --json 输出的单条记录
15+
// explainType is a single record of `stdagent explain --json` output
1616
type explainType struct {
1717
Type string `json:"type"`
1818
Semantics string `json:"semantics"`
@@ -21,61 +21,61 @@ type explainType struct {
2121
ExampleFM string `json:"example_frontmatter"`
2222
}
2323

24-
// explainTypes 是 5 种 type 的结构化语义,供 --json 输出。
25-
// 与 explain_text.md 内容对应,保持同步。
24+
// explainTypes holds the structured semantics of the 5 types for --json output.
25+
// It mirrors explain_text.md; keep them in sync.
2626
var explainTypes = []explainType{
2727
{
2828
Type: "rules",
29-
Semantics: "持续生效的编码、架构和操作约束;由 target 自动加载或按路径匹配。",
30-
WhenToUse: "违反会造成真实风险、且值得占用常驻上下文的稳定约束。",
31-
WhenNot: "长背景用 references,按需工作流用 skills,用户模板用 commands。",
32-
ExampleFM: "---\ntype: rules\nname: exception-handling\ndescription: Go 错误传播与边界转换\npriority: high\napplyTo:\n - \"**/*.go\"\n---",
29+
Semantics: "Always-on coding, architecture, and operational constraints; auto-loaded by targets or matched by path.",
30+
WhenToUse: "Stable constraints whose violation causes real risk and that deserve resident context.",
31+
WhenNot: "Long background goes in references, on-demand workflows in skills, user templates in commands.",
32+
ExampleFM: "---\ntype: rules\nname: exception-handling\ndescription: Go error propagation and boundary conversion\npriority: high\napplyTo:\n - \"**/*.go\"\n---",
3333
},
3434
{
3535
Type: "skills",
36-
Semantics: "AI 根据 description 按需调用的能力包,可携带辅助资源。",
37-
WhenToUse: "可复用、有明确结果和成功标准的工作流。",
38-
WhenNot: "持续约束用 rules,用户显式模板用 commands。",
39-
ExampleFM: "---\ntype: skills\nname: code-review\ndescription: 审查当前改动并报告正确性、安全和回归问题\n---",
36+
Semantics: "Capability packages the AI invokes on demand based on description; may carry supporting resources.",
37+
WhenToUse: "Reusable workflows with a clear outcome and success criteria.",
38+
WhenNot: "Ongoing constraints go in rules, explicit user templates in commands.",
39+
ExampleFM: "---\ntype: skills\nname: code-review\ndescription: Review current changes and report correctness, security, and regression issues\n---",
4040
},
4141
{
4242
Type: "commands",
43-
Semantics: "用户输入 /command-name 显式触发的操作模板。",
44-
WhenToUse: "用户需要主动调用的固定操作。",
45-
WhenNot: "AI 自动判断的流程用 skills,持续约束用 rules。",
46-
ExampleFM: "---\ntype: commands\nname: review\ndescription: 审查当前分支改动并生成 review 报告\n---",
43+
Semantics: "Operation templates the user triggers explicitly via /command-name.",
44+
WhenToUse: "Fixed operations the user wants to invoke directly.",
45+
WhenNot: "AI-judged flows go in skills, ongoing constraints in rules.",
46+
ExampleFM: "---\ntype: commands\nname: review\ndescription: Review current branch changes and produce a review report\n---",
4747
},
4848
{
4949
Type: "references",
50-
Semantics: "仅在需要时查阅的架构、协议、API 和长篇背景。",
51-
WhenToUse: "不应占用默认上下文但需要保留的领域知识。",
52-
WhenNot: "持续约束用 rules,可执行工作流用 skills。",
53-
ExampleFM: "---\ntype: references\nname: transformer-design\ndescription: transformer 协议层架构说明\napplyTo:\n - \"internal/transformer/**\"\n---",
50+
Semantics: "Architecture, protocol, API, and long-form background consulted only when needed.",
51+
WhenToUse: "Domain knowledge worth keeping but not worth occupying default context.",
52+
WhenNot: "Ongoing constraints go in rules, executable workflows in skills.",
53+
ExampleFM: "---\ntype: references\nname: transformer-design\ndescription: Transformer protocol-layer architecture notes\napplyTo:\n - \"internal/transformer/**\"\n---",
5454
},
5555
{
5656
Type: "subagents",
57-
Semantics: "在隔离上下文中执行的代理定义。",
58-
WhenToUse: "可独立执行、需要专门上下文或可安全并行的任务。",
59-
WhenNot: "当前 session 内的流程用 skills,简单模板用 commands。",
60-
ExampleFM: "---\ntype: subagents\nname: code-reviewer\ndescription: 在隔离上下文中审查代码并返回问题清单\n---",
57+
Semantics: "Agent definitions executed in an isolated context.",
58+
WhenToUse: "Tasks that run independently, need a dedicated context, or parallelize safely.",
59+
WhenNot: "In-session flows go in skills, simple templates in commands.",
60+
ExampleFM: "---\ntype: subagents\nname: code-reviewer\ndescription: Review code in an isolated context and return an issue list\n---",
6161
},
6262
}
6363

6464
func newExplainCmd() *cobra.Command {
6565
var asJSON bool
6666
cmd := &cobra.Command{
6767
Use: "explain [type]",
68-
Short: "解释 std-agent 5 种类型(rules/skills/commands/references/subagents)的语义",
69-
Long: `输出 std-agent 5 种 type 的语义速查:每种类型的触发语义 / 何时使用 / 何时不用 / 示例 frontmatter。
68+
Short: "Explain the semantics of the 5 std-agent types (rules/skills/commands/references/subagents)",
69+
Long: `Print a semantics cheat sheet for the 5 std-agent types: trigger semantics / when to use / when not to / example frontmatter for each type.
7070
71-
不带参数时输出全部 5 种。带 type 参数时只输出该 type 一段。
71+
Without arguments, print all 5 types. With a type argument, print only that type's section.
7272
73-
示例:
73+
Examples:
7474
75-
stdagent explain # 全部 5 种
76-
stdagent explain rules # 只看 rules
77-
stdagent explain --json # JSON 输出(AI 集成)
78-
stdagent explain rules --json # rules 单项 JSON
75+
stdagent explain # all 5 types
76+
stdagent explain rules # rules only
77+
stdagent explain --json # JSON output (AI integration)
78+
stdagent explain rules --json # single rules item as JSON
7979
`,
8080
Args: cobra.MaximumNArgs(1),
8181
RunE: func(cmd *cobra.Command, args []string) error {
@@ -89,11 +89,12 @@ func newExplainCmd() *cobra.Command {
8989
return writeExplainMarkdown(cmd, filter)
9090
},
9191
}
92-
cmd.Flags().BoolVar(&asJSON, "json", false, "JSON 输出(给 AI / 自动化集成)")
92+
cmd.Flags().BoolVar(&asJSON, "json", false, "JSON output (for AI / automation integration)")
9393
return cmd
9494
}
9595

96-
// writeExplainMarkdown 输出 markdown 速查。filter 为空输出全部,否则只输出对应 type 段。
96+
// writeExplainMarkdown prints the markdown cheat sheet. An empty filter prints
97+
// everything, otherwise only the matching type's section.
9798
func writeExplainMarkdown(cmd *cobra.Command, filter string) error {
9899
if filter == "" {
99100
cmd.Print(explainText)
@@ -110,7 +111,7 @@ func writeExplainMarkdown(cmd *cobra.Command, filter string) error {
110111
return nil
111112
}
112113

113-
// writeExplainJSON 输出 []explainType(全部)或 [explainType](单项过滤)。
114+
// writeExplainJSON prints []explainType (all) or [explainType] (single-type filter).
114115
func writeExplainJSON(cmd *cobra.Command, filter string) error {
115116
enc := json.NewEncoder(cmd.OutOrStdout())
116117
enc.SetIndent("", " ")
@@ -137,21 +138,23 @@ func isKnownType(t string) bool {
137138
return false
138139
}
139140

140-
// extractSection 从 explain_text.md 抽出 ## <type> 标题对应的段落(含标题,到下一个 ## 之前)。
141+
// extractSection extracts the `## <type>` section from explain_text.md
142+
// (including the header, up to the next `##` header).
141143
//
142-
// explain_text.md 用 `## rules` / `## skills` 等二级标题分段,最后有一个 `## 速查表` 总表段。
143-
// 单项查询时只返回该 type 的段,不包含速查表(避免重复信息)。
144+
// explain_text.md is divided by `## rules` / `## skills` ... level-2 headers
145+
// plus a trailing `## Quick reference` summary section. Single-type queries
146+
// return only that type's section, without the summary (to avoid duplication).
144147
func extractSection(text, typeName string) string {
145148
header := "## " + typeName
146149
idx := strings.Index(text, header+"\n")
147150
if idx < 0 {
148151
return ""
149152
}
150153
rest := text[idx:]
151-
// 找下一个 `## ` 二级标题(注意要在行首)
154+
// Find the next `## ` level-2 header (must be at line start)
152155
next := strings.Index(rest[len(header):], "\n## ")
153156
if next < 0 {
154157
return rest
155158
}
156-
return rest[:len(header)+next+1] // +1 包含 next 之前的换行
159+
return rest[:len(header)+next+1] // +1 keeps the newline before next
157160
}

‎internal/cli/explain_test.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ func TestExplainAll(t *testing.T) {
1717
}
1818
got := out.String()
1919
for _, want := range []string{
20-
"std-agent 类型",
20+
"std-agent types",
2121
"## rules",
2222
"## skills",
2323
"## commands",
2424
"## references",
2525
"## subagents",
26-
"## 选择标准",
26+
"## Quick reference",
2727
"stdagent budget --rendered",
2828
} {
2929
if !strings.Contains(got, want) {
@@ -48,7 +48,7 @@ func TestExplainSingle(t *testing.T) {
4848
if !strings.Contains(got, "applyTo") {
4949
t.Error("rules example frontmatter missing applyTo")
5050
}
51-
// 不应含其他 type 段
51+
// must not contain other type sections
5252
for _, other := range []string{"## skills", "## commands", "## references", "## subagents"} {
5353
if strings.Contains(got, other) {
5454
t.Errorf("single explain rules should not contain %q", other)

0 commit comments

Comments
 (0)