hop2 0.11.0 → 0.11.2

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.
Files changed (82) hide show
  1. package/.github/workflows/ci.yml +25 -0
  2. package/.github/workflows/publish.yml +130 -0
  3. package/DESIGN-input-acks.md +349 -0
  4. package/MULTI-AGENT-CHECKLIST.md +5 -5
  5. package/README-MCP.md +84 -5
  6. package/README.md +55 -4
  7. package/demo/README.md +52 -0
  8. package/demo/capture/capture-env.mjs +108 -0
  9. package/demo/capture/capture.mjs +634 -0
  10. package/demo/capture/dispatch-task.mjs +44 -0
  11. package/demo/capture/mcp-client.mjs +61 -0
  12. package/demo/capture/native-css.mjs +49 -0
  13. package/demo/capture/setup-04.mjs +67 -0
  14. package/demo/capture/setup-sessions.mjs +40 -0
  15. package/demo/capture/spawn-aurora.mjs +53 -0
  16. package/demo/capture/tools/agent.sh +30 -0
  17. package/demo/capture/tools/build.sh +18 -0
  18. package/demo/capture/tools/metrics.sh +17 -0
  19. package/docs/hero-overview.svg +119 -0
  20. package/docs/hero.svg +118 -0
  21. package/docs/restore-reliability.md +139 -0
  22. package/docs/session-identity.md +76 -0
  23. package/docs/switcher-first-ux.md +103 -0
  24. package/hay/CHANGELOG.md +59 -1
  25. package/hay/apps/cli/package.json +3 -2
  26. package/hay/apps/cli/src/index.ts +705 -28
  27. package/hay/apps/server/src/pty.ts +36 -2
  28. package/hay/apps/server/src/rooms.ts +525 -34
  29. package/hay/apps/server/test/rooms.test.ts +411 -1
  30. package/hay/apps/web/index.html +2 -0
  31. package/hay/apps/web/package.json +14 -12
  32. package/hay/apps/web/scripts/run-e2e.mjs +42 -5
  33. package/hay/apps/web/src/App.tsx +2492 -281
  34. package/hay/apps/web/src/components/ContextMenu.tsx +109 -0
  35. package/hay/apps/web/src/components/MobileKeyboard.tsx +135 -14
  36. package/hay/apps/web/src/components/SecondaryPane.tsx +218 -0
  37. package/hay/apps/web/src/components/SessionSwitcher.tsx +2525 -0
  38. package/hay/apps/web/src/setupTests.ts +23 -0
  39. package/hay/apps/web/src/styles.css +2460 -180
  40. package/hay/apps/web/src/utils/fileDrop.ts +15 -0
  41. package/hay/apps/web/src/utils/keyboardProtocol.ts +22 -0
  42. package/hay/apps/web/src/utils/optimisticEcho.ts +83 -7
  43. package/hay/apps/web/src/utils/scrollFlywheel.ts +145 -0
  44. package/hay/apps/web/src/utils/switcherModel.ts +314 -0
  45. package/hay/apps/web/src/utils/terminalSearch.ts +54 -0
  46. package/hay/apps/web/src/vite-env.d.ts +3 -0
  47. package/hay/apps/web/test/SessionSwitcher.test.tsx +324 -0
  48. package/hay/apps/web/test/fileDrop.test.ts +22 -0
  49. package/hay/apps/web/test/optimisticEcho.test.ts +17 -0
  50. package/hay/apps/web/test/scrollFlywheel.test.ts +113 -0
  51. package/hay/apps/web/test/switcherModel.test.ts +377 -0
  52. package/hay/apps/web/test/terminalSearch.test.ts +37 -0
  53. package/hay/apps/web/vite.config.ts +20 -0
  54. package/hay/package-lock.json +1696 -1171
  55. package/hay/packages/shared/src/protocol.ts +25 -2
  56. package/hay-web/assets/index-BohAWpcL.css +1 -0
  57. package/hay-web/assets/index-DfGT3DuO.js +130 -0
  58. package/hay-web/assets/index-DfGT3DuO.js.map +1 -0
  59. package/hay-web/index.html +4 -2
  60. package/hop +4348 -302
  61. package/hopa +465 -0
  62. package/mcp/hop-mcp.js +2878 -122
  63. package/package.json +7 -4
  64. package/scripts/claude-session-hook.js +152 -0
  65. package/scripts/hay-host.js +227 -4
  66. package/scripts/hop-math.js +316 -0
  67. package/scripts/salvage-and-restart-host.js +217 -0
  68. package/skills/hop-manager/SKILL.md +62 -0
  69. package/skills/hop-session/SKILL.md +42 -0
  70. package/skills/hopa/SKILL.md +75 -0
  71. package/tests/agent-skill.unit.test.cjs +60 -0
  72. package/tests/composer-state.unit.test.cjs +102 -0
  73. package/tests/hop-mcp.integration.test.cjs +476 -94
  74. package/tests/hopa-parity.unit.test.cjs +188 -0
  75. package/tests/readable-echo.unit.test.cjs +94 -0
  76. package/tests/readable-flush.unit.test.cjs +93 -0
  77. package/tests/submit-verification.unit.test.cjs +331 -0
  78. package/tests/terminal-api.integration.test.cjs +301 -13
  79. package/hay-web/assets/index-BzJB9r_I.js +0 -64
  80. package/hay-web/assets/index-BzJB9r_I.js.map +0 -1
  81. package/hay-web/assets/index-DG8Ntznc.css +0 -32
  82. package/sessions.html +0 -726
