Skip to content

v0.1.4: emodul install / uninstall — one-command AI client setup#2

Merged
hculap merged 1 commit into
mainfrom
feat/v0.1.4-install-command
May 19, 2026
Merged

v0.1.4: emodul install / uninstall — one-command AI client setup#2
hculap merged 1 commit into
mainfrom
feat/v0.1.4-install-command

Conversation

@hculap
Copy link
Copy Markdown
Owner

@hculap hculap commented May 19, 2026

Summary

After pipx install emodul, users had to manually edit claude_desktop_config.json to wire up the MCP server, then separately run emodul skill install for Claude Code. This PR adds a single emodul install <target> command that handles everything per-client.

  • emodul install claude-code → drops CLI-flavored SKILL.md into ~/.claude/skills/emodul/
  • emodul install claude-desktop → drops MCP-flavored SKILL_MCP.md (new file) into ~/.claude/skills/emodul-mcp/ AND adds mcpServers.emodul to claude_desktop_config.json with a timestamped backup
  • emodul install --all → fans out to detected clients
  • emodul install --dry-run → preview without writing
  • emodul uninstall <target> → reverse

The two skill folders coexist (different name: YAML slugs), so each client picks the right one via description-keyword matching. Backward compat: emodul skill install / emodul skill uninstall keep working unchanged.

Architecture

  • emodul/_client_paths.py — per-platform config paths (macOS / Win / Linux) + client detection via marker-file probes
  • emodul/_config_writer.py — atomic JSON merge with tempfile + os.replace, timestamped .bak-YYYYMMDDTHHMMSS (keep last 5), fcntl.flock for serialization, strict JSON parse (fail-loud on comments/non-object roots), shallow merge that preserves sibling top-level keys (preferences, other mcpServers entries)
  • SKILL_MCP.md — MCP-flavored skill: 16-tool inventory, {ok: false} envelope semantics, no shell examples, description: keywords target Claude Desktop / Cursor chat / Continue / Cline / Zed
  • emodul/commands/install.py — Click commands with JSON envelope output, conflict detection (requires --force to overwrite existing emodul entry with different args), idempotent (re-runs are unchanged)

Test plan

  • _config_writer unit-tested (12 scenarios: missing file, empty, malformed JSON, non-object root, merge add/unchanged/conflict/force, remove + prune, atomic write + backup, UTF-8 roundtrip)
  • emodul install claude-desktop --dry-run → preview correct
  • emodul install claude-desktop → drops skill, merges config, creates backup, preserves siblings (vkvm-local-poc, preferences)
  • Re-run is unchanged (idempotent)
  • Manual config tampering → next install detects conflict, exits 1
  • --force → updates entry, creates new backup
  • emodul uninstall --all → removes both skill files + mcpServers.emodul, leaves siblings intact
  • Both skills auto-discovered by Claude (validated live: emodul + emodul-mcp both shown in available-skills list)
  • MCP server smoke test still passes (16 tools)
  • ruff check --select=E,F,I,B clean

Release

After merge: tag v0.1.4 → OIDC publish to PyPI.

After pipx install emodul, users had to:
  1. emodul auth login --browser
  2. emodul skill install        (for Claude Code)
  3. manually edit claude_desktop_config.json (for Claude Desktop)

This collapses steps 2-3 into a single command per client:

  emodul install claude-code      # CLI-flavored skill → ~/.claude/skills/emodul/
  emodul install claude-desktop   # MCP-flavored skill → ~/.claude/skills/emodul-mcp/
                                  # + mcpServers.emodul into config.json
  emodul install --all            # fans out to every detected client
  emodul install --dry-run        # preview
  emodul uninstall <target>       # reverse

Implementation:

- emodul/_client_paths.py — per-platform config paths + client detection
- emodul/_config_writer.py — atomic JSON merge with timestamped backups,
  fcntl flock for serialization, strict JSON parse (fail-loud on comments),
  shallow merge that preserves sibling top-level keys (preferences,
  other mcpServers entries)
- SKILL_MCP.md — MCP-flavored skill (16 tool inventory, no shell examples)
- emodul/commands/install.py — install + uninstall click commands with
  --all / --dry-run / --force, JSON envelope output, conflict detection
  that requires --force to overwrite existing emodul entry with different args

The two skills coexist (different folder names → different YAML name slugs),
so Claude Code and Claude Desktop each load the right one based on
description keyword matching.

Backward compat: emodul skill install / uninstall keep working as before.
@hculap hculap merged commit 6378941 into main May 19, 2026
9 checks passed
@hculap hculap deleted the feat/v0.1.4-install-command branch May 19, 2026 13:26
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.

1 participant