@jmoyers/harness 0.1.10 → 0.1.20

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 (239) hide show
  1. package/README.md +31 -35
  2. package/package.json +31 -11
  3. package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
  4. package/packages/harness-ai/src/stream-text.ts +13 -91
  5. package/packages/harness-ui/src/frame-primitives.ts +158 -0
  6. package/packages/harness-ui/src/index.ts +18 -0
  7. package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
  8. package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
  9. package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
  10. package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
  11. package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
  12. package/packages/harness-ui/src/interaction/input.ts +420 -0
  13. package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
  14. package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
  15. package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
  16. package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
  17. package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
  18. package/packages/harness-ui/src/kit.ts +476 -0
  19. package/packages/harness-ui/src/layout.ts +238 -0
  20. package/{src/ui/modals/manager.ts → packages/harness-ui/src/modal-manager.ts} +94 -64
  21. package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
  22. package/packages/harness-ui/src/surface.ts +252 -0
  23. package/packages/harness-ui/src/text-layout.ts +210 -0
  24. package/packages/nim-core/src/contracts.ts +239 -0
  25. package/packages/nim-core/src/event-store.ts +299 -0
  26. package/packages/nim-core/src/events.ts +53 -0
  27. package/packages/nim-core/src/index.ts +9 -0
  28. package/packages/nim-core/src/provider-router.ts +129 -0
  29. package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
  30. package/packages/nim-core/src/runtime-factory.ts +49 -0
  31. package/packages/nim-core/src/runtime.ts +1797 -0
  32. package/packages/nim-core/src/session-store.ts +516 -0
  33. package/packages/nim-core/src/telemetry.ts +48 -0
  34. package/packages/nim-test-tui/src/index.ts +150 -0
  35. package/packages/nim-ui-core/src/index.ts +1 -0
  36. package/packages/nim-ui-core/src/projection.ts +87 -0
  37. package/scripts/codex-live-mux-runtime.ts +2 -3721
  38. package/scripts/control-plane-daemon.ts +24 -2
  39. package/scripts/harness-bin.js +5 -0
  40. package/scripts/harness-commands.ts +300 -0
  41. package/scripts/harness-runtime.ts +82 -0
  42. package/scripts/harness.ts +33 -3007
  43. package/scripts/nim-tui-smoke.ts +748 -0
  44. package/src/cli/auth/runtime.ts +948 -0
  45. package/src/cli/default-gateway-pointer.ts +193 -0
  46. package/src/cli/gateway/runtime.ts +1872 -0
  47. package/src/cli/parsing/flags.ts +23 -0
  48. package/src/cli/parsing/session.ts +42 -0
  49. package/src/cli/runtime/context.ts +193 -0
  50. package/src/cli/runtime-app/application.ts +392 -0
  51. package/src/cli/runtime-infra/gateway-control.ts +729 -0
  52. package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
  53. package/src/cli/workflows/runtime.ts +965 -0
  54. package/src/clients/tui/left-rail-interactions.ts +519 -0
  55. package/src/clients/tui/main-pane-interactions.ts +509 -0
  56. package/src/clients/tui/modal-input-routing.ts +71 -0
  57. package/src/clients/tui/render-snapshot-adapter.ts +88 -0
  58. package/src/clients/web/synced-selectors.ts +132 -0
  59. package/src/codex/live-session.ts +82 -29
  60. package/src/config/config-core.ts +361 -10
  61. package/src/config/harness-paths.ts +4 -7
  62. package/src/config/harness-runtime-migration.ts +142 -19
  63. package/src/config/harness.config.template.jsonc +33 -0
  64. package/src/config/secrets-core.ts +92 -4
  65. package/src/control-plane/agent-realtime-api.ts +82 -427
  66. package/src/control-plane/prompt/thread-title-namer.ts +49 -23
  67. package/src/control-plane/session-summary.ts +10 -81
  68. package/src/control-plane/status/reducer-base.ts +12 -12
  69. package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
  70. package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
  71. package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
  72. package/src/control-plane/stream-client.ts +12 -2
  73. package/src/control-plane/stream-command-parser.ts +83 -143
  74. package/src/control-plane/stream-protocol.ts +53 -37
  75. package/src/control-plane/stream-server-background.ts +18 -2
  76. package/src/control-plane/stream-server-command.ts +376 -69
  77. package/src/control-plane/stream-server-session-runtime.ts +3 -2
  78. package/src/control-plane/stream-server.ts +943 -80
  79. package/src/control-plane/stream-session-runtime-types.ts +41 -0
  80. package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
  81. package/src/core/state/observed-stream-cursor.ts +43 -0
  82. package/src/core/state/synced-observed-state.ts +273 -0
  83. package/src/core/store/harness-synced-store.ts +81 -0
  84. package/src/diff/budget.ts +136 -0
  85. package/src/diff/build.ts +289 -0
  86. package/src/diff/chunker.ts +146 -0
  87. package/src/diff/git-invoke.ts +315 -0
  88. package/src/diff/git-parse.ts +472 -0
  89. package/src/diff/hash.ts +70 -0
  90. package/src/diff/index.ts +24 -0
  91. package/src/diff/normalize.ts +134 -0
  92. package/src/diff/types.ts +178 -0
  93. package/src/diff-ui/args.ts +346 -0
  94. package/src/diff-ui/commands.ts +123 -0
  95. package/src/diff-ui/finder.ts +94 -0
  96. package/src/diff-ui/highlight.ts +127 -0
  97. package/src/diff-ui/index.ts +2 -0
  98. package/src/diff-ui/model.ts +141 -0
  99. package/src/diff-ui/pager.ts +412 -0
  100. package/src/diff-ui/render.ts +337 -0
  101. package/src/diff-ui/runtime.ts +379 -0
  102. package/src/diff-ui/state.ts +224 -0
  103. package/src/diff-ui/types.ts +236 -0
  104. package/src/domain/conversations.ts +11 -7
  105. package/src/domain/workspace.ts +76 -4
  106. package/src/mux/control-plane-op-queue.ts +93 -7
  107. package/src/mux/conversation-rail.ts +28 -71
  108. package/src/mux/dual-pane-core.ts +13 -13
  109. package/src/mux/harness-core-ui.ts +313 -42
  110. package/src/mux/input-shortcuts.ts +22 -112
  111. package/src/mux/keybinding-catalog.ts +340 -0
  112. package/src/mux/keybinding-registry.ts +103 -0
  113. package/src/mux/live-mux/command-menu-open-in.ts +280 -0
  114. package/src/mux/live-mux/command-menu.ts +167 -4
  115. package/src/mux/live-mux/conversation-state.ts +13 -0
  116. package/src/mux/live-mux/directory-resolution.ts +1 -1
  117. package/src/mux/live-mux/git-parsing.ts +16 -0
  118. package/src/mux/live-mux/git-snapshot.ts +33 -2
  119. package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
  120. package/src/mux/live-mux/home-pane-drop.ts +1 -1
  121. package/src/mux/live-mux/home-pane-pointer.ts +10 -0
  122. package/src/mux/live-mux/input-forwarding.ts +59 -2
  123. package/src/mux/live-mux/left-nav-activation.ts +124 -7
  124. package/src/mux/live-mux/left-nav.ts +35 -0
  125. package/src/mux/live-mux/link-click.ts +292 -0
  126. package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
  127. package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
  128. package/src/mux/live-mux/modal-input-reducers.ts +106 -8
  129. package/src/mux/live-mux/modal-overlays.ts +210 -31
  130. package/src/mux/live-mux/modal-pointer.ts +3 -7
  131. package/src/mux/live-mux/modal-prompt-handlers.ts +107 -1
  132. package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
  133. package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
  134. package/src/mux/live-mux/pointer-routing.ts +5 -2
  135. package/src/mux/live-mux/project-pane-pointer.ts +8 -0
  136. package/src/mux/live-mux/rail-layout.ts +33 -30
  137. package/src/mux/live-mux/release-notes.ts +383 -0
  138. package/src/mux/live-mux/render-trace-analysis.ts +52 -7
  139. package/src/mux/live-mux/repository-folding.ts +3 -0
  140. package/src/mux/live-mux/selection.ts +0 -4
  141. package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
  142. package/src/mux/project-pane-github-review.ts +271 -0
  143. package/src/mux/render-frame.ts +4 -0
  144. package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
  145. package/src/mux/task-composer.ts +21 -14
  146. package/src/mux/task-focused-pane.ts +118 -117
  147. package/src/mux/task-screen-keybindings.ts +19 -82
  148. package/src/mux/workspace-rail-model.ts +270 -104
  149. package/src/mux/workspace-rail.ts +45 -22
  150. package/src/pty/session-broker.ts +1 -1
  151. package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
  152. package/src/services/control-plane.ts +50 -32
  153. package/src/services/conversation-lifecycle.ts +118 -87
  154. package/src/services/conversation-startup-hydration.ts +20 -12
  155. package/src/services/directory-hydration.ts +21 -16
  156. package/src/services/event-persistence.ts +7 -0
  157. package/src/services/left-rail-pointer-handler.ts +329 -0
  158. package/src/services/mux-ui-state-persistence.ts +5 -1
  159. package/src/services/recording.ts +34 -26
  160. package/src/services/runtime-command-menu-agent-tools.ts +1 -1
  161. package/src/services/runtime-control-actions.ts +79 -61
  162. package/src/services/runtime-control-plane-ops.ts +122 -83
  163. package/src/services/runtime-conversation-actions.ts +40 -26
  164. package/src/services/runtime-conversation-activation.ts +82 -30
  165. package/src/services/runtime-conversation-starter.ts +80 -48
  166. package/src/services/runtime-conversation-title-edit.ts +91 -80
  167. package/src/services/runtime-envelope-handler.ts +107 -105
  168. package/src/services/runtime-git-state.ts +42 -29
  169. package/src/services/runtime-layout-resize.ts +3 -1
  170. package/src/services/runtime-left-rail-render.ts +99 -63
  171. package/src/services/runtime-nim-cli-session.ts +438 -0
  172. package/src/services/runtime-nim-session.ts +705 -0
  173. package/src/services/runtime-nim-tool-bridge.ts +141 -0
  174. package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
  175. package/src/services/runtime-process-wiring.ts +29 -36
  176. package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
  177. package/src/services/runtime-render-flush.ts +63 -70
  178. package/src/services/runtime-render-lifecycle.ts +65 -64
  179. package/src/services/runtime-render-orchestrator.ts +55 -45
  180. package/src/services/runtime-render-pipeline.ts +106 -103
  181. package/src/services/runtime-render-state.ts +62 -49
  182. package/src/services/runtime-repository-actions.ts +97 -70
  183. package/src/services/runtime-right-pane-render.ts +80 -53
  184. package/src/services/runtime-shutdown.ts +38 -35
  185. package/src/services/runtime-stream-subscriptions.ts +35 -27
  186. package/src/services/runtime-task-composer-persistence.ts +71 -59
  187. package/src/services/runtime-task-composer-snapshot.ts +14 -0
  188. package/src/services/runtime-task-editor-actions.ts +46 -29
  189. package/src/services/runtime-task-pane-actions.ts +220 -134
  190. package/src/services/runtime-task-pane-shortcuts.ts +323 -123
  191. package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
  192. package/src/services/runtime-workspace-observed-events.ts +33 -184
  193. package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
  194. package/src/services/session-diagnostics-store.ts +217 -0
  195. package/src/services/startup-background-resume.ts +26 -21
  196. package/src/services/startup-orchestrator.ts +16 -13
  197. package/src/services/startup-paint-tracker.ts +29 -21
  198. package/src/services/startup-persisted-conversation-queue.ts +19 -13
  199. package/src/services/startup-settled-gate.ts +25 -15
  200. package/src/services/startup-shutdown.ts +18 -22
  201. package/src/services/startup-state-hydration.ts +44 -34
  202. package/src/services/startup-visibility.ts +12 -4
  203. package/src/services/task-pane-selection-actions.ts +89 -72
  204. package/src/services/task-planning-hydration.ts +24 -18
  205. package/src/services/task-planning-observed-events.ts +50 -52
  206. package/src/services/workspace-observed-events.ts +66 -63
  207. package/src/storage/storage-lifecycle-core.ts +438 -0
  208. package/src/store/control-plane-store-normalize.ts +33 -242
  209. package/src/store/control-plane-store-types.ts +1 -35
  210. package/src/store/control-plane-store.ts +396 -56
  211. package/src/store/event-store.ts +397 -3
  212. package/src/terminal/snapshot-oracle.ts +207 -94
  213. package/src/ui/mux-theme.ts +112 -8
  214. package/src/ui/panes/home-gridfire.ts +40 -31
  215. package/src/ui/panes/home.ts +10 -2
  216. package/src/ui/panes/nim.ts +315 -0
  217. package/src/mux/live-mux/actions-task.ts +0 -115
  218. package/src/mux/live-mux/left-rail-actions.ts +0 -118
  219. package/src/mux/live-mux/left-rail-conversation-click.ts +0 -82
  220. package/src/mux/live-mux/left-rail-pointer.ts +0 -74
  221. package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
  222. package/src/services/runtime-directory-actions.ts +0 -164
  223. package/src/services/runtime-input-pipeline.ts +0 -50
  224. package/src/services/runtime-input-router.ts +0 -189
  225. package/src/services/runtime-main-pane-input.ts +0 -230
  226. package/src/services/runtime-modal-input.ts +0 -119
  227. package/src/services/runtime-navigation-input.ts +0 -197
  228. package/src/services/runtime-rail-input.ts +0 -278
  229. package/src/services/runtime-task-pane.ts +0 -62
  230. package/src/services/runtime-workspace-actions.ts +0 -158
  231. package/src/ui/conversation-input-forwarder.ts +0 -114
  232. package/src/ui/conversation-selection-input.ts +0 -103
  233. package/src/ui/global-shortcut-input.ts +0 -89
  234. package/src/ui/input.ts +0 -238
  235. package/src/ui/kit.ts +0 -509
  236. package/src/ui/left-nav-input.ts +0 -80
  237. package/src/ui/left-rail-pointer-input.ts +0 -148
  238. package/src/ui/repository-fold-input.ts +0 -91
  239. package/src/ui/surface.ts +0 -224
