Skip to content

Latest commit

 

History

History
366 lines (275 loc) · 12.6 KB

File metadata and controls

366 lines (275 loc) · 12.6 KB

CLAUDE.md — CliLoci 开发与使用指南

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

项目概述

Loci 是一个极简 CLI 启动器,用 Rust 编写。它扫描 PATH 环境变量,列出所有可执行文件,并允许通过模糊搜索启动。设计哲学:"只列出,只跳转" — 不管理版本、不安装包、不记忆别名。

  • 包名: loci-cli(crates.io) / 二进制名 loci
  • 版本: 0.2.1(见 Cargo.toml
  • 仓库: 同时是 Rust 项目和 AI Agent Skill 包
  • Agent 入口: SKILL.md · 工作流见 AGENTS.md

构建与测试

cargo build              # Debug 构建
cargo build --release    # Release(opt-level="z" + LTO + strip + panic=abort)
cargo test               # 运行所有单元测试 + 集成测试
cargo test -- --test-threads=1  # 单线程运行(usage 测试共享磁盘文件,需串行)
cargo test args::tests          # 只跑某个模块,如 cargo test scanner::tests
cargo test --test cli           # 只跑集成测试
cargo test parse_args_default   # 按名称过滤
cargo run -- [args]      # 直接运行(无需安装)
cargo install --path .   # 安装到本地 PATH

Release profile 在 Cargo.toml 中定义:opt-level = "z", lto = true, strip = true, codegen-units = 1, panic = "abort"

架构

src/
├── main.rs      # 薄入口:解析 → 扫描 → 分发 list/exec;report_error 双通道错误
├── args.rs      # 参数解析(ParsedArgs / SelectMode / SortMode / fuzzy_match)
├── list.rs      # 列表模式:过滤 → meta → 标签 → 排序 → limit/count → 文本/JSON
├── exec.rs      # 选择与启动:interactive / pick-first / exact / index + launch
├── scanner.rs   # PATH 扫描、SHA-256 缓存、黑名单、--project 检测
├── metadata.rs  # 版本探测、类别推断、用户 tags 合并
├── ui.rs        # skim 模糊查找 TUI
├── usage.rs     # 使用频率追踪与排序(~/.local/share/loci/usage.json)
└── platform.rs  # 可执行检测(Unix 权限位 / Windows PATHEXT)

tests/
└── cli.rs       # 端到端集成测试(26 场景,驱动编译后的二进制)

npm/             # npm 分发包 @yaemikoreal/loci(postinstall 下载二进制)
completions/     # Shell 补全(bash/zsh/fish)
docs/usage.md    # 完整操作文档
scripts/loci-list # Python 封装,供 Agent 调用

入口数据流

CLI args
  → args::parse_args()
  → Scanner::new()
  → project_mode ? collect_project() : collect()
  → list_mode ? list::output_list() : exec::exec_select()

PATH 扫描流

PATH (+ LOCI_PATH_EXTRA)
  → scanner::collect()
    → SHA-256 指纹检查缓存 (~/.cache/loci/cache.json)
    → 命中 → 直接返回
    → 未命中 → 扫描 → 黑名单过滤 → 首次出现去重 → 字母序 → 写缓存
  → 列表:filter → (meta/tag) → sort → limit/count → 文本/JSON
  → 交互:skim TUI → record_usage → launch

测试架构

130 个测试:~104 单元(各模块 #[cfg(test)])+ 26 集成(tests/cli.rs)。

模块 约计 说明
args 47 参数解析与 fuzzy_match
metadata 20 类别推断、版本探测、tags
scanner 19 扫描、缓存、项目目录
usage 13 频率排序与持久化
platform 5 可执行检测
cli 集成 26 真二进制 E2E

单元测试模式

模式 说明 位置
temp_dir(label) $TMPDIR/loci-*-<label>-<pid> scanner/metadata/platform
touch_exec(path) 可执行文件(Unix 0o755 / Windows .exe scanner
scan_name(name) 平台感知扫描名(Windows 含 .exe scanner 断言
write_version_script(...) 版本探测用脚本 metadata
setup_usage / teardown_usage 写/清真实 data 目录 usage.json usage
Scanner::test_new(...) 跳过真实配置加载 scanner

重要usage.rs 测试共享 dirs::data_dir()/loci/usage.json,必须串行:cargo test -- --test-threads=1

集成测试(tests/cli.rs

通过 env!("CARGO_BIN_EXE_loci") 驱动二进制;用自定义 PATH 隔离系统工具:

fn create_executable(dir: &Path, name: &str) -> PathBuf
fn loci_with_path(args: &[&str], path: &str) -> Output
fn tool_display_name(name: &str) -> String

Windows 兼容

  • 测试工具用 touch_exec / create_executable 自动加 .exe
  • 断言用 scan_name / tool_display_name 适配平台名
  • platform 测试按 #[cfg(windows)] / Unix 分支编译

关键实现细节

参数解析(args.rs

parse_args()ParsedArgs

字段 含义
list_mode -l / --list(必须在 args[1]
json_mode --json
meta_mode --meta(显式才做版本探测)
project_mode --project
exists_mode --exists / --check(廉价存在性检查,exit 0/1)
tag_filter --tag <name>
count_mode --count(只输出数量)
limit --limit N--top N
select_mode Interactive / PickFirst / Exact / Index(n)
sort_mode Alpha / Freq(--top 强制 Freq)
filter 非标志 token 拼接
forwarded -- 之后透传参数

规则要点:

  • 标志位(--json / --meta / --project / --count / --pick-first / --exact)全局扫描,不限于 -l
  • 值消费型:--tag / --index / --sort / --limit / --top;缺值时 eprintln! 警告
  • --top N ≡ 截断 N 条 + --sort freq
  • 例:loci -l --json git → list + json + filter=git

列表管道(list.rs

name filter → meta_cache(可选) → tag filter → sort → limit → count short-circuit → text/JSON
  • explicit_meta = parsed.meta_modemeta_mode = explicit_meta || tag_filter.is_some()
  • explicit_meta 时做版本探测;仅 --tag 只做类别/标签,不 spawn --version
  • meta_cache 在 name filter 后算一次,供 tag 过滤与 JSON 共用

选择与启动(exec.rs

模式 行为 失败
Interactive skim TUI 用户取消 → exit 0
--pick-first 过滤后取第一个 exit 1 + no_matching_tool
--exact 全等匹配(区分大小写) exit 1 + exact_not_found
--index N 0-based exit 1 + index_out_of_range

launch:Unix Command::exec 替换进程;Windows status() 等待子进程。启动前 usage::record_usage

错误报告(main.rs

report_error(json_mode, code, msg)

  • 始终:eprintln!("loci: {}", msg)
  • json_mode 时额外:{"loci_error":{"code":"...","message":"..."}} 到 stderr(Agent 可解析)

缓存(scanner.rs

  • 路径:~/.cache/loci/cache.json
  • 指纹:PATH 目录路径 + mtime 的 SHA-256
  • 失效:PATH 或目录 mtime 变化;用户黑名单变化不触发指纹变化(进程重启会重建)
  • 写入:cache.json.tmprename(原子)
  • --project 不走磁盘缓存

黑名单

  • 内置 DEFAULT_BLACKLIST:shell builtins(cd/echo/export/kill/test/true/false 等)
  • 用户:~/.config/loci/blacklist(每行一名,# 注释)
  • 顺序:内置 → 用户

平台检测(platform.rs

  • Unix:mode & 0o111 != 0
  • Windows:PATHEXT(默认 .EXE;.BAT;.CMD;.COM;.PS1
  • 均先 is_file()

过滤语义

  • 列表/程序化选择的 fuzzy_match大小写不敏感子串(非真正模糊)
  • 真正模糊匹配仅在 skim TUI(ui.rs

LOCI_PATH_EXTRA

追加扫描目录(Unix : / Windows ;),不修改原始 PATH

功能特性

元数据(--meta

loci -l --json --meta 每工具 meta

字段 来源 示例
version tool --version(3s 超时) "git version 2.52.0"
category infer_category() "scm" / "python"
tags 内置 category + ~/.config/loci/tags.json ["scm","devops"]
path PATH 解析 "/usr/bin/git"

版本探测:先 --version-V;首条非空行 <120 字符;50ms 轮询;超时 kill+wait
VERSION_PROBE_BLACKLISTgitk / git-gui / gvim(GUI 勿探测)。
Windows:CREATE_NO_WINDOW + stdin(null)

标签(--tag

内置 10 类:scm / container / python / node / compress / network / editor / rust / go / database。
用户:~/.config/loci/tags.json{"tool": ["tag1"]}
--tag 自动 meta 模式,探测版本,除非同时 --meta

项目模式(--project

类型 标志 目录
Node package.json node_modules/.bin/
Python venv pyvenv.cfg .venv/venvbinScripts
Rust Cargo.toml target/debug + target/release
Conda $CONDA_PREFIX $CONDA_PREFIX/bin

截断与计数

参数 行为
--limit N 输出最多 N 条
--top N 最多 N 条 + 强制按频率排序
--count 只输出数量;JSON 为 {"skill_version","total"}

排序(--sort

行为
alpha 字母序(默认)
freq 频率降序 → 最近使用 → 字母序

数据:~/.local/share/loci/usage.json(tmp+rename)。UTC 自实现,无 chrono。

JSON 输出

{
  "skill_version": "v0.2.1",
  "total": 3,
  "executables": ["cargo", "git", "python"],
  "filter": "git",
  "project": true,
  "tag_filter": "scm",
  "meta": {
    "git": {
      "version": "git version 2.52.0",
      "category": "scm",
      "tags": ["scm"],
      "path": "/usr/bin/git"
    }
  }
}
  • skill_version 始终有(env!("CARGO_PKG_VERSION")
  • meta 仅在 --meta--tag 时出现;version 字段仅显式 --meta
  • 序列化失败时降级紧凑 JSON

错误处理与鲁棒性(修改时保持一致)

原子持久化

cache.json / usage.json:写 .tmprename

子进程

  • 版本探测 3s 超时 + 50ms 轮询;超时 kill+wait
  • GUI 探测黑名单;Windows 静默窗
  • try_wait 已收割后用 stdout.take(),勿再 wait_with_output
  • Windows status.code() == None 时打印诊断而非静默 exit 1

扫描容错

  • 不可读目录 continue
  • 非 UTF-8 文件名(含 U+FFFD)跳过并警告
  • 不存在的 PATH 项静默丢弃
  • 无效 LOCI_PATH_EXTRA 路径 eprintln! 警告

不变式

tag_filtermeta_cache 必为 Some(debug debug_assert!;release 子串降级)。

依赖

Crate 用途
skim (frizbee) 模糊 TUI
dirs 配置/缓存/数据目录
serde / serde_json 缓存 + JSON
sha2 PATH 指纹

其余用标准库。

发布

推送 v* tag → .github/workflows/release.yml

  1. test — ubuntu / macos / windows cargo test --release
  2. build — 5 目标(linux-x64/arm64, macos-x64/arm64, windows-x64)
  3. release — artifact + checksums + GitHub Release

npm:@yaemikoreal/lociinstall.js 拉二进制,bin/loci.js spawnSync 透传。

文档入口

文件 用途
docs/usage.md 用户操作手册
SKILL.md Agent Skill 协议
AGENTS.md Agent 工作流
README.md 项目首页

常用命令

cargo build --release
cargo test -- --test-threads=1
cargo test args::tests
cargo test --test cli
cargo test parse_args_default
cargo clippy
cargo install --path .
cargo run -- -l --json

使用场景速查

loci                              # 交互 TUI
loci git                          # 预过滤后 TUI
loci --exists git                 # 存在性检查(Agent 廉价 API)
loci --exists --json git          # 存在性 + JSON
loci -l                           # 文本列表
loci -l --json                    # JSON(Agent 首选)
loci -l --json git                # JSON + 关键词
loci -l --json --meta             # JSON + 版本/类别/路径
loci -l --json --tag scm          # 标签过滤(无版本探测)
loci -l --json --tag scm --meta   # 标签 + 版本
loci -l --project --json          # 项目本地工具
loci -l --sort freq               # 按频率
loci -l --top 10                  # Top10 常用
loci -l --count                   # 仅数量
loci -l --limit 20 git            # 截断
loci --exact python.exe           # 精确启动
loci --pick-first git             # 首个匹配启动
loci --index 0 python -- --version
loci git -- log --oneline         # 预过滤 + 透传