@sickr/replay 0.6.0 → 0.7.0

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.
@@ -0,0 +1,76 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, chmodSync, unlinkSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ export const AGENT_API_URL = (process.env.SICKR_API_URL ?? 'https://support-backend.sickr.ai').replace(/\/+$/, '');
5
+ function agentCredsPath() {
6
+ return join(homedir(), '.sickr', 'agent.json');
7
+ }
8
+ export function readAgentCredentials() {
9
+ try {
10
+ return JSON.parse(readFileSync(agentCredsPath(), 'utf8'));
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ export function writeAgentCredentials(c) {
17
+ const dir = join(homedir(), '.sickr');
18
+ mkdirSync(dir, { recursive: true });
19
+ const p = agentCredsPath();
20
+ writeFileSync(p, JSON.stringify(c, null, 2) + '\n');
21
+ try {
22
+ chmodSync(p, 0o600);
23
+ }
24
+ catch { /* Windows: skip */ }
25
+ }
26
+ export function clearAgentCredentials() {
27
+ try {
28
+ unlinkSync(agentCredsPath());
29
+ }
30
+ catch { /* none */ }
31
+ }
32
+ export async function startAgentConnect(apiUrl, agentId) {
33
+ const r = await fetch(`${apiUrl.replace(/\/+$/, '')}/agent-connect/start`, {
34
+ method: 'POST',
35
+ headers: { 'Content-Type': 'application/json' },
36
+ body: JSON.stringify({ agent_id: agentId }),
37
+ });
38
+ if (!r.ok)
39
+ throw new Error(`agent-connect/start failed: ${r.status}`);
40
+ return await r.json();
41
+ }
42
+ export async function pollAgentConnect(apiUrl, deviceCode) {
43
+ const r = await fetch(`${apiUrl.replace(/\/+$/, '')}/agent-connect/poll`, {
44
+ method: 'POST',
45
+ headers: { 'Content-Type': 'application/json' },
46
+ body: JSON.stringify({ device_code: deviceCode }),
47
+ });
48
+ if (!r.ok)
49
+ throw new Error(`agent-connect/poll failed: ${r.status}`);
50
+ return await r.json();
51
+ }
52
+ export async function fetchAgentStatus(c) {
53
+ const r = await fetch(`${c.api_url.replace(/\/+$/, '')}/agent/self/status`, {
54
+ headers: { Authorization: `Bearer ${c.api_key}` },
55
+ });
56
+ if (!r.ok)
57
+ throw new Error(`agent/self/status failed: ${r.status}`);
58
+ return await r.json();
59
+ }
60
+ export async function rotateAgentKey(c) {
61
+ const r = await fetch(`${c.api_url.replace(/\/+$/, '')}/agent/self/rotate`, {
62
+ method: 'POST',
63
+ headers: { Authorization: `Bearer ${c.api_key}` },
64
+ });
65
+ if (!r.ok)
66
+ throw new Error(`agent/self/rotate failed: ${r.status}`);
67
+ return await r.json();
68
+ }
69
+ export async function disconnectAgent(c) {
70
+ const r = await fetch(`${c.api_url.replace(/\/+$/, '')}/agent/self/disconnect`, {
71
+ method: 'POST',
72
+ headers: { Authorization: `Bearer ${c.api_key}` },
73
+ });
74
+ if (!r.ok)
75
+ throw new Error(`agent/self/disconnect failed: ${r.status}`);
76
+ }
package/dist/cli.js CHANGED
@@ -9,69 +9,77 @@ import { mergeHooks, removeHooks } from './hookConfig.js';
9
9
  import { renderRunHtml, renderCombinedHtml } from './render.js';
10
10
  import { buildSharePayload, buildCombinedPayload, publish, PublishError } from './share.js';
11
11
  import { readCredentials, writeCredentials, clearCredentials, startDevice, pollDevice, sleep } from './auth.js';
12
+ import { AGENT_API_URL, clearAgentCredentials, disconnectAgent, fetchAgentStatus, pollAgentConnect, readAgentCredentials, rotateAgentKey, startAgentConnect, writeAgentCredentials, } from './agentAuth.js';
12
13
  const REPLAY_ENDPOINT = process.env.SICKR_REPLAY_ENDPOINT ?? 'https://sickr.ai/api/replay';
13
- const COMMANDS = ['init', 'record', 'open', 'list', 'share', 'stop', 'clear', 'login', 'logout', 'whoami', 'help'];
14
+ const COMMANDS = ['init', 'record', 'open', 'list', 'share', 'stop', 'clear', 'login', 'logout', 'whoami', 'agent', 'help'];
14
15
  export function parseCommand(argv) {
15
16
  const c = argv[0];
16
17
  return c && COMMANDS.includes(c) ? c : null;
17
18
  }
18
- export const HELP = `SICKR Replay — audit & replay what your AI coding agent did.
19
-
20
- Records your Claude Code or Codex session (prompts, edits, commands) to a local,
21
- redacted timeline you can replay — and optionally share as a public link.
22
-
23
- Why: a durable record of every agent action — a dashcam for your coding agent.
24
- If your agent (Claude or Codex) loses context or can't reload a past chat, the
25
- replay log helps you — and it — recall exactly what was just done.
26
-
27
- Usage: npx @sickr/replay <command> [options]
28
-
29
- Commands:
30
- init <agent> Install recording hooks for an agent (REQUIRED — no default)
31
- and start capturing to ~/.sickr/runs (secrets redacted):
32
- claude Claude Code (.claude/settings.json)
33
- codex Codex (.codex/hooks.json — needs Codex 0.133+)
34
- all both of the above (feeds the combined view)
35
- Flag: --no-name (label prompts "Human", not your login name)
36
- open [run] Render a run to a local HTML timeline and open it. 100% local.
37
- Defaults to the newest run; pass a run id, or --codex/--claude
38
- for the newest run of that agent. Combine across agents with a
39
- window: --today, --since <2h|30m|1d>, or --all (interleaved,
40
- filterable by agent, sortable by prompt/response time).
41
- share [run] Redact and publish ONE run to a public sickr.ai/r/<id> link
42
- (shows a preview and asks first). Links expire after 24h.
43
- --open also open the published link in your browser
44
- --yes skip the confirmation prompt
45
- Or publish a COMBINED multi-agent view with a window:
46
- --today / --since <2h|30m|1d> / --all (+ --claude/--codex).
47
- list List recorded runs, newest first.
48
- stop Stop recording — removes SICKR's hooks from this project.
49
- Your recorded runs are kept; run \`init\` to start again.
50
- clear Delete all local runs in ~/.sickr/runs (asks first).
51
- login Sign in with GitHub (optional — unlocks persistent shares and
52
- Replay Pro cohort eligibility). Zero-account use still works.
53
- logout Forget the local login. Server-side session stays valid until
54
- it expires; revoke from your account page if needed.
55
- whoami Show who you're logged in as.
56
- help Show this help.
57
-
58
- Requires Node 18+. Codex capture needs Codex CLI 0.133+ (run /hooks to trust);
59
- Claude Code: any hooks-capable build.
60
-
61
- ────────────────────────────────────────────────────────────────────
62
- This replays your AI coding agents on ONE machine. SICKR governs your whole team.
63
- Issue tracking + your team + automation + agents — one governed workflow for
64
- audit, accountability, productivity and confidence.
65
-
66
- · Gates & approvals — work holds at plan sign-off, review, merge and
67
- validation checks until each one passes.
68
- · Humans + agents on one board — agents are first-class teammates with
69
- roles, capacity and accountability, not a side channel.
70
- · A full, signed-off audit trail across every actor and every change.
71
- · Runs 24/7 produce as much work as you like; the team handles it.
72
-
73
- Free tier available · bring your own Claude or Codex subscription.
74
- https://sickr.ai
19
+ export const HELP = `SICKR Replay — audit & replay what your AI coding agent did.
20
+
21
+ Records your Claude Code or Codex session (prompts, edits, commands) to a local,
22
+ redacted timeline you can replay — and optionally share as a public link.
23
+
24
+ Why: a durable record of every agent action — a dashcam for your coding agent.
25
+ If your agent (Claude or Codex) loses context or can't reload a past chat, the
26
+ replay log helps you — and it — recall exactly what was just done.
27
+
28
+ Usage: npx @sickr/replay <command> [options]
29
+
30
+ Commands:
31
+ init <agent> Install recording hooks for an agent (REQUIRED — no default)
32
+ and start capturing to ~/.sickr/runs (secrets redacted):
33
+ claude Claude Code (.claude/settings.json)
34
+ codex Codex (.codex/hooks.json — needs Codex 0.133+)
35
+ all both of the above (feeds the combined view)
36
+ Flag: --no-name (label prompts "Human", not your login name)
37
+ open [run] Render a run to a local HTML timeline and open it. 100% local.
38
+ Defaults to the newest run; pass a run id, or --codex/--claude
39
+ for the newest run of that agent. Combine across agents with a
40
+ window: --today, --since <2h|30m|1d>, or --all (interleaved,
41
+ filterable by agent, sortable by prompt/response time).
42
+ share [run] Redact and publish ONE run to a public sickr.ai/r/<id> link
43
+ (shows a preview and asks first). Links expire after 24h.
44
+ --open also open the published link in your browser
45
+ --yes skip the confirmation prompt
46
+ Or publish a COMBINED multi-agent view with a window:
47
+ --today / --since <2h|30m|1d> / --all (+ --claude/--codex).
48
+ list List recorded runs, newest first.
49
+ stop Stop recording — removes SICKR's hooks from this project.
50
+ Your recorded runs are kept; run \`init\` to start again.
51
+ clear Delete all local runs in ~/.sickr/runs (asks first).
52
+ login Sign in with GitHub (optional — unlocks persistent shares and
53
+ Replay Pro cohort eligibility). Zero-account use still works.
54
+ logout Forget the local login. Server-side session stays valid until
55
+ it expires; revoke from your account page if needed.
56
+ whoami Show who you're logged in as.
57
+ agent connect --agent-id <id>
58
+ Connect this machine to a configured SICKR agent using GitHub
59
+ browser approval. Stores the agent key in ~/.sickr/agent.json.
60
+ agent status Show the connected agent, org and team.
61
+ agent rotate Rotate this machine's agent key.
62
+ agent disconnect
63
+ Revoke this machine's agent key and remove it locally.
64
+ help Show this help.
65
+
66
+ Requires Node 18+. Codex capture needs Codex CLI 0.133+ (run /hooks to trust);
67
+ Claude Code: any hooks-capable build.
68
+
69
+ ────────────────────────────────────────────────────────────────────
70
+ This replays your AI coding agents on ONE machine. SICKR governs your whole team.
71
+ Issue tracking + your team + automation + agents one governed workflow for
72
+ audit, accountability, productivity and confidence.
73
+
74
+ · Gates & approvals work holds at plan sign-off, review, merge and
75
+ validation checks until each one passes.
76
+ · Humans + agents on one board — agents are first-class teammates with
77
+ roles, capacity and accountability, not a side channel.
78
+ · A full, signed-off audit trail across every actor and every change.
79
+ · Runs 24/7 — produce as much work as you like; the team handles it.
80
+
81
+ Free tier available · bring your own Claude or Codex subscription.
82
+ → https://sickr.ai
75
83
  `;
76
84
  export function currentRunId(cc) {
77
85
  return String(cc.session_id ?? 'session');
@@ -350,17 +358,24 @@ async function confirmPublish(yes, what) {
350
358
  }
351
359
  return true;
352
360
  }
353
- /** Publish with a single friendly retry on 429 (the WAF allows ~1/10s). Returns the URL. */
361
+ /** Publish with a single friendly retry on 429 (the WAF allows ~1/10s).
362
+ * If the user is logged in, attach their token so the share lands in the
363
+ * 7-day daily slot (free_authed) instead of the 24h anon dedup pool. */
354
364
  async function publishWithRetry(payload) {
365
+ const token = readCredentials()?.token ?? null;
366
+ const post = async () => {
367
+ const r = await publish(payload, REPLAY_ENDPOINT, { token });
368
+ return { url: r.url, ttl_days: r.ttl_days ?? 1 };
369
+ };
355
370
  try {
356
- return (await publish(payload, REPLAY_ENDPOINT)).url;
371
+ return await post();
357
372
  }
358
373
  catch (err) {
359
374
  if (err instanceof PublishError && err.status === 429) {
360
375
  process.stdout.write('sickr: rate-limited — you can publish about once every 10s. Waiting to retry once...\n');
361
376
  await new Promise((r) => setTimeout(r, 11_000));
362
377
  try {
363
- return (await publish(payload, REPLAY_ENDPOINT)).url;
378
+ return await post();
364
379
  }
365
380
  catch (retryErr) {
366
381
  if (retryErr instanceof PublishError && retryErr.status === 429) {
@@ -373,6 +388,13 @@ async function publishWithRetry(payload) {
373
388
  throw err;
374
389
  }
375
390
  }
391
+ function expiryLine(ttl_days) {
392
+ if (ttl_days >= 30)
393
+ return `sickr: this link is live for ${ttl_days} days (Replay Pro retention).\n`;
394
+ if (ttl_days >= 2)
395
+ return `sickr: this link is live for ${ttl_days} days — re-share before it expires to extend.\n`;
396
+ return `sickr: this link expires in 24h. Run \`replay login\` to extend to 7 days.\n`;
397
+ }
376
398
  async function handleShare(runId, yes, open) {
377
399
  const id = runId ?? latestRunId();
378
400
  if (!id) {
@@ -385,8 +407,8 @@ async function handleShare(runId, yes, open) {
385
407
  `sickr: tip — run \`npx @sickr/replay open ${id}\` to review the full timeline locally before sharing.\n`);
386
408
  if (!(await confirmPublish(yes, 'this run')))
387
409
  return;
388
- const url = await publishWithRetry(payload);
389
- process.stdout.write(`sickr: published → ${url}\nsickr: this link expires in 24h.\nsickr: Replay Pro (live + remote) — early access, rolling out in cohorts → https://sickr.ai/#waitlist\n`);
410
+ const { url, ttl_days } = await publishWithRetry(payload);
411
+ process.stdout.write(`sickr: published → ${url}\n${expiryLine(ttl_days)}sickr: Replay Pro (live + remote) — early access, rolling out in cohorts → https://sickr.ai/#waitlist\n`);
390
412
  if (open)
391
413
  openInBrowser(url);
392
414
  }
@@ -406,8 +428,8 @@ async function handleShareCombined(sel, yes, open) {
406
428
  `sickr: tip — run the matching \`open\` window to review locally before sharing.\n`);
407
429
  if (!(await confirmPublish(yes, 'this combined replay')))
408
430
  return;
409
- const url = await publishWithRetry(payload);
410
- process.stdout.write(`sickr: published → ${url}\nsickr: this link expires in 24h.\nsickr: Replay Pro (live + remote) — early access, rolling out in cohorts → https://sickr.ai/#waitlist\n`);
431
+ const { url, ttl_days } = await publishWithRetry(payload);
432
+ process.stdout.write(`sickr: published → ${url}\n${expiryLine(ttl_days)}sickr: Replay Pro (live + remote) — early access, rolling out in cohorts → https://sickr.ai/#waitlist\n`);
411
433
  if (open)
412
434
  openInBrowser(url);
413
435
  }
@@ -498,6 +520,152 @@ export function handleWhoami() {
498
520
  }
499
521
  process.stdout.write(`sickr: ${c.login}${c.name ? ` (${c.name})` : ''} · since ${c.login_at}\n`);
500
522
  }
523
+ function valueAt(rest, flag) {
524
+ const i = rest.indexOf(flag);
525
+ return i >= 0 && rest[i + 1] && !rest[i + 1].startsWith('-') ? rest[i + 1] : null;
526
+ }
527
+ function isRecord(value) {
528
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
529
+ }
530
+ function agentContextLabel(context) {
531
+ const root = isRecord(context) ? context : {};
532
+ const agent = isRecord(root.agent) ? root.agent : {};
533
+ const org = isRecord(root.org) ? root.org : {};
534
+ const team = isRecord(root.team) ? root.team : {};
535
+ return {
536
+ agentId: typeof agent.agent_id === 'string' ? agent.agent_id : 'unknown',
537
+ provider: typeof agent.provider === 'string' ? agent.provider : 'unknown',
538
+ status: typeof agent.status === 'string' ? agent.status : 'unknown',
539
+ org: typeof org.name === 'string' ? org.name : 'unknown org',
540
+ team: typeof team.name === 'string' ? team.name : 'unknown team',
541
+ };
542
+ }
543
+ export async function handleAgentStatus() {
544
+ const c = readAgentCredentials();
545
+ if (!c) {
546
+ process.stdout.write('sickr: agent is not connected. Run `sickr agent connect --agent-id <id>`.\n');
547
+ return;
548
+ }
549
+ try {
550
+ const label = agentContextLabel(await fetchAgentStatus(c));
551
+ process.stdout.write(`sickr: connected as ${label.agentId} (${label.provider}, ${label.status})\n` +
552
+ `org: ${label.org} team: ${label.team}\n` +
553
+ `api: ${c.api_url}\n`);
554
+ }
555
+ catch (e) {
556
+ process.stderr.write(`sickr: agent status failed (${e.message}).\n`);
557
+ process.exit(1);
558
+ }
559
+ }
560
+ export async function handleAgentRotate() {
561
+ const c = readAgentCredentials();
562
+ if (!c) {
563
+ process.stderr.write('sickr: agent is not connected. Run `sickr agent connect --agent-id <id>`.\n');
564
+ process.exit(1);
565
+ return;
566
+ }
567
+ try {
568
+ const rotated = await rotateAgentKey(c);
569
+ writeAgentCredentials({ ...c, api_key: rotated.api_key, key_id: rotated.key?.id ?? c.key_id });
570
+ process.stdout.write(`sickr: rotated key for ${c.agent_id}.\n`);
571
+ }
572
+ catch (e) {
573
+ process.stderr.write(`sickr: agent rotate failed (${e.message}).\n`);
574
+ process.exit(1);
575
+ }
576
+ }
577
+ export async function handleAgentDisconnect() {
578
+ const c = readAgentCredentials();
579
+ if (!c) {
580
+ process.stdout.write('sickr: agent is not connected.\n');
581
+ return;
582
+ }
583
+ try {
584
+ await disconnectAgent(c);
585
+ clearAgentCredentials();
586
+ process.stdout.write(`sickr: disconnected ${c.agent_id} and removed the local key.\n`);
587
+ }
588
+ catch (e) {
589
+ process.stderr.write(`sickr: agent disconnect failed (${e.message}).\n`);
590
+ process.exit(1);
591
+ }
592
+ }
593
+ function storeApprovedAgent(apiUrl, result) {
594
+ writeAgentCredentials({
595
+ api_url: apiUrl,
596
+ agent_id: result.agent_id,
597
+ api_key: result.api_key,
598
+ key_id: result.key_id ?? null,
599
+ org_id: result.org_id,
600
+ team_id: result.team_id,
601
+ connected_at: new Date().toISOString(),
602
+ });
603
+ }
604
+ export async function handleAgentConnect(rest) {
605
+ const agentId = valueAt(rest, '--agent-id') ?? rest.find((a) => !a.startsWith('-')) ?? null;
606
+ if (!agentId) {
607
+ process.stderr.write('sickr: `agent connect` requires --agent-id <id>.\n');
608
+ process.exit(1);
609
+ return;
610
+ }
611
+ const apiUrl = (valueAt(rest, '--api-url') ?? AGENT_API_URL).replace(/\/+$/, '');
612
+ let started;
613
+ try {
614
+ started = await startAgentConnect(apiUrl, agentId);
615
+ }
616
+ catch (e) {
617
+ process.stderr.write(`sickr: could not start agent connect (${e.message}).\n`);
618
+ process.exit(1);
619
+ return;
620
+ }
621
+ const verifyUrl = started.verification_uri_complete ?? started.verification_uri;
622
+ process.stdout.write(`\nsickr: approve agent ${agentId} in your browser:\n\n` +
623
+ ` ${verifyUrl}\n\n` +
624
+ `code: ${started.user_code}\n` +
625
+ `(opening browser...)\n`);
626
+ openInBrowser(verifyUrl);
627
+ const deadline = Date.now() + started.expires_in * 1000;
628
+ const intervalMs = Math.max(1, started.interval) * 1000;
629
+ while (Date.now() < deadline) {
630
+ await sleep(intervalMs);
631
+ let polled;
632
+ try {
633
+ polled = await pollAgentConnect(apiUrl, started.device_code);
634
+ }
635
+ catch (e) {
636
+ process.stderr.write(`sickr: agent connect poll failed (${e.message}).\n`);
637
+ process.exit(1);
638
+ return;
639
+ }
640
+ if (polled.status === 'pending')
641
+ continue;
642
+ if (polled.status === 'approved') {
643
+ storeApprovedAgent(apiUrl, polled);
644
+ process.stdout.write(`\nsickr: connected ${polled.agent_id}. Run \`sickr agent status\` to verify.\n`);
645
+ return;
646
+ }
647
+ if (polled.status === 'expired') {
648
+ process.stderr.write('sickr: agent connect code expired. Run `sickr agent connect` again.\n');
649
+ process.exit(1);
650
+ return;
651
+ }
652
+ if (polled.status === 'denied') {
653
+ process.stderr.write('sickr: agent connect was denied.\n');
654
+ process.exit(1);
655
+ return;
656
+ }
657
+ if (polled.status === 'consumed') {
658
+ process.stderr.write('sickr: agent connect code was already used. Run `sickr agent connect` again.\n');
659
+ process.exit(1);
660
+ return;
661
+ }
662
+ process.stderr.write(`sickr: agent connect error: ${polled.error ?? 'unknown'}.\n`);
663
+ process.exit(1);
664
+ return;
665
+ }
666
+ process.stderr.write('sickr: agent connect timed out. Run `sickr agent connect` again.\n');
667
+ process.exit(1);
668
+ }
501
669
  async function readStdin() {
502
670
  const chunks = [];
503
671
  for await (const chunk of process.stdin)
@@ -563,6 +731,29 @@ async function main() {
563
731
  case 'whoami':
564
732
  handleWhoami();
565
733
  return;
734
+ case 'agent': {
735
+ const sub = rest[0];
736
+ const agentRest = rest.slice(1);
737
+ if (sub === 'connect') {
738
+ await handleAgentConnect(agentRest);
739
+ return;
740
+ }
741
+ if (sub === 'status') {
742
+ await handleAgentStatus();
743
+ return;
744
+ }
745
+ if (sub === 'disconnect') {
746
+ await handleAgentDisconnect();
747
+ return;
748
+ }
749
+ if (sub === 'rotate') {
750
+ await handleAgentRotate();
751
+ return;
752
+ }
753
+ process.stderr.write('sickr: unknown agent command. Use `sickr agent connect|status|disconnect|rotate`.\n');
754
+ process.exit(1);
755
+ return;
756
+ }
566
757
  case 'share': {
567
758
  const yes = rest.includes('--yes') || rest.includes('-y');
568
759
  const openAfter = rest.includes('--open');
package/dist/share.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { gzipSync } from 'node:zlib';
1
2
  /** Strip the local id; events are already redacted at capture time. */
2
3
  export function buildSharePayload(run) {
3
4
  return { run: { cwd: run.cwd, startedAt: run.startedAt, events: run.events } };
@@ -14,12 +15,15 @@ export class PublishError extends Error {
14
15
  this.name = 'PublishError';
15
16
  }
16
17
  }
17
- export async function publish(payload, endpoint) {
18
- const res = await fetch(endpoint, {
19
- method: 'POST',
20
- headers: { 'Content-Type': 'application/json' },
21
- body: JSON.stringify(payload),
22
- });
18
+ export async function publish(payload, endpoint, opts = {}) {
19
+ // Gzip the JSON body: ~5-10x smaller for typical sessions, comfortably fits
20
+ // the worker's 4 MB cap even for combined multi-day windows.
21
+ const raw = JSON.stringify(payload);
22
+ const gz = gzipSync(raw);
23
+ const headers = { 'Content-Type': 'application/json', 'Content-Encoding': 'gzip' };
24
+ if (opts.token)
25
+ headers.Authorization = `Bearer ${opts.token}`;
26
+ const res = await fetch(endpoint, { method: 'POST', headers, body: gz });
23
27
  if (!res.ok)
24
28
  throw new PublishError(res.status);
25
29
  return (await res.json());
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
- {
2
- "name": "@sickr/replay",
3
- "version": "0.6.0",
4
- "type": "module",
5
- "description": "npx @sickr/replay — local Claude Code audit + one-click share. The free wedge into SICKR.",
6
- "bin": { "replay": "dist/cli.js" },
7
- "files": ["dist"],
8
- "publishConfig": { "access": "public" },
9
- "scripts": {
10
- "build": "tsc",
11
- "test": "vitest run",
12
- "dev": "tsc -w"
13
- },
14
- "engines": { "node": ">=20" },
15
- "license": "UNLICENSED",
16
- "devDependencies": {
17
- "@types/node": "^20.14.0",
18
- "typescript": "^5.6.2",
19
- "vitest": "^2.1.1"
20
- }
21
- }
1
+ {
2
+ "name": "@sickr/replay",
3
+ "version": "0.7.0",
4
+ "type": "module",
5
+ "description": "npx @sickr/replay — local Claude Code audit + one-click share. The free wedge into SICKR.",
6
+ "bin": { "replay": "dist/cli.js", "sickr": "dist/cli.js" },
7
+ "files": ["dist"],
8
+ "publishConfig": { "access": "public" },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "vitest run",
12
+ "dev": "tsc -w"
13
+ },
14
+ "engines": { "node": ">=20" },
15
+ "license": "UNLICENSED",
16
+ "devDependencies": {
17
+ "@types/node": "^20.14.0",
18
+ "typescript": "^5.6.2",
19
+ "vitest": "^2.1.1"
20
+ }
21
+ }