vibestreak 0.3.0 → 0.3.1

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 ADDED
@@ -0,0 +1,45 @@
1
+ # Changelog
2
+
3
+ Any version runs with `npx vibestreak@<version>`; `npx vibestreak@latest autosync on`
4
+ moves the background sync to the newest one.
5
+
6
+ ## 0.3.1 (2026-10-25)
7
+
8
+ - `vibestreak --version` (or `-v`), and the version in `help` and `status`.
9
+ - Says when a newer version is out: after `vibestreak`, `sync` and in `status`.
10
+ - Sends its version with each sync. Settings shows each machine's version, and
11
+ the server can turn away versions too old to count correctly, with the
12
+ command that updates them.
13
+
14
+ ## 0.3.0 (2026-10-25)
15
+
16
+ - First run: plain `npx vibestreak` signs in through the browser (no token to
17
+ copy), syncs, and offers background sync and the evening nudge.
18
+ - `vibestreak setup [--token <t>]`: all of that with the defaults and no
19
+ questions; commit addresses come from git config.
20
+ - `vibestreak login` signs in through the browser; `--token` still works.
21
+ - `vibestreak pause [1h|3d|tomorrow]` and `resume`: the background syncs and
22
+ nudge skip their runs for up to 7 days, and nothing is lost.
23
+
24
+ ## 0.2.0 (2026-10-25)
25
+
26
+ - `vibestreak` shows a dashboard: streak, level, a 12-week heatmap and your rank.
27
+ - `vibestreak me`, `profile <handle>`, `board`, `today` in the terminal.
28
+ - Syncs after the first one send only the last 7 days: about 3 seconds.
29
+
30
+ ## 0.1.2 (2026-10-25)
31
+
32
+ - `vibestreak nudge at <time>`: when the evening streak check runs (17:00 to
33
+ 23:30; 20:30 by default).
34
+
35
+ ## 0.1.1 (2026-10-25)
36
+
37
+ - `vibestreak status`; plain `vibestreak nudge` shows the nudge's status.
38
+ - The evening job runs `nudge run` (older schedules keep working).
39
+
40
+ ## 0.1.0 (2026-10-25)
41
+
42
+ - First release: reads Claude Code, Codex, Cursor, Copilot (CLI and VS Code),
43
+ Gemini CLI, Antigravity and OpenCode history and git; syncs daily totals.
44
+ - Background sync on macOS (launchd), Linux (systemd or cron) and Windows
45
+ (Task Scheduler), with the evening streak nudge.
@@ -10,12 +10,13 @@ import { CONFIG_PATH, loadConfig, saveConfig } from '../src/config.mjs'
10
10
  import { configuredEmails, DEFAULT_ROOTS, globalEmail, scanEmails } from '../src/git.mjs'
11
11
  import { deviceLogin } from '../src/login.mjs'
12
12
  import { parsePause, pausedUntil, untilText } from '../src/pause.mjs'
13
+ import { updateCommand, updateNotice, VERSION } from '../src/version.mjs'
13
14
  import { boardView, dashboardView, profileView, todayView } from '../src/views.mjs'
14
15
 
15
16
  const BRAND = process.env.VIBESTREAK_BRAND ?? 'vibestreak'
16
17
  const DEFAULT_SERVER = process.env.VIBESTREAK_SERVER ?? 'https://vibestreak.kingsbridge-consultancy.com'
17
18
 
18
- const HELP = `${BRAND} — share your AI coding heatmap: what you shipped, and how steadily.
19
+ const HELP = `${BRAND} ${VERSION} — share your AI coding heatmap: what you shipped, and how steadily.
19
20
 
20
21
  ${BRAND} your streak, heatmap and rank at a glance
21
22
  (the first time: sets this machine up)
@@ -44,6 +45,7 @@ const HELP = `${BRAND} — share your AI coding heatmap: what you shipped, and h
44
45
  days; default until tomorrow 06:00)
45
46
  ${BRAND} resume end a pause now
46
47
  ${BRAND} whoami the profile this machine syncs to
48
+ ${BRAND} --version this CLI's version
47
49
 
48
50
  Reads: Claude Code, Codex, Cursor, Copilot (CLI and VS Code), Gemini CLI,
49
51
  Antigravity and OpenCode history, and git.
@@ -59,7 +61,7 @@ async function api(config, path, init = {}) {
59
61
  if (!config.server || !config.token) throw new Error(`Not linked yet: run \`${BRAND} login\`.`)
60
62
  const response = await fetch(new URL(path, config.server), {
61
63
  ...init,
62
- headers: { authorization: `Bearer ${config.token}`, 'content-type': 'application/json', ...init.headers },
64
+ headers: { authorization: `Bearer ${config.token}`, 'content-type': 'application/json', 'x-vibestreak-cli': VERSION, 'user-agent': `${BRAND}/${VERSION}`, ...init.headers },
63
65
  })
64
66
  const body = await response.json().catch(() => ({}))
65
67
  if (!response.ok) throw new Error(body.error ?? `${response.status} ${response.statusText}`)
@@ -83,6 +85,7 @@ async function main() {
83
85
  // Set by the background jobs, so a pause skips them and nothing else.
84
86
  scheduled: { type: 'boolean' },
85
87
  yes: { type: 'boolean', short: 'y' },
88
+ version: { type: 'boolean', short: 'v' },
86
89
  },
