Skip to content
Development
Command

/update

Sync this project with the current LETS release - a self-driving one-step loop; self-heals .env + rules (rules deferred while the plugin is behind), installs the binary in-session on approval, and points at the single next action until everything is on the same version

From plugin
lets-workflow
1622 skills15 agents22 commands
Install
$ npx -y skills add restarter/lets-workflow --agent claude-code

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/update

Context preview

What this command does when you run it.

Sync this project with the current LETS release - a self-driving one-step loop; self-heals .env + rules (rules deferred while the plugin is behind), installs the binary in-session on approval, and points at the single next action until everything is on the same version

Command definition

update.md
description: Sync this project with the current LETS release - a self-driving one-step loop; self-heals .env + rules (rules deferred while the plugin is behind), installs the binary in-session on approval, and points at the single next action until everything is on the same version

Update LETS

Sync the drift-able LETS artifacts (four core + two optional: the user-scope global rules and the active `tracker-<name>.md` adapter) with the current release. Bridges to `lets update --json` (Go binary): auto-syncs `.lets/.env` (header refresh when `LETS_ENV_VERSION` is stale), `.claude/rules/lets-rules.md` (re-copy when outdated/missing - but **deferred** when the plugin is behind, so rules never sync to a stale plugin), and `~/.claude/rules/lets-rules.md` (user-scope global rules - the `user-rules` row appears only when that file exists). It then computes a single ordered `next_action` (the self-driving loop): the binary step can be run in-session (approval-gated), the plugin step is a user-only Claude Code slash command, and a fully-synced machine prints `✓ Everything on vX.Y.Z`.

> **MANDATORY:** Execute every Step's bash block **literally as written**. Do not substitute output from earlier `ls`/`cat` in this conversation - `.env` and other dotfiles are invisible to plain `ls`. The `test -f` checks below ARE the contract.

Difference from `/lets:init`: `/lets:init` is first-time setup (it also asks config questions and sets up the statusline + beads). `/lets:update` only syncs what a new release changes - it never prompts and never touches `settings.json` or beads.

Step 1: Pre-checks

command -v lets >/dev/null 2>&1 || { echo "NO_LETS_BINARY"; exit 0; }
LETS_PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || { echo "NOT_GIT_REPO"; exit 0; }
test -f "$LETS_PROJECT_ROOT/.lets/.env" && echo "ENV_EXISTS" || echo "ENV_ABSENT"

Branch on output:

  • `NO_LETS_BINARY` → tell user: "`lets` binary not found on `$PATH`. Install it — `! curl -fsSL https://raw.githubusercontent.com/restarter/lets-workflow/main/scripts/install.sh | bash` (the leading `!` runs it in this session; or the same command without `!` in a terminal). See the README → Quick Start." NO LETS box. STOP.
  • `NOT_GIT_REPO` → tell user: "Not a git repository. `/lets:update` runs inside a LETS project." NO LETS box. STOP.
  • `ENV_ABSENT` → not initialized. Tell user: "This project hasn't been set up - run `/lets:init` first." Then still run Step 2 (it reports `.env: not-initialized` plus the binary/plugin status, which is useful). In Step 4 show the LETS box pointing at `/lets:init` instead of `/lets:start`.
  • `ENV_EXISTS` → normal path. Continue to Step 2.

(No bash worktree pre-check here - `lets update` itself runs the robust `DetectInsideWorktree()` check and exits with `ok == false` + a clear `error`; surfacing that via Step 3 is enough, and it avoids the fragile `*"/worktrees/"*` substring heuristic. `init.md` does the same - no bash worktree check.)

Step 2: Exec

LETS_PROJECT_ROOT=$(git rev-parse --show-toplevel)
lets update --json --plugin-root="${CLAUDE_PLUGIN_ROOT}"

Capture stdout (single JSON object). On network trouble the binary degrades gracefully: `binary`/`plugin` come back `unknown` with an explanatory `detail`; it never fails the run for that. If you're in a git worktree, `lets update` exits with `ok == false` and an `error` explaining `.claude/` isn't shared into worktrees - render that per Step 3 (no LETS box).

Step 3: Render

Parse JSON.

`lets update` is a **self-driving loop**: each run advances ONE step and tells you the single next thing to do. Do that one thing, re-run `/lets:update`, repeat until it prints `✓ Everything on vX.Y.Z`.

1. **Artifact table** - one line per `artifacts[]` entry: `<name> v<current_version> <status> (latest v<latest_version>) - <detail>` Omit `(latest …)` / `- <detail>` when those fields are empty; print `?` for an empty `current_version` (`dev` prints as-is, not `vdev`). **When `next_action.kind == "done"`, SKIP the table entirely** - print only the `✓ Everything on v<next_action.version>` line (no status matrix on a fully-synced machine). Status vocabulary: `.env`/`rules` report `in-sync` - they track a *local* source (the `lets` binary for `.env`, the plugin for `rules`), not the latest release. `binary`/`plugin` report `up-to-date`/`outdated` against the *latest release*. So `.env` and `rules` can sit at different versions and both be `in-sync` - expected, not a contradiction; their `detail` names what they track and flags "itself behind latest v…" when that source is itself stale. `rules`/`user-rules` may report `deferred` - the plugin is behind, so syncing the rules now would write a stale lower version; the row's `detail` explains it and `next_action` steers you to the plugin step. Not an error, not a contradiction. `user-rules` (only present with a user-scope install) joins the `in-sync` frame: it tracks the *installed plugin*, same as `rules`. Its `ahead` status means the global file is newer than the plugin (customized or newer release) - deliberately NOT overwritten; relay the `detail` and do not treat it as an error. `rules` may also report `delegated` (`LETS_RULES_SCOPE=user`): the project deliberately has no own copy and lives on the global rules - a healthy state, not an error. `tracker-rules` (present when `LETS_TRACKER` names a shipped adapter, OR a user-authored/unshipped adapter with an installed `.claude/rules/tracker-<name>.md` copy) joins the same frame - for a shipped adapter it tracks the *installed plugin* like `rules` and reports `in-sync` / `deferred` / `updated`, and for a user-authored adapter with no shipped source it reports `delegated` ("left as-is", a healthy green state, not plugin-managed); relay its `detail` switch notes verbatim (a failed stale-adapter removal is a user action item). Relay any `detail` hints verbatim (duplication / missing-global) - they are the user's action items

Read more
Ships withlets-workflow

A development workflow plugin for Claude Code Stop babysitting your AI. Start shipping with it.

Get the whole plugin, auto-invoked
Stats
16
Stars
1
Views
3
Forks
Active
Maintenance
Go
Language
MIT
License
3d ago
Last commit
5mo ago
Created

Repo: restarter/lets-workflow