From 95bc5401602abeaa2a9d2b34e8e0e2930f47de5f Mon Sep 17 00:00:00 2001 From: elkaix Date: Mon, 3 Aug 2026 23:13:05 -0400 Subject: [PATCH 1/4] fix: clear the terminal before the install intro animation The intro absolute-positions the logo grid and progress bar to fixed screen rows, which assumes a clean screen. With prior shell output the frames interleave with old text. Clear the screen at animation start in both install.sh and install.ps1; the non-animated path is unchanged. --- .changeset/install-clear-screen.md | 5 +++++ apps/pythinker-web/public/install.ps1 | 4 ++++ apps/pythinker-web/public/install.sh | 5 +++++ 3 files changed, 14 insertions(+) create mode 100644 .changeset/install-clear-screen.md diff --git a/.changeset/install-clear-screen.md b/.changeset/install-clear-screen.md new file mode 100644 index 00000000..61525c0d --- /dev/null +++ b/.changeset/install-clear-screen.md @@ -0,0 +1,5 @@ +--- +'@pythoughts/pythinker-code': patch +--- + +Clear the terminal before the install script's animated intro so earlier shell output no longer interleaves with the logo animation. diff --git a/apps/pythinker-web/public/install.ps1 b/apps/pythinker-web/public/install.ps1 index c595be19..35871c9e 100644 --- a/apps/pythinker-web/public/install.ps1 +++ b/apps/pythinker-web/public/install.ps1 @@ -148,6 +148,10 @@ function Stop-AntennaSpin { } function Write-LogoAnimated { + # The intro absolute-positions to screen rows (grid at 5-9, progress at + # 17), which assumes a clean screen. Clear it first — otherwise earlier + # shell output interleaves with the animation. + Write-Host -NoNewline "${ESC}[2J${ESC}[H" $rows = 5; $cols = 13 $frameMs = 60; $staggerMs = 40 if ($env:PYTHINKER_LOGO_FRAME_DELAY) { try { $frameMs = [int]([double]$env:PYTHINKER_LOGO_FRAME_DELAY * 1000) } catch {} } diff --git a/apps/pythinker-web/public/install.sh b/apps/pythinker-web/public/install.sh index 0032b487..793d9f56 100755 --- a/apps/pythinker-web/public/install.sh +++ b/apps/pythinker-web/public/install.sh @@ -327,6 +327,11 @@ print_logo_animated() { local FRAME_DELAY="${PYTHINKER_LOGO_FRAME_DELAY:-0.06}" local STAGGER_DELAY="${PYTHINKER_LOGO_STAGGER_DELAY:-0.04}" + # The whole intro absolute-positions to screen rows (grid at 5-9, + # progress at 17), which assumes a clean screen. Clear it first — + # otherwise earlier shell output interleaves with the animation. + printf '\033[2J\033[H' + # Hide the text cursor while we redraw in place — otherwise the block # cursor on terminals like Terminal.app renders as a stray white block # at the cursor's current cell. Show it again before returning so the From 0e07fb32e64debbcd496707ee119b4fc9f4b7a8b Mon Sep 17 00:00:00 2001 From: elkaix Date: Mon, 3 Aug 2026 23:13:21 -0400 Subject: [PATCH 2/4] feat: restyle OAuth callback pages to match the site's light design --- .changeset/oauth-pages-light-redesign.md | 5 ++ packages/oauth/src/oauth-pages.ts | 72 ++++++++++++++++-------- 2 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 .changeset/oauth-pages-light-redesign.md diff --git a/.changeset/oauth-pages-light-redesign.md b/.changeset/oauth-pages-light-redesign.md new file mode 100644 index 00000000..48d89936 --- /dev/null +++ b/.changeset/oauth-pages-light-redesign.md @@ -0,0 +1,5 @@ +--- +"@pythoughts/pythinker-code": patch +--- + +Restyle the browser OAuth sign-in confirmation pages for all providers to match the website's light design. diff --git a/packages/oauth/src/oauth-pages.ts b/packages/oauth/src/oauth-pages.ts index 326c5122..1956779e 100644 --- a/packages/oauth/src/oauth-pages.ts +++ b/packages/oauth/src/oauth-pages.ts @@ -39,46 +39,69 @@ const PYTHINKER_LOGO_SVG = ''; const SUCCESS_BADGE_SVG = - '' + @@ -99,7 +123,8 @@ function renderOAuthPage(options: { '

Pythinker Code

' + `
${options.badge}
` + `

${options.heading}

` + - `

${options.message}

` + + `

${options.message}

` + + `
${options.statusLine}
` + '' ); } @@ -110,6 +135,7 @@ export function renderOAuthSuccessPage(): string { heading: "You're logged in to Pythinker", message: 'You can close this tab and return to Pythinker.', badge: SUCCESS_BADGE_SVG, + statusLine: 'status: authenticated', }); } @@ -119,6 +145,7 @@ export function renderOpenAICodexOAuthSuccessPage(): string { heading: 'OpenAI Codex sign-in complete', message: 'You can close this tab and return to Pythinker Code.', badge: SUCCESS_BADGE_SVG, + statusLine: 'provider: openai-codex · status: authenticated', }); } @@ -128,5 +155,6 @@ export function renderOAuthErrorPage(): string { heading: 'Sign-in failed', message: 'The authorization server reported an error. Return to Pythinker for details.', badge: ERROR_BADGE_SVG, + statusLine: 'status: failed', }); } From b0cddf5020e14126e3eaad0bdf6b918553eb2015 Mon Sep 17 00:00:00 2001 From: elkaix Date: Mon, 3 Aug 2026 23:21:23 -0400 Subject: [PATCH 3/4] fix: point native install commands at the CDN install scripts The pythinker.com/install.{sh,ps1} URLs serve a stale bootstrap from an older installer flow, so the update notice and docs pointed users at a script that cannot find current release assets. Use the CDN-hosted scripts under code.pythinker.com/pythinker-code/ everywhere. --- apps/pythinker-code/src/constant/app.ts | 4 ++-- .../test/cli/update/preflight.test.ts | 4 ++-- apps/pythinker-web/public/install.ps1 | 8 ++++---- apps/pythinker-web/public/install.sh | 18 +++++++++--------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/pythinker-code/src/constant/app.ts b/apps/pythinker-code/src/constant/app.ts index 7e0871b8..a5925214 100644 --- a/apps/pythinker-code/src/constant/app.ts +++ b/apps/pythinker-code/src/constant/app.ts @@ -67,8 +67,8 @@ export const ANTHROPIC_PLUGIN_MARKETPLACE_REPOSITORY = 'anthropics/claude-plugin export const CLAUDE_PLUGIN_MARKETPLACE_PATH = '.claude-plugin/marketplace.json'; export const ANTHROPIC_PLUGIN_MARKETPLACE_URL = `https://raw.githubusercontent.com/${ANTHROPIC_PLUGIN_MARKETPLACE_REPOSITORY}/HEAD/${CLAUDE_PLUGIN_MARKETPLACE_PATH}`; -export const PYTHINKER_CODE_INSTALL_SH_URL = 'https://pythinker.com/install.sh'; -export const PYTHINKER_CODE_INSTALL_PS1_URL = 'https://pythinker.com/install.ps1'; +export const PYTHINKER_CODE_INSTALL_SH_URL = 'https://code.pythinker.com/pythinker-code/install.sh'; +export const PYTHINKER_CODE_INSTALL_PS1_URL = 'https://code.pythinker.com/pythinker-code/install.ps1'; // Native install commands, split by platform. Use these for prompt copy and spawn calls only; do not assemble the strings elsewhere. export const NATIVE_INSTALL_COMMAND_UNIX = `curl -fsSL ${PYTHINKER_CODE_INSTALL_SH_URL} | bash`; diff --git a/apps/pythinker-code/test/cli/update/preflight.test.ts b/apps/pythinker-code/test/cli/update/preflight.test.ts index 646b12e7..183438b9 100644 --- a/apps/pythinker-code/test/cli/update/preflight.test.ts +++ b/apps/pythinker-code/test/cli/update/preflight.test.ts @@ -494,7 +494,7 @@ describe('runUpdatePreflight', () => { // pipefail must come before the pipeline so a failed `curl` is not masked // by the trailing `bash` exiting 0 (see "surfaces a failed curl" below). expect(script).toContain('set -o pipefail'); - expect(script).toContain('curl -fsSL https://pythinker.com/install.sh'); + expect(script).toContain('curl -fsSL https://code.pythinker.com/pythinker-code/install.sh'); expect(script).toContain('| bash'); } finally { Object.defineProperty(process, 'platform', { value: originalPlatform }); @@ -510,7 +510,7 @@ describe('runUpdatePreflight', () => { try { const { stdout, options } = captureOutput(); await expect(runUpdatePreflight('0.4.0', options)).resolves.toBe('continue'); - expect(stdout.join('')).toContain('irm https://pythinker.com/install.ps1 | iex'); + expect(stdout.join('')).toContain('irm https://code.pythinker.com/pythinker-code/install.ps1 | iex'); expect(promptForInstallChoice).not.toHaveBeenCalled(); expect(mocks.spawn).not.toHaveBeenCalled(); } finally { diff --git a/apps/pythinker-web/public/install.ps1 b/apps/pythinker-web/public/install.ps1 index 35871c9e..0bfe048d 100644 --- a/apps/pythinker-web/public/install.ps1 +++ b/apps/pythinker-web/public/install.ps1 @@ -6,10 +6,10 @@ # (added to the user PATH). # # Usage: -# irm https://pythinker.com/install.ps1 | iex +# irm https://code.pythinker.com/pythinker-code/install.ps1 | iex # # To pin a version when running the hosted script, set: -# $env:PYTHINKER_VERSION = "0.6.0"; irm https://pythinker.com/install.ps1 | iex +# $env:PYTHINKER_VERSION = "0.6.0"; irm https://code.pythinker.com/pythinker-code/install.ps1 | iex # # Or run the script directly: # .\install.ps1 -Version 0.6.0 @@ -26,8 +26,8 @@ $Repo = "Pythoughts-labs/pythinker-code" # CDN source of truth for the latest published version — same endpoint the # in-app updater reads, so a fresh install and an auto-update always agree. $CdnLatestUrl = "https://code.pythinker.com/pythinker-code/latest" -$InstallShUrl = "https://pythinker.com/install.sh" -$InstallPs1Url = "https://pythinker.com/install.ps1" +$InstallShUrl = "https://code.pythinker.com/pythinker-code/install.sh" +$InstallPs1Url = "https://code.pythinker.com/pythinker-code/install.ps1" $NoColor = $env:NO_COLOR try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} diff --git a/apps/pythinker-web/public/install.sh b/apps/pythinker-web/public/install.sh index 793d9f56..c6605fb0 100755 --- a/apps/pythinker-web/public/install.sh +++ b/apps/pythinker-web/public/install.sh @@ -7,13 +7,13 @@ # ~/.local/bin/pythinker # # Usage: -# curl -fsSL https://pythinker.com/install.sh | bash +# curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash # # # Pin a specific version: -# curl -fsSL https://pythinker.com/install.sh | bash -s -- --version 0.6.0 +# curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash -s -- --version 0.6.0 # # # Custom install prefix (default $HOME/.local): -# curl -fsSL https://pythinker.com/install.sh | bash -s -- --prefix /opt/pythinker +# curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash -s -- --prefix /opt/pythinker # # Supported targets (matches release artifact names pythinker-code-.zip): # linux-x64 (Linux x86_64) @@ -21,7 +21,7 @@ # darwin-arm64 (macOS Apple Silicon) # darwin-x64 (macOS Intel) # -# Windows users: irm https://pythinker.com/install.ps1 | iex +# Windows users: irm https://code.pythinker.com/pythinker-code/install.ps1 | iex set -euo pipefail VERSION="" @@ -38,13 +38,13 @@ installs it at ~/.local/bin/pythinker Usage: - curl -fsSL https://pythinker.com/install.sh | bash + curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash # Pin a specific version: - curl -fsSL https://pythinker.com/install.sh | bash -s -- --version 0.6.0 + curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash -s -- --version 0.6.0 # Custom install prefix (default $HOME/.local): - curl -fsSL https://pythinker.com/install.sh | bash -s -- --prefix /opt/pythinker + curl -fsSL https://code.pythinker.com/pythinker-code/install.sh | bash -s -- --prefix /opt/pythinker Supported targets (matches release artifact names pythinker-code-.zip): linux-x64 (Linux x86_64) @@ -52,7 +52,7 @@ Supported targets (matches release artifact names pythinker-code-.zip): darwin-arm64 (macOS Apple Silicon) darwin-x64 (macOS Intel) -Windows users: irm https://pythinker.com/install.ps1 | iex +Windows users: irm https://code.pythinker.com/pythinker-code/install.ps1 | iex EOF } @@ -594,7 +594,7 @@ case "$os/$arch" in platform_display="macOS x64" ;; MINGW*/*|MSYS*/*|CYGWIN*/*) fail "On Windows, use the PowerShell installer: -powershell -c \"irm https://pythinker.com/install.ps1 | iex\"" ;; +powershell -c \"irm https://code.pythinker.com/pythinker-code/install.ps1 | iex\"" ;; *) fail "unsupported target: $os/$arch" ;; esac From cdbda364816a65e26bb5624b484b4797ce0253d1 Mon Sep 17 00:00:00 2001 From: elkaix Date: Mon, 3 Aug 2026 23:22:56 -0400 Subject: [PATCH 4/4] fix: let OAuth page status text wrap on narrow screens --- packages/oauth/src/oauth-pages.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/oauth/src/oauth-pages.ts b/packages/oauth/src/oauth-pages.ts index 1956779e..84a051c5 100644 --- a/packages/oauth/src/oauth-pages.ts +++ b/packages/oauth/src/oauth-pages.ts @@ -93,7 +93,7 @@ const PAGE_STYLES = 'padding:10px 14px;border-radius:12px;' + 'background:#f7f7f8;border:1px solid #e5e7eb;' + "font-family:'JetBrains Mono',ui-monospace,'SF Mono',monospace;" + - 'font-size:13px;color:#55565c;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;' + + 'font-size:13px;color:#55565c;' + '}' + '.status .ok{color:#15803d}' + '.status .err{color:#dc2626}' +