cli-logging-ux

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
CLI Logging UX expert persona
CLI日志UX专家角色

CLI Logging & Developer Experience

CLI日志与开发者体验

Decision framework

决策框架

Apply these three tests to every piece of user-facing output. If a message fails any test, redesign it.
对每一条面向用户的输出应用以下三项测试。如果消息未通过任何一项测试,请重新设计。

1. The "So What?" Test

1. “那又如何?”测试

Every warning must answer: what should the user do about this?
undefined
每条警告必须回答:用户应该对此采取什么行动?
undefined

Fails — not actionable, user can't do anything

未通过——无操作性,用户无法采取任何措施

Sub-skill 'my-skill' from 'my-package' overwrites existing skill
Sub-skill 'my-skill' from 'my-package' overwrites existing skill

Passes — tells the user exactly what to do

通过——明确告知用户该怎么做

Skipping my-skill — local file exists (not managed by APM). Use 'apm install --force' to overwrite.

If the user can't act on it, it's not a warning — it's noise. Demote to `--verbose` or remove.
Skipping my-skill — local file exists (not managed by APM). Use 'apm install --force' to overwrite.

如果用户无法采取行动,那它就不是警告——而是噪音。降级为`--verbose`输出或直接移除。

2. The Traffic Light Rule

2. 交通灯规则

Use color semantics consistently. Never use a warning color for an informational state.
ColorHelperMeaningWhen to use
Green
_rich_success()
Success / completedOperation finished as expected
Yellow
_rich_warning()
User action neededSomething requires user decision
Red
_rich_error()
Error / failureOperation failed, cannot continue
Blue
_rich_info()
InformationalStatus updates, progress, summaries
Dim
_rich_echo(color="dim")
Secondary detailVerbose-mode details, grouping headers
始终一致地使用颜色语义。绝不能用警告色表示信息性状态。
颜色辅助函数含义适用场景
绿色
_rich_success()
成功/已完成操作按预期完成
黄色
_rich_warning()
需要用户操作某些内容需要用户决策
红色
_rich_error()
错误/失败操作失败,无法继续
蓝色
_rich_info()
信息性内容状态更新、进度、摘要
暗淡色
_rich_echo(color="dim")
次要细节详细模式下的细节、分组标题

3. The Newspaper Test

3. 报纸测试

Can the user scan output like headlines? Top-level = what happened. Details = drill down.
undefined
用户能否像浏览新闻标题一样快速扫描输出?顶层内容=发生了什么,细节内容=深入查看。
undefined

Bad — warnings break the visual flow between status and summary

不佳——警告打断了状态与摘要之间的视觉流程

[checkmark] package-name [warning] something happened [warning] something else happened [tree] 3 skill(s) integrated
[checkmark] package-name [warning] something happened [warning] something else happened [tree] 3 skill(s) integrated

Good — clean tree, diagnostics at the end

良好——清晰的树状结构,诊断信息放在末尾

[checkmark] package-name [tree] 3 skill(s) integrated
── Diagnostics ── [warning] 2 skills replaced by a different package (last installed wins) Run with --verbose to see details
undefined
[checkmark] package-name [tree] 3 skill(s) integrated
── Diagnostics ── [warning] 2 skills replaced by a different package (last installed wins) Run with --verbose to see details
undefined

Inline output vs deferred diagnostics

内联输出 vs 延迟诊断

Use inline output for:

