@hwip/cli 0.1.9 → 0.1.10

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/wrapper.d.ts CHANGED
@@ -32,6 +32,8 @@ export declare const herdrSessionArgs: (session: string, env?: NodeJS.ProcessEnv
32
32
  * an older tmux gets none, as before.
33
33
  */
34
34
  export declare const herdrEnvArgs: (agentKind: string | undefined, env: NodeJS.ProcessEnv, tmuxVersion: string) => string[];
35
+ export declare const agentLaunchEnv: (agentKind: string | undefined, env: NodeJS.ProcessEnv) => Record<string, string>;
36
+ export declare const agentLaunchArgs: (agentKind: string | undefined, extra: string[]) => string[];
35
37
  /** `tmux -V`, read once per process; empty when tmux does not answer. */
36
38
  export declare const installedTmuxVersion: () => string;
37
39
  /**
@@ -91,6 +93,8 @@ export interface SpawnTarget {
91
93
  args: string[];
92
94
  /** The tmux session name for the two tmux kinds — carried, not re-read off argv. */
93
95
  session?: string;
96
+ /** Variables the agent runs with on top of this process's env (`direct`); the tmux kinds carry them as `-e`. */
97
+ env?: Record<string, string>;
94
98
  }
95
99
  /**
96
100
  * `hwip <agent> --detach --label <x> --wt <name> …` — the wrapper-owned flags. They
package/dist/wrapper.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleShellSession = exports.shellTargets = exports.readShellSessionState = exports.planShellSession = exports.handleAgentSession = exports.tmuxMissingMessage = exports.inPaneNote = exports.handOffExec = exports.ensureListenerRunning = exports.takeListenerLaunchMarker = exports.listenerVersionDrifted = exports.targetForAgent = exports.splitAgentOptions = exports.sanitizeLabel = exports.findAvailableSession = exports.familyNames = exports.firstFreeSession = exports.tmuxSessionName = exports.installedTmuxVersion = exports.herdrEnvArgs = exports.herdrSessionArgs = exports.sessionSidecarArgs = exports.detectProjectName = exports.detectProjectDir = exports.safeBasename = void 0;
3
+ exports.handleShellSession = exports.shellTargets = exports.readShellSessionState = exports.planShellSession = exports.handleAgentSession = exports.tmuxMissingMessage = exports.inPaneNote = exports.handOffExec = exports.ensureListenerRunning = exports.takeListenerLaunchMarker = exports.listenerVersionDrifted = exports.targetForAgent = exports.splitAgentOptions = exports.sanitizeLabel = exports.findAvailableSession = exports.familyNames = exports.firstFreeSession = exports.tmuxSessionName = exports.installedTmuxVersion = exports.agentLaunchArgs = exports.agentLaunchEnv = exports.herdrEnvArgs = exports.herdrSessionArgs = exports.sessionSidecarArgs = exports.detectProjectName = exports.detectProjectDir = exports.safeBasename = void 0;
4
4
  /**
5
5
  * `hwip cc` / `hwip codex` / `hwip gemini` — spawn an agent inside a named
6
6
  * tmux session so the resident listener (`hwip listener`) can address it
@@ -106,6 +106,36 @@ const herdrEnvArgs = (agentKind, env, tmuxVersion) => {
106
106
  return config_js_1.HERDR_ENV_VARS.flatMap((name) => ['-e', `${name}=${native ? env[name] ?? '' : ''}`]);
107
107
  };
108
108
  exports.herdrEnvArgs = herdrEnvArgs;
109
+ /**
110
+ * Env that keeps an agent's transcript in tmux scrollback, by kind. A
111
+ * full-screen renderer draws on the alternate screen, where tmux keeps no
112
+ * history, so the phone cannot page back. Claude Code's fullscreen renderer
113
+ * is a user setting (`/tui fullscreen`); this forces the classic one. A value
114
+ * the launching shell already sets wins.
115
+ */
116
+ const AGENT_LAUNCH_ENV = {
117
+ claude: { CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN: '1' },
118
+ };
119
+ const agentLaunchEnv = (agentKind, env) => Object.fromEntries(Object.entries(AGENT_LAUNCH_ENV[agentKind ?? ''] ?? {}).map(([name, value]) => [name, env[name] ?? value]));
120
+ exports.agentLaunchEnv = agentLaunchEnv;
121
+ /**
122
+ * Leading flags with the same aim, by kind, for an agent whose renderer is a
123
+ * flag: pi defaults to `--tui-mode fullscreen`. Left off when the caller
124
+ * passes the flag itself.
125
+ */
126
+ const AGENT_LAUNCH_ARGS = {
127
+ pi: ['--tui-mode', 'regular'],
128
+ };
129
+ const agentLaunchArgs = (agentKind, extra) => {
130
+ const lead = AGENT_LAUNCH_ARGS[agentKind ?? ''] ?? [];
131
+ const flag = lead[0];
132
+ if (flag === undefined || extra.some((a) => a === flag || a.startsWith(`${flag}=`)))
133
+ return extra;
134
+ return [...lead, ...extra];
135
+ };
136
+ exports.agentLaunchArgs = agentLaunchArgs;
137
+ /** `new-session -e` flags for `agentLaunchEnv`; none before tmux 3.2, as for herdr. */
138
+ const launchEnvArgs = (launchEnv, tmuxVersion) => sessionEnvSupported(tmuxVersion) ? Object.entries(launchEnv).flatMap(([name, value]) => ['-e', `${name}=${value}`]) : [];
109
139
  /** `tmux -V` says 3.2 or later (`new-session -e`); a development build (`next-3.7`, `master`) is newer. */