@@ -72,7 +72,7 @@ function parseArgs(argv: string[], invocationDirectory: string): DaemonOptions {
72
72
  const defaultAuthToken = process.env.HARNESS_CONTROL_PLANE_AUTH_TOKEN ?? null;
73
73
  const defaultStateDbPath = resolveHarnessRuntimePath(
74
74
  invocationDirectory,
75
- process.env.HARNESS_CONTROL_PLANE_DB_PATH ?? '.harness/control-plane.sqlite',
75
+ '.harness/control-plane.sqlite',
76
76
  );
77
77
 
78
78
  let host = defaultHost;
@@ -117,7 +117,7 @@ function parseArgs(argv: string[], invocationDirectory: string): DaemonOptions {
117
117
  if (value === undefined) {
118
118
  throw new Error('missing value for --state-db-path');
119
119
  }
120
- stateDbPath = value;
120
+ stateDbPath = resolveHarnessRuntimePath(invocationDirectory, value);
121
121
  idx += 1;
122
122
  continue;
123
123
  }
@@ -132,6 +132,17 @@ function parseArgs(argv: string[], invocationDirectory: string): DaemonOptions {
132
132
  if (!loopbackHosts.has(host) && authToken === null) {
133
133
  throw new Error('non-loopback hosts require --auth-token or HARNESS_CONTROL_PLANE_AUTH_TOKEN');
134
134
  }
135
+ const workspaceRuntimeRoot = resolveHarnessWorkspaceDirectory(invocationDirectory, process.env);
136
+ const normalizedRuntimeRoot = resolve(workspaceRuntimeRoot);
137
+ const normalizedStateDbPath = resolve(stateDbPath);
138
+ if (
139
+ normalizedStateDbPath !== normalizedRuntimeRoot &&
140
+ !normalizedStateDbPath.startsWith(`${normalizedRuntimeRoot}/`)
141
+ ) {
142
+ throw new Error(
143
+ `invalid --state-db-path: ${stateDbPath}. state db path must be under workspace runtime root ${workspaceRuntimeRoot}`,
144
+ );
145
+ }
135
146
 
136
147
  return {
137
148
  host,
@@ -220,7 +231,18 @@ async function main(): Promise<number> {
220
231
  branchStrategy: loadedConfig.config.github.branchStrategy,
221
232
  viewerLogin: loadedConfig.config.github.viewerLogin,
222
233
  },
234
+ linear: {
235
+ enabled: loadedConfig.config.linear.enabled,
236
+ apiBaseUrl: loadedConfig.config.linear.apiBaseUrl,
237
+ tokenEnvVar: loadedConfig.config.linear.tokenEnvVar,
238
+ },
223
239
  lifecycleHooks: loadedConfig.config.hooks.lifecycle,
240
+ storageLifecyclePolicy: loadedConfig.config.storage.lifecycle,
241
+ storageLifecyclePolicyReload: {
242
+ cwd: invocationDirectory,
243
+ filePath: configPath,
244
+ env: process.env,
245
+ },
224
246
  startSession: (input) => {
225
247
  const sessionOptions: Parameters<typeof startCodexLiveSession>[0] = {
226
248
  args: input.args,
@@ -67,8 +67,13 @@ if (!ensureBunAvailable({ command: bunCommand })) {
67
67
  const here = dirname(fileURLToPath(import.meta.url));
68
68
  const scriptPath = resolve(here, './harness.ts');
69
69
  const runtimeArgs = [scriptPath, ...process.argv.slice(2)];
70
+ const runtimeEnv = {
71
+ ...process.env,
72
+ HARNESS_INVOKE_CWD: process.cwd(),
73
+ };
70
74
  const child = spawn(bunCommand, runtimeArgs, {
71
75
  stdio: 'inherit',
76
+ env: runtimeEnv,
72
77
  });
73
78
 
74
79
  child.once('exit', (code, signal) => {
@@ -0,0 +1,300 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import {
3
+ parseSessionName,
4
+ runAnimateCli,
5
+ runAuthCli,
6
+ runClientCli,
7
+ runCursorHooksCli,
8
+ runDiffCli,
9
+ runGatewayCli,
10
+ runNimCli,
11
+ runProfileCli,
12
+ runRenderTraceCli,
13
+ runStatusTimelineCli,
14
+ runUpdateCli,
15
+ } from './harness-runtime.ts';
16
+
17
+ const sessionFlag = Flags.string({
18
+ description:
19
+ 'Session namespace used to isolate gateway record/log/db/profile/status-trace artifacts.',
20
+ });
21
+
22
+ interface SessionParseResult {
23
+ sessionName: string | null;
24
+ argv: readonly string[];
25
+ }
26
+
27
+ abstract class HarnessCommandBase extends Command {
28
+ static override strict = false;
29
+
30
+ protected extractSessionArg(argv: readonly string[]): SessionParseResult {
31
+ const passthrough: string[] = [];
32
+ let sessionName: string | null = null;
33
+
34
+ for (let index = 0; index < argv.length; index += 1) {
35
+ const arg = argv[index]!;
36
+ if (arg === '--session') {
37
+ const value = argv[index + 1];
38
+ if (value === undefined) {
39
+ throw new Error('missing value for --session');
40
+ }
41
+ sessionName = parseSessionName(value);
42
+ index += 1;
43
+ continue;
44
+ }
45
+ passthrough.push(arg);
46
+ }
47
+
48
+ return {
49
+ sessionName,
50
+ argv: passthrough,
51
+ };
52
+ }
53
+
54
+ protected exitIfNeeded(code: number): void {
55
+ if (code !== 0) {
56
+ this.exit(code);
57
+ }
58
+ }
59
+ }
60
+
61
+ class ClientCommand extends HarnessCommandBase {
62
+ static override summary = 'Launch the mux client (default when no explicit command is provided).';
63
+
64
+ static override usage = [
65
+ '[--session <name>] [mux-args...]',
66
+ 'client [--session <name>] [mux-args...]',
67
+ ];
68
+
69
+ static override flags = {
70
+ help: Flags.help({ char: 'h' }),
71
+ session: sessionFlag,
72
+ };
73
+
74
+ override async run(): Promise<void> {
75
+ const parsed = this.extractSessionArg(this.argv);
76
+ const code = await runClientCli(parsed.argv, parsed.sessionName);
77
+ this.exitIfNeeded(code);
78
+ }
79
+ }
80
+
81
+ class GatewayCommand extends HarnessCommandBase {
82
+ static override summary = 'Manage the control-plane gateway lifecycle and direct command calls.';
83
+
84
+ static override usage = [
85
+ 'gateway [--session <name>] start [--host <host>] [--port <port>] [--auth-token <token>] [--state-db-path <path>]',
86
+ 'gateway [--session <name>] run [--host <host>] [--port <port>] [--auth-token <token>] [--state-db-path <path>]',
87
+ 'gateway [--session <name>] stop [--force] [--timeout-ms <ms>] [--cleanup-orphans|--no-cleanup-orphans]',
88
+ 'gateway [--session <name>] status',
89
+ 'gateway [--session <name>] restart [--host <host>] [--port <port>] [--auth-token <token>] [--state-db-path <path>]',
90
+ 'gateway [--session <name>] call --json \'{"type":"session.list"}\'',
91
+ 'gateway [--session <name>] gc [--older-than-days <days>]',
92
+ ];
93
+
94
+ static override flags = {
95
+ help: Flags.help({ char: 'h' }),
96
+ session: sessionFlag,
97
+ };
98
+
99
+ override async run(): Promise<void> {
100
+ const parsed = this.extractSessionArg(this.argv);
101
+ if (parsed.argv.length === 0) {
102
+ this.error('missing gateway subcommand', { exit: 2 });
103
+ }
104
+ const code = await runGatewayCli(parsed.argv, parsed.sessionName);
105
+ this.exitIfNeeded(code);
106
+ }
107
+ }
108
+
109
+ class ProfileCommand extends HarnessCommandBase {
110
+ static override summary =
111
+ 'Manage gateway/client CPU profiling and live inspector profiling sessions.';
112
+
113
+ static override usage = [
114
+ 'profile [--session <name>] start [--profile-dir <path>]',
115
+ 'profile [--session <name>] stop [--timeout-ms <ms>]',
116
+ 'profile [--session <name>] run [--profile-dir <path>] [mux-args...]',
117
+ 'profile [--session <name>] [--profile-dir <path>] [mux-args...]',
118
+ ];
119
+
120
+ static override flags = {
121
+ help: Flags.help({ char: 'h' }),
122
+ session: sessionFlag,
123
+ };
124
+
125
+ override async run(): Promise<void> {
126
+ const parsed = this.extractSessionArg(this.argv);
127
+ const code = await runProfileCli(parsed.argv, parsed.sessionName);
128
+ this.exitIfNeeded(code);
129
+ }
130
+ }
131
+
132
+ class StatusTimelineCommand extends HarnessCommandBase {
133
+ static override summary = 'Start or stop writing status timeline artifacts for a session.';
134
+
135
+ static override usage = [
136
+ 'status-timeline [--session <name>] start [--output-path <path>]',
137
+ 'status-timeline [--session <name>] stop',
138
+ 'status-timeline [--session <name>] [--output-path <path>]',
139
+ ];
140
+
141
+ static override flags = {
142
+ help: Flags.help({ char: 'h' }),
143
+ session: sessionFlag,
144
+ };
145
+
146
+ override async run(): Promise<void> {
147
+ const parsed = this.extractSessionArg(this.argv);
148
+ const code = await runStatusTimelineCli(parsed.argv, parsed.sessionName);
149
+ this.exitIfNeeded(code);
150
+ }
151
+ }
152
+
153
+ class RenderTraceCommand extends HarnessCommandBase {
154
+ static override summary = 'Start or stop render trace recording for a session.';
155
+
156
+ static override usage = [
157
+ 'render-trace [--session <name>] start [--output-path <path>] [--conversation-id <id>]',
158
+ 'render-trace [--session <name>] stop',
159
+ 'render-trace [--session <name>] [--output-path <path>] [--conversation-id <id>]',
160
+ ];
161
+
162
+ static override flags = {
163
+ help: Flags.help({ char: 'h' }),
164
+ session: sessionFlag,
165
+ };
166
+
167
+ override async run(): Promise<void> {
168
+ const parsed = this.extractSessionArg(this.argv);
169
+ const code = await runRenderTraceCli(parsed.argv, parsed.sessionName);
170
+ this.exitIfNeeded(code);
171
+ }
172
+ }
173
+
174
+ class AuthCommand extends HarnessCommandBase {
175
+ static override summary = 'Authenticate providers and manage saved credential state.';
176
+
177
+ static override usage = [
178
+ 'auth login <github|linear> [--no-browser] [--timeout-ms <ms>] [--scopes <list>] [--callback-port <port>]',
179
+ 'auth logout <github|linear|all>',
180
+ 'auth status [--json]',
181
+ ];
182
+
183
+ static override flags = {
184
+ help: Flags.help({ char: 'h' }),
185
+ session: sessionFlag,
186
+ };
187
+
188
+ override async run(): Promise<void> {
189
+ const parsed = this.extractSessionArg(this.argv);
190
+ const code = await runAuthCli(parsed.argv, parsed.sessionName);
191
+ this.exitIfNeeded(code);
192
+ }
193
+ }
194
+
195
+ class UpdateCommand extends HarnessCommandBase {
196
+ static override summary = 'Install the latest Harness package globally using Bun.';
197
+
198
+ static override aliases = ['upgrade'];
199
+
200
+ static override usage = ['update', 'upgrade'];
201
+
202
+ static override flags = {
203
+ help: Flags.help({ char: 'h' }),
204
+ session: sessionFlag,
205
+ };
206
+
207
+ override async run(): Promise<void> {
208
+ const parsed = this.extractSessionArg(this.argv);
209
+ const code = runUpdateCli(parsed.argv, parsed.sessionName);
210
+ this.exitIfNeeded(code);
211
+ }
212
+ }
213
+
214
+ class CursorHooksCommand extends HarnessCommandBase {
215
+ static override summary = 'Install or uninstall managed Cursor hooks.';
216
+
217
+ static override usage = [
218
+ 'cursor-hooks [--session <name>] install [--hooks-file <path>]',
219
+ 'cursor-hooks [--session <name>] uninstall [--hooks-file <path>]',
220
+ ];
221
+
222
+ static override flags = {
223
+ help: Flags.help({ char: 'h' }),
224
+ session: sessionFlag,
225
+ };
226
+
227
+ override async run(): Promise<void> {
228
+ const parsed = this.extractSessionArg(this.argv);
229
+ const code = await runCursorHooksCli(parsed.argv, parsed.sessionName);
230
+ this.exitIfNeeded(code);
231
+ }
232
+ }
233
+
234
+ class AnimateCommand extends HarnessCommandBase {
235
+ static override summary = 'Render the terminal animation benchmark scene.';
236
+
237
+ static override usage = [
238
+ 'animate [--fps <fps>] [--frames <count>] [--duration-ms <ms>] [--seed <seed>] [--no-color]',
239
+ ];
240
+
241
+ static override flags = {
242
+ help: Flags.help({ char: 'h' }),
243
+ session: sessionFlag,
244
+ };
245
+
246
+ override async run(): Promise<void> {
247
+ const parsed = this.extractSessionArg(this.argv);
248
+ const code = await runAnimateCli(parsed.argv);
249
+ this.exitIfNeeded(code);
250
+ }
251
+ }
252
+
253
+ class NimCommand extends HarnessCommandBase {
254
+ static override summary = 'Run Nim interactive TUI smoke/debug client.';
255
+
256
+ static override usage = ['nim [options]'];
257
+
258
+ static override flags = {
259
+ help: Flags.help({ char: 'h' }),
260
+ session: sessionFlag,
261
+ };
262
+
263
+ override async run(): Promise<void> {
264
+ const parsed = this.extractSessionArg(this.argv);
265
+ const code = await runNimCli(parsed.argv, parsed.sessionName);
266
+ this.exitIfNeeded(code);
267
+ }
268
+ }
269
+
270
+ class DiffCommand extends HarnessCommandBase {
271
+ static override summary = 'Render git diffs in the diff TUI/CLI view.';
272
+
273
+ static override usage = ['diff [--session <name>] [diff-options...]'];
274
+
275
+ static override flags = {
276
+ session: sessionFlag,
277
+ };
278
+
279
+ override async run(): Promise<void> {
280
+ const parsed = this.extractSessionArg(this.argv);
281
+ const code = await runDiffCli(parsed.argv, parsed.sessionName);
282
+ this.exitIfNeeded(code);
283
+ }
284
+ }
285
+
286
+ const commands = {
287
+ client: ClientCommand,
288
+ gateway: GatewayCommand,
289
+ profile: ProfileCommand,
290
+ 'status-timeline': StatusTimelineCommand,
291
+ 'render-trace': RenderTraceCommand,
292
+ auth: AuthCommand,
293
+ update: UpdateCommand,
294
+ 'cursor-hooks': CursorHooksCommand,
295
+ nim: NimCommand,
296
+ animate: AnimateCommand,
297
+ diff: DiffCommand,
298
+ } satisfies Record<string, Command.Class>;
299
+
300
+ export default commands;
@@ -0,0 +1,82 @@
1
+ import { runHarnessAnimate } from './harness-animate.ts';
2
+ import { runNimTuiSmoke } from './nim-tui-smoke.ts';
3
+ import {
4
+ createDefaultHarnessRuntimeApplication,
5
+ type HarnessRuntimeApplication,
6
+ } from '../src/cli/runtime-app/application.ts';
7
+ import { parseGlobalCliOptions, parseSessionName } from '../src/cli/parsing/session.ts';
8
+
9
+ const app: HarnessRuntimeApplication = createDefaultHarnessRuntimeApplication();
10
+
11
+ export { parseGlobalCliOptions, parseSessionName };
12
+
13
+ export async function runGatewayCli(
14
+ args: readonly string[],
15
+ sessionName: string | null,
16
+ ): Promise<number> {
17
+ return await app.runGatewayCli(args, sessionName);
18
+ }
19
+
20
+ export async function runProfileCli(
21
+ args: readonly string[],
22
+ sessionName: string | null,
23
+ ): Promise<number> {
24
+ return await app.runProfileCli(args, sessionName);
25
+ }
26
+
27
+ export async function runStatusTimelineCli(
28
+ args: readonly string[],
29
+ sessionName: string | null,
30
+ ): Promise<number> {
31
+ return await app.runStatusTimelineCli(args, sessionName);
32
+ }
33
+
34
+ export async function runRenderTraceCli(
35
+ args: readonly string[],
36
+ sessionName: string | null,
37
+ ): Promise<number> {
38
+ return await app.runRenderTraceCli(args, sessionName);
39
+ }
40
+
41
+ export async function runAuthCli(
42
+ args: readonly string[],
43
+ sessionName: string | null,
44
+ ): Promise<number> {
45
+ return await app.runAuthCli(args, sessionName);
46
+ }
47
+
48
+ export function runUpdateCli(args: readonly string[], sessionName: string | null): number {
49
+ return app.runUpdateCli(args, sessionName);
50
+ }
51
+
52
+ export async function runCursorHooksCli(
53
+ args: readonly string[],
54
+ sessionName: string | null,
55
+ ): Promise<number> {
56
+ return await app.runCursorHooksCli(args, sessionName);
57
+ }
58
+
59
+ export async function runDiffCli(
60
+ args: readonly string[],
61
+ sessionName: string | null,
62
+ ): Promise<number> {
63
+ return await app.runDiffCli(args, sessionName);
64
+ }
65
+
66
+ export async function runClientCli(
67
+ args: readonly string[],
68
+ sessionName: string | null,
69
+ ): Promise<number> {
70
+ return await app.runClientCli(args, sessionName);
71
+ }
72
+
73
+ export async function runAnimateCli(args: readonly string[]): Promise<number> {
74
+ return await runHarnessAnimate(args);
75
+ }
76
+
77
+ export async function runNimCli(
78
+ args: readonly string[],
79
+ sessionName: string | null,
80
+ ): Promise<number> {
81
+ return await runNimTuiSmoke(args, { sessionName });
82
+ }