87
90
  })
88
91
  const [command = 'dashboard', ...rest] = positionals
@@ -90,6 +93,7 @@ async function main() {
90
93
  const log = (...args) => values.quiet || console.log(...args)
91
94
 
92
95
  if (command === 'help' || values.help) return console.log(HELP)
96
+ if (command === 'version' || values.version) return console.log(VERSION)
93
97
  const columns = process.stdout.columns || 100
94
98
  const server = config.server ?? DEFAULT_SERVER
95
99
 
@@ -101,7 +105,8 @@ async function main() {
101
105
  return setup(config, values, { ask: !values.yes })
102
106
  }
103
107
  const me = await api(config, '/api/v1/me')
104
- return dashboardView({ server, token: config.token, handle: me.handle, columns })
108
+ await dashboardView({ server, token: config.token, handle: me.handle, columns })
109
+ return notice(config, me.cli)
105
110
  }
106
111
  if (command === 'me') {
107
112
  const me = await api(config, '/api/v1/me')
@@ -323,9 +328,16 @@ async function sync(config, values, log, isSync) {
323
328
  // Always one line, quiet or not: the background sync's log is how a person sees it ran.
324
329
  const streak = result.streak ? ` · streak ${result.streak.current}d${result.streak.activeToday ? '' : ' (not yet today)'}` : ''
325
330
  console.log(`${config.lastSync} uploaded ${result.days} days, ${result.commits} commits from ${s.repos} repos${streak} → ${result.profileUrl}`)
331
+ if (!values.quiet) notice(config, result.cli)
326
332
  return result
327
333
  }
328
334
 
335
+ // Says so when a newer CLI is out (the server checks npm).
336
+ function notice(config, cli) {
337
+ const line = updateNotice(cli, { brand: BRAND, autosync: config.autosync })
338
+ if (line) console.log(`\n${line}`)
339
+ }
340
+
329
341
  const ago = iso => {
330
342
  if (!iso) return 'never'
331
343
  const minutes = Math.round((Date.now() - Date.parse(iso)) / 60000)
@@ -382,6 +394,8 @@ async function status(config) {
382
394
  row('Nudge', nudgeOn ? `on · checks at ${clock(nudgeTimeOf(config))} · last nudged ${config.lastNudge ?? 'never'}` : config.nudge === false ? 'off' : 'off (needs autosync)')
383
395
  const github = me.error ? 0 : (me.emails?.length ?? 0)
384
396
  row('Addresses', `${config.emails.join(', ') || 'none'}${github ? ` · +${github} from GitHub` : ''}`)
397
+ const update = me.error ? undefined : updateNotice(me.cli, { brand: BRAND, autosync: config.autosync })
398
+ row('Version', update ? `${VERSION} · ${me.cli.latest} is out: ${updateCommand(BRAND, config.autosync)}` : `${VERSION}${me.cli?.latest ? ' (latest)' : ''}`)
385
399
  row('Config', CONFIG_PATH)
386
400
  }
387
401
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibestreak",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Your AI coding streak: a heatmap of the days you coded with Claude Code, Codex, Cursor, Copilot, Gemini and OpenCode, and the commits you shipped with them. Sends daily totals only.",
5
5
  "keywords": ["claude-code", "codex", "cursor", "copilot", "gemini", "opencode", "ai-coding", "heatmap", "streak", "leaderboard", "cli"],
6
6
  "homepage": "https://vibestreak.kingsbridge-consultancy.com/cli",
@@ -8,7 +8,7 @@
8
8
  "author": "Kingsbridge Consultancy LLC <support@kingsbridge-consultancy.com>",
9
9
  "type": "module",
10
10
  "bin": { "vibestreak": "bin/vibestreak.mjs" },
11
- "files": ["bin", "src", "README.md", "LICENSE"],
11
+ "files": ["bin", "src", "README.md", "CHANGELOG.md", "LICENSE"],
12
12
  "engines": { "node": ">=22.13" },
13
13
  "os": ["darwin", "linux", "win32"],
14
14
  "scripts": { "test": "node --test test/*.test.mjs" }
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from 'node:fs'
2
+
3
+ export const VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version
4
+
5
+ // Release versions only (x.y.z): negative when a is older than b.
6
+ export function compareVersions(a, b) {
7
+ const pa = String(a).split('.').map(Number)
8
+ const pb = String(b).split('.').map(Number)
9
+ for (let i = 0; i < 3; i++) if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0)
10
+ return 0
11
+ }
12
+
13
+ // One line when the server says a newer CLI is out; nothing otherwise. With
14
+ // autosync on, the update command also moves the background jobs to it.
15
+ export function updateNotice(cli, { brand = 'vibestreak', autosync = false, current = VERSION } = {}) {
16
+ if (!cli?.latest || compareVersions(current, cli.latest) >= 0) return undefined
17
+ return `${brand} ${cli.latest} is out (this is ${current}): ${updateCommand(brand, autosync)}.`
18
+ }
19
+
20
+ export const updateCommand = (brand, autosync) =>
21
+ autosync ? `\`npx ${brand}@latest autosync on\` updates the background sync to it` : `\`npx ${brand}@latest\` runs it`