110
140
  const sessionEnvSupported = (version) => {
111
141
  if (/\b(master|next-)/.test(version))
@@ -310,8 +340,10 @@ const targetForAgent = (agent, extra, agentKind, opts = {}) => {
310
340
  // is exactly where it runs from.
311
341
  // A label asks for a tmux session by name; running in the current pane
312
342
  // would drop that silently, so a label is the other exception.
343
+ const launchEnv = (0, exports.agentLaunchEnv)(agentKind, process.env);
344
+ extra = (0, exports.agentLaunchArgs)(agentKind, extra);
313
345
  if (process.env.TMUX && !opts.detach && !opts.label) {
314
- return { kind: 'direct', cmd: agent, args: extra };
346
+ return { kind: 'direct', cmd: agent, args: extra, ...(Object.keys(launchEnv).length ? { env: launchEnv } : {}) };
315
347
  }
316
348
  const projectDir = opts.projectDir ?? (0, exports.detectProjectDir)();
317
349
  const base = (0, exports.tmuxSessionName)((0, exports.safeBasename)(projectDir));
@@ -342,11 +374,13 @@ const targetForAgent = (agent, extra, agentKind, opts = {}) => {
342
374
  if (opts.detach) {
343
375
  // Attached to no herdr pane: blank, whatever pane this command was typed in.
344
376
  const herdrEnv = (0, exports.herdrEnvArgs)(agentKind, {}, (0, exports.installedTmuxVersion)());
345
- return { kind: 'tmux-detached', cmd: 'tmux', args: ['new', '-d', '-s', session, '-c', projectDir, ...herdrEnv, shellCmd, ...sidecar], session };
377
+ const agentEnv = launchEnvArgs(launchEnv, (0, exports.installedTmuxVersion)());
378
+ return { kind: 'tmux-detached', cmd: 'tmux', args: ['new', '-d', '-s', session, '-c', projectDir, ...herdrEnv, ...agentEnv, shellCmd, ...sidecar], session };
346
379
  }
347
380
  // `tmux new -A`: attach if the named session exists, else create it. `-e` applies only when it creates.
348
381
  const herdrEnv = (0, exports.herdrEnvArgs)(agentKind, process.env, (0, exports.installedTmuxVersion)());
349
- return { kind: 'tmux-new', cmd: 'tmux', args: ['new', '-A', '-s', session, ...herdrEnv, shellCmd, ...sidecar, ...(0, exports.herdrSessionArgs)(session)], session };
382
+ const agentEnv = launchEnvArgs(launchEnv, (0, exports.installedTmuxVersion)());
383
+ return { kind: 'tmux-new', cmd: 'tmux', args: ['new', '-A', '-s', session, ...herdrEnv, ...agentEnv, shellCmd, ...sidecar, ...(0, exports.herdrSessionArgs)(session)], session };
350
384
  };
351
385
  exports.targetForAgent = targetForAgent;
352
386
  // ── Background listener auto-start ────────────────────────────────────
@@ -561,7 +595,7 @@ const handleAgentSession = async (agent, extra = [], opts = {}, deps = {}) => {
561
595
  // spawned from here would inherit the worktree as its cwd, and it is the one that later removes the directory.
562
596
  if (opts.worktree && opts.projectDir)
563
597
  process.chdir(opts.projectDir);
564
- const { kind, cmd, args, session } = (0, exports.targetForAgent)(agent.binary, extra, agent.kind, opts);
598
+ const { kind, cmd, args, session, env } = (0, exports.targetForAgent)(agent.binary, extra, agent.kind, opts);
565
599
  const note = kind === 'direct' ? (0, exports.inPaneNote)(agent.subcommands[0], currentTmuxSession()) : undefined;
566
600
  if (note)
567
601
  console.error(note);
@@ -575,7 +609,7 @@ const handleAgentSession = async (agent, extra = [], opts = {}, deps = {}) => {
575
609
  return 1;
576
610
  }
577
611
  }
578
- return (deps.launch ?? launchTarget)({ kind, cmd, args, session }, agent.binary);
612
+ return (deps.launch ?? launchTarget)({ kind, cmd, args, session, env }, agent.binary);
579
613
  };
580
614
  exports.handleAgentSession = handleAgentSession;
581
615
  /**
@@ -583,7 +617,8 @@ exports.handleAgentSession = handleAgentSession;
583
617
  * hands the terminal over (execve where it can, a waited child where not) and
584
618
  * forwards the exit code. `what` names the program in the detached message.
585
619
  */
586
- const launchTarget = async ({ kind, cmd, args, session }, what) => {
620
+ const launchTarget = async ({ kind, cmd, args, session, env: agentEnv }, what) => {
621
+ const env = agentEnv ? { ...process.env, ...agentEnv } : process.env;
587
622
  if (kind === 'tmux-detached' && session) {
588
623
  const r = (0, child_process_1.spawnSync)(cmd, args, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
589
624
  if (r.error || r.status !== 0) {
@@ -601,7 +636,7 @@ const launchTarget = async ({ kind, cmd, args, session }, what) => {
601
636
  // exit code that execve gives us for free by being the same process.
602
637
  const handOff = (0, exports.handOffExec)({
603
638
  execve: process.execve,
604
- env: process.env,
639
+ env,
605
640
  kind,
606
641
  stdinTTY: process.stdin.isTTY,
607
642
  stdoutTTY: process.stdout.isTTY,
@@ -621,7 +656,7 @@ const launchTarget = async ({ kind, cmd, args, session }, what) => {
621
656
  }
622
657
  return new Promise((resolve) => {
623
658
  const start = Date.now();
624
- const child = (0, child_process_1.spawn)(cmd, args, { stdio: 'inherit' });
659
+ const child = (0, child_process_1.spawn)(cmd, args, { stdio: 'inherit', env });
625
660
  child.on('exit', (code) => {
626
661
  const dur = Date.now() - start;
627
662
  // Short-lived non-zero exits are the symptom of "ran from a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hwip/cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Hwip CLI + push notification SDK for AI agents",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",