内联输出适用于:

  • Success confirmations (
    _rich_success
    )
  • Progress updates (
    _rich_info
    with indented
    └─
    prefix)
  • Errors that halt the current operation (
    _rich_error
    )
  • 成功确认(
    _rich_success
  • 进度更新(带缩进
    └─
    前缀的
    _rich_info
  • 终止当前操作的错误(
    _rich_error

Use DiagnosticCollector for:

DiagnosticCollector适用于:

  • Warnings that apply across multiple packages (collisions, overwrites)
  • Issues the user should know about but that don't stop the operation
  • Anything that would repeat N times in a loop
python
undefined
  • 跨多个包的警告(冲突、覆盖)
  • 用户需要知晓但不会终止操作的问题
  • 循环中会重复N次的内容
python
undefined

Bad — inline warning repeated per file, clutters output

不佳——内联警告随文件重复输出,造成混乱

for file in files: if collision: _rich_warning(f"Skipping {file}...")
for file in files: if collision: _rich_warning(f"Skipping {file}...")

Good — collect during loop, render grouped summary at the end

良好——循环期间收集,最后渲染分组摘要

for file in files: if collision: diagnostics.skip(file, package=pkg_name)
for file in files: if collision: diagnostics.skip(file, package=pkg_name)

Later, after the loop:

循环结束后:

if diagnostics.has_diagnostics: diagnostics.render_summary()

DiagnosticCollector categories: `skip()` for collisions, `overwrite()` for cross-package replacements, `warn()` for general warnings, `error()` for failures.
if diagnostics.has_diagnostics: diagnostics.render_summary()

DiagnosticCollector分类:`skip()`用于冲突,`overwrite()`用于跨包替换,`warn()`用于常规警告,`error()`用于失败。

Console helper conventions

控制台辅助函数约定

Always use the helpers from
apm_cli.utils.console
— never raw
print()
or bare
click.echo()
.
Emojis are banned. Never use emoji characters anywhere in CLI output — not in messages, symbols, help text, or status indicators. Use ASCII text symbols exclusively via
STATUS_SYMBOLS
.
python
from apm_cli.utils.console import (
    _rich_success, _rich_error, _rich_warning, _rich_info, _rich_echo
)

_rich_success("Installed 3 APM dependencies")        # green, bold
_rich_info("  └─ 2 prompts integrated → .github/prompts/")  # blue
_rich_warning("Config drift detected — re-run apm install")  # yellow
_rich_error("Failed to download package")              # red
_rich_echo("    [pkg-name]", color="dim")              # dim, for verbose details
Use
STATUS_SYMBOLS
dict with
symbol=
parameter for consistent ASCII prefixes:
python
_rich_info("Starting operation...", symbol="gear")     # renders as "[*] Starting operation..."
始终使用
apm_cli.utils.console
中的辅助函数——绝不要使用原生
print()
或直接
click.echo()
禁止使用表情符号。在CLI输出的任何位置都不要使用表情符号字符——包括消息、符号、帮助文本或状态指示器。仅通过
STATUS_SYMBOLS
使用ASCII文本符号。
python
from apm_cli.utils.console import (
    _rich_success, _rich_error, _rich_warning, _rich_info, _rich_echo
)

_rich_success("Installed 3 APM dependencies")        # 绿色、加粗
_rich_info("  └─ 2 prompts integrated → .github/prompts/")  # 蓝色
_rich_warning("Config drift detected — re-run apm install")  # 黄色
_rich_error("Failed to download package")              # 红色
_rich_echo("    [pkg-name]", color="dim")              # 暗淡色,用于详细细节
使用带
symbol=
参数的
STATUS_SYMBOLS
字典以保持一致的ASCII前缀:
python
_rich_info("Starting operation...", symbol="gear")     # 渲染为 "[*] Starting operation..."

Output structure pattern

输出结构模式

Follow this visual hierarchy for multi-package operations:
[checkmark] package-name-1                      # _rich_success — download/copy ok
  [tree] 2 prompts integrated → .github/prompts/     # _rich_info — indented summary
  [tree] 1 skill(s) integrated → .github/skills/
[checkmark] package-name-2
  [tree] 1 instruction(s) integrated → .github/instructions/

── Diagnostics ──                         # Only if diagnostics.has_diagnostics
  [warning] N files skipped — ...                   # Grouped by category
    Run with --verbose to see details

Installed 2 APM dependencies              # _rich_success — final summary
多包操作遵循以下视觉层级:
[checkmark] package-name-1                      # _rich_success — 下载/复制完成
  [tree] 2 prompts integrated → .github/prompts/     # _rich_info — 缩进摘要
  [tree] 1 skill(s) integrated → .github/skills/
[checkmark] package-name-2
  [tree] 1 instruction(s) integrated → .github/instructions/

── Diagnostics ──                         # 仅当diagnostics.has_diagnostics时显示
  [warning] N files skipped — ...                   # 按分类分组
    Run with --verbose to see details

Installed 2 APM dependencies              # _rich_success — 最终摘要

Content-awareness principle

内容感知原则

Before reporting changes, check if anything actually changed. Don't report no-ops.
python
undefined
在报告更改前,检查是否真的有内容变更。不要报告无操作(no-ops)。
python
undefined

Bad — always copies and reports, even when content is identical

不佳——无论内容是否相同都复制并报告

shutil.rmtree(target) shutil.copytree(source, target) _rich_info(f" └─ Skill updated")
shutil.rmtree(target) shutil.copytree(source, target) _rich_info(f" └─ Skill updated")

Good — skip when content matches

良好——内容匹配时跳过

if SkillIntegrator._dirs_equal(source, target): continue # Nothing changed, nothing to report
undefined
if SkillIntegrator._dirs_equal(source, target): continue # 无变更,无需报告
undefined

CommandLogger Architecture

CommandLogger架构

APM is a large and growing CLI with 10+ commands, 8+ integrators, and dozens of output sites. The logging architecture enforces Separation of Concerns: commands declare what happened; the logger decides how to render it. This keeps output consistent, testable, and evolvable without shotgun surgery across command files.
APM是一个规模不断扩大的CLI,拥有10+命令、8+集成器和数十个输出点。日志架构遵循关注点分离原则:命令层声明发生了什么;日志层决定如何渲染。这确保了输出的一致性、可测试性,且无需在命令文件中进行零散修改即可演进。

The three layers

三层结构

┌─────────────────────────────────────────────────────┐
│  Command layer  (install.py, pack.py, audit.py …)   │
│  Calls: logger.success(), logger.tree_item(), …      │
│  NEVER calls: _rich_*, click.echo(), print()         │
├─────────────────────────────────────────────────────┤
│  Logger layer   (command_logger.py)                  │
│  CommandLogger ← InstallLogger, future subclasses    │
│  Owns: verbose gating, symbol choice, indentation    │
│  Delegates to: _rich_* helpers                       │
├─────────────────────────────────────────────────────┤
│  Rendering layer (console.py)                        │
│  _rich_echo, _rich_success, _rich_error, …           │
│  Owns: Rich/colorama fallback, color, STATUS_SYMBOLS │
└─────────────────────────────────────────────────────┘
Changes to output style (colors, symbols, indentation) happen in the logger or rendering layer only — command code is untouched. New output patterns (e.g. a tree sub-item, a package metadata line) become new logger methods, not ad-hoc format strings in commands.
┌─────────────────────────────────────────────────────┐
│  命令层  (install.py, pack.py, audit.py …)   │
│  调用:logger.success(), logger.tree_item(), …      │
│  禁止调用:_rich_*, click.echo(), print()         │
├─────────────────────────────────────────────────────┤
│  日志层   (command_logger.py)                  │
│  CommandLogger ← InstallLogger,未来子类    │
│  负责:详细模式控制、符号选择、缩进    │
│  委托给:_rich_* 辅助函数                       │
├─────────────────────────────────────────────────────┤
│  渲染层 (console.py)                        │
│  _rich_echo, _rich_success, _rich_error, …           │
│  负责:Rich/colorama降级处理、颜色、STATUS_SYMBOLS │
└─────────────────────────────────────────────────────┘
输出样式的变更(颜色、符号、缩进)仅在日志层或渲染层进行——命令代码无需修改。新的输出模式(如树状子项、包元数据行)应成为新的日志方法,而非命令中的临时格式化字符串。

Base class:
CommandLogger

基类:
CommandLogger

src/apm_cli/core/command_logger.py
— base for all commands.
MethodPurposeWhen to use
start(msg, symbol=)
Operation startBeginning of a command
progress(msg, symbol=)
Status update with
[i]
prefix
Mid-operation phase changes
success(msg, symbol=)
Green successOperation completed
warning(msg, symbol=)
Yellow warningUser action needed
error(msg, symbol=)
Red errorOperation failed
verbose_detail(msg)
Dim text, verbose-onlyInternal details (paths, hashes)
tree_item(msg)
Green text, no symbol prefix
└─
sub-items under a package
package_inline_warning(msg)
Yellow text, verbose-onlyPer-package diagnostic hints
dry_run_notice(msg)
[dry-run]
prefix
Dry-run explanation
auth_step(step, success, detail)
Auth resolution stepVerbose auth tracing
render_summary()
Render DiagnosticCollectorEnd of command
src/apm_cli/core/command_logger.py
—— 所有命令的基类。
方法用途适用场景
start(msg, symbol=)
操作启动命令开始时
progress(msg, symbol=)
[i]
前缀的状态更新
操作中期状态变更
success(msg, symbol=)
绿色成功提示操作完成时
warning(msg, symbol=)
黄色警告提示需要用户操作时
error(msg, symbol=)
红色错误提示操作失败时
verbose_detail(msg)
暗淡色文本,仅详细模式显示内部细节(路径、哈希)
tree_item(msg)
绿色文本,无符号前缀包下的
└─
子项
package_inline_warning(msg)
黄色文本,仅详细模式显示每个包的诊断提示
dry_run_notice(msg)
[dry-run]
前缀
试运行说明
auth_step(step, success, detail)
认证步骤追踪详细模式下的认证追踪
render_summary()
渲染DiagnosticCollector命令结束时

Subclass:
InstallLogger(CommandLogger)

子类:
InstallLogger(CommandLogger)

Install-specific phases. Commands that don't need these use
CommandLogger
directly.
MethodPurposeOutput
validation_start(count)
Start validation
[*] Validating N package(s)...
validation_pass(name, present)
Package OK
[+] name
or
name (already in apm.yml)
validation_fail(name, reason)
Package bad
[x] name -- reason
resolution_start(count, lockfile)
Start resolutionContext-aware install/update message
download_complete(name, ref=, sha=, cached=)
Package installed
[+] name #tag @sha
or
(cached)
download_failed(name, error)
Download error
[x] name -- error
lockfile_entry(key, ref=, sha=)
Lockfile verbose line
key: locked at sha
/
pinned to ref
/ omitted
package_auth(source, token_type=)
Auth source verbose
Auth: source (type)
package_type_info(label)
Package type verbose
Package type: label
install_summary(apm, mcp, errors)
Final summary
Installed N APM dependencies.
安装流程专属阶段。不需要这些阶段的命令直接使用
CommandLogger
方法用途输出
validation_start(count)
开始验证
[*] Validating N package(s)...
validation_pass(name, present)
包验证通过
[+] name
name (already in apm.yml)
validation_fail(name, reason)
包验证失败
[x] name -- reason
resolution_start(count, lockfile)
开始依赖解析上下文感知的安装/更新消息
download_complete(name, ref=, sha=, cached=)
包安装完成
[+] name #tag @sha
(cached)
download_failed(name, error)
下载失败
[x] name -- error
lockfile_entry(key, ref=, sha=)
锁文件详细行
key: locked at sha
/
pinned to ref
/ 省略
package_auth(source, token_type=)
认证源详细信息
Auth: source (type)
package_type_info(label)
包类型详细信息
Package type: label
install_summary(apm, mcp, errors)
最终摘要
Installed N APM dependencies.

When to add a new logger method

何时添加新的日志方法

If a command needs a new output pattern (new indentation level, new semantic meaning, new verbose gate), add a method to CommandLogger or a subclass. Signs you need a new method:
  • You're writing
    _rich_echo(f"    Something: {value}", color="dim")
    in a command file
  • You're checking
    if logger.verbose:
    before calling
    _rich_echo
    in a command
  • You're formatting a string with specific indentation that other commands might reuse
  • Multiple commands emit the same kind of line (e.g., file lists, auth info)
如果命令需要新的输出模式(新的缩进级别、新的语义、新的详细模式控制),请向CommandLogger或其子类添加方法。需要添加新方法的信号:
  • 你在命令文件中编写
    _rich_echo(f"    Something: {value}", color="dim")
  • 你在命令中调用
    _rich_echo
    前检查
    if logger.verbose:
  • 你正在格式化带有特定缩进的字符串,而其他命令可能会复用
  • 多个命令输出相同类型的行(如文件列表、认证信息)

Rule: No direct
_rich_*
in commands

规则:命令中禁止直接调用
_rich_*

Command functions must NOT call
_rich_info()
,
_rich_error()
, etc. directly. Use
logger.progress()
,
logger.error()
, etc. instead. The
_rich_*
helpers are internal to the logger and rendering layers.
Exception: Rich tables and panels for display (not lifecycle logging) may use
console.print()
directly — these are data presentation, not status reporting.
命令函数绝不能直接调用
_rich_info()
_rich_error()
等。请改用
logger.progress()
logger.error()
等方法。
_rich_*
辅助函数是日志层和渲染层的内部函数
例外: 用于展示的Rich表格和面板(非生命周期日志)可直接使用
console.print()
——这些属于数据展示,而非状态报告。

Rule: Every command gets a
CommandLogger

规则:每个命令都要实例化
CommandLogger

Every Click command function must instantiate a
CommandLogger
(or subclass) and pass it to helpers:
python
@cli.command()
@click.option("--verbose", "-v", is_flag=True)
@click.option("--dry-run", is_flag=True)
def my_command(verbose, dry_run):
    logger = CommandLogger("my-command", verbose=verbose, dry_run=dry_run)
    logger.start("Starting operation...")
    _do_work(logger=logger)
    logger.render_summary()
每个Click命令函数必须实例化
CommandLogger
(或其子类)并传递给辅助函数:
python
@cli.command()
@click.option("--verbose", "-v", is_flag=True)
@click.option("--dry-run", is_flag=True)
def my_command(verbose, dry_run):
    logger = CommandLogger("my-command", verbose=verbose, dry_run=dry_run)
    logger.start("Starting operation...")
    _do_work(logger=logger)
    logger.render_summary()

Rule: Verbose gating lives in the logger

规则:详细模式控制由日志层负责

Never check
if verbose:
in command code. Use methods that gate internally:
python
undefined
绝不要在命令代码中检查
if verbose:
。请使用内部已实现控制的方法:
python
undefined

Bad — manual verbose check in command

不佳——命令中手动检查详细模式

if verbose: _rich_echo(f" Auth: {source}", color="dim")
if verbose: _rich_echo(f" Auth: {source}", color="dim")

Good — logger handles the gate

良好——日志层处理控制

logger.package_auth(source, token_type) # No-ops when not verbose logger.verbose_detail(f" Path: {path}") # No-ops when not verbose
undefined
logger.package_auth(source, token_type) # 非详细模式下无操作 logger.verbose_detail(f" Path: {path}") # 非详细模式下无操作
undefined

DiagnosticCollector integration

DiagnosticCollector集成

Access via
logger.diagnostics
(lazy-initialized). The collector owns the collect-then-render lifecycle:
python
undefined
通过
logger.diagnostics
访问(延迟初始化)。收集器负责“先收集后渲染”的生命周期:
python
undefined

During operation — collect

操作期间——收集

diagnostics.skip(file, package=pkg_name) # Collision diagnostics.overwrite(file, package=pkg_name) # Cross-package replacement diagnostics.error(msg, package=pkg_name) # Failure diagnostics.auth(msg, package=pkg_name) # Auth issue
diagnostics.skip(file, package=pkg_name) # 冲突 diagnostics.overwrite(file, package=pkg_name) # 跨包替换 diagnostics.error(msg, package=pkg_name) # 失败 diagnostics.auth(msg, package=pkg_name) # 认证问题

Query during operation (e.g., for inline verbose hints)

操作期间查询(如内联详细提示)

count = diagnostics.count_for_package(pkg_name, category="collision") if count > 0: logger.package_inline_warning(f" [!] {count} files skipped")
count = diagnostics.count_for_package(pkg_name, category="collision") if count > 0: logger.package_inline_warning(f" [!] {count} files skipped")

After operation — render grouped summary

操作结束后——渲染分组摘要

logger.render_summary() # Delegates to diagnostics.render_summary()
undefined
logger.render_summary() # 委托给diagnostics.render_summary()
undefined

Visual hierarchy contract

视觉层级约定

Multi-package operations follow this tree structure:
  [+] package-name #v1.0 @b0cbd3df                    # download_complete
      Auth: git-credential-fill (oauth)                # package_auth (verbose)
      Package type: Skill (SKILL.md detected)          # package_type_info (verbose)
    └─ 3 skill(s) integrated -> .github/skills/        # tree_item
    └─ 1 prompt integrated -> .github/prompts/         # tree_item
      [!] 2 files skipped (local files exist)          # package_inline_warning (verbose)
  [+] another-package (cached)                         # download_complete

── Diagnostics ──                                      # render_summary
  [!] 2 files skipped -- local files exist             # Grouped by category
    Use 'apm install --force' to overwrite

[*] Installed 2 APM dependencies.                      # install_summary
Key rules:
  • [+]
    package lines are the top-level anchors (green, no indent beyond 2-space)
  • Verbose metadata (Auth, Package type) uses 4-space indent, dim color
  • Tree items (
    └─
    ) use 4-space indent, green color, no symbol prefix
  • Inline warnings use 4-space indent, yellow color, verbose-only
  • Diagnostics summary appears AFTER all packages, not inline (except verbose hints)
多包操作遵循以下树状结构:
  [+] package-name #v1.0 @b0cbd3df                    # download_complete
      Auth: git-credential-fill (oauth)                # package_auth (详细模式)
      Package type: Skill (SKILL.md detected)          # package_type_info (详细模式)
    └─ 3 skill(s) integrated -> .github/skills/        # tree_item
    └─ 1 prompt integrated -> .github/prompts/         # tree_item
      [!] 2 files skipped (local files exist)          # package_inline_warning (详细模式)
  [+] another-package (cached)                         # download_complete

── Diagnostics ──                                      # render_summary
  [!] 2 files skipped -- local files exist             # 按分类分组
    Use 'apm install --force' to overwrite

[*] Installed 2 APM dependencies.                      # install_summary
关键规则:
  • [+]
    包行是顶层锚点(绿色,缩进不超过2空格)
  • 详细元数据(认证、包类型)使用4空格缩进、暗淡色
  • 树状项(
    └─
    )使用4空格缩进、绿色、无符号前缀
  • 内联警告使用4空格缩进、黄色、仅详细模式显示
  • 诊断摘要出现在所有包之后,而非内联(详细提示除外)

Scaling guidance

扩展指导

As the CLI grows, this architecture scales by:
  • New commands: Instantiate
    CommandLogger
    , use existing methods. Add subclass only if the command has distinct phases (like
    InstallLogger
    ).
  • New output patterns: Add methods to
    CommandLogger
    . Every command benefits.
  • New integrators: Accept
    diagnostics=
    param, push to collector. No direct output.
  • Theme changes: Modify rendering layer (
    console.py
    ). Zero command changes.
  • Testing: Mock
    CommandLogger
    in tests to assert semantic calls without parsing output strings.
随着CLI规模增长,此架构通过以下方式扩展:
  • 新命令:实例化
    CommandLogger
    ,使用现有方法。仅当命令有独特阶段时才添加子类(如
    InstallLogger
    )。
  • 新输出模式:向
    CommandLogger
    添加方法。所有命令均可受益。
  • 新集成器:接收
    diagnostics=
    参数,将警告推送到收集器。禁止直接输出。
  • 主题变更:修改渲染层(
    console.py
    )。无需修改任何命令。
  • 测试:在测试中模拟
    CommandLogger
    ,断言语义调用,无需解析输出字符串。

Anti-patterns

反模式

  1. Warning for non-actionable state — If the user can't do anything about it, use
    _rich_info
    or defer to
    --verbose
    , not
    _rich_warning
    .
  2. Inline warnings in loops — Use
    DiagnosticCollector
    to collect, then render a grouped summary after the loop.
  3. Missing
    diagnostics
    parameter
    — When calling integrators, always pass
    diagnostics=diagnostics
    so warnings route to the deferred summary.
  4. No emojis, ever — Emojis are completely banned from all CLI output. Use ASCII text symbols from
    STATUS_SYMBOLS
    exclusively. This applies to messages, help text, status indicators, and table titles.
  5. Inconsistent symbols — Always use
    STATUS_SYMBOLS
    dict with
    symbol=
    param, not inline characters.
  6. Walls of text — Use Rich tables for structured data, panels for grouped content. Break up long output with visual hierarchy (indentation,
    └─
    tree connectors).
  7. Direct
    _rich_*
    calls in commands
    — Use
    logger.start()
    ,
    logger.progress()
    ,
    logger.tree_item()
    etc. The
    _rich_*
    helpers are internal to CommandLogger and console.py. Adding a
    _rich_echo
    call in a command file is a SoC violation.
  8. Manual
    if verbose:
    checks
    — Use
    logger.verbose_detail()
    ,
    logger.package_auth()
    , or other verbose-gated methods. The logger owns the gate.
  9. Manual
    if dry_run:
    checks
    — Use
    logger.should_execute
    or
    logger.dry_run_notice()
    .
  10. Format strings for indentation in commands — Don't write
    f"    Auth: {source}"
    in command code. Use
    logger.package_auth(source)
    which owns the indent level. When a new indentation pattern is needed, add a method to CommandLogger.
  11. Re-creating shared objects per iteration — Expensive objects like
    AuthResolver
    should be created once before loops and reused per-package. The logger and diagnostics collector are already singletons per command invocation.
  12. Using
    logger.progress()
    for tree sub-items
    progress()
    adds a
    [i]
    symbol prefix. Tree continuation lines (
    └─
    ) should use
    logger.tree_item()
    which renders with no symbol.
  1. 对非可操作状态发出警告——如果用户无法采取任何行动,请使用
    _rich_info
    或降级到
    --verbose
    ,而非
    _rich_warning
  2. 循环中的内联警告——使用
    DiagnosticCollector
    收集,然后在循环结束后渲染分组摘要。
  3. 缺少
    diagnostics
    参数
    ——调用集成器时,始终传递
    diagnostics=diagnostics
    ,以便警告路由到延迟摘要。
  4. 绝对禁止使用表情符号——CLI输出的所有位置完全禁止使用表情符号。仅使用
    STATUS_SYMBOLS
    中的ASCII文本符号。这适用于消息、帮助文本、状态指示器和表格标题。
  5. 符号不一致——始终使用带
    symbol=
    参数的
    STATUS_SYMBOLS
    字典,而非内联字符。
  6. 文本墙——使用Rich表格展示结构化数据,使用面板分组内容。通过视觉层级(缩进、
    └─
    树状连接器)拆分长输出。
  7. 命令中直接调用
    _rich_*
    ——使用
    logger.start()
    logger.progress()
    logger.tree_item()
    等方法。
    _rich_*
    辅助函数是CommandLogger和console.py的内部函数。在命令文件中添加
    _rich_echo
    调用违反关注点分离原则。
  8. 手动检查
    if verbose:
    ——使用
    logger.verbose_detail()
    logger.package_auth()
    或其他已实现详细模式控制的方法。日志层负责控制逻辑。
  9. 手动检查
    if dry_run:
    ——使用
    logger.should_execute
    logger.dry_run_notice()
  10. 命令中使用格式化字符串控制缩进——不要在命令代码中编写
    f"    Auth: {source}"
    。使用
    logger.package_auth(source)
    ,它负责缩进级别。当需要新的缩进模式时,向CommandLogger添加方法。
  11. 每次迭代重新创建共享对象——像
    AuthResolver
    这样的昂贵对象应在循环前创建一次,并在每个包中复用。日志器和诊断收集器在每次命令调用中已是单例。
  12. 使用
    logger.progress()
    处理树状子项
    ——
    progress()
    会添加
    [i]
    符号前缀。树状延续行(
    └─
    )应使用
    logger.tree_item()
    ,它渲染时无符号。