@leftoff/cli 0.9.18-beta.1 → 0.9.18-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,45 @@ What changed in Leftoff (called Checkpoints until 0.4.5), newest first. The boar
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ - **Decisions show where you look.** A session's panel lists every decision it made, with its status and Accept / Reject right there, even after a newer checkpoint; a repo outside a project gets its own Decisions section and a ◌ count on its chip, and an empty log says where decisions come from. The checkpoint fold now names what's inside (approach notes, files…), and another tool's 401 or 403, such as an MCP server's, no longer reads as Claude signed out. Needs `leftoff restart`.
8
+ - **Session names say what the session is doing.** The name is the summarizer's title, or else the first prompt tidied deterministically: no leading “Please”/“Can you”/“Hey”, a pasted GitHub link read as “PR 408”, a capital first letter, cut at a word with “…” and never on a dangling “and”/“the”/“to”. A “folder@branch · …” stuck to the front of a name by whatever named it comes off; folder@branch is now a small muted second line under the name (full folder in its tip) in full screen's bar, on the session page and in its panel, and the digest uses the same name. A session whose only name is such a stand-in gets a generated one after its first turn, as a first-prompt name does.
9
+ - **Fewer false "isn't signed in" warnings.** A login date in the past from Claude Code no longer turns the banner red on its own; only a turn that actually failed does. An MCP server that couldn't sign in, or a differently worded sign-in reminder, no longer reads as Claude being signed out, and Claude Code's config folder can have a space in its path. Needs `leftoff restart`.
10
+ - **leftoff ↗ shows in Claude Code's status line again.** The npm package was missing the small script the status line runs, so nothing showed at the bottom of Claude Code, and `leftoff doctor` still said it was fine. The script ships now, and doctor says when the status line points at a file or a Node that's gone. Run `leftoff upgrade --beta`; your status line setting stays as it is.
11
+ - **Clearer reasons when Claude Code can't answer.** If Claude Code's config file (~/.claude.json) isn't valid JSON, Claude Code 2.1.318 refuses to start instead of resetting it. Ask, summaries and walkthroughs now say so, name the file and how to fix it, and let Codex answer meanwhile. `leftoff doctor` points it out too. Claude Code's newer "signed out" wordings are recognised everywhere, and its sign-in-ends warning in the last 10 days no longer hides the real error. Needs `leftoff restart`.
12
+ - **The login warning reads the date from Claude Code.** With Claude Code 2.1.318 or later, Leftoff asks Claude Code itself when its claude.ai sign-in ends (and every board turn, Ask and walkthrough refreshes it), so the day-ahead warning works on a Mac without turning on the Keychain check, and an old leftover login file can no longer raise a false "isn't signed in". Older Claude Code works as before. Needs `leftoff restart`.
13
+ - **Switching keyboards while typing keeps the reply box in view.** On iPhone, switching from Apple's keyboard to another one (Raycast, Gboard, SwiftKey) mid-message could leave the reply box hidden behind the keyboard, and switching back didn't bring it back until you closed the keyboard. A swap changes the keyboard's height, iOS can report a passing "no keyboard" on the way, and the new height can arrive with no report of its own, so full screen stayed sized for no keyboard. While you type, full screen now follows every report from that moment's numbers, looks again a few beats after each one, and moves the reply box back into view if it still isn't. A board reload is enough.
14
+ - **Photos picked on a phone attach again.** On iPhone, choosing a photo from the paperclip's picker closed the picker and nothing showed: coming back from the picker counts as a wake-up, which redraws the page, and iOS hands over the photo only after that, to the old picker the redraw had thrown away. The picker now lives outside the page, so a redraw can't lose what you picked, and a pick that still can't attach says why. Full screen also measures again as a phone's keyboard comes up, for keyboards (Raycast, Gboard, SwiftKey) that report late. A board reload is enough.
15
+ - **Walkthroughs say whether a step follows or departs from a decision.** A decision chip on a step now reads ≠ Departs when the step goes against it, and the step always gets a Look closely note naming the decision; from there the decision viewer offers to link one of the session's own decisions as its replacement. A session's own decisions show in its walkthrough right away, marked ◌ Proposed, with Accept and Reject in the viewer. Needs `leftoff restart` and `leftoff upgrade --beta`.
16
+ - **Weak tests caught without the model.** Walk me through it flags a test change that removes assertions and adds none, adds a skip or .only, asserts a value against itself, or mocks the module the test is for, and gives it a Look closely note even if the model put it under Safe to skip. Needs `leftoff restart`.
17
+ - **Test plans from merged PRs.** A project's page, its decision log and the PRs page (pick merged PRs, then Test plan for these) make a short manual script for what merged lately: up to about 12 checks that fit 15, 30 or 60 minutes, each with where to go, the steps, what you should see and why it's on the list, ordered by risk. Risky callouts, weak tests and changed decisions always become checks or are listed under Not covered with the reason. Tick checks off, note what failed and propose a decision from it, then Copy as Markdown or Download .html. `leftoff testplan --project NAME` prints it. Needs `leftoff restart`.
18
+ - **Decisions in the digest and Catch me up.** The daily digest has a short Decisions part per project: what was accepted and superseded yesterday ("D-a → D-b") and how many wait for review, each linking to the log. Catch me up on a session lists what it decided since you last looked, with each decision's status, and Ask can answer "why did we switch from X?" from the decision that replaced it. Needs `leftoff restart`.
19
+ - **Review decisions from the session panel.** A session's Decided list has Accept, Reject and Edit right there for each proposed decision, so reviewing one no longer means leaving the session. A project's page and its board chip show a quiet ◌ count of decisions waiting for review. When a project keeps its log in the repo and CLAUDE.md already imports docs/DECISIONS.md, a new session is only given the decisions accepted since that file was written. Needs `leftoff restart`.
20
+ - **See whether a session started with its decisions, and give them on demand.** The session panel says "Started with 4 decisions" under its checkpoints. When Claude Code skipped Leftoff's SessionStart hook (its Security default runs first), it says the session wasn't given its repo's decisions and why; type `/cp-decisions` in the session to give Claude the same list. Needs `leftoff upgrade --beta && leftoff restart`.
21
+ - **Walk me through it cites decisions and flags weak tests.** A step shows a small chip for each accepted decision it follows; tap it to see the decision and what it replaced. A step that departs from one gets a Look closely note, and a walkthrough citing a decision that's since been superseded says so, with Update. A test that doesn't really test anything (a loosened or deleted assertion, a mock of the thing under test, a skipped test) gets a Look closely note at its line instead of landing in Safe to skip.
22
+ - **New sessions and Ask know what was decided.** A new Claude Code session in a repo starts with that repo's accepted decisions (newest first, about 15 at most, with where the full log is), added beside what your own hooks give it. Ask about a project can cite a decision when you ask why something was done; a decision it didn't get from the log is dropped.
23
+ - **A decision log for every repo.** Decisions from checkpoints arrive as proposals on the repo's decision log (a project's Decisions, or Decision log → in the session panel); Accept, Reject or Edit each one. When a new one changes an accepted decision Leftoff suggests the link, you confirm it, and the timeline shows the chain: what each decision replaced and what replaced it, with dates, the session it came from and its PR. Copy as Markdown gives it in a DECISIONS.md style.
24
+ - **Keep a project's decision log in its repos.** A project's Decisions has one choice, Keep decision log in this repo (off: it stays in Leftoff). On, each repo's docs/DECISIONS.md gets its accepted decisions in a block between Leftoff's markers, written on a Mac with a checkout; the rest of the file is left alone and nothing is ever staged or committed.
25
+ - **Checkpoints record what was decided.** A checkpoint now has a Decided list: a question you answered from its options (with the ones you didn't pick), a plan you approved, a correction you gave, or a trade-off Claude chose and said so. It shows under the newest checkpoint in the session panel, folded with a count; most checkpoints have none, and none are made up.
26
+ - **No gap above full screen after you send from a phone.** With the keyboard up, iOS pans the screen up and full screen follows it so the composer stays above the keyboard. When the keyboard closed after a send, iOS could still report that pan (or report nothing), so full screen stayed about 300px down the screen with the board showing through above it. Now the pan only counts while the keyboard is really up, the layout is checked again as the keyboard goes down, and if iOS scrolled the page underneath, it scrolls back.
27
+ - **Let me answer prompts here.** When Claude Code's Security default skips Leftoff's hooks, the session's note now has a **Let me answer prompts here** button, and `leftoff doctor --fix` says the same. Both show the managed-settings lines that seat Leftoff after the Security default, with Copy: enable it, list it in appendPlugins and declare its marketplace. They also show the file the lines go in and who can write it (your admin, or you with sudo on your own Mac). Leftoff never writes managed settings. Earlier betas' doctor said to add `"appendPlugins": ["leftoff@checkpoints"]` to ~/.claude/settings.json instead. That doesn't work: Claude Code ignores the key there whenever the Security default is seated. `leftoff doctor --unfix` (or **Remove the ignored entry**) takes out just Leftoff's entry, after showing the diff and asking (default No), with a backup in ~/.config/checkpoints/backups. It never runs on a managed Mac. Once managed settings list Leftoff, a session that started before shows a quiet “restart Claude Code to apply” until its hooks arrive.
28
+ - **Needs you and turn ends work when another plugin takes Claude Code's hooks first.** On a Team or Enterprise plan, or a Mac with managed Claude Code settings, Claude Code 2.1.31x runs its built-in “cc-plugin-sec-default” ahead of the plugins you installed, and it sends every classic hook event (Stop, PermissionRequest, Notification, your prompt, SessionStart) on past them, so Leftoff's hooks never ran (“leftoff: its classic.Stop hook was skipped …”). A permission prompt now shows as Needs you anyway: the mod reads it from Claude Code's permission check, which still reaches it, and Needs you ends when the tool runs or the turn does (answer it in the terminal; the board can't answer it in this setup). Turn ends come from the mod and the transcript as before. The session's header note now says which hooks were skipped and why (“Stop and PermissionRequest skipped: cc-plugin-sec-default runs first”, the rest in its tip), and `leftoff doctor` names any plugin set to run ahead of Leftoff and the fix.
29
+ - **A finished turn stops spinning within seconds.** When Claude Code's end-of-turn signal never reached Leftoff, a session kept its spinner (“active”) until the whole dormancy window passed, and some never stopped at all. Now any Claude Code session (a board terminal, My terminal app, Live, the hooks, a headless one whose process is gone) settles about 20 seconds after its transcript goes still with Claude's answer last, or within seconds when the transcript itself marks the end; a tool call still running, a permission prompt or a queued message keeps it working or Needs you. It also reads turns Claude Code ends on a tool result, answers through the Live reply tool, and messages pulled back out of the queue, which used to keep a session “working” forever. The mod now reports a turn's end itself when no Stop follows it, a late Stop never counts a turn twice, and a terminal Leftoff opens for a daemon on its own folder reports to that folder. A session whose turns arrive but none of Claude Code's own hook events do now says so: one line in the daemon log, and a quiet “hooks not reporting” note beside its pills.
30
+ - **Fix: a file two walkthrough steps share shows its diff in both.** When two steps named exactly the same lines (a new test file cited by “Pin the bug” and again by the next step), they got one step id, so the board drew that file's diff into the later step only and left the first step's row open and empty; Mark reviewed on one also marked the other. Every step now has its own id (walkthroughs kept from before are mended when they load). The first step shows the file open; a later step that shows exactly the same lines folds it with a quiet “Also in step 1” that goes back there, and opens to the same diff. When steps point at different lines of one file, each step marks its own lines (“Lines 12–30 for this step”) and the rest steps back. A test file a step names now starts open even when some of its lines sit in Tests updated to match. Any changed lines no step names still land under Other changes.
31
+
32
+ - **This session · Whole folder.** The walkthrough's Files view, What it did and Ask about this change now cover only what the session changed, like the walkthrough, with a toggle that shows the counts on both sides (“This session 4 · Whole folder 24”). Whole folder marks each file that isn't the session's “not from this session”; switching marks the story out of date (Update writes it again) and never runs a model on its own. Ask says which scope it drafted from, and the choice is kept on this device.
33
+
34
+ - **Walk me through it covers only what the session changed.** A session started in a repo with uncommitted or untracked files counted them as its own (a new session read “20 files” before its first prompt). Leftoff now notes what was already changed as a session starts, on this Mac only, and the walkthrough and its count leave those files out; a file the session edited on top of earlier changes shows only its own change. A session with no turns yet shows no count, and one from before this update counts only the files it edited plus its commits, with a note saying so. Each repo of a multi-repo session gets the same rule.
35
+
36
+ - **Fix: terminals from the npm package start again.** The detached terminal host looked for `bin/pty-host-main.js`, which the npm build never shipped, so Board terminal and My terminal app sessions died at once (`[it exited]`). The build now ships it beside the bundle and refuses to build without it.
37
+
38
+ - **Fewer “access data from other apps” prompts.** Leftoff no longer reads inside other apps' data in your Mac's Library. The board terminal's Match my Ghostty reads Ghostty's settings only when you pick it, Syncthing's settings are read every 10 minutes instead of every minute, and a git repo at your home folder never walks Library.
39
+
40
+ - **Permission prompts say “Leftoff”, once.** On a Mac, the login service now runs through a small Leftoff.app built once on your Mac. macOS asks as “Leftoff” instead of “node”, for everything Leftoff starts (headless Claude and Codex, board terminals, git), and an Allow survives Leftoff and node upgrades. Run `leftoff update` once. It needs the Command Line Tools; without them nothing changes.
41
+
42
+ - **`leftoff update` and `leftoff restart`.** `leftoff update` brings the login service up to date (and on a Mac builds Leftoff.app) and restarts only if something changed; running it again changes nothing. `leftoff restart` always restarts the daemon.
43
+
44
+ - **`leftoff doctor --tcc`.** Shows who asked macOS for a permission and for which program: the privacy log for the last 2 hours, then a live minute while you use Leftoff. No sudo. The docs page “macOS permission prompts” says how to reset an answer.
45
+
7
46
  - **Continue in terminal / Continue headless.** An idle session can switch how it runs and keep its conversation: a headless one continues in a terminal of Leftoff's (`claude --resume` / `codex resume`, same session), and one in Leftoff's terminal continues headless. The old process ends before the new one starts, so the session never runs twice; one that's mid-turn or waiting on you is refused, and if the new side can't start, it goes back where it was.
8
47
 
9
48
  - **Terminal sessions keep running.** A session in the board terminal or your terminal app now survives closing that window or tab, closing the board, and restarting or updating Leftoff: each terminal runs in a small detached host of its own, and Leftoff finds it again when it starts. With nothing showing it, it reads “◌ running · detached”; Go brings back the terminal app window attached to it (or shows it in the board), Open in Terminal attaches a window to the same session, and `leftoff attach <id>` works even while the daemon is stopped, including a session started just before the restart. Only Stop or Exit ends it. (After updating, Leftoff rewrites its login service once so a restart leaves these running.)
@@ -42,7 +81,6 @@ What changed in Leftoff (called Checkpoints until 0.4.5), newest first. The boar
42
81
 
43
82
  - **“Away” never claims more than a session’s age.** A session that started while you were away still reads at full depth, but its Catch me up label now says how long it really went unseen (a 30-minute-old session said “Away 12h”).
44
83
 
45
- - **Choose which machine hosts your board, and it fails over on its own.** Settings → Use from anywhere → Hosted by lists your machines (● online / ○ offline), marks the host, and has Make host and a Preferred choice (or Automatic); `leftoff relay host [name|auto]` does the same and `leftoff relay status` shows it. The relay keeps the host while it’s online (a machine reconnecting after an upgrade no longer takes the board), prefers a Mac that can open your paired browsers’ keys, hands the board to another online machine when the host’s lid closes (about 25 s, was 75), and gives it back to your preferred host 10 s after it returns. The reconnecting screen says which machine is out, switches, and offers “Use nightblood instead” when several are online. Needs the relay update for the host choice; phones stay paired.
46
84
  - **Choose which machine hosts your board, and it fails over on its own.** Settings → Use from anywhere → Hosted by lists your machines (● online / ○ offline), marks the host, and has Make host and a Preferred choice (or Automatic); `leftoff relay host [name|auto]` does the same and `leftoff relay status` shows it. The relay keeps the host while it’s online (a machine reconnecting after an upgrade no longer takes the board), prefers a Mac that can open your paired browsers’ keys, hands the board to another online machine when the host’s lid closes (about 25 s, was 75), and gives it back to your preferred host 10 s after it returns. The reconnecting screen says which machine is out, switches, and offers “Use laptop instead” when several are online. Needs the relay update for the host choice; phones stay paired.
