Skip to content

refactor: organize tools/ into a distilly package without changing any command - #212

Open
titanwings wants to merge 4 commits into
dot-skillfrom
refactor/tools-package
Open

titanwings wants to merge 4 commits into
dot-skillfrom
refactor/tools-package

Conversation

@titanwings

Copy link
Copy Markdown
Owner

Requested by Tianyi · project thread

Summary

Before: tools/ held 19 flat scripts. Four collectors each had their own copy of the config read/write code and their own sys.path.insert. The Chrome-profile and OpenClaw-workspace lookups were each written twice, and skill_writer.py (719 lines) covered templates, rendering, patching, corrections, storage, host installs and the CLI all at once.

After: the code lives in a tools/distilly/ package with layers: core/ (config, credentials, hosts, paths, chrome), collect/, profile/, write/ and install/. Every script and module path under tools/ and tools/research/ still exists as a short entry point. SKILL.md, prompts, INSTALL guides, the npm installer and the existing imports work unchanged.

How: each old path is now about 6 lines. Run as a script, it calls the package's main(). Imported, it replaces itself in sys.modules with the package module, so import skill_writer and mock.patch.object(feishu_auto_collector, ...) reach the real code. Running python3 tools/<script>.py already puts tools/ on the import path, so the package needs no path edits. The installers already copied tools/ whole, so the package ships automatically.

Changes

One commit per phase. CI is green on each commit, so any of them can be reverted on its own.

  • Phase 0: lock current behavior (tests/test_cli_contract.py)
    • Snapshots, for every runnable script, its --help options, its subcommands and its public names.
    • Runs that check from the repo and from a freshly installed copy.
    • Also checks that every tools/ path named in SKILL.md, prompts and INSTALL exists.
  • Phase 1: merge duplicated code into tools/distilly/core/
    • config.py holds the collectors' ~/.distilly config read/write, with the legacy ~/.colleague-skill fallback and mode 0600.
    • chrome.py holds the Chrome profile lookup.
    • paths.py holds the OpenClaw workspace lookup.
    • Old names such as CONFIG_PATH, load_config, get_default_chrome_profile and openclaw_workspace stay on each module.
  • Phase 2: move into the package
    • Files were moved one to one; the only changes are import lines and the removed shebang and __main__ blocks.
    • The four sys.path.insert lines are gone.
    • install_generated_skill.py and install_generated_skill_common.py now share install/generated.py.
    • CONTRIBUTING describes the layout and the rule that entry scripts are never renamed or removed.
  • Phase 3: split the writer and merge duplicate install branches
    • write/render.py (templates and rendering) and write/patch.py (patch merge and corrections) were split out of the writer. The writer re-exports their names.
    • The two copy-pasted OpenClaw and Codex install branches after create/update are now one loop. Flags, defaults and printed lines are unchanged.
    • Added tests/test_generated_output_golden.py. It records a full run with the pre-refactor code: create, update, correction, rollback, list, every installer and two error paths, writing 70 files and printing 19 outputs. It then checks that the refactored code writes the same files and prints the same output.

Not in this PR:

  • Splitting the Feishu and DingTalk collectors into api/flow modules. Tests patch functions such as get_tenant_token on the collector module itself, so moving them would need test changes. I left them for a separate decision.
  • The unified distilly command and the Feishu/Lark wording (phases 4 and 5).

Motivation

Make tools/ easier to read and change without breaking any existing command, skill flow, collector or generated skill. Plan and diagrams: https://claude.ai/artifact/Nk9ci88xFF9W5PuNFovZe5

Testing

  • python -m unittest discover -s tests -p 'test_*.py': 134 tests OK on Python 3.13 and on Python 3.9.25, with requirements.txt and requirements-optional.txt installed. Base dot-skill has 123 tests; this PR adds 11.
  • Each intermediate commit passes the suite on its own: 127, 133, 133 and 134 tests.
  • python -m compileall tools/ and ruff check tools/ tests/ pass.
  • Golden run: the output recorded from the pre-refactor commit is identical to the output of this branch, with and without pypinyin installed.
  • Manual check: node bin/distilly.mjs --check-package passes. HOME=<tmp> node bin/distilly.mjs install codex succeeds. From the installed copy, owner_sources.py discover --json and install_distilly.py --host pi --dry-run both run.

Checklist

  • I read CONTRIBUTING.md
  • Docs updated if behavior or usage changed. Usage did not change; CONTRIBUTING now describes the package layout.
  • No secrets, tokens, or personal data committed
  • No new dependencies
  • Tests added: CLI contract, core helpers, and the generated-output recording

🤖 Generated with Claude Code

https://claude.ai/code/session_01N65qP8LxH1kiQynvqf5JLf


Generated by Claude Code

Records each script's --help options, subcommands and public names, and
checks them from the repository and from a freshly installed copy. Also
checks that every tools/ path named in SKILL.md, prompts and the install
guides exists.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N65qP8LxH1kiQynvqf5JLf
The four collectors read and wrote their ~/.distilly config with four
copies of the same code; feishu_browser and the DingTalk collector each
had the Chrome profile lookup; owner_profile and owner_sources each
resolved the OpenClaw workspace. They now call tools/distilly/core.

Module names that tests and callers use (CONFIG_PATH, load_config,
get_default_chrome_profile, openclaw_workspace, ...) stay in place.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N65qP8LxH1kiQynvqf5JLf
…write and install layers

Implementations now live in tools/distilly/:
  core/     config, credentials, hosts, paths, chrome
  collect/  feishu/{auto,mcp,browser,export}, dingtalk, slack, mailbox,
            owner_sources, research/{xquik,subtitles,transcribe}
  profile/  presets, schema, owner, research_merge, quality
  write/    writer, versions
  install/  creator, generated, claude

Every old script and module path under tools/ and tools/research/ is
kept as a short entry point. Run as a script it calls the package's
main(); imported, it replaces itself with the package module, so
existing imports and mock.patch targets keep working. The four
sys.path.insert lines are gone because running tools/<script>.py
already puts tools/ on the import path.

install_generated_skill.py and install_generated_skill_common.py now
share one module, install/generated.py.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N65qP8LxH1kiQynvqf5JLf
…over generated hosts

write/writer.py was 719 lines covering templates, rendering, patch
merging, corrections, storage, host installs and the CLI. Rendering now
lives in write/render.py and patch/correction handling in write/patch.py;
the writer re-exports those names so tools/skill_writer.py importers
keep working.

The OpenClaw and Codex install branches after create/update were two
copies of the same code; they are one loop over a small host table. The
flags, defaults and printed lines are unchanged.

tests/test_generated_output_golden.py records a full run (create,
update, correction, rollback, list, every installer) made with the code
before this refactor and checks the refactored code writes the same
files and prints the same output.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N65qP8LxH1kiQynvqf5JLf
@titanwings titanwings self-assigned this Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants