From b8480dd4884c0eba582d06345ad34525413e9416 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Mon, 19 Oct 2026 13:05:00 +0000 Subject: [PATCH] readm3 update and readm3 uninstall (remove), via @profullstack/cli (v0.13.2) Both act the way this copy was installed: the curl installer (keeping --desktop when the desktop app came with it, and the install prefix), npm, bun, pnpm or yarn. update checks npm first (--check only reports); uninstall asks first (--yes). Co-Authored-By: Claude Opus 5.5 --- README.md | 4 +++- bun.lock | 3 +++ desktop/package.json | 2 +- package.json | 3 ++- src/cli.ts | 18 +++++++++++++++--- test/cli.test.ts | 5 +++++ 6 files changed, 29 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f48b9a9..1908c98 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,9 @@ on the machine it fetches a private official Node first: curl -fsSL https://readm3.com/install.sh | sh ``` -Run it again to update; remove with `sh ~/.local/share/readm3/uninstall.sh`. +Update with `readm3 update` and remove with `readm3 uninstall`; both work the way readm3 +was installed (this installer, npm, bun, pnpm or yarn), through +[@profullstack/cli](https://github.com/profullstack/cli). Add `--desktop` to install the desktop editor as well (Linux x64; macOS and Windows download it, see [Desktop app](#desktop-app)): diff --git a/bun.lock b/bun.lock index d4ba3c6..66812ba 100644 --- a/bun.lock +++ b/bun.lock @@ -6,6 +6,7 @@ "name": "@profullstack/readm3", "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", + "@profullstack/cli": "^0.1.0", "@profullstack/hqtui": "^0.5.0", "@profullstack/synconfig": "^0.1.3", "highlight.js": "^11.12.0", @@ -140,6 +141,8 @@ "@playwright/test": ["@playwright/test@1.63.0", "", { "dependencies": { "playwright": "1.63.0" }, "bin": { "playwright": "cli.js" } }, "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ=="], + "@profullstack/cli": ["@profullstack/cli@0.1.0", "", {}, "sha512-kX2mMvxsfsz89u3RIj2cGRIb89TUVUgwNO1rlTowPuHQrsStsOCNtz8KQ/VqPgJ2FjpfAj0CCQwCRgXaehPL5w=="], + "@profullstack/hqtui": ["@profullstack/hqtui@0.5.0", "", { "bin": { "hqtui": "bin/hqtui.mjs" } }, "sha512-jDwILBmdQx8pQA0ao/tzzty8u5nFNS4XhvU9xXhZvdIaMeiF3HBJIVtP6cS6kigS4lXmHR7sztbb9QJpaWwhig=="], "@profullstack/synconfig": ["@profullstack/synconfig@0.1.3", "", {}, "sha512-Ty/90ibgu2DVlablDeoFdItcobK+c8cMQFvuTfJOUlCfyvQZWoPjgt0JHPwTvq+ynOQ4i1ivBQ3K3PY/S4rObA=="], diff --git a/desktop/package.json b/desktop/package.json index 634e01f..93229aa 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,7 +1,7 @@ { "name": "readm3-desktop", "productName": "readm3", - "version": "0.13.1", + "version": "0.13.2", "private": true, "description": "readm3 as a desktop app: a plain, fast text editor for Markdown and code.", "author": "Profullstack, Inc. ", diff --git a/package.json b/package.json index 6bc622f..9086acf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@profullstack/readm3", - "version": "0.13.1", + "version": "0.13.2", "description": "Markdown editing and sharing for people and teams. Web/PWA, CLI, API and MCP with version history, view/edit links, and a terminal reader.", "type": "module", "license": "MIT", @@ -43,6 +43,7 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.30.0", + "@profullstack/cli": "^0.1.0", "@profullstack/hqtui": "^0.5.0", "@profullstack/synconfig": "^0.1.3", "highlight.js": "^11.12.0", diff --git a/src/cli.ts b/src/cli.ts index 4816abd..b89cf60 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,8 +1,9 @@ /** * Argument parsing and entry point. `readm3 --help` is the contract. */ -import { statSync } from "node:fs"; -import { dirname, resolve } from "node:path"; +import { existsSync, realpathSync, statSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { detectInstall, lifecycle, lifecycleUsage } from "@profullstack/cli"; import { pathToFileURL } from "node:url"; import { themeList } from "@profullstack/hqtui"; import { FLAVORS, isFlavor, type Flavor } from "./flavors.ts"; @@ -11,7 +12,7 @@ import { run, type ViewerOptions } from "./viewer.ts"; import { isCloudCommand, cloudMain, CLOUD_USAGE } from "./cloud-cli.ts"; import { isDesktopCommand, desktopMain, installedDesktop, prefersDesktop, DESKTOP_USAGE } from "./desktop-cli.ts"; -export const VERSION = "0.13.1"; +export const VERSION = "0.13.2"; /** The help text, exported so a test can hold it to the flags it documents. */ export const USAGE = `readm3 — a plain text editor for Markdown and code @@ -61,6 +62,7 @@ Editing ctrl+g keys for editing tab indent two spaces ${DESKTOP_USAGE} +${lifecycleUsage("readm3")} ${CLOUD_USAGE}`; export interface ParsedArgs extends ViewerOptions { @@ -186,7 +188,17 @@ export function parseArgs(argv: string[]): ParsedArgs { return parsed; } +/** An installer copy that also installed the desktop app keeps it on update. */ +function keepDesktop(): string[] { + let script: string | undefined; + try { script = process.argv[1] && realpathSync(process.argv[1]); } catch { return []; } + const { share } = detectInstall({ name: "readm3", script }); + return share && (existsSync(join(share, "desktop")) || existsSync(join(share, "readm3.AppImage"))) ? ["--desktop"] : []; +} + export async function main(argv: string[] = process.argv.slice(2)): Promise { + // readm3 update / uninstall (remove): done the way this copy was installed. + if (await lifecycle(argv, { name: "readm3", package: "@profullstack/readm3", version: VERSION, installer: "https://readm3.com/install.sh", installerArgs: keepDesktop })) return; const { accountCommand } = await import("./sync-cli.ts"); if (await accountCommand(argv)) return; if (isCloudCommand(argv[0])) { await cloudMain(argv); return; } diff --git a/test/cli.test.ts b/test/cli.test.ts index 662bce0..e2e8102 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -7,6 +7,11 @@ import { readFile } from "node:fs/promises"; import { parseArgs, USAGE, VERSION } from "../src/cli.ts"; import { FLAVORS } from "../src/flavors.ts"; +test("help names update and uninstall", () => { + assert.match(USAGE, /readm3 update \[--check\]/); + assert.match(USAGE, /readm3 uninstall \[--yes\]/); +}); + test("no arguments browse the working directory", () => { const args = parseArgs([]); assert.equal(args.root, process.cwd());