Skip to content

Add experimental entire learn command with embedded markdown + release-time regen#1146

Open
alishakawaguchi wants to merge 19 commits into
mainfrom
entire-learn
Open

Add experimental entire learn command with embedded markdown + release-time regen#1146
alishakawaguchi wants to merge 19 commits into
mainfrom
entire-learn

Conversation

@alishakawaguchi
Copy link
Copy Markdown
Contributor

@alishakawaguchi alishakawaguchi commented May 7, 2026

https://entire.io/gh/entireio/cli/trails/323

Made entire learn so I can stop teaching agents how to use the CLI. Tired of pasting instructions in every prompt and watching them -h their way through every subcommand, or having to update my agent.md file all the time. Now I can just point at entire learn.

Summary

Adds entire learn, an experimental Hidden top-level command (advertised under entire labs) that renders a state-aware tour of the CLI. Two rendering modes:

  1. Default entire learn — reads pre-rendered markdown from cmd/entire/cli/learn/embedded/learn.md, passes it through glamour, prints. ~50ms file-read + render.
  2. entire learn --regenerate (hidden, maintainer-only) — runs a TextGenerator agent against the live cobra tree to refresh embedded/learn.md before each release. Output is raw markdown to stdout; the command refuses to run unless stdout is redirected.

Release CI integration in .github/workflows/release.yml runs mise run learn:regenerate (atomic write + validation: ≥4 ## headers, presence of the docs.entire.io/cli footer) before GoReleaser. The regen step is continue-on-error: true so a flaky agent or missing ANTHROPIC_API_KEY doesn't block the release; a dedicated Slack notification fires on regen failure separately from the existing release-failure alert.

State-aware rendering: when Entire isn't enabled in the current repo, prints a short setup prompt; when enabled but no agent hooks are installed, prints an agent-install prompt; once a first commit has been captured, falls through to the full embedded tour (with a one-paragraph tail explaining that search/resume/rewind will gain real data after the next commit).

Mirrors entire review's shape: top-level Hidden: true, advertised in experimentalCommands for entire labs.

Demos
https://cleanshot.com/share/35CFYwL1
https://cleanshot.com/share/6ZL9h4RP
<img width="767" height="1539" alt="Screenshot 2026-05-08 at 4 07 01 PM" src="https://github.com/user-attachments/assets/ef520058-c939-4458-8b61-c0630e22d8f3\" />

Test plan

  • mise run learn:regenerate locally with ANTHROPIC_API_KEY set produces a valid embedded/learn.md (≥4 ## headers, includes docs.entire.io/cli footer)
  • entire learn from a fresh checkout shows the embedded tour, rendered through glamour
  • entire learn --regenerate > /tmp/learn.md produces sanitized markdown (control sequences stripped); refuses to run if stdout is a TTY
  • entire learn --help shows correct help text
  • entire labs lists entire learn
  • State-aware fallbacks: setup prompt when disabled, agent-install prompt when enabled-without-agent, first-capture tail before any commit
  • Existing tests: go test ./cmd/entire/cli/learn/... passes (tests parallelized and hardened against host environment)
  • Release workflow triggered on a -rc.test tag exercises the regen path end-to-end

Notes

The branch went through several code review passes (Claude-driven parallel agent reviews + codex adversarial review). Major findings addressed:

  • NBSP-mid-tagname Unicode bypass in tag escape
  • Terminal-injection sanitization on agent output before persistence
  • Atomic-write mise task with stronger CI validation
  • External-agent discovery in ResolveTextGenerator (mirrors resolveCheckpointSummaryProvider)
  • Settings double-load avoided via cached closure
  • Per-command descriptions required in capability sections of the regenerated markdown
  • t.Parallel() added to all learn-package tests; tests hardened against host environment (no reliance on real $HOME, real git config, or ambient ENTIRE_* env)

The earlier --latest blog-digest mode (RSS fetch + summarize most recent entire.io post) was removed during the rename back to learn: it added a network-dependent path that didn't fit the "teach the CLI surface" framing. The embedded-markdown + maintainer-regenerate flow is the whole feature now.

Deferred to follow-up PRs: spinner/TUI dedup with dispatch_tui.go, checkpoint.HasCommitted short-circuit, dispatch's sanitizeDispatchPromptString consolidation with the new stripInvisibles.

🤖 Generated with Claude Code


Note

Medium Risk
Adds a new top-level CLI command plus agent-driven generation paths and modifies the release workflow; failures are best-effort but could affect shipped tour content and release-time automation.

Overview
Adds an experimental hidden entire learn command (advertised via entire labs) that renders a state-aware CLI tour from an embedded learn.md by default, with --regenerate (hidden) producing the embedded markdown via an agent.

Introduces a new cmd/entire/cli/learn package to discover the cobra command surface, resolve repo stage (setup/agent install/first capture/workflow), build hardened agent prompts (tag-escape + control-sequence stripping), and select an available TextGenerator (including external entire-agent-*). Adds a small TUI spinner while agent work runs and extends mdrender to allow per-command style overrides.

Updates the release GitHub Action to best-effort regenerate the embedded tour (installs pinned Claude CLI, runs mise run learn:regenerate with validation/atomic write), annotate failures, and send a dedicated Slack warning when regeneration fails without failing the release.

Originally reviewed by Cursor Bugbot for an earlier commit when the command was named tour. Configure here.

alishakawaguchi and others added 7 commits May 7, 2026 12:00
Renders a state-aware Entire CLI tour by piping the discovered cobra
command tree, the labs registry, and the user's repo state through
their locally-installed TextGenerator agent (claude, codex, gemini,
cursor, copilot, or any external entire-agent-* plugin that declares
text_generator). The agent owns capability grouping, titles, and
blurb prose; the prompt encodes only format rules, stage routing,
and the two URL-bound blurbs (external-agents, skills) that the CLI
has no way to derive on its own.

Registered as a top-level hidden command (mirroring `entire review`)
and advertised under `entire labs`. While generating, a bubbletea
spinner runs in interactive terminals; on completion the markdown
goes through mdrender for terminal styling and stays as raw markdown
for pipelines.

Adds `mdrender.RenderForWriterWithOverride` so commands can tweak
the shared palette without forking it. `entire learn` uses it to
recolor H2 to violet (#a78bfa) so the section headers stand apart
from the orange inline-code, list-item, and accent surfaces that
already dominate the rendered tour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renames the experimental command from `learn` to `tour` and tightens
its Short to "Tour the Entire CLI" (down from the longer "tailored to
your repo state" framing). Internal symbols, files, and the labs
registry entry follow. Mirrors the registration shape of `entire
review`: top-level Hidden command advertised under `entire labs`.

Adds a `--latest` flag that swaps the state-aware tour for a quick
"what's new" digest. Fetches the most recent post from
https://entire.io/feed.xml, hard-caps the body via `io.LimitReader`
to bound memory against malformed feeds, and pipes the parsed item
through the same TextGenerator + spinner + glamour pipeline. The
spinner title swaps to "Fetching the latest dispatch" while keeping
the same "This can take a moment." subtitle.

Cleanups from the simplify pass:
  - Reuse jsonutil.MarshalIndentWithNewline for prompt payload encoding;
    drop the hand-rolled placeholderWriter shim.
  - io.LimitReader cap on the RSS body so a malformed feed can't pull
    arbitrary memory.
  - Drop an unused `width` field from the spinner model; have View
    skip the subtitle line when empty.
  - Flatten cancellation handling in executeTour with an early return.
  - Internal symbols renamed from "New" to "Latest" so they match the
    user-facing flag (GenerateLatest, BuildLatestPrompt,
    latestPromptSystem, runTourGenerateLatest).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switches the default tour from an agent call at runtime to a
pre-rendered markdown file embedded in the binary. Wall time drops
from a multi-second agent call to ~1s (process startup + glamour
render) with no network or token cost per user.

Three rendering modes:

  - Workflow / first-capture: serve embedded/tour.md via go:embed.
    First-capture stage appends a 3-line tail noting that checkpoints
    will appear once the user runs their agent and commits.
  - Setup / agent-install: render hand-written prose (4-6 lines) that
    walks the user through `entire enable` / `entire agent add`.
  - --latest: unchanged, still hits the agent + entire.io blog feed
    live since "latest post" is genuinely time-varying.

The embedded tour content is NOT committed. The repo carries a stub
at cmd/entire/cli/tour/embedded/tour.md that explains it's a build-
time placeholder. The release pipeline overwrites it with real
content before GoReleaser builds:

  - mise.toml gains a `tour:regenerate` task that runs
    `go run ./cmd/entire tour --regenerate > .../tour.md`. This
    invokes the agent-driven path via a hidden `--regenerate` flag
    on `entire tour`.
  - .github/workflows/release.yml installs the Claude CLI and runs
    `mise run tour:regenerate` before GoReleaser, with a sanity
    check that the output contains markdown headers (so a stale
    auth or a transient agent error fails the release loudly
    rather than silently shipping the stub).

Local devs see the stub on `entire tour` from a fresh checkout.
Running `mise run tour:regenerate` locally (with `claude` on PATH
and `ANTHROPIC_API_KEY` exported) refreshes it for the rest of the
session.

The TUI spinner now only runs for the agent-driven paths
(`--latest` and `--regenerate`); the embedded path returns fast
enough that no progress UI is needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code review fixes:

  - Drop dead Result.State field (written 5 places, read 0).
  - Tighten escapeForTags from four ReplaceAll calls to a single
    case-insensitive regex covering '</POST>', '</post >', '</post\n>'.
  - Cache settings via cachedTourSettingsLoader so ResolveState
    doesn't re-load settings.json a second time per invocation.
  - Discover external entire-agent-* plugins in ResolveTextGenerator
    so users with no built-in claude/codex/etc. but an external
    TextGenerator plugin still get a tour. Mirrors what
    resolveCheckpointSummaryProvider in the cli package does.
  - Align error wrap "regenerate tour with X" -> "generate tour
    with X" so log/grep stays predictable across paths.
  - Fix stale "output streams in" claim in tour --help — the path
    is buffered, not streaming.
  - Pin @anthropic-ai/claude-code to 2.1.132 in release.yml so the
    agent's output format isn't a moving target.

Make tour regeneration non-blocking:

  - continue-on-error: true on both the npm install and regen
    steps. A flaky agent, missing ANTHROPIC_API_KEY, or an npm
    registry blip no longer blocks the release; GoReleaser ships
    the committed stub instead.
  - 'Note tour regeneration outcome' step (if: always()) emits a
    GitHub annotation so degraded releases are visible in the run
    summary.
  - 'Notify Slack of tour regeneration failure' step fires a
    dedicated ⚠️ alert when steps.regen.outcome != 'success'.
    The existing notify-slack job only fires on full-job failure
    and would no longer trigger when regen-only fails.

Deferred (worth their own PRs):

  - Spinner/TUI dedup with dispatch_tui.go — duplication is real,
    but extraction is a separate refactor.
  - checkpoint.HasCommitted short-circuit — biggest measurable
    default-path win, but adds a new method to the checkpoint
    package and should land with its own tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass-2 Claude review:
  - Slack regen-failure step now uses
    `if: always() && steps.regenerate-tour.outcome == 'failure'`
    so it fires only on actual regen failures, not on earlier-step
    skips that leave outcome unset.
  - mdrender fallback now writes a one-line breadcrumb to stderr
    when glamour fails, instead of swallowing the error.
  - Workflow step id renamed `regen` -> `regenerate-tour` for grep
    parity with the mise task and cobra flag.
  - Drop stale "original" word in regenerateFromAgent docstring.

Codex adversarial review (security-sensitive):

  closingTagPattern (tour/prompt.go) — unicode bypass.
    The earlier `(?i)</\s*(state|...)\s*>` regex was bypassed by
    `</po​st>` (zero-width space inside tag name) and
    `</post >` (NO-BREAK SPACE before close). Both could let
    untrusted feed/cobra-tree content break out of its <state>
    /<commands>/<labs>/<post> wrapper and confuse the model into
    treating untrusted text as wrapper structure.
    Fix: strip Unicode format chars (\p{Cf}) and C0/C1 controls
    from the payload first, then run a regex extended with
    [\s\p{Z}\p{Cf}]* whitespace so visible Unicode whitespace
    doesn't slip past either.

  StripControlSequences (tour/run.go) — terminal injection.
    A compromised agent could embed ANSI/OSC/C1 control sequences
    in the regen output. The output gets piped to disk, embedded
    via go:embed, and shipped to every future user — one bad
    release would persist malicious title-rewrites or fake
    hyperlinks across the entire user base.
    Fix: regenerateFromAgent now runs its rendered string through
    StripControlSequences (CSI / OSC / other ESC sequences /
    C0 except whitespace / DEL / C1) before returning.

  mise tour:regenerate — atomic write + stronger validation.
    The previous shell-redirect form truncated embedded/tour.md
    *before* the agent finished. A transient agent failure left
    the repo with an empty stub. The CI grep-only validation
    accepted any single `^## ` line — a malicious or partial
    response could pass.
    Fix: the mise task now writes to a tempfile, validates >=4
    `^## ` headers + the docs.entire.io footer, and only mvs
    on success. CI step's redundant grep dropped.

Adds prompt_test.go covering the new escape-bypass and
control-stripping cases. 22 assertions in total.

Deferred (worth their own follow-ups):
  - tour_tui.go ctx-cancellation join: when user hits ctrl+c the
    Bubble Tea program returns immediately but the worker
    goroutine keeps running. Needs a careful redesign of the
    runGenerate -> tea.Quit handshake.
  - claudecode.GenerateText subprocess cleanup: no WaitDelay /
    process-group kill, so Ctrl+C on the parent may leak grand-
    children. Touches the agent package, broader scope.
  - Embedded markdown runtime sanitization (defense-in-depth
    against a poisoned release artifact).
  - ConfiguredProvider explicit-pin failure should hard-fail
    rather than silently fall back to a different agent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass-3 review caught a real security regression in the pass-2
escape fix: payloads like `</po<NBSP>st>` (NO-BREAK SPACE between
letters of the tag name) bypassed the escape because:

  1. The strip step used `\p{Cf}` (format chars) only — NBSP is
     `\p{Zs}` (space separator), so it survived.
  2. The regex's `(state|commands|labs|post)` alternation is
     literal; it can't match `po<NBSP>st`.

The pass-2 commit message specifically claimed to fix `</post >`
(NBSP outside the name) and did, but missed NBSP-mid-name.

Fix: replace the regex-based invisibleCharPattern with a
strings.Map-based stripInvisibles that drops:

  - \p{Cf} (format chars) — zero-width spaces, RTL marks, etc.
  - \p{Z} (separators) EXCEPT ASCII space — NBSP, NARROW NBSP,
    IDEOGRAPHIC SPACE, ogham space, line/paragraph separators.
  - C0 controls except \t \n \r, plus DEL.
  - C1 controls (U+0080-U+009F).

ASCII space stays for legitimate prose; everything else gets
dropped on the way into the tag wrapper. strings.Map is also
faster than the regex on the legitimate-content common case
(single-pass, no transient buffer when nothing matches).

Adds six regression tests:
  - NBSP / NARROW NBSP / IDEOGRAPHIC SPACE inside tag name
  - Combined visible+invisible (NBSP and ZWSP) bypass
  - Empty payload
  - Idempotence on already-escaped input

Doc cleanup — stale "entire labs tour" references from before
the rename to top-level `entire tour`:
  - tour_cmd.go's tourNoTextGeneratorMessage user-facing text
  - tour/discovery.go's package doc
  - mdrender/mdrender.go's StyleOverride example comment
  - labs_test.go's test comment

Deferred (not security-relevant):
  - StripControlSequences shared helper extraction with codex /
    review packages — the existing CSI-only helpers in those
    packages are narrower than this one; consolidation worth
    its own PR.
  - Mise task move from mise.toml inline run to a mise-tasks/
    standalone script (style alignment with rest of repo).
  - DCS/SOS/PM/APC sequence handling in StripControlSequences
    (modern terminals don't render those payloads visibly).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pass-4 review caught four items worth applying:

  - stripInvisibles docstring claimed the function "only runs on
    the --regenerate path" — wrong. It runs on --latest too via
    BuildLatestPrompt. Corrected to "the --latest and --regenerate
    paths".
  - stripControlSequences was exported with no out-of-package
    caller. Pass-2 justified the export as "made testable" but
    same-package tests can call unexported funcs. Unexported.
    No external API impact.
  - LOAD-BEARING: inline comment was an outlier tone (the cli
    package uses IMPORTANT:/NOTE:/WARNING: about 30 places).
    Normalized to IMPORTANT:.
  - TestEscapeForTags_Idempotent's t.Run(in, ...) produced subtest
    names like #00 (empty) and </post_> (NBSP rewritten to
    underscore), which made failure output ambiguous. Switched
    to fmt.Sprintf("%q", in) so subtests render as "" and
    "</post >" — codepoint-faithful and grep-friendly.

Two prior unfixed items confirmed deferred (not regressed):

  - Spinner/TUI dedup with dispatch_tui.go (architectural).
  - dispatch's sanitizeDispatchPromptString uses a weaker BMP-
    range list than stripInvisibles; consolidation worth its own
    cross-package PR.

Pass-4 was the fourth review pass on this branch; subsequent passes
have been showing diminishing returns. Tour feature is ready to
ship — 35 unit tests pass, all known security findings addressed,
embedded path latency is ~1s instead of 5-15s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 7, 2026 22:50
@alishakawaguchi
Copy link
Copy Markdown
Contributor Author

bugbot run

Comment thread cmd/entire/cli/tour/state.go Outdated
Comment thread cmd/entire/cli/learn_cmd.go
Comment thread cmd/entire/cli/learn_cmd.go
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an experimental, hidden top-level entire tour command (discoverable via entire labs) that renders a state-aware CLI tour, primarily from embedded pre-rendered markdown, with optional agent-driven regeneration and a --latest blog-digest mode. It also wires release-time regeneration into CI and extends markdown rendering to support per-command style overrides.

Changes:

  • Introduces the tour package (repo state classification, cobra surface discovery, prompt construction + sanitization, RSS fetch, agent selection, and embedded markdown plumbing).
  • Adds the entire tour command + spinner TUI integration, and lists it under entire labs.
  • Adds mise run tour:regenerate and a best-effort release workflow step to regenerate embedded/tour.md before GoReleaser.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
mise.toml Adds an atomic, validated tour:regenerate task for release/local regeneration.
cmd/entire/cli/tour/state.go Implements repo “stage” classification and TextGenerator agent resolution (incl. external plugins).
cmd/entire/cli/tour/run.go Implements tour generation paths (embedded vs agent), --latest digest, and control-sequence stripping for persisted output.
cmd/entire/cli/tour/prompt.go Builds the system prompts and escapes/untrusts payloads (tag-escape + invisible stripping).
cmd/entire/cli/tour/prompt_test.go Adds table tests for tag-escape hardening and control-sequence stripping.
cmd/entire/cli/tour/embedded/tour.md Adds a committed stub placeholder to be overwritten at release time.
cmd/entire/cli/tour/embedded.go Embeds the tour markdown and provides static text for setup/agent-install stages.
cmd/entire/cli/tour/discovery.go Discovers a user-facing cobra command surface for prompting (filters hidden/deprecated/plumbing).
cmd/entire/cli/tour/discovery_test.go Tests discovery filtering and long-help paragraph trimming.
cmd/entire/cli/tour/blog.go Fetches/parses the RSS feed to get the latest blog post for --latest.
cmd/entire/cli/tour_tui.go Adds a bubbletea spinner program used while agent-backed generation runs.
cmd/entire/cli/tour_cmd.go Adds the hidden entire tour cobra command, wiring options, rendering, and error translation.
cmd/entire/cli/root.go Registers tour at the top level (hidden).
cmd/entire/cli/mdrender/mdrender.go Adds Render*WithOverride hooks to allow palette tweaks (used to recolor H2 in tour).
cmd/entire/cli/labs.go Adds tour to the labs registry; improves unknown-topic hinting and aligns listing width dynamically.
cmd/entire/cli/labs_test.go Updates canonical-path verification to use Invocation segments (supports multi-segment entries).
.github/workflows/release.yml Adds best-effort tour regeneration (install Claude CLI, run task, annotate + Slack notify on failure).

Comment thread cmd/entire/cli/tour_cmd.go Outdated
Comment thread cmd/entire/cli/tour_cmd.go Outdated
Comment thread cmd/entire/cli/tour_cmd.go Outdated
Comment thread cmd/entire/cli/tour/run.go Outdated
Comment thread cmd/entire/cli/tour/prompt.go Outdated
  - ST1005: multi-line user-facing messages were stuffed into
    errors.New() and tripped the "error strings end with
    punctuation" rule. Reshaped translateTourError to print the
    message to its writer first and return a SilentError with a
    short go-convention message — same shape recap.go uses.
  - exhaustive: switch on tour.Stage in Generate() was missing
    StageNotGitRepo / StageFirstCapture / StageWorkflow cases.
    Added explicit branches; the function is now a clean
    stage-dispatch.
  - nilerr: ResolveState's "not in a git repo -> StageNotGitRepo"
    branch was returning nil despite paths.WorktreeRoot reporting
    an error. The behavior is intentional (not-a-repo is a stage,
    not a runtime error), so kept the return shape and added a
    nolint:nilerr directive with explanation.
  - wrapcheck: marshalIndentNoHTMLEscape returned an unwrapped
    error from jsonutil.MarshalIndentWithNewline. Wrapped with
    fmt.Errorf("marshal payload: %w", err).
  - mise.toml multi-line script lint: tour:regenerate's inline
    run = '''...''' (16+ lines) violated the lint rule for long
    inline scripts. Moved to mise-tasks/tour/regenerate as a
    standalone shell script with #MISE description header,
    matching the rest of the repo's mise-tasks/ convention.
  - shellcheck SC3040: the new tour/regenerate script used
    `set -euo pipefail` (bashism). Switched to `set -e` and
    dropped pipefail/-u — no pipelines or unset-var lookups
    that benefit from them.
  - gofmt drift on root.go, tour_cmd.go, tour/blog.go, tour/run.go
    fixed via `mise run fmt`.
  - errors import was missing on tour/prompt.go (errors.New used
    in BuildLatestPrompt). Added.

35 unit tests still pass. golangci-lint reports 0 issues.

Co-Authored-By: Claude <noreply@anthropic.com>
@alishakawaguchi
Copy link
Copy Markdown
Contributor Author

bugbot run

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

3 issues from previous reviews remain unresolved.

Fix All in Cursor

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 9f4a616. Configure here.

CI lint (ST1018): replaced literal Unicode bytes with \uXXXX escape
sequences in tour/prompt_test.go (U+200B ZWSP, U+200F RTL mark,
U+009B CSI control) and tour/run.go's controlSequencePattern
C1 range. Locally these were dismissed as auto-fix conflicts, but
in CI without auto-fix they fired as errors.

bugbot review (real bugs):
  - HIGH: repoHasHistory only queried the v1 GitStore, so any user
    on `checkpoints_version: 2` always had has_history=false in the
    rendered tour. Now checks v1 first, falls back to v2 store.
  - MEDIUM: cachedTourSettingsLoader hardcoded isSetUp=true whenever
    Load() succeeded, but settings.Load returns a non-nil EntireSettings
    even when no settings.json exists. Threaded a real isSetUp value
    from settings.IsSetUpAny into the closure.
  - LOW: SummaryGeneration.Model from settings.json was never read,
    so users who configured both provider and model had the model
    silently dropped. Wired through opts.SummarizeModel.

Copilot review (real wording fixes):
  - tourNoTextGeneratorMessage incorrectly described default `entire
    tour` as needing an agent. Reworded to say the default uses
    embedded markdown and only --latest/--regenerate need an agent.
  - "Fetching the latest dispatch" -> "Fetching the latest post"
    (avoids confusion with `entire dispatch`).
  - "generate latest dispatch with X" wrap -> "summarize latest
    blog post with X".
  - marshalIndentNoHTMLEscape comment claimed json.Marshal would
    turn "<id|sha>" into "<id|sha>" (visually identical). Fixed
    to show the actual default escape (`<id|sha>`).

35 unit tests pass. golangci-lint reports 0 issues. shellcheck/
gofmt clean.

Co-Authored-By: Claude <noreply@anthropic.com>
@alishakawaguchi
Copy link
Copy Markdown
Contributor Author

bugbot run

Comment thread cmd/entire/cli/tour/run.go Outdated
Per CLAUDE.md: "Always use t.Parallel() in tests. Every top-level test
function and subtest should call t.Parallel() unless it modifies
process-global state." These tour unit tests are pure in-memory and
qualify for parallelization.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 704de7f0df2d
@alishakawaguchi
Copy link
Copy Markdown
Contributor Author

bugbot run

Comment thread cmd/entire/cli/tour/run.go Outdated
alishakawaguchi and others added 3 commits May 8, 2026 14:44
Generate previously called ResolveState before branching on
opts.Regenerate. In CI checkouts there is no .entire/settings.json,
so IsSetUpAny returns false and ResolveState routes to StageSetup —
the agent then produces a 4-line setup stub that the release-pipeline
validation rejects, leaving every release shipping the unrendered
embedded tour.

The --regenerate path produces the canonical content shipped to all
users via embedded/tour.md, and the embedded markdown is only ever
served to first-capture / workflow stages (setup and agent-install
render hand-written prose constants). The runtime repo's state is
irrelevant for regen — we always want StageWorkflow output. Short-
circuit before ResolveState and pass a synthetic workflow state.

Flagged by Cursor Bugbot on PR #1146.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 490be5d78be1
The embedded tour.md was a build-time stub waiting for the release
pipeline to overwrite it. Now that the regen path is fixed, ship a
real generated tour as the baseline so anyone building from main
between merge and the next tagged release sees the actual content,
and so a future regen failure (continue-on-error: true) silently
falls back to a real tour rather than the stub.

Generated via mise run tour:regenerate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: d06e1534bcd5
@alishakawaguchi
Copy link
Copy Markdown
Contributor Author

bugbot run

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c851c7e. Configure here.

The capability-section rule in the tour prompt previously said the
em-dash short description was optional, so the agent dropped it
across capability sections like "Track & Resume Sessions" and "Find
& Understand Prior Work" while still rendering descriptions in Labs
and Other commands. The result was inconsistent and made the
capability bullets feel less informative than the surrounding
sections.

Tighten the rule to require the description on every capability
bullet (verbatim cobra short, or rephrased when too generic), and
regenerate embedded/tour.md against the new prompt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 5db73c2f3924
@alishakawaguchi alishakawaguchi self-assigned this May 8, 2026
@alishakawaguchi alishakawaguchi marked this pull request as ready for review May 8, 2026 23:12
@alishakawaguchi alishakawaguchi requested a review from a team as a code owner May 8, 2026 23:12
alishakawaguchi and others added 5 commits May 11, 2026 11:09
Hard rename — no alias. Drop --latest blog-feed digest entirely
(blog.go, GenerateLatest, BuildLatestPrompt, latestPromptSystem, the
flag, and the <post> tag from closingTagPattern). Move embedded-
markdown regeneration out of release.yml and into the changelog
skill (`mise run learn:regenerate` runs as Step 0) so the shipped
tour is deterministic and can be hand-tweaked in the same PR as the
CHANGELOG bump.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: b7fab1d96c0e
Atomic write: mktemp now creates the temp file inside
cmd/entire/cli/learn/embedded/ so the rename onto learn.md stays
on the same filesystem (single inode swap). The previous /tmp
form degraded to copy+unlink when /tmp was a different mount,
which could leave learn.md truncated on an interrupted run.

Release pipeline: re-add a learn.md freshness gate before
GoReleaser — same well-formedness checks the regenerate script
enforces (file non-empty, >=4 ## headers, docs.entire.io footer).
Runs on both stable and nightly tags, so prerelease builds can't
silently ship a stub if learn.md ever lands the tree malformed.
The error message points at 'mise run learn:regenerate' so the
fix is obvious.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 60d5b9bf6714
mktemp template: the previous `$target_dir/learn.XXXXXX.md` form is
silently broken on macOS — mktemp leaves the literal X's in the
filename when they're not the final characters of the template, so
two parallel regenerations collide on the same path and the rename
guarantee is gone. Use `$target_dir/.learn-XXXXXX` (hidden file,
X's at end) and rename to learn.md inside the same directory so
the swap stays atomic on Linux and macOS.

Stale references: state.go and learn_cmd.go had godoc / spinner
strings still referencing `entire tour`, the renamed
translateLearnError function, and the old `tour` package. Replaced
with `entire learn`, translateLearnError, and `learn package`
accordingly. The user-visible spinner now reads "Regenerating
embedded learn markdown" so a user running `entire learn
--regenerate` doesn't see the old command name.

PR validation: add .github/workflows/learn.yml so the same
well-formedness check (non-empty, >=4 ## headers, docs footer) runs
on any PR that touches embedded/learn.md. Catches hand-edit
truncation or bad-merge corruption before it lands rather than only
at release time. No agent call required, so it's safe to gate PRs
on it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 0bd6aa5b2705
TTY+--regenerate refusal: --regenerate writes raw markdown to stdout
for piping into embedded/learn.md. Running it interactively dumped
that raw markdown over the spinner-clear and left the user with no
way to recapture it. Refuse the combination early with a hint
pointing at `mise run learn:regenerate` (canonical) or explicit
shell redirect. That makes the spinner path dead code, so delete
learn_tui.go entirely — the embedded-file read is fast and the
maintainer regen flow is non-interactive by construction.

Test coverage: add learn/run_test.go with two tests that pin the
ResolveState routing contract — Regenerate=true bypasses
LoadSettings/ListInstalledAgents entirely, the default path
consults LoadSettings and routes to the setup-stage markdown when
enabled=false. These were untested and the regen-without-settings
behavior is the load-bearing piece for CI checkouts that have no
.entire/settings.json.

learn.yml paths filter: add cmd/entire/cli/learn/embedded.go.
embedded.go owns the //go:embed directive, so a PR repointing the
embed at a different filename or wrapping it must re-trigger the
well-formedness check even if learn.md itself isn't touched.

Cosmetic: rename "generate tour with %s" error to "regenerate
embedded learn with %s" so the user-visible string reflects the new
command name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 2e177be57b57
TestGenerate_RegenerateBypassesResolveState previously relied on a
pre-cancelled context to short-circuit ResolveTextGenerator.
external.DiscoverAndRegisterAlways happens to check ctx.Err() in
its loop today, but the test would silently leak a real
entire-agent-* registration into the package-shared global registry
if that check ever moved. Use t.Setenv("PATH", "") so discovery is
a no-op AND the built-in agents' CLI-availability checks all return
false; ResolveTextGenerator now deterministically returns
ErrNoTextGenerator and the test asserts on the error too. Cost:
drop t.Parallel since t.Setenv requires it.

TestGenerate_DefaultPathConsultsResolveState depended on the test
running inside a git repo (paths.WorktreeRoot walks up from CWD).
That holds today because go test runs from the package directory
inside this worktree, but a hermetic CI runner with a stripped
checkout would short-circuit to StageNotGitRepo before LoadSettings
got consulted, masking the bypass we want to assert. Stand up an
isolated tmp repo via testutil.InitRepo + t.Chdir so the test is
independent of host CWD. Cost: drop t.Parallel since t.Chdir
requires it.

Also: scrub the last two "embedded tour" mentions in the changelog
skill since the rename brief was thorough.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 008e52fd97f8
@alishakawaguchi alishakawaguchi changed the title Add experimental entire tour command with embedded markdown + release-time regen Add experimental entire learn command with embedded markdown + release-time regen May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants