@polderlabs/bizar-omp 0.4.2 → 0.4.4
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/README.md +137 -62
- package/dist/cli/agents-ink.d.ts +3 -1
- package/dist/cli/agents-ink.d.ts.map +1 -1
- package/dist/cli/agents-ink.js +161 -25
- package/dist/cli/agents-ink.js.map +1 -1
- package/dist/cli/agents.d.ts.map +1 -1
- package/dist/cli/agents.js +15 -27
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/directory-selector.d.ts +20 -0
- package/dist/cli/directory-selector.d.ts.map +1 -0
- package/dist/cli/directory-selector.js +138 -0
- package/dist/cli/directory-selector.js.map +1 -0
- package/dist/cli/main.js +33 -3
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/setup-ui.d.ts +38 -0
- package/dist/cli/setup-ui.d.ts.map +1 -0
- package/dist/cli/setup-ui.js +90 -0
- package/dist/cli/setup-ui.js.map +1 -0
- package/dist/omp/omb-settings.d.ts +2 -0
- package/dist/omp/omb-settings.d.ts.map +1 -1
- package/dist/omp/omb-settings.js +5 -0
- package/dist/omp/omb-settings.js.map +1 -1
- package/docs/assets/bizar-omp-banner.svg +20 -0
- package/docs/assets/sponsored-by-polderlabs.svg +9 -0
- package/docs/compatibility/baseline.json +1 -1
- package/docs/releases/0.4.2.md +1 -1
- package/docs/releases/0.4.3.md +6 -0
- package/docs/releases/0.4.4.md +6 -0
- package/docs/releases/native-stable.md +1 -1
- package/docs/releases/support-matrix.md +1 -1
- package/package.json +1 -1
- package/skills/bizar-omp/SKILL.md +2 -2
package/README.md
CHANGED
|
@@ -1,122 +1,197 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="docs/assets/bizar-omp-banner.svg" alt="BizarHarness OMP: autonomous engineering with evidence in the loop" width="100%" />
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
<p>
|
|
5
|
+
<a href="https://www.npmjs.com/package/@polderlabs/bizar-omp"><img src="https://img.shields.io/npm/v/%40polderlabs%2Fbizar-omp?style=flat-square&color=0f766e&label=npm" alt="npm version" /></a>
|
|
6
|
+
<a href="https://github.com/PolderLabsVOF/BizarHarness-OMP/releases"><img src="https://img.shields.io/github/v/release/PolderLabsVOF/BizarHarness-OMP?style=flat-square&color=2563eb&label=release" alt="latest GitHub release" /></a>
|
|
7
|
+
<img src="https://img.shields.io/badge/OMP-18.2.6-2563eb?style=flat-square" alt="OMP 18.2.6" />
|
|
8
|
+
<img src="https://img.shields.io/badge/Node.js-22%2B-18181b?style=flat-square&logo=nodedotjs&logoColor=5fa04e" alt="Node.js 22 or newer" />
|
|
9
|
+
<img src="https://img.shields.io/badge/Bun-1.3.14%2B-18181b?style=flat-square&logo=bun&logoColor=fbf0df" alt="Bun 1.3.14 or newer" />
|
|
10
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-18181b?style=flat-square" alt="MIT license" /></a>
|
|
11
|
+
</p>
|
|
4
12
|
|
|
5
|
-
|
|
13
|
+
<p><strong>Native autonomous engineering workflows for oh-my-pi.</strong><br />
|
|
14
|
+
Plan proportionally, run work in durable sessions, and finish with evidence you can inspect.</p>
|
|
15
|
+
</div>
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What Bizar OMP is
|
|
20
|
+
|
|
21
|
+
BizarHarness OMP is a native [oh-my-pi](https://github.com/can1357/oh-my-pi) extension for engineering work. It adds workflow intent, acceptance criteria, evidence freshness, specialist roles, scoped integration, and a durable session launcher while leaving OMP in charge of sessions, models, credentials, tools, task execution, and conversation state.
|
|
22
|
+
|
|
23
|
+
| You need | Bizar OMP provides |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| A sensible process | Focused, bounded, and full workflow tiers selected from task scope and risk |
|
|
26
|
+
| Background work | `omb` sessions inside tmux, with reconnect and a cross-project session hub |
|
|
27
|
+
| Useful specialists | Native Bizar roles for orchestration, planning, research, implementation, review, security, verification, and documentation |
|
|
28
|
+
| Stronger completion signals | Host-observed checks, fresh evidence, explicit acceptance, and serialized integration |
|
|
29
|
+
| A readable operator experience | Native Advisor enabled by default, visible active agents, hidden tool trace, and readable chat output |
|
|
30
|
+
|
|
31
|
+
Bizar does not start a second agent runtime, replace OMP's task system, or include a Claude Code, MCP, or background daemon compatibility layer.
|
|
8
32
|
|
|
9
33
|
## Install
|
|
10
34
|
|
|
11
|
-
|
|
35
|
+
### Full installation
|
|
36
|
+
|
|
37
|
+
The supported installation path configures the complete OMP experience in one command.
|
|
12
38
|
|
|
13
|
-
|
|
39
|
+
Requires Node.js 22 or newer, Bun 1.3.14 or newer, and OMP 18.2.6 on `PATH`. OMP 18.2.5 and 18.2.4 remain regression-qualified.
|
|
14
40
|
|
|
15
41
|
```sh
|
|
16
|
-
|
|
17
|
-
omp
|
|
42
|
+
npx --yes @polderlabs/bizar-omp setup
|
|
18
43
|
```
|
|
19
44
|
|
|
20
|
-
|
|
45
|
+
The installer:
|
|
46
|
+
|
|
47
|
+
1. Installs the exact Bizar version globally, providing `omb` and `bizar-omp`.
|
|
48
|
+
2. Registers and enables the package through `omp plugin install`.
|
|
49
|
+
3. Installs the extension, eight specialist agents, skills, rules, prompts, tools, and model roles.
|
|
50
|
+
4. Makes the Bizar Orchestrator the default conversation and enables OMP's native Advisor.
|
|
51
|
+
5. Applies the autonomous preset for background tasks, steerable Collab, visible agents, and readable output.
|
|
52
|
+
6. Writes a recoverable receipt and preserves credentials, provider choices, approval policies, and project overrides.
|
|
21
53
|
|
|
22
|
-
|
|
54
|
+
Interactive terminals show a phase dashboard with detected versions, applied settings, the recovery receipt, and the next commands. Preview or script the same operation with:
|
|
23
55
|
|
|
24
56
|
```sh
|
|
25
|
-
npx --yes @polderlabs/bizar-omp
|
|
57
|
+
npx --yes @polderlabs/bizar-omp setup --dry-run
|
|
58
|
+
npx --yes @polderlabs/bizar-omp setup --json
|
|
26
59
|
```
|
|
27
60
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
The companion CLI is optional for diagnostics and repair:
|
|
61
|
+
### Start OMP
|
|
31
62
|
|
|
32
63
|
```sh
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
omb
|
|
64
|
+
omp # normal direct OMP session
|
|
65
|
+
omb # durable tmux-backed session
|
|
66
|
+
omb agents # session hub and new-session selector
|
|
36
67
|
```
|
|
37
68
|
|
|
38
|
-
`
|
|
69
|
+
`omp` remains untouched. `omb` is the explicit durable mode. Plain `omb` reconnects to the most recently active live session in the current directory, or creates that directory's default session when none exists.
|
|
39
70
|
|
|
40
|
-
|
|
71
|
+
### Verify, repair, or remove
|
|
41
72
|
|
|
42
|
-
|
|
73
|
+
```sh
|
|
74
|
+
bizar-omp install-doctor
|
|
75
|
+
omp plugin list
|
|
76
|
+
omp plugin doctor
|
|
77
|
+
```
|
|
43
78
|
|
|
44
|
-
|
|
79
|
+
For a profile-scoped installation:
|
|
45
80
|
|
|
46
81
|
```sh
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
npm run verify:package
|
|
82
|
+
npx --yes @polderlabs/bizar-omp setup --profile bizar
|
|
83
|
+
omp --profile bizar
|
|
84
|
+
bizar-omp install-doctor --profile bizar
|
|
51
85
|
```
|
|
52
86
|
|
|
53
|
-
|
|
87
|
+
Each profile has its own OMP login and model configuration. Remove Bizar's plugin configuration with `bizar-omp uninstall`. The command restores settings only when they still contain the values Bizar applied, preserving later edits. Remove the global shell commands separately if needed:
|
|
54
88
|
|
|
55
|
-
|
|
89
|
+
```sh
|
|
90
|
+
npm uninstall --global @polderlabs/bizar-omp
|
|
91
|
+
```
|
|
56
92
|
|
|
57
|
-
|
|
93
|
+
### Extension-only installation
|
|
58
94
|
|
|
59
|
-
|
|
95
|
+
Use the native plugin command only when you already manage the shell commands yourself or only need the OMP extension:
|
|
60
96
|
|
|
61
|
-
|
|
97
|
+
```sh
|
|
98
|
+
omp plugin install @polderlabs/bizar-omp
|
|
99
|
+
```
|
|
62
100
|
|
|
63
|
-
|
|
101
|
+
This does not install `omb` or `bizar-omp` on `PATH`. For checkout development, use `omp plugin link /path/to/bizaromp`.
|
|
64
102
|
|
|
65
|
-
|
|
103
|
+
## How to work with Bizar
|
|
66
104
|
|
|
67
|
-
|
|
105
|
+
The main OMP conversation is the Bizar Orchestrator. It chooses the smallest method that fits the work:
|
|
68
106
|
|
|
69
|
-
|
|
107
|
+
| Tier | Use it for | Typical proof |
|
|
108
|
+
| --- | --- | --- |
|
|
109
|
+
| Focused | A local bug fix, styling change, typo, or narrow documentation edit | Targeted check, diff review, or smoke test |
|
|
110
|
+
| Bounded | A contained behavior change or small feature | Acceptance criteria and focused tests |
|
|
111
|
+
| Full | Security, architecture, migrations, concurrency, unknown regressions, or cross-cutting work | Planning, isolation, fresh evidence, review, and serialized integration |
|
|
70
112
|
|
|
71
|
-
|
|
113
|
+
The route can escalate when scope or risk changes. A focused task is still validated; it simply avoids ceremony that does not improve the result.
|
|
72
114
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
115
|
+
### Useful commands inside OMP
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
/bizar run <objective> start a bounded workflow
|
|
119
|
+
/bizar status inspect the active workflow
|
|
120
|
+
/bizar evidence inspect current proof
|
|
121
|
+
/bizar cancel cancel the active workflow
|
|
122
|
+
/bizar-models map available OMP models to Bizar roles
|
|
123
|
+
/bizar-models health validate Bizar role selectors without changing them
|
|
76
124
|
```
|
|
77
125
|
|
|
78
|
-
`
|
|
126
|
+
`/bizar-models` reads OMP's authenticated model catalog locally, assigns the best available selectors to Bizar roles, validates native resolution, and preserves unrelated roles.
|
|
127
|
+
|
|
128
|
+
## Durable sessions and the agents hub
|
|
79
129
|
|
|
80
|
-
|
|
130
|
+
Use `omb` when a session must keep running after the terminal client closes. OMP runs inside a Bizar-owned tmux host from the first process. The client can detach without stopping the host or its background agents.
|
|
81
131
|
|
|
82
132
|
```sh
|
|
83
133
|
omb new "Investigate the API timeout"
|
|
84
134
|
omb --session api-retries
|
|
85
|
-
omb
|
|
135
|
+
omb agents
|
|
86
136
|
```
|
|
87
137
|
|
|
88
|
-
|
|
138
|
+
The `omb agents` hub provides:
|
|
89
139
|
|
|
90
|
-
|
|
140
|
+
- A workspace rail and grouped session list across projects.
|
|
141
|
+
- A global registry of every Bizar session, including sessions running outside the directory where `omb agents` was opened.
|
|
142
|
+
- Session attach, rename, delete, refresh, settings, help, and reconnect controls.
|
|
143
|
+
- A new-session selector opened with `n`.
|
|
144
|
+
- Fuzzy directory search, parent and child navigation, recent project shortcuts, and direct absolute paths.
|
|
145
|
+
- A configurable Projects folder in Settings; its child directories are indexed for quick new-session dispatch from anywhere.
|
|
146
|
+
- Optional initial task text and session naming without leaving the TUI.
|
|
147
|
+
- Native OMP session titles with deterministic project/task fallbacks.
|
|
91
148
|
|
|
92
|
-
|
|
93
|
-
omb agents
|
|
94
|
-
```
|
|
149
|
+
Key controls are `Enter` attach, `n` new session, `e` rename, `d` delete, `s` settings, `r` refresh, `?` help, and `q` exit. Inside a session, `Ctrl-d` or `Ctrl-c` returns to the hub without sending EOF or interrupt input to OMP. Mouse scrolling is enabled by default and can be changed in Settings.
|
|
95
150
|
|
|
96
|
-
|
|
151
|
+
To configure fast project picking, open `s`, select `Projects folder`, press `Enter`, enter a path such as `~/projects`, and press `s` to save. The new-session selector keeps direct absolute paths and parent navigation, while also showing the configured folder and its immediate child directories.
|
|
97
152
|
|
|
98
|
-
|
|
153
|
+
## Model roles and native assets
|
|
99
154
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
omp join "<control-link>"
|
|
104
|
-
```
|
|
155
|
+
Bizar registers visible `bizar_*` roles without replacing operator-owned mappings. The Orchestrator and Advisor are enabled by default; specialist definitions are available to OMP when the orchestrator delegates work. Installation also provides the `bizar-omp` orientation skill, workflow skills, native rules, prompts, tools, and the OMP-native development reference skill.
|
|
156
|
+
|
|
157
|
+
## Security boundaries
|
|
105
158
|
|
|
106
|
-
|
|
159
|
+
- Bizar is trusted in-process extension code. OMP extensions and shell commands are not an OS sandbox.
|
|
160
|
+
- API keys, tokens, `.env` contents, and private control links are never written to source, logs, commits, or evidence.
|
|
161
|
+
- Credentials, providers, explicit tool policies, model selections, and project overrides remain owned by OMP.
|
|
162
|
+
- Managed worker changes remain isolated and are retained for explicit parent integration. Bizar does not silently apply worker patches.
|
|
163
|
+
- Install only in projects and environments you trust. Review changes with `setup --dry-run` and `bizar-omp install-doctor`.
|
|
107
164
|
|
|
108
|
-
|
|
165
|
+
## Compatibility
|
|
109
166
|
|
|
110
|
-
|
|
167
|
+
| Component | Qualified baseline |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| OMP | 18.2.6, with 18.2.5 and 18.2.4 regression-qualified |
|
|
170
|
+
| Node.js | 22.x and 24.x in CI |
|
|
171
|
+
| Bun | 1.3.14 compatibility job |
|
|
172
|
+
| Package | `@polderlabs/bizar-omp@0.4.4` |
|
|
111
173
|
|
|
112
|
-
|
|
174
|
+
Upstream OMP changes require a new compatibility baseline and contract run. The package does not widen a version range to hide an unqualified runtime.
|
|
113
175
|
|
|
114
|
-
##
|
|
176
|
+
## Development
|
|
115
177
|
|
|
116
|
-
|
|
178
|
+
```sh
|
|
179
|
+
npm install
|
|
180
|
+
make verify
|
|
181
|
+
```
|
|
117
182
|
|
|
118
|
-
|
|
119
|
-
|
|
183
|
+
The verification gate runs typecheck, tests, package checks, generated documentation checks, and secret scanning. A native OMP qualification run is also available when the OMP source package is installed:
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
npm run verify:omp
|
|
120
187
|
```
|
|
121
188
|
|
|
122
|
-
|
|
189
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for branches, Conventional Commits, release tags, and trusted publishing. Release notes live in [`docs/releases/`](docs/releases/).
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT. See [LICENSE](LICENSE).
|
|
194
|
+
|
|
195
|
+
<div align="center">
|
|
196
|
+
<a href="https://polderlabs.io/"><img src="docs/assets/sponsored-by-polderlabs.svg" alt="Sponsored by PolderLabs" width="100%" /></a>
|
|
197
|
+
</div>
|
package/dist/cli/agents-ink.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents-ink.d.ts","sourceRoot":"","sources":["../../src/cli/agents-ink.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,cAAc,EAAwB,MAAM,wBAAwB,CAAC;AAC3H,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"agents-ink.d.ts","sourceRoot":"","sources":["../../src/cli/agents-ink.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,cAAc,EAAwB,MAAM,wBAAwB,CAAC;AAC3H,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAG1D,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,cAAc,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,WAAW,CAAA;CAAE,CAAC;AAsfhD,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CAOtJ"}
|
package/dist/cli/agents-ink.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { Box, Text, useApp, useInput, useWindowSize, render } from "ink";
|
|
4
4
|
import { listOmbSessions, ombSessionDisplayName } from "../omp/omb-sessions.js";
|
|
5
|
+
import { filterDirectoryOptions, readDirectoryOptions } from "./directory-selector.js";
|
|
5
6
|
const statusLabel = (state) => state === "attached" ? "CONNECTED" : state === "detached" ? "DISCONNECTED" : "STOPPED";
|
|
6
7
|
const statusColor = (state) => state === "attached" ? "green" : state === "detached" ? "yellow" : "gray";
|
|
7
8
|
const statusMark = (state) => state === "attached" ? "●" : state === "detached" ? "◌" : "○";
|
|
@@ -32,7 +33,7 @@ function Header({ cwd, sessions }) {
|
|
|
32
33
|
const connected = sessions.filter(session => session.state === "attached").length;
|
|
33
34
|
const disconnected = sessions.filter(session => session.state === "detached").length;
|
|
34
35
|
const stopped = sessions.filter(session => session.state === "dead").length;
|
|
35
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { bold: true, color: "cyan", children: "\u25C6 BIZAR / AGENTS HUB" }),
|
|
36
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { bold: true, color: "cyan", children: "\u25C6 BIZAR / AGENTS HUB" }), _jsxs(Text, { dimColor: true, children: ["GLOBAL \u00B7 launched from ", short(cwd, 34)] })] }), _jsxs(Box, { marginTop: 1, gap: 2, children: [_jsxs(Text, { color: "green", children: ["\u25CF ", connected, " connected"] }), _jsxs(Text, { color: "yellow", children: ["\u25CC ", disconnected, " disconnected"] }), _jsxs(Text, { color: "gray", children: ["\u25CB ", stopped, " stopped"] })] })] }));
|
|
36
37
|
}
|
|
37
38
|
function WorkspaceRail({ sessions, selected }) {
|
|
38
39
|
const projects = projectCounts(sessions);
|
|
@@ -59,36 +60,178 @@ function Inspector({ session }) {
|
|
|
59
60
|
return (_jsxs(Box, { width: "29%", minWidth: 25, flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "SELECTED SESSION" }), !session ? _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Select a session to inspect it." }) }) : (_jsxs(Box, { marginTop: 1, flexDirection: "column", gap: 1, children: [_jsxs(Text, { bold: true, color: statusColor(session.state), children: [statusMark(session.state), " ", statusLabel(session.state)] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "NAME" }), _jsx(Text, { children: ombSessionDisplayName(session) }), _jsx(Text, { dimColor: true, children: session.nativeName ? "OMP session title" : "Generated from task or project" })] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "PROJECT" }), _jsx(Text, { children: short(session.cwd, 30) })] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "COMMAND" }), _jsx(Text, { children: short(session.command || "omp", 30) })] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "LAST ACTIVITY" }), _jsx(Text, { children: age(session) })] })] }))] }));
|
|
60
61
|
}
|
|
61
62
|
function Footer() {
|
|
62
|
-
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, justifyContent: "space-between", children: [_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "Enter" }), " attach ", _jsx(Text, { color: "cyan", children: "n" }), " new ", _jsx(Text, { color: "cyan", children: "e" }), " rename ", _jsx(Text, { color: "cyan", children: "d" }), " delete"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "s" }), " settings ", _jsx(Text, { color: "cyan", children: "r" }), " refresh ", _jsx(Text, { color: "cyan", children: "?" }), " help ", _jsx(Text, { color: "cyan", children: "q" }), " quit"] })] }));
|
|
63
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, justifyContent: "space-between", children: [_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "Enter" }), " attach ", _jsx(Text, { color: "cyan", children: "n" }), " new selector ", _jsx(Text, { color: "cyan", children: "e" }), " rename ", _jsx(Text, { color: "cyan", children: "d" }), " delete"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "s" }), " settings ", _jsx(Text, { color: "cyan", children: "r" }), " refresh ", _jsx(Text, { color: "cyan", children: "?" }), " help ", _jsx(Text, { color: "cyan", children: "q" }), " quit"] })] }));
|
|
63
64
|
}
|
|
64
65
|
function Modal({ title, children, width, columns }) {
|
|
65
66
|
return (_jsxs(Box, { position: "absolute", top: 2, left: Math.max(0, Math.floor((columns - width) / 2)), width: width, flexDirection: "column", borderStyle: "double", borderColor: "cyan", backgroundColor: "black", paddingX: 2, paddingY: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: title }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: children })] }));
|
|
66
67
|
}
|
|
67
|
-
function
|
|
68
|
+
function NewSessionModal({ cwd, sessions, projectsFolder, selected, columns, rows, onSubmit, onClose, }) {
|
|
69
|
+
const initialDirectory = selected?.cwd && selected.cwd !== "?" ? selected.cwd : cwd;
|
|
70
|
+
const [mode, setMode] = useState("directory");
|
|
71
|
+
const [directoryPath, setDirectoryPath] = useState(initialDirectory);
|
|
72
|
+
const [directoryQuery, setDirectoryQuery] = useState("");
|
|
73
|
+
const [directoryOptions, setDirectoryOptions] = useState([]);
|
|
74
|
+
const [directorySelected, setDirectorySelected] = useState(0);
|
|
75
|
+
const [projectCwd, setProjectCwd] = useState(initialDirectory);
|
|
76
|
+
const [task, setTask] = useState("");
|
|
77
|
+
const [sessionId, setSessionId] = useState("");
|
|
78
|
+
const [loading, setLoading] = useState(true);
|
|
79
|
+
const recentPaths = useMemo(() => [...new Set(sessions.map(session => session.cwd).filter(path => path && path !== "?"))], [sessions]);
|
|
80
|
+
const filteredDirectories = useMemo(() => filterDirectoryOptions(directoryOptions, directoryQuery), [directoryOptions, directoryQuery]);
|
|
81
|
+
const visibleCount = Math.max(4, Math.min(9, rows - 14));
|
|
82
|
+
const directoryStart = Math.max(0, Math.min(Math.max(0, filteredDirectories.length - visibleCount), directorySelected - Math.floor(visibleCount / 2)));
|
|
83
|
+
const visibleDirectories = filteredDirectories.slice(directoryStart, directoryStart + visibleCount);
|
|
84
|
+
const selectedDirectory = filteredDirectories[directorySelected];
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
let active = true;
|
|
87
|
+
setLoading(true);
|
|
88
|
+
void readDirectoryOptions(directoryPath, recentPaths, directoryQuery, projectsFolder).then(options => {
|
|
89
|
+
if (!active)
|
|
90
|
+
return;
|
|
91
|
+
setDirectoryOptions(options);
|
|
92
|
+
setDirectorySelected(0);
|
|
93
|
+
setLoading(false);
|
|
94
|
+
});
|
|
95
|
+
return () => { active = false; };
|
|
96
|
+
}, [directoryPath, directoryQuery, projectsFolder, recentPaths]);
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
setDirectorySelected(value => Math.min(value, Math.max(0, filteredDirectories.length - 1)));
|
|
99
|
+
}, [filteredDirectories.length]);
|
|
100
|
+
const selectDirectory = (option) => {
|
|
101
|
+
if (!option)
|
|
102
|
+
return;
|
|
103
|
+
if (option.kind === "parent") {
|
|
104
|
+
setDirectoryPath(option.path);
|
|
105
|
+
setDirectoryQuery("");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
setProjectCwd(option.path);
|
|
109
|
+
setMode("task");
|
|
110
|
+
};
|
|
111
|
+
const openDirectory = (option) => {
|
|
112
|
+
if (!option || option.kind === "current")
|
|
113
|
+
return;
|
|
114
|
+
setDirectoryPath(option.path);
|
|
115
|
+
setDirectoryQuery("");
|
|
116
|
+
};
|
|
117
|
+
const goToParent = () => {
|
|
118
|
+
const parent = directoryPath.replace(/[\\/]$/, "").split(/[\\/]/).slice(0, -1).join("/") || "/";
|
|
119
|
+
if (parent !== directoryPath) {
|
|
120
|
+
setDirectoryPath(parent);
|
|
121
|
+
setDirectoryQuery("");
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
useInput((input, key) => {
|
|
125
|
+
if (key.escape) {
|
|
126
|
+
if (mode === "name")
|
|
127
|
+
setMode("task");
|
|
128
|
+
else if (mode === "task")
|
|
129
|
+
setMode("directory");
|
|
130
|
+
else
|
|
131
|
+
onClose();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (mode === "directory") {
|
|
135
|
+
if (key.upArrow || input === "k")
|
|
136
|
+
setDirectorySelected(value => Math.max(0, value - 1));
|
|
137
|
+
else if (key.downArrow || input === "j")
|
|
138
|
+
setDirectorySelected(value => Math.min(Math.max(0, filteredDirectories.length - 1), value + 1));
|
|
139
|
+
else if (key.return)
|
|
140
|
+
selectDirectory(selectedDirectory);
|
|
141
|
+
else if (key.rightArrow || input === "l")
|
|
142
|
+
openDirectory(selectedDirectory);
|
|
143
|
+
else if (key.leftArrow || input === "h")
|
|
144
|
+
goToParent();
|
|
145
|
+
else if (key.backspace || (key.ctrl && input === "h")) {
|
|
146
|
+
setDirectoryQuery(value => value.slice(0, -1));
|
|
147
|
+
setDirectorySelected(0);
|
|
148
|
+
}
|
|
149
|
+
else if (key.ctrl && input === "u") {
|
|
150
|
+
setDirectoryQuery("");
|
|
151
|
+
setDirectorySelected(0);
|
|
152
|
+
}
|
|
153
|
+
else if (input && !key.ctrl && !key.meta && input >= " ") {
|
|
154
|
+
setDirectoryQuery(value => `${value}${input}`);
|
|
155
|
+
setDirectorySelected(0);
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const updateText = (setter) => {
|
|
160
|
+
if (key.backspace || (key.ctrl && input === "h"))
|
|
161
|
+
setter(value => value.slice(0, -1));
|
|
162
|
+
else if (key.ctrl && input === "u")
|
|
163
|
+
setter("");
|
|
164
|
+
else if (input && !key.ctrl && !key.meta && input >= " ")
|
|
165
|
+
setter(value => `${value}${input}`);
|
|
166
|
+
};
|
|
167
|
+
if (mode === "task") {
|
|
168
|
+
if (key.return)
|
|
169
|
+
setMode("name");
|
|
170
|
+
else
|
|
171
|
+
updateText(setTask);
|
|
172
|
+
}
|
|
173
|
+
else if (key.return) {
|
|
174
|
+
onSubmit({ projectCwd, task: task.trim(), sessionId: sessionId.trim() });
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
updateText(setSessionId);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
const width = Math.min(92, Math.max(54, columns - 6));
|
|
181
|
+
return (_jsx(Modal, { title: "NEW BIZAR SESSION", width: width, columns: columns, children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: mode === "directory" ? "cyan" : undefined, bold: mode === "directory", children: "1 Project directory" }), mode === "directory" ? (_jsxs(_Fragment, { children: [_jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "cyan", children: "\u2315 " }), _jsx(Text, { children: directoryQuery || "Type to fuzzy-search directories" })] }), _jsx(Text, { dimColor: true, children: directoryPath }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: loading ? _jsx(Text, { color: "yellow", children: "Scanning directories\u2026" }) : visibleDirectories.length === 0 ? _jsx(Text, { color: "yellow", children: "No matching directories. Type an absolute path to jump." }) : visibleDirectories.map((option, offset) => {
|
|
182
|
+
const index = directoryStart + offset;
|
|
183
|
+
const isSelected = index === directorySelected;
|
|
184
|
+
return (_jsxs(Box, { flexDirection: "column", backgroundColor: isSelected ? "blue" : undefined, children: [_jsxs(Text, { color: isSelected ? "white" : option.kind === "recent" ? "green" : "cyan", bold: isSelected, children: [isSelected ? "›" : " ", " ", option.kind === "parent" ? "↰" : option.kind === "recent" ? "●" : option.kind === "current" ? "◆" : option.kind === "projects-root" ? "▣" : option.kind === "project" ? "◇" : "□", " ", option.label] }), isSelected && _jsxs(Text, { dimColor: true, children: [" ", option.detail ?? option.path] })] }, `${option.kind}:${option.path}`));
|
|
185
|
+
}) }), _jsxs(Text, { dimColor: true, children: [filteredDirectories.length ? `${directoryStart + 1}-${Math.min(directoryStart + visibleCount, filteredDirectories.length)} of ${filteredDirectories.length}` : "0 directories", " \u00B7 Enter choose \u2192 open \u2190 parent"] })] })) : (_jsxs(Text, { dimColor: true, children: [" ", projectCwd] })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: mode === "task" ? "cyan" : undefined, bold: mode === "task", children: "2 Initial task" }), mode === "task" ? _jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { color: "cyan", children: "\u203A " }), _jsx(Text, { children: task || "Optional task description" })] }) : _jsxs(Text, { dimColor: true, children: [" ", task || "Optional"] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: mode === "name" ? "cyan" : undefined, bold: mode === "name", children: "3 Session name" }), mode === "name" ? _jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, children: [_jsx(Text, { color: "cyan", children: "\u203A " }), _jsx(Text, { children: sessionId || "Blank generates a name" })] }) : _jsxs(Text, { dimColor: true, children: [" ", sessionId || "Generated automatically"] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Type to search or edit \u00B7 Up/Down select \u00B7 Enter continue \u00B7 Esc back" }), _jsx(Text, { color: "yellow", children: mode === "directory" ? projectsFolder ? `Global sessions are listed below; ${projectsFolder} is indexed here.` : "Global sessions are listed below; configure a projects folder in settings for faster picking." : mode === "name" ? "Enter starts the durable OMB host." : "Enter moves to the session name." })] })] }) }));
|
|
186
|
+
}
|
|
187
|
+
function SettingsModal({ settings, selected, columns, onToggle, onMove, onSetProjectsFolder, onSave, onClose }) {
|
|
68
188
|
const options = [
|
|
69
189
|
["Attach plain omb to latest live session", settings.autoAttachLatest],
|
|
70
190
|
["Enable mouse scrolling in OMB sessions", settings.mouseScrolling],
|
|
71
191
|
["Show stopped sessions in this view", settings.showStoppedSessions],
|
|
192
|
+
["Projects folder", settings.projectsFolder || "not set"],
|
|
72
193
|
];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
function SettingsKeyHandler({ onToggle, onSave, onClose }) {
|
|
194
|
+
const [editingFolder, setEditingFolder] = useState(false);
|
|
195
|
+
const [folderInput, setFolderInput] = useState(settings.projectsFolder);
|
|
76
196
|
useInput((input, key) => {
|
|
77
|
-
if (
|
|
197
|
+
if (editingFolder) {
|
|
198
|
+
if (key.escape) {
|
|
199
|
+
setFolderInput(settings.projectsFolder);
|
|
200
|
+
setEditingFolder(false);
|
|
201
|
+
}
|
|
202
|
+
else if (key.return) {
|
|
203
|
+
onSetProjectsFolder(folderInput.trim());
|
|
204
|
+
setEditingFolder(false);
|
|
205
|
+
}
|
|
206
|
+
else if (key.backspace || (key.ctrl && input === "h"))
|
|
207
|
+
setFolderInput(value => value.slice(0, -1));
|
|
208
|
+
else if (key.ctrl && input === "u")
|
|
209
|
+
setFolderInput("");
|
|
210
|
+
else if (input && !key.ctrl && !key.meta && input >= " ")
|
|
211
|
+
setFolderInput(value => `${value}${input}`);
|
|
78
212
|
return;
|
|
79
|
-
|
|
213
|
+
}
|
|
214
|
+
if (key.upArrow)
|
|
215
|
+
onMove(Math.max(0, selected - 1));
|
|
216
|
+
else if (key.downArrow)
|
|
217
|
+
onMove(Math.min(options.length - 1, selected + 1));
|
|
218
|
+
else if (key.escape || input === "q")
|
|
80
219
|
onClose();
|
|
81
|
-
else if (input === " " || key.return)
|
|
82
|
-
onToggle();
|
|
83
220
|
else if (input === "s")
|
|
84
221
|
onSave();
|
|
222
|
+
else if (input === " " || key.return) {
|
|
223
|
+
if (selected === 3)
|
|
224
|
+
setEditingFolder(true);
|
|
225
|
+
else
|
|
226
|
+
onToggle();
|
|
227
|
+
}
|
|
85
228
|
});
|
|
86
|
-
return
|
|
229
|
+
return (_jsxs(Modal, { title: "OMB SETTINGS", width: Math.min(70, Math.max(46, columns - 6)), columns: columns, children: [options.map(([label, enabled], index) => (_jsxs(Text, { color: index === selected ? "white" : undefined, backgroundColor: index === selected ? "blue" : undefined, children: [index === selected ? "›" : " ", " ", typeof enabled === "boolean" ? `[${enabled ? "on " : "off"}]` : enabled, " ", label] }, label))), selected === 3 && (_jsxs(Box, { marginTop: 1, borderStyle: "single", borderColor: editingFolder ? "cyan" : "gray", paddingX: 1, children: [_jsx(Text, { color: "cyan", children: "\u2315 " }), _jsx(Text, { children: editingFolder ? folderInput || "Type ~/projects or an absolute path" : settings.projectsFolder || "Press Enter to set a projects folder" })] })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: editingFolder ? "Type path Enter apply Ctrl-U clear Esc cancel" : "Up/Down select Space/Enter edit s save Esc cancel" }), _jsx(Text, { color: "yellow", children: selected === 1 ? "Mouse mode applies to existing OMB sessions when saved." : selected === 3 ? "Child directories from this folder appear in the new-session picker." : "The hub always shows every Bizar session across projects." })] }));
|
|
87
230
|
}
|
|
88
231
|
function HelpModal({ columns, onClose }) {
|
|
89
232
|
useInput((input, key) => { if (key.escape || key.return || input === "q")
|
|
90
233
|
onClose(); });
|
|
91
|
-
return (_jsxs(Modal, { title: "OMB AGENTS SHORTCUTS", width: Math.min(64, Math.max(42, columns - 6)), columns: columns, children: [_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "Enter" }), " attach to the selected session"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "n" }), "
|
|
234
|
+
return (_jsxs(Modal, { title: "OMB AGENTS SHORTCUTS", width: Math.min(64, Math.max(42, columns - 6)), columns: columns, children: [_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "Enter" }), " attach to the selected session"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "n" }), " open the fuzzy directory session selector"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "e" }), " rename the selected session"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "d" }), " delete after confirmation"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "s" }), " open launcher settings"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "r" }), " refresh the session list"] }), _jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "q / Esc" }), " leave this view"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "Global view: every tmux session with the bizar- prefix is shown." }), _jsx(Text, { dimColor: true, children: "Press Enter or Esc to return." })] }));
|
|
92
235
|
}
|
|
93
236
|
function DeleteModal({ session, columns, onConfirm, onClose }) {
|
|
94
237
|
useInput((input, key) => {
|
|
@@ -135,7 +278,7 @@ function AgentsApp({ cwd, env, sessions: initialSessions, settings: initialSetti
|
|
|
135
278
|
else if (key.return && selectedSession)
|
|
136
279
|
finish({ type: "attach", session: selectedSession });
|
|
137
280
|
else if (input === "n")
|
|
138
|
-
|
|
281
|
+
setOverlay("new");
|
|
139
282
|
else if (input === "e" && selectedSession)
|
|
140
283
|
finish({ type: "rename", session: selectedSession });
|
|
141
284
|
else if (input === "d" && selectedSession) {
|
|
@@ -152,19 +295,12 @@ function AgentsApp({ cwd, env, sessions: initialSessions, settings: initialSetti
|
|
|
152
295
|
setOverlay("settings");
|
|
153
296
|
}
|
|
154
297
|
});
|
|
155
|
-
return (_jsxs(Box, { flexDirection: "column", width: "100%", minHeight: Math.max(12, rows - 1), paddingX: 1, children: [_jsx(Header, { cwd: cwd, sessions: sessions }), _jsxs(Box, { flexGrow: 1, marginTop: 1, gap: 1, children: [_jsx(WorkspaceRail, { sessions: sessions, selected: selectedSession }), _jsx(SessionList, { sessions: sessions, selected: selected, rows: rows, width: Math.max(34, Math.floor(columns * 0.42)) }), _jsx(Inspector, { session: selectedSession })] }), _jsx(Footer, {}), overlay === "help" && _jsx(HelpModal, { columns: columns, onClose: () => setOverlay(undefined) }), overlay === "delete" && selectedSession && _jsx(DeleteModal, { session: selectedSession, columns: columns, onConfirm: () => finish({ type: "delete", session: selectedSession }), onClose: () => setOverlay(undefined) }), overlay === "settings" && (_jsx(SettingsModal, { settings: draftSettings, selected: overlaySelected, columns: columns, onToggle: () => {
|
|
298
|
+
return (_jsxs(Box, { flexDirection: "column", width: "100%", minHeight: Math.max(12, rows - 1), paddingX: 1, children: [_jsx(Header, { cwd: cwd, sessions: sessions }), _jsxs(Box, { flexGrow: 1, marginTop: 1, gap: 1, children: [_jsx(WorkspaceRail, { sessions: sessions, selected: selectedSession }), _jsx(SessionList, { sessions: sessions, selected: selected, rows: rows, width: Math.max(34, Math.floor(columns * 0.42)) }), _jsx(Inspector, { session: selectedSession })] }), _jsx(Footer, {}), overlay === "new" && (_jsx(NewSessionModal, { cwd: cwd, sessions: allSessions, projectsFolder: settings.projectsFolder, selected: selectedSession, columns: columns, rows: rows, onSubmit: value => finish({ type: "new", ...value }), onClose: () => setOverlay(undefined) })), overlay === "help" && _jsx(HelpModal, { columns: columns, onClose: () => setOverlay(undefined) }), overlay === "delete" && selectedSession && _jsx(DeleteModal, { session: selectedSession, columns: columns, onConfirm: () => finish({ type: "delete", session: selectedSession }), onClose: () => setOverlay(undefined) }), overlay === "settings" && (_jsx(SettingsModal, { settings: draftSettings, selected: overlaySelected, onMove: setOverlaySelected, columns: columns, onToggle: () => {
|
|
299
|
+
if (overlaySelected === 3)
|
|
300
|
+
return;
|
|
156
301
|
const field = overlaySelected === 0 ? "autoAttachLatest" : overlaySelected === 1 ? "mouseScrolling" : "showStoppedSessions";
|
|
157
302
|
setDraftSettings(value => ({ ...value, [field]: !value[field] }));
|
|
158
|
-
}, onSave: () => finish({ type: "settings", settings: draftSettings }), onClose: () => setOverlay(undefined) }))
|
|
159
|
-
}
|
|
160
|
-
function SettingsNavigation({ selected, onChange }) {
|
|
161
|
-
useInput((_input, key) => {
|
|
162
|
-
if (key.upArrow)
|
|
163
|
-
onChange(Math.max(0, selected - 1));
|
|
164
|
-
else if (key.downArrow)
|
|
165
|
-
onChange(Math.min(2, selected + 1));
|
|
166
|
-
});
|
|
167
|
-
return null;
|
|
303
|
+
}, onSetProjectsFolder: value => setDraftSettings(current => ({ ...current, projectsFolder: value })), onSave: () => finish({ type: "settings", settings: draftSettings }), onClose: () => setOverlay(undefined) }))] }));
|
|
168
304
|
}
|
|
169
305
|
export async function runInkAgentsHub(cwd, env, sessions, settings) {
|
|
170
306
|
let action = { type: "quit" };
|