47
85
  - **A Needs-you reply shows as your words.** Claude Code wraps a prompt the plugin sends between turns as “The leftoff plugin sent a message: … Address the message above.”; the thread, titles and previews now show only what you typed (“You · from Needs you”).
48
86
  - **PRs named as `repo#123`, even from a sandbox with no git remote.** When Claude writes a session's PRs as `api#574` or `acme/web#12` (in its words or yours), or suggests a `gh pr edit 574` to run on another machine, they link: a bare repo name is matched against the GitHub repos your other sessions use (never a guess when two orgs share the name), and whichever machine has gh signed in checks each one, so a session in a sandbox without git remote or gh still shows its PRs. A session can list PRs from several repos. Merged and closed PRs show too, each with an icon and its word (Open, Draft, Merged, Closed). A reference that can't be checked yet shows as a quiet “api#574 · not checked” chip that says why. An issue number never shows as a PR. After updating, run `leftoff restart`.
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import i from"node:fs";import c from"node:os";import l from"node:path";import{spawnSync as U}from"node:child_process";function O(e,t=40){return String(e||"").toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,t).replace(/-+$/g,"")||"untitled"}function g(e){return O(String(e||"").replace(/\.local$/i,""),40)||"unknown"}function T({env:e,hostname:t,cfg:n={}}={}){if(e)return g(e);let r=n.machineNames||{};return t&&r[t]?g(r[t]):n.machine?g(n.machine):g(t||"unknown")}var _="[A-Za-z0-9_.-]*(?:key|token|secret|passw(?:or)?d|pwd|auth(?!or)|credential|cookie|signature)[A-Za-z0-9_.-]*",ee=[[/\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+\/=-]{6,}/gi,"$1 …"],[/\b(authorization|cookie|x-api-key)(\s*[:=]\s*)("[^"]*"|'[^']*'|\S+(?:\s+[A-Za-z0-9._~+\/=-]{12,})?)/gi,"$1$2…"],[/(:\/\/)[^\s\/@:]+:[^\s\/@]+@/g,"$1…@"],[new RegExp("(--?"+_+`)(=|\\s+)("[^"]*"|'[^']*'|\\S+)`,"gi"),"$1$2…"],[new RegExp("\\b("+_+`)(\\s*[=:]\\s*)("[^"]*"|'[^']*'|[^\\s&;,]+)`,"gi"),"$1$2…"],[/\b([A-Z][A-Z0-9_]{1,})=("[^"]*"|'[^']*'|[^\s;&|]+)/g,"$1=…"],[/\b(?:sk|pk|rk|ghp|gho|ghs|ghu|ghr|github_pat|glpat|xox[abprs]|AKIA|ASIA|npm|hf|ya29)[-_][A-Za-z0-9_-]{8,}/g,"…"],[/\bAKIA[A-Z0-9]{12,}\b/g,"…"],[/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}(?:\.[A-Za-z0-9_-]+)?/g,"…"],[/(?<![\/\w.-])(?=[A-Za-z0-9_+=-]*\d)(?=[A-Za-z0-9_+=-]*[A-Za-z])[A-Za-z0-9_+=-]{32,}(?![\/\w.-])/g,"…"]];var te=10*6e4;var v={short:{label:"Short",maxTokens:300,approx:"~300 tokens",blurb:"Doing / Stopped at / Next. Three lines, nothing else."},informative:{label:"Informative",maxTokens:700,approx:"~700 tokens",blurb:"The three lines, plus key decisions, open questions, files touched and why, and what didn't work."},long:{label:"Long",maxTokens:1500,approx:"~1500 tokens",blurb:"Everything in Informative, plus a short narrative recap and the next 3 steps."}},ne=Object.keys(v);var x=["You write checkpoints for a developer juggling many coding-agent sessions, read when they come back after half an hour, a day or a weekend. Given the recent transcript of ONE session, start with exactly these five lines:","Goal: <the outcome the developer is after, from what they asked for, in a few plain words; not the latest step. Keep the previous goal unless they changed course or it is done and they moved on>","Progress: <one of Just started, Underway, Mostly done, Done, Stuck> - <what is left between here and the goal, a few words>","Doing: <what the session was working on, one sentence, concrete nouns: files, features, errors>","Stopped at: <the exact state when it went quiet: what passed, what failed, what is half-done; say what was actually run or checked, and what is only assumed>","Next: <the single next action a human or the agent should take>",'If the session is blocked on the human (a question, a permission, a decision), start Next with "YOU:".',"Describe only what the transcript shows; the lines above it (repo, branch, title, what was asked, previous checkpoint) are background and may be out of date."],y=['Only if the transcript shows a decision (a choice between real alternatives that the developer made or approved, a plan they approved, a correction they gave such as "no, do X instead", or a trade-off the agent chose and said so), add after those lines a "Decided:" heading and one "- " line per decision, at most 4:',"- <what was decided, in the words the transcript uses> · why: <the reason given, one line> · over: <alternative>; <alternative> · from: <asked | plan | correction | agent>","A task done, a file edited or a routine step is not a decision. Never invent a choice or a reason the transcript does not show. When unsure, or when there is none, leave the Decided heading out entirely. Skip decisions listed as already recorded."],A=['Then these sections, each a heading line followed by "- " bullets (write "- none" when there is nothing; never invent):',"Approach notes:","- <how the work is being done: the approach taken, a pattern or constraint it follows; one line each, at most 4. Not a choice between alternatives: those go only under Decided>","Open questions:","- <an unresolved question or uncertainty, at most 3>","Files:","- <path> — <why it was touched, a few words; at most 6 files>","Didn't work:","- <an approach that was tried and abandoned or failed, and why; at most 3>"],R=["Then:","Recap: <a narrative paragraph of 4-7 sentences: the goal, how the session got here, the current state>","Next steps:","1. <the next step>","2. <the step after>","3. <the step after that>"],M={short:[...x,...y,"Write only those five lines (and Decided, when there is one).",'Optionally a 6th line "Title: <max 6 words>" if asked for a title.',"No preamble. No markdown. Under 90 words total."].join(`
3
+ `),informative:[...x,...y,...A,'Optionally a final line "Title: <max 6 words>" if asked for a title.',"No preamble. No other markdown. Under 280 words total."].join(`
4
+ `),long:[...x,...y,...A,...R,'Optionally a final line "Title: <max 6 words>" if asked for a title.',"No preamble. No other markdown. Under 630 words total."].join(`
5
+ `)},re=M.short;var D="Write it in the same language as the user's own messages (English if unsure), in one language only: never mix in words or characters from another language.",oe="Write a title of at most 6 words for a coding session, based on its first prompt. "+D+" Reply with the title only, no quotes, no punctuation at the end.";var ie=[["latin",new RegExp("\\p{Script=Latin}","u")],["han",/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Bopomofo}]/u],["hangul",new RegExp("\\p{Script=Hangul}","u")],["cyrillic",new RegExp("\\p{Script=Cyrillic}","u")],["greek",new RegExp("\\p{Script=Greek}","u")],["arabic",new RegExp("\\p{Script=Arabic}","u")],["hebrew",new RegExp("\\p{Script=Hebrew}","u")],["thai",new RegExp("\\p{Script=Thai}","u")],["devanagari",new RegExp("\\p{Script=Devanagari}","u")]];var $=new Set("the a an and or but if then so of to in on at by for from with without into onto about over under after before is are was were be been being am do does did done has have had having can could will would should shall may might must i me my we us our you your he him his she her it its they them their this that these those there here what which who whom whose when where why how not no yes all any some each every both more most less many much few other another such only own same than too very just also again still now today up down out off via per as because while until since though although whether let lets please hey hi thanks ok okay one two three first last next new old way something anything nothing everything".split(" ")),P=new Set([...$,..."add added adding adds allow api app apps ask asks asking answer answers auth auto backend background bad base basic batch better big board bot box branch break broken bug bugs build builds button cache call calls card cards case change changes chat check checks clean cleanup clear cli click client close code column columns command commit config connect copy core count create cron css current dark data date db debug default delete deploy design detail details dev diff doc docs driver drop edit editor element empty end error errors event events explain export extra fail fails failing fast feature field fields file files filter find fix fixes flag flaky flow folder font form format full get git give go good graph group grouping guard guide handle handler header help hide history hook hooks host icon idea image import improve info input install issue issues item items job jobs join json key keys kind label layout lazy line lines link links lint list lists live load local lock log logic login logs look loop main make map mark menu merge message messages method migrate mobile mode model module move multi name names nav note notes number open option options order output overview page pages panel parse parser part pass patch path phone pick plan play polish port post prompt prompts proof pull push query question questions queue quick read readme real record refactor release remote remove rename render repo report request reset response rest result results retry review route router rule run runs runner save scan schema screen script scroll search session sessions set setup ship show sidebar sign size slow small sort source spec split start state status step stop store style sync table tables tag task tasks test tests text theme time timeout tip title titles today token tool tools top track tree trim try tune type typo typos ui undo unit up update upgrade url use user users value values view wait warn watch web wire word words work worker write wrong yaml focus bonus plus bus virus menu cursor support transcript transcripts tidy bump speed toggle suite crash startup prototype investigate audit align avoid batch bind boot cancel cap cart chart cleanup compare compose convert cover cut define detect disable enable draft drag embed ensure expose extract fetch finish fit fold forms gate handoff hover inline keep limit lint map match measure memo mock mute note pin poll port preview profile prune rate reach rebase redo reduce refresh reload repair replace reply resize resolve restore reuse revert rework rewrite scope seed send serve share shrink skip slot snap spike stub swap tidy trace tweak unify unlock untangle verify wrap zoom basics summary context notes intro".split(" ")]),se=new Set("de del la las el los un una unos unas y con para por que como sobre su sus le les des du une et pour avec sur dans au aux ce cette ces est der das und mit für fur von zu auf ein eine einen dem ist nicht über uber gli della delle dei di per con nel uma não nao cum est sunt atque quae qui quod".split(" ").filter(e=>!P.has(e)));var F="leftoff ↗",j=" · ",S=7429,k="\x1B",E=k+"\\";function W(e,t){return k+"]8;;"+e+E+t+k+"]8;;"+E}function z(e){return"/#/s/"+encodeURIComponent(String(e))}function q(e,t){return"http://127.0.0.1:"+(Number(e)||S)+z(t)}function I(e=process.env,t=c.homedir()){if(e.LEFTOFF_DIR||e.CHECKPOINTS_DIR)return e.LEFTOFF_DIR||e.CHECKPOINTS_DIR;try{let n=l.join(e.XDG_CONFIG_HOME||l.join(t,".config"),"checkpoints","folder"),r=i.readFileSync(n,"utf8").trim();if(r)return r}catch{}return l.join(t,"checkpoints")}function H(e=process.env,t=c.homedir()){let n=e.LEFTOFF_PORT||e.CHECKPOINTS_PORT;if(n&&Number(n))return Number(n);try{let r=JSON.parse(i.readFileSync(l.join(I(e,t),"config.json"),"utf8"));if(Number(r.port))return Number(r.port)}catch{}return S}function B(e,{port:t=S}={}){let n=e&&typeof e.session_id=="string"?e.session_id.trim():"";return n?W(q(t,n),F):""}function K(e){let t=String(e||"");if(!t.startsWith("b64:"))return null;try{return Buffer.from(t.slice(4),"base64url").toString("utf8")}catch{return null}}function Z(e,t){let n=String(e||"").replace(/\r?\n+$/,"");if(!t)return n;if(!n.trim())return t;let r=n.split(`
6
+ `),s=/\x1b\[[0-9;]*m/.test(r[0])&&!/\x1b\[0?m(?!.*\x1b\[)/.test(r[0]);return r[0]=r[0]+(s?k+"[0m":"")+j+t,r.join(`
7
+ `)}function G(e){for(let t of[e.LEFTOFF_STATUS_SHELL,"/bin/bash","/bin/sh"])if(t&&i.existsSync(t))return t;return!0}function Y(e,t,{env:n=process.env,cwd:r=process.cwd(),timeoutMs:s=1e4,run:d=U,home:a=c.homedir()}={}){try{let o=d(e,{shell:G(n),input:t,encoding:"utf8",env:n,cwd:X(r,a),timeout:s,maxBuffer:1048576});return{out:String(o&&o.stdout||""),code:o&&typeof o.status=="number"?o.status:1}}catch{return{out:"",code:1}}}function N(e){try{return!!e&&i.statSync(e).isDirectory()}catch{return!1}}function X(e,t=c.homedir()){if(N(e))return e;let n="";try{n=process.cwd()}catch{}for(let r of[n,t])if(N(r))return r;return"/"}function C(e,t,n={}){let{env:r=process.env,home:s=c.homedir(),cwd:d=process.cwd(),run:a}=n,o=null;try{o=JSON.parse(t||"{}")}catch{}let p=(e||[]).find(w=>String(w).startsWith("--wrap=")),f=p?K(p.slice(7)):null,u=S;try{u=H(r,s)}catch{}let h=B(o,{port:u});try{n.noName||Q(o,{env:r,home:s})}catch{}if(!f)return h;let m=Y(f,t,{env:r,cwd:o&&o.cwd||d,run:a,home:s});return Z(m.out,h)}function Q(e,{env:t=process.env,home:n=c.homedir(),tmp:r=c.tmpdir(),now:s=Date.now(),machine:d=null}={}){let a=e&&typeof e.session_id=="string"?e.session_id.trim():"",o=e&&typeof e.session_name=="string"?e.session_name.trim().slice(0,200):"";if(!a||!o||/[\/]/.test(a)||a.length>100)return!1;let p=l.join(r,"leftoff-names"),f=l.join(p,a.replace(/[^A-Za-z0-9_-]/g,"_"));try{if(i.readFileSync(f,"utf8")===o)return!1}catch{}let u=I(t,n);if(!i.existsSync(l.join(u,"config.json")))return!1;let h=d||V(t,u),m=l.join(u,".spool",h,a.replace(/[^A-Za-z0-9_-]/g,"_"));i.mkdirSync(m,{recursive:!0});let w={v:1,sessionId:a,at:new Date(s).toISOString(),type:"activity",data:{kind:"name",generatedTitle:o}};return i.writeFileSync(l.join(m,s+"-"+Math.random().toString(16).slice(2,10)+".json"),JSON.stringify(w)+`
8
+ `,{flag:"wx"}),i.mkdirSync(p,{recursive:!0}),i.writeFileSync(f,o),!0}function V(e,t){let n={};try{n=JSON.parse(i.readFileSync(l.join(t,"config.json"),"utf8"))}catch{}return J({env:e.CHECKPOINTS_MACHINE,hostname:c.hostname(),cfg:n})}function J(e){return T(e)}function L(e=0){try{return i.readFileSync(e,"utf8")}catch{return""}}var b="";try{b=C(process.argv.slice(2),L())}catch{}b&&process.stdout.write(b+`
9
+ `);process.exitCode=0;