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 +45 -0
- package/bin/vibestreak.mjs +17 -3
- package/package.json +2 -2
- package/src/version.mjs +21 -0
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.
|
package/bin/vibestreak.mjs
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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" }
|
package/src/version.mjs
ADDED
|
@@ -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`
|