@sickr/cli 0.9.6 → 0.9.7
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/dist/cli.js +87 -98
- package/dist/run.js +23 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -51,113 +51,72 @@ export function buildWorkflowInvocation(rest, command = 'uvx') {
|
|
|
51
51
|
}
|
|
52
52
|
return { command, args: rest };
|
|
53
53
|
}
|
|
54
|
-
export const HELP = `
|
|
54
|
+
export const HELP = `sickr — record, replay, and remote-control your AI coding agents.
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
redacted timeline you can replay — and optionally share as a public link.
|
|
56
|
+
Usage: npx @sickr/cli <command> [options]
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
REPLAY (free) — local recording of every prompt, edit and command.
|
|
59
|
+
replay Install Claude + Codex recording hooks. Use the agents
|
|
60
|
+
as normal — a redacted timeline is captured to
|
|
61
|
+
~/.sickr/runs.
|
|
62
|
+
replay open [id] Render the newest run (or a specific id) as a local
|
|
63
|
+
HTML timeline. Combine across agents with --today,
|
|
64
|
+
--since <2h|30m|1d>, or --all (+ --claude / --codex).
|
|
65
|
+
replay share [id] Publish a redacted run to sickr.ai/r/<id> (asks first).
|
|
66
|
+
Add --yes to skip the prompt; --open to open after.
|
|
67
|
+
Or combine a window: --today / --since <dur> / --all.
|
|
68
|
+
Anon links live 24h; signed-in links live 7 days.
|
|
69
|
+
replay list List recorded runs, newest first (+ --claude/--codex).
|
|
70
|
+
replay stop Remove sickr hooks from this project (keeps runs).
|
|
71
|
+
replay clear Delete all local runs in ~/.sickr/runs (asks first).
|
|
62
72
|
|
|
63
|
-
|
|
73
|
+
LIVE LOOK (Replay Pro) — passive streaming to a watching browser.
|
|
74
|
+
live Stream the current session to sickr.ai/r/<your-link> in
|
|
75
|
+
real time. Browser steer messages land in
|
|
76
|
+
~/.sickr/inbox/<urlid>.md and your terminal prints them.
|
|
77
|
+
Requires \`sickr login\` + Replay Pro entitlement.
|
|
78
|
+
live status Show pid + connection state.
|
|
79
|
+
live stop Stop the sidecar.
|
|
64
80
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
REMOTE CONTROL (Replay Pro) — browser keystrokes drive the agent.
|
|
82
|
+
run <agent> Wrap an agent in a PTY sickr owns; browser steer
|
|
83
|
+
messages are written directly into the agent's stdin.
|
|
84
|
+
Real remote control, not pasteboard.
|
|
85
|
+
sickr run claude Claude Code under sickr
|
|
86
|
+
sickr run codex Codex under sickr
|
|
87
|
+
sickr run <bin> arbitrary binary
|
|
88
|
+
Flags:
|
|
89
|
+
--mode auto (default) inject the agent's
|
|
90
|
+
"no prompt / full perms" flag so it
|
|
91
|
+
doesn't stall on tool confirms the
|
|
92
|
+
operator can't see.
|
|
93
|
+
--mode interactive pass agent flags through
|
|
94
|
+
verbatim — agent prompts for tool use.
|
|
95
|
+
Requires \`sickr login\` + Replay Pro entitlement.
|
|
96
|
+
Codex 0.133+ needs \`/hooks\` typed once to trust the
|
|
97
|
+
recorder; Claude is silent (hooks auto-installed).
|
|
80
98
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
all both of the above (feeds the combined view)
|
|
87
|
-
Flag: --no-name (label prompts "Human", not your login name)
|
|
88
|
-
open [run] Render a run to a local HTML timeline and open it. 100% local.
|
|
89
|
-
Defaults to the newest run; pass a run id, or --codex/--claude
|
|
90
|
-
for the newest run of that agent. Combine across agents with a
|
|
91
|
-
window: --today, --since <2h|30m|1d>, or --all (interleaved,
|
|
92
|
-
filterable by agent, sortable by prompt/response time).
|
|
93
|
-
share [run] Redact and publish ONE run to a public sickr.ai/r/<id> link
|
|
94
|
-
(shows a preview and asks first). Links expire after 24h.
|
|
95
|
-
--open also open the published link in your browser
|
|
96
|
-
--yes skip the confirmation prompt
|
|
97
|
-
Or publish a COMBINED multi-agent view with a window:
|
|
98
|
-
--today / --since <2h|30m|1d> / --all (+ --claude/--codex).
|
|
99
|
-
list List recorded runs, newest first.
|
|
100
|
-
stop Stop recording — removes SICKR's hooks from this project.
|
|
101
|
-
Your recorded runs are kept; run \`init\` to start again.
|
|
102
|
-
clear Delete all local runs in ~/.sickr/runs (asks first).
|
|
103
|
-
login Sign in with GitHub (optional — unlocks persistent shares and
|
|
104
|
-
Replay Pro cohort eligibility). Zero-account use still works.
|
|
105
|
-
logout Forget the local login. Server-side session stays valid until
|
|
106
|
-
it expires; revoke from your account page if needed.
|
|
107
|
-
whoami Show who you're logged in as.
|
|
108
|
-
live Replay Pro: stream the current session to sickr.ai/r/<your-link>
|
|
109
|
-
in real time. Requires \`login\` and Replay Pro entitlement.
|
|
110
|
-
replay live start the sidecar (foreground; ^C exits)
|
|
111
|
-
replay live status show pid + connection state
|
|
112
|
-
replay live stop stop the sidecar
|
|
113
|
-
While running, steer messages from the watching browser are
|
|
114
|
-
saved to ~/.sickr/inbox/<urlid>.md and printed in your terminal.
|
|
115
|
-
run <agent> Replay Pro (Phase 2): wrap an agent in a PTY this CLI owns.
|
|
116
|
-
Browser steer messages land directly in the agent's stdin —
|
|
117
|
-
real remote control, no copy-paste from inbox.
|
|
118
|
-
sickr run claude spawn Claude Code under sickr (auto-perms)
|
|
119
|
-
sickr run codex same for Codex (auto-perms)
|
|
120
|
-
sickr run <bin> arbitrary command
|
|
121
|
-
Modes:
|
|
122
|
-
--mode auto (default) inject the agent's "no prompt /
|
|
123
|
-
full permissions" flag so it
|
|
124
|
-
doesn't stall on tool confirms
|
|
125
|
-
the operator can't see (claude:
|
|
126
|
-
--dangerously-skip-permissions;
|
|
127
|
-
codex: --dangerously-bypass-
|
|
128
|
-
approvals-and-sandbox)
|
|
129
|
-
--mode interactive pass agent flags through verbatim;
|
|
130
|
-
agent will prompt for tool use
|
|
131
|
-
\`node-pty\` ships as an optional dep + auto-installs with the
|
|
132
|
-
CLI on supported platforms (mac, Linux, Windows 10+).
|
|
133
|
-
Browsers can override per-message: {mode:'queue'|'steer',
|
|
134
|
-
submit:true|false}; default is auto-steer with submit.
|
|
135
|
-
agent connect --agent-id <id>
|
|
136
|
-
Connect this machine to a configured SICKR agent using GitHub
|
|
137
|
-
browser approval. Stores the agent key in ~/.sickr/agent.json.
|
|
138
|
-
agent status Show the connected agent, org and team.
|
|
139
|
-
agent rotate Rotate this machine's agent key.
|
|
140
|
-
agent disconnect
|
|
141
|
-
Revoke this machine's agent key and remove it locally.
|
|
142
|
-
help Show this help.
|
|
99
|
+
ACCOUNT
|
|
100
|
+
login Sign in with GitHub. Unlocks persistent shares
|
|
101
|
+
(24h → 7d) and Replay Pro entitlement.
|
|
102
|
+
logout Forget the local login.
|
|
103
|
+
whoami Show who you're signed in as.
|
|
143
104
|
|
|
144
|
-
|
|
145
|
-
|
|
105
|
+
WORKFLOW (sickr-managed agent + ticketing)
|
|
106
|
+
workflow connect --agent-id <id> Approve this machine for a configured
|
|
107
|
+
workflow agent (GitHub browser flow).
|
|
108
|
+
workflow start --agent-id <id> Start the orchestrator for an agent.
|
|
109
|
+
workflow status Show running daemon status.
|
|
110
|
+
workflow rotate | disconnect Rotate or revoke this machine's key.
|
|
146
111
|
|
|
147
|
-
|
|
148
|
-
This replays your AI coding agents on ONE machine. SICKR governs your whole team.
|
|
149
|
-
Issue tracking + your team + automation + agents — one governed workflow for
|
|
150
|
-
audit, accountability, productivity and confidence.
|
|
112
|
+
help Show this help.
|
|
151
113
|
|
|
152
|
-
|
|
153
|
-
validation checks until each one passes.
|
|
154
|
-
· Humans + agents on one board — agents are first-class teammates with
|
|
155
|
-
roles, capacity and accountability, not a side channel.
|
|
156
|
-
· A full, signed-off audit trail across every actor and every change.
|
|
157
|
-
· Runs 24/7 — produce as much work as you like; the team handles it.
|
|
114
|
+
Requires Node 20+. Codex capture needs Codex CLI 0.133+.
|
|
158
115
|
|
|
159
|
-
|
|
160
|
-
|
|
116
|
+
────────────────────────────────────────────────────────────────────
|
|
117
|
+
sickr also governs your whole team — issue tracking + humans + agents on one
|
|
118
|
+
board, gates & approvals, signed audit trail, runs 24/7. Free tier available;
|
|
119
|
+
bring your own Claude or Codex subscription. → https://sickr.ai
|
|
161
120
|
`;
|
|
162
121
|
export function currentRunId(cc) {
|
|
163
122
|
return String(cc.session_id ?? 'session');
|
|
@@ -860,6 +819,24 @@ async function fetchReplayProEntitlement() {
|
|
|
860
819
|
return false;
|
|
861
820
|
}
|
|
862
821
|
}
|
|
822
|
+
/** Gate a Pro-only command. Returns true if the user is allowed; otherwise
|
|
823
|
+
* prints a friendly explanation and returns false. Distinguishes the three
|
|
824
|
+
* failure modes (not-logged-in / logged-in-no-pro / network-fail) so the
|
|
825
|
+
* operator knows exactly what to do next. */
|
|
826
|
+
async function requireReplayPro(commandLabel) {
|
|
827
|
+
const creds = readCredentials();
|
|
828
|
+
if (!creds) {
|
|
829
|
+
process.stderr.write(`sickr: \`${commandLabel}\` is a Replay Pro feature.\n` +
|
|
830
|
+
` run \`sickr login\` first — your Pro entitlement is attached to your GitHub account.\n`);
|
|
831
|
+
return false;
|
|
832
|
+
}
|
|
833
|
+
if (await fetchReplayProEntitlement())
|
|
834
|
+
return true;
|
|
835
|
+
process.stderr.write(`sickr: \`${commandLabel}\` is a Replay Pro feature.\n` +
|
|
836
|
+
` you are signed in as ${creds.login}, but your account isn't on Replay Pro.\n` +
|
|
837
|
+
` join the rolling-cohort waitlist → https://sickr.ai/#waitlist\n`);
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
863
840
|
async function handleReplay(rest) {
|
|
864
841
|
const sub = replaySubcommand(rest);
|
|
865
842
|
if (!sub) {
|
|
@@ -933,6 +910,10 @@ async function handleReplay(rest) {
|
|
|
933
910
|
liveStatus();
|
|
934
911
|
return;
|
|
935
912
|
}
|
|
913
|
+
if (!(await requireReplayPro('sickr replay live'))) {
|
|
914
|
+
process.exit(3);
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
936
917
|
await startLive({ verbose: replayRest.includes('--verbose') || replayRest.includes('-v'), background: replayRest.includes('--background') });
|
|
937
918
|
return;
|
|
938
919
|
}
|
|
@@ -1132,6 +1113,10 @@ async function main() {
|
|
|
1132
1113
|
liveStatus();
|
|
1133
1114
|
return;
|
|
1134
1115
|
}
|
|
1116
|
+
if (!(await requireReplayPro('sickr live'))) {
|
|
1117
|
+
process.exit(3);
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1135
1120
|
// default: start (foreground)
|
|
1136
1121
|
const opts = { verbose: rest.includes('--verbose') || rest.includes('-v'), background: rest.includes('--background') };
|
|
1137
1122
|
await startLive(opts);
|
|
@@ -1180,6 +1165,10 @@ async function main() {
|
|
|
1180
1165
|
const passthroughFlags = flags.filter((f) => f !== '--verbose' && f !== '-v');
|
|
1181
1166
|
const agentArgs = [...passthroughFlags, ...positional.slice(1)];
|
|
1182
1167
|
const verbose = flags.includes('--verbose') || flags.includes('-v');
|
|
1168
|
+
if (!(await requireReplayPro('sickr run'))) {
|
|
1169
|
+
process.exit(3);
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1183
1172
|
const { startRun } = await import('./run.js');
|
|
1184
1173
|
try {
|
|
1185
1174
|
await startRun({ agent, agentArgs, verbose, mode });
|
package/dist/run.js
CHANGED
|
@@ -295,6 +295,11 @@ export async function startRun(opts) {
|
|
|
295
295
|
else if (mode === 'interactive') {
|
|
296
296
|
process.stdout.write(` mode=interactive — agent will prompt for tool use; you confirm in the PTY\n`);
|
|
297
297
|
}
|
|
298
|
+
if (opts.agent === 'codex') {
|
|
299
|
+
// Codex 0.133+ gates new hooks until trusted; without trust the recorder
|
|
300
|
+
// is dormant and /r/<urlid> stays empty even though the PTY is live.
|
|
301
|
+
process.stdout.write(` codex hooks: type \`/hooks\` once inside codex to trust the recorder.\n`);
|
|
302
|
+
}
|
|
298
303
|
process.stdout.write(` browser steer messages land directly in your agent. ^C exits.\n\n`);
|
|
299
304
|
const pty = ptySpawn(agentBin, effectiveArgs, {
|
|
300
305
|
name: 'xterm-256color',
|
|
@@ -388,12 +393,28 @@ export async function startRun(opts) {
|
|
|
388
393
|
if (m.kind === 'steer' && m.text) {
|
|
389
394
|
const decision = decideSteer(m);
|
|
390
395
|
if (decision.target === 'pty' && decision.bytes != null) {
|
|
396
|
+
// Submit-fix (2026-06-01): Claude Code's TUI treats a burst of
|
|
397
|
+
// characters arriving with no inter-keystroke gap as a paste
|
|
398
|
+
// — and within a paste, \r is an inline newline, NOT a submit.
|
|
399
|
+
// Text appeared in the input box but never got sent until the
|
|
400
|
+
// operator manually hit Enter. Split the write: body first,
|
|
401
|
+
// then a short delay, then a standalone \r so it lands as a
|
|
402
|
+
// discrete Enter keypress instead of "tail of a paste".
|
|
403
|
+
const bytes = decision.bytes;
|
|
404
|
+
const bodyOnly = bytes.endsWith('\r') ? bytes.slice(0, -1) : bytes;
|
|
405
|
+
const submit = bytes.endsWith('\r');
|
|
391
406
|
try {
|
|
392
|
-
|
|
407
|
+
if (bodyOnly.length > 0)
|
|
408
|
+
pty.write(bodyOnly);
|
|
409
|
+
if (submit)
|
|
410
|
+
setTimeout(() => { try {
|
|
411
|
+
pty.write('\r');
|
|
412
|
+
}
|
|
413
|
+
catch { /* pty exited */ } }, 80);
|
|
393
414
|
}
|
|
394
415
|
catch { /* PTY may have exited */ }
|
|
395
416
|
if (opts.verbose)
|
|
396
|
-
process.stderr.write(`sickr: pty steer ${
|
|
417
|
+
process.stderr.write(`sickr: pty steer ${bytes.length}b${submit ? ' (split for submit)' : ''}\n`);
|
|
397
418
|
}
|
|
398
419
|
else {
|
|
399
420
|
appendInbox(urlid, m.text, m.at ?? new Date().toISOString());
|
package/package.json
CHANGED