From 1a2ce0e6ff2b20ab2fd44475ed1db3c0769c5f53 Mon Sep 17 00:00:00 2001 From: tak Date: Wed, 4 Nov 2026 19:43:34 +0900 Subject: [PATCH] feat(install): link the phone app on the last screen Print the App Store and Google Play links with ct/utm_source=cli-install after the closing line, in the same shape as the landing's campaign URLs. --- src/install-flow.test.ts | 9 +++++++++ src/installer.ts | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/src/install-flow.test.ts b/src/install-flow.test.ts index 8615985..3d2364a 100644 --- a/src/install-flow.test.ts +++ b/src/install-flow.test.ts @@ -97,6 +97,15 @@ describe('hwip install', () => { expect(logs.join('\n')).toContain('Done! Restart your agents.'); }); + it('points a new user at the phone app with campaign links, after the closing line', async () => { + expect(await install({ key: 'ak_1' })).toBe(0); + const out = logs.join('\n'); + expect(out).toContain(' Get the app: https://apps.apple.com/app/id6818053606?ct=cli-install\n'); + expect(out).toContain(' Android: https://play.google.com/store/apps/details?id=app.hwip&referrer=utm_source%3Dcli-install\n'); + expect(out.indexOf('Done! Restart your agents.')).toBeLessThan(out.indexOf('Get the app:')); + expect(out.indexOf('Get the app:')).toBeLessThan(out.indexOf('Notifications now fire')); + }); + // C107: a config.json that is not JSON used to load as {}; the install then replaced it with the new key alone. it('leaves a config.json it cannot read as JSON alone, and says so before asking anything', async () => { const { mkdirSync, writeFileSync } = await import('node:fs'); diff --git a/src/installer.ts b/src/installer.ts index 48f488d..75b3b96 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -167,6 +167,10 @@ export const closingLine = (failed: number): string => { return `Done, but ${steps} you: see the lines marked "-" above. Then restart your agents.`; }; +/** Where the install's last screen sends a new user for the phone app. `ct`/`utm_source` name this surface. */ +export const APP_STORE_URL = 'https://apps.apple.com/app/id6818053606?ct=cli-install'; +export const PLAY_STORE_URL = 'https://play.google.com/store/apps/details?id=app.hwip&referrer=utm_source%3Dcli-install'; + /** An edit that would lose a comment of the user's names the file, like every other refusal. */ const namingFile = (filePath: string, edit: () => T): T => { try { @@ -881,6 +885,9 @@ export const handleInstall = async (args: Record): Pro console.log(`\n ${closingLine(failedSteps)}\n`); + console.log(` Get the app: ${APP_STORE_URL}`); + console.log(` Android: ${PLAY_STORE_URL}\n`); + // The single most-reported surprise: pushes are not scoped to `hwip cc`. // Hooks fire for every session of every configured agent, so say it here — // at the moment the hooks get installed — together with the volume dials.