@@ -8,6 +8,14 @@ export type PtyFactoryOptions = {
8
8
  cwd: string;
9
9
  env?: Record<string, string>;
10
10
  shell?: string;
11
+ // Launch the session ON this command instead of typing it into a shell
12
+ // afterwards. Restore used to attach, wait a fixed delay, and type the
13
+ // relaunch — which silently lost the keystrokes whenever the shell wasn't
14
+ // at its prompt yet. Passing the command as argv removes the whole class:
15
+ // there is no prompt to race, nothing to half-type, and no command echo in
16
+ // the scrollback. The shell is re-exec'd afterwards so the session behaves
17
+ // exactly as before once the command exits.
18
+ command?: string;
11
19
  };
12
20
 
13
21
  export type PtyFactory = (options: PtyFactoryOptions) => IPty;
@@ -25,7 +33,7 @@ const resolveShell = (shellOverride?: string) => {
25
33
  return "/bin/bash";
26
34
  };
27
35
 
28
- export const createPty: PtyFactory = ({ cols, rows, cwd, env: envOverrides, shell }) => {
36
+ export const createPty: PtyFactory = ({ cols, rows, cwd, env: envOverrides, shell, command }) => {
29
37
  const mode = process.env.PTY_MODE ?? "native";
30
38
  if (mode === "mock") {
31
39
  return createMockPty();
@@ -37,6 +45,12 @@ export const createPty: PtyFactory = ({ cols, rows, cwd, env: envOverrides, shel
37
45
  env[key] = value;
38
46
  }
39
47
  }
48
+ // Shared PTYs get resized whenever a differently-sized device attaches
49
+ // (autofit claims), and every resize makes zsh re-emit its end-of-line
50
+ // mark ("%" + a line of spaces + CR) into history at the OLD width —
51
+ // rendering as stray reverse-video % at line ends for every later viewer.
52
+ // Suppress the mark for hop-spawned shells; user env overrides still win.
53
+ env.PROMPT_EOL_MARK = "";
40
54
  if (envOverrides && typeof envOverrides === "object") {
41
55
  for (const [key, value] of Object.entries(envOverrides)) {
42
56
  if (!key || typeof value !== "string") continue;
@@ -47,7 +61,27 @@ export const createPty: PtyFactory = ({ cols, rows, cwd, env: envOverrides, shel
47
61
  if (!env.COLORTERM) {
48
62
  env.COLORTERM = "truecolor";
49
63
  }
50
- return pty.spawn(resolveShell(shell), [], {
64
+ // A hop session is an INTERACTIVE terminal, so it must never inherit a
65
+ // "this output isn't for a human" color suppressor from whatever
66
+ // launched the daemon. Claude Code sets NO_COLOR=1 for the shells it
67
+ // runs commands in — so a daemon (re)started from inside an agent
68
+ // session propagated NO_COLOR into every PTY it spawned afterwards, and
69
+ // every app in every session silently went monochrome: measured, claude
70
+ // emits ZERO colored SGR with NO_COLOR=1 and full 24-bit truecolor
71
+ // without it. That is the whole "claude has no color in hop but does in
72
+ // iTerm" report. Explicit per-session env overrides above still win.
73
+ if (!envOverrides || !("NO_COLOR" in envOverrides)) delete env.NO_COLOR;
74
+ if (env.FORCE_COLOR === "0" && (!envOverrides || !("FORCE_COLOR" in envOverrides))) {
75
+ delete env.FORCE_COLOR;
76
+ }
77
+ const shellPath = resolveShell(shell);
78
+ // `-lc` runs the command in a login shell (rc files, aliases, PATH all
79
+ // as the user has them), then `exec <shell> -l` replaces it with the
80
+ // normal interactive shell, so exiting the app leaves a usable session.
81
+ const args = command
82
+ ? ["-lc", `${command}; exec ${shellPath} -l`]
83
+ : [];
84
+ return pty.spawn(shellPath, args, {
51
85
  cols,
52
86
  rows,
53
87
  cwd,