@hachej/boring-ui-cli 0.1.40 → 0.1.42
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 +74 -178
- package/dist/server/modeApps.js +75 -2
- package/dist/server/pluginDiscovery.js +5 -4
- package/dist/server/pluginFrontRuntime.js +88 -0
- package/package.json +7 -6
- package/public/assets/{DebugDrawer-BKnTv0ta.js → DebugDrawer-gDfs30E4.js} +1 -1
- package/public/assets/{_baseUniq-BLLh7JYg.js → _baseUniq-Cq5XC-pK.js} +1 -1
- package/public/assets/{arc-CCKoqe23.js → arc-BmAfBjRZ.js} +1 -1
- package/public/assets/{architectureDiagram-Q4EWVU46-BiezsVHo.js → architectureDiagram-Q4EWVU46-Eklp421i.js} +1 -1
- package/public/assets/{blockDiagram-DXYQGD6D-BiontjNM.js → blockDiagram-DXYQGD6D-DtGFygpY.js} +1 -1
- package/public/assets/{c4Diagram-AHTNJAMY-BBwk8oFU.js → c4Diagram-AHTNJAMY-BZswzGDM.js} +1 -1
- package/public/assets/channel-Dq1UFgB_.js +1 -0
- package/public/assets/{chunk-4BX2VUAB-DPKS56FY.js → chunk-4BX2VUAB-F_g9lHXM.js} +1 -1
- package/public/assets/{chunk-4TB4RGXK-BQsSw8RM.js → chunk-4TB4RGXK-BTm8fprd.js} +1 -1
- package/public/assets/{chunk-55IACEB6-Ci72MzDy.js → chunk-55IACEB6-BcF-RbgG.js} +1 -1
- package/public/assets/{chunk-EDXVE4YY-DEmniXcF.js → chunk-EDXVE4YY-jdXrQWwg.js} +1 -1
- package/public/assets/{chunk-FMBD7UC4-BlX3J66_.js → chunk-FMBD7UC4-Bg13Lq7M.js} +1 -1
- package/public/assets/{chunk-OYMX7WX6-CVgYvnDy.js → chunk-OYMX7WX6-BPLuWVtk.js} +1 -1
- package/public/assets/{chunk-QZHKN3VN-CByUXwie.js → chunk-QZHKN3VN-CKAy04iy.js} +1 -1
- package/public/assets/{chunk-YZCP3GAM-kVASw0Ps.js → chunk-YZCP3GAM-bw0yarJe.js} +1 -1
- package/public/assets/classDiagram-6PBFFD2Q-BJUw8mCg.js +1 -0
- package/public/assets/classDiagram-v2-HSJHXN6E-BJUw8mCg.js +1 -0
- package/public/assets/clone-S7Dw6doI.js +1 -0
- package/public/assets/{cose-bilkent-S5V4N54A-P1wJoSYn.js → cose-bilkent-S5V4N54A-BhFSor4L.js} +1 -1
- package/public/assets/{dagre-KV5264BT-COwIFIWR.js → dagre-KV5264BT-BP-JJ5-u.js} +1 -1
- package/public/assets/{diagram-5BDNPKRD-Cu1uZiY8.js → diagram-5BDNPKRD-rEEUrLN8.js} +1 -1
- package/public/assets/{diagram-G4DWMVQ6-LYnPYs25.js → diagram-G4DWMVQ6-kLIqIjZl.js} +1 -1
- package/public/assets/{diagram-MMDJMWI5-BHdCIIZQ.js → diagram-MMDJMWI5-Ba5AbEKB.js} +1 -1
- package/public/assets/{diagram-TYMM5635-DOP9v9nq.js → diagram-TYMM5635-evStAp2X.js} +1 -1
- package/public/assets/{erDiagram-SMLLAGMA-r1ECuX9K.js → erDiagram-SMLLAGMA-D-bE3gq8.js} +1 -1
- package/public/assets/{flowDiagram-DWJPFMVM-BGfCv6DR.js → flowDiagram-DWJPFMVM-CaJjL3ue.js} +1 -1
- package/public/assets/{ganttDiagram-T4ZO3ILL-BT1XjcZL.js → ganttDiagram-T4ZO3ILL-D5RchBme.js} +1 -1
- package/public/assets/{gitGraphDiagram-UUTBAWPF-CI1rp0X1.js → gitGraphDiagram-UUTBAWPF-Qf423d8K.js} +1 -1
- package/public/assets/{graph-DI_xMEn_.js → graph-DCHxrEzC.js} +1 -1
- package/public/assets/{highlighted-body-OFNGDK62-8n743sC6.js → highlighted-body-OFNGDK62-C5r0PLJ8.js} +1 -1
- package/public/assets/index-BMbY-d9g.css +1 -0
- package/public/assets/{index-BbHTuYde.js → index-CyW2LgWg.js} +375 -375
- package/public/assets/{infoDiagram-42DDH7IO-BpRHk6VQ.js → infoDiagram-42DDH7IO-D-904xJW.js} +1 -1
- package/public/assets/{ishikawaDiagram-UXIWVN3A-DKm4l4s3.js → ishikawaDiagram-UXIWVN3A-BRPxrqA5.js} +1 -1
- package/public/assets/{journeyDiagram-VCZTEJTY-BF6q66EE.js → journeyDiagram-VCZTEJTY-DEsDr2q-.js} +1 -1
- package/public/assets/{kanban-definition-6JOO6SKY-ghGywoOn.js → kanban-definition-6JOO6SKY-8wHPWuXt.js} +1 -1
- package/public/assets/{layout-rNIh0dh7.js → layout-ChfR3Hlu.js} +1 -1
- package/public/assets/{linear-uZeMuiY-.js → linear-CL3edB6-.js} +1 -1
- package/public/assets/{min-CUa0xbzO.js → min-CyD06bdU.js} +1 -1
- package/public/assets/{mindmap-definition-QFDTVHPH-CqAtG59w.js → mindmap-definition-QFDTVHPH-BQaZ0zVu.js} +1 -1
- package/public/assets/{pieDiagram-DEJITSTG-p9Q1gHs7.js → pieDiagram-DEJITSTG-DQSS1DuA.js} +1 -1
- package/public/assets/{quadrantDiagram-34T5L4WZ-CXVkk7JI.js → quadrantDiagram-34T5L4WZ-BpHur_Z8.js} +1 -1
- package/public/assets/{requirementDiagram-MS252O5E-Cjtu6Q56.js → requirementDiagram-MS252O5E-C8Op3GXH.js} +1 -1
- package/public/assets/{sankeyDiagram-XADWPNL6-BpnNV-v0.js → sankeyDiagram-XADWPNL6-C-L9CUps.js} +1 -1
- package/public/assets/{sequenceDiagram-FGHM5R23-Dvp42ano.js → sequenceDiagram-FGHM5R23-Po3qK1MI.js} +1 -1
- package/public/assets/{stateDiagram-FHFEXIEX-Cr5QWnxH.js → stateDiagram-FHFEXIEX-CJSrVG_f.js} +1 -1
- package/public/assets/stateDiagram-v2-QKLJ7IA2-CLnVqZGL.js +1 -0
- package/public/assets/{timeline-definition-GMOUNBTQ-zevXOd6g.js → timeline-definition-GMOUNBTQ-DtezkzAy.js} +1 -1
- package/public/assets/{vennDiagram-DHZGUBPP-6KiZ2GA5.js → vennDiagram-DHZGUBPP-BVQGCd3J.js} +1 -1
- package/public/assets/{wardley-RL74JXVD-DiCKkv9h.js → wardley-RL74JXVD-CjGArPjb.js} +1 -1
- package/public/assets/{wardleyDiagram-NUSXRM2D-BV7_buYN.js → wardleyDiagram-NUSXRM2D-4A2kwq7j.js} +1 -1
- package/public/assets/{xychartDiagram-5P7HB3ND-C-mSzQZo.js → xychartDiagram-5P7HB3ND-zHlCL4kA.js} +1 -1
- package/public/index.html +2 -2
- package/public/assets/channel-B1YUFW3e.js +0 -1
- package/public/assets/classDiagram-6PBFFD2Q-CcrldmCi.js +0 -1
- package/public/assets/classDiagram-v2-HSJHXN6E-CcrldmCi.js +0 -1
- package/public/assets/clone-BUiJZ_PH.js +0 -1
- package/public/assets/index-o6MvaI3V.css +0 -1
- package/public/assets/stateDiagram-v2-QKLJ7IA2-Fp_LJrX2.js +0 -1
package/README.md
CHANGED
|
@@ -7,237 +7,133 @@
|
|
|
7
7
|
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
|
-
**Turn an agent into an app — in one command.** Start a full IDE-style workbench pointed at
|
|
10
|
+
**Turn an agent into an app — in one command.** Start a full IDE-style workbench pointed at a folder: chat, file tree, editor, command palette, plugins. No clone, no database, no config.
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
13
|
npx @hachej/boring-ui-cli
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
## TL;DR
|
|
19
|
-
|
|
20
|
-
**The Problem**: You want a coding agent in a browser IDE — but you don't want to clone a repo, set up Postgres, configure auth, or deploy anything. You just want to talk to an AI about your code.
|
|
21
|
-
|
|
22
|
-
**The Solution**: `npx @hachej/boring-ui-cli` starts a full workbench locally, using your current directory as the workspace. It opens a browser tab with chat, file explorer, and panels. Zero setup, zero config, zero deploy.
|
|
23
|
-
|
|
24
|
-
### Why Use @hachej/boring-ui-cli?
|
|
25
|
-
|
|
26
|
-
| Feature | What It Does |
|
|
27
|
-
|---------|--------------|
|
|
28
|
-
| **Zero-config startup** | `npx @hachej/boring-ui-cli` — that's it. Opens your browser to a full agent workbench. |
|
|
29
|
-
| **Simple auth** | Set `ANTHROPIC_API_KEY` in your environment. The agent runs with direct filesystem access to your cwd. |
|
|
30
|
-
| **Full workspace** | Chat, file tree, editor panels, command palette — all running against your real directory. |
|
|
31
|
-
| **No database** | Runs in-memory. State persists for the session. No external dependencies. |
|
|
32
|
-
| **Customizable port + root** | `PORT=8080` and `BORING_AGENT_WORKSPACE_ROOT=/path` env vars for power users. |
|
|
16
|
+
The binary is `boring-ui`.
|
|
33
17
|
|
|
34
18
|
---
|
|
35
19
|
|
|
36
|
-
## Quick
|
|
20
|
+
## Quick start
|
|
37
21
|
|
|
38
22
|
```bash
|
|
39
|
-
#
|
|
40
|
-
cd /path/to/my-project
|
|
41
|
-
|
|
42
|
-
# Start the CLI — opens browser at localhost:5200
|
|
23
|
+
# Open the current folder as a workspace (browser opens at localhost:5200)
|
|
43
24
|
npx @hachej/boring-ui-cli
|
|
44
25
|
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# Point at a specific directory
|
|
49
|
-
BORING_AGENT_WORKSPACE_ROOT=/path/to/project npx @hachej/boring-ui-cli
|
|
26
|
+
# Open a specific folder
|
|
27
|
+
npx @hachej/boring-ui-cli ~/projects/foo
|
|
50
28
|
|
|
51
|
-
#
|
|
52
|
-
|
|
29
|
+
# Custom port / host
|
|
30
|
+
npx @hachej/boring-ui-cli --port 8080 --host 127.0.0.1
|
|
53
31
|
```
|
|
54
32
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"rewrite the test file to use vitest"
|
|
61
|
-
```
|
|
33
|
+
The CLI does not take an API key flag. On first run, if no LLM provider is
|
|
34
|
+
configured it prints a guide: in another terminal run `pi` (or
|
|
35
|
+
`npx @earendil-works/pi-coding-agent`) and use `/login` to add an API key or
|
|
36
|
+
sign in to a subscription (Claude Pro/Max, ChatGPT Plus, Copilot). Credentials
|
|
37
|
+
are saved at `~/.pi/agent/auth.json`; refresh the browser afterward.
|
|
62
38
|
|
|
63
39
|
---
|
|
64
40
|
|
|
65
|
-
##
|
|
66
|
-
|
|
67
|
-
No installation needed — use `npx`:
|
|
41
|
+
## Commands
|
|
68
42
|
|
|
69
|
-
```bash
|
|
70
|
-
npx @hachej/boring-ui-cli
|
|
71
43
|
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
# pnpm
|
|
80
|
-
pnpm add -g @hachej/boring-ui-cli
|
|
81
|
-
|
|
82
|
-
# Then just run:
|
|
83
|
-
boring-ui
|
|
44
|
+
boring-ui [folder] [options] Open a single folder as a workspace (folder mode)
|
|
45
|
+
boring-ui workspaces Start the multi-workspace hub (workspaces mode)
|
|
46
|
+
boring-ui workspaces add <folder> Register a folder as a saved workspace
|
|
47
|
+
boring-ui workspaces list List saved workspaces
|
|
48
|
+
boring-ui workspaces remove <id> Remove a saved workspace
|
|
49
|
+
boring-ui workspaces rename <id> <name> Rename a saved workspace
|
|
50
|
+
boring-ui plugin <subcommand> … Plugin authoring (delegates to boring-ui-plugin)
|
|
84
51
|
```
|
|
85
52
|
|
|
86
|
-
|
|
53
|
+
`boring-ui plugin …` forwards to `@hachej/boring-ui-plugin-cli`; run
|
|
54
|
+
`boring-ui plugin` with no subcommand for its usage.
|
|
87
55
|
|
|
88
|
-
|
|
89
|
-
git clone https://github.com/hachej/boring-ui.git
|
|
90
|
-
cd boring-ui && pnpm install
|
|
91
|
-
pnpm --filter @hachej/boring-ui-cli build
|
|
92
|
-
npx ./packages/cli/dist/index.js
|
|
93
|
-
```
|
|
56
|
+
### Options
|
|
94
57
|
|
|
95
|
-
|
|
58
|
+
| Flag | Default | Description |
|
|
59
|
+
|------|---------|-------------|
|
|
60
|
+
| `-p, --port <port>` | `5200` (or `$PORT`) | HTTP port |
|
|
61
|
+
| `--host <host>` | `0.0.0.0` (or `$HOST`) | Listen host |
|
|
62
|
+
| `-m, --mode <mode>` | `local` | `local` (no sandbox, full network) or `local-sandbox` (bwrap-isolated, no network, Linux only) |
|
|
63
|
+
| `-h, --help` | | Show help |
|
|
96
64
|
|
|
97
|
-
|
|
65
|
+
### Environment variables
|
|
98
66
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
67
|
+
| Variable | Description |
|
|
68
|
+
|----------|-------------|
|
|
69
|
+
| `PORT`, `HOST` | Fallbacks for `--port` / `--host` |
|
|
70
|
+
| `BORING_MODE` | Fallback for `--mode` |
|
|
71
|
+
| `BORING_AGENT_WORKSPACE_ROOT` | Overrides the folder argument in folder mode |
|
|
72
|
+
| `BORING_UI_WORKSPACES_PATH` | Path to the workspaces registry (default `~/.boring-ui/workspaces.yaml`) |
|
|
73
|
+
| `BORING_USE_LOCAL_PACKAGES` | `1` to resolve the bundled plugin-cli runtime from the local monorepo checkout |
|
|
105
74
|
|
|
106
75
|
---
|
|
107
76
|
|
|
108
|
-
##
|
|
77
|
+
## Installation
|
|
109
78
|
|
|
110
|
-
|
|
79
|
+
No install needed — `npx @hachej/boring-ui-cli`. Or install globally:
|
|
111
80
|
|
|
112
81
|
```bash
|
|
113
|
-
|
|
82
|
+
npm install -g @hachej/boring-ui-cli # or: pnpm add -g @hachej/boring-ui-cli
|
|
83
|
+
boring-ui
|
|
114
84
|
```
|
|
115
85
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## Options
|
|
121
|
-
|
|
122
|
-
| Environment Variable | Default | Description |
|
|
123
|
-
|---------------------|---------|-------------|
|
|
124
|
-
| `ANTHROPIC_API_KEY` | Yes | Anthropic API key. The agent requires a valid key to function. |
|
|
125
|
-
| `PORT` | `5200` | Port to run the server on |
|
|
126
|
-
| `BORING_AGENT_WORKSPACE_ROOT` | `.` (cwd) | Root directory for the workspace. The agent sees this as its filesystem. |
|
|
127
|
-
| `BORING_AGENT_MODE` | `direct` | `direct` (no sandbox) or `local` (bwrap sandbox, Linux only) |
|
|
128
|
-
| `BORING_AGENT_DEFAULT_MODEL_ID` | `claude-sonnet-4-6` | Default model to use |
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## Architecture
|
|
86
|
+
### From source
|
|
133
87
|
|
|
88
|
+
```bash
|
|
89
|
+
git clone https://github.com/hachej/boring-ui.git
|
|
90
|
+
cd boring-ui && pnpm install
|
|
91
|
+
pnpm --filter @hachej/boring-ui-cli build:full # builds front bundle + server
|
|
92
|
+
node packages/cli/dist/index.js
|
|
134
93
|
```
|
|
135
|
-
npx @hachej/boring-ui-cli
|
|
136
|
-
├── Boot Fastify server (direct mode, in-memory)
|
|
137
|
-
├── Serve frontend SPA (Vite-built bundle)
|
|
138
|
-
├── Open browser → http://localhost:5200
|
|
139
|
-
└── Workspace = your current directory (or $BORING_AGENT_WORKSPACE_ROOT)
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
The CLI is the zero-config entry point to the full boring-ui stack. Under the hood it wires together:
|
|
143
|
-
|
|
144
|
-
- `@hachej/boring-agent` — agent runtime, tools, chat UI
|
|
145
|
-
- `@hachej/boring-workspace` — file tree, panels, command palette, plugins
|
|
146
|
-
- `@hachej/boring-ui-kit` — shared UI primitives
|
|
147
|
-
|
|
148
|
-
All running locally against your real filesystem with no database.
|
|
149
|
-
|
|
150
|
-
---
|
|
151
94
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
| Feature | @hachej/boring-ui-cli | Claude Code | Codex CLI | Cursor |
|
|
155
|
-
|---------|------------------------|-------------|-----------|--------|
|
|
156
|
-
| Browser UI | ✅ Full IDE with panels | ❌ Terminal only | ❌ Terminal only | ✅ Desktop app |
|
|
157
|
-
| File tree | ✅ Side panel | ❌ | ❌ | ✅ |
|
|
158
|
-
| Zero setup | ✅ `npx` anywhere | ⚠️ Install + login | ⚠️ Install + login | ❌ Desktop app download |
|
|
159
|
-
| Panel system | ✅ Dockview splittable panels | ❌ | ❌ | ❌ |
|
|
160
|
-
| Plugin extensibility | ✅ Panels, commands, catalogs | ❌ | ❌ | ⚠️ Extensions |
|
|
161
|
-
| Local filesystem | ✅ Direct access | ✅ | ✅ | ✅ |
|
|
162
|
-
| Database required | ❌ None | ❌ | ❌ | ❌ |
|
|
163
|
-
|
|
164
|
-
**When to use @hachej/boring-ui-cli:**
|
|
165
|
-
- You want a browser-based coding agent with file tree and panels
|
|
166
|
-
- You don't want to install anything — just `npx`
|
|
167
|
-
- You want plugin extensibility (custom panels, data catalogs, etc.)
|
|
168
|
-
|
|
169
|
-
**When it might not fit:**
|
|
170
|
-
- You prefer terminal-only agent workflows (use Claude Code or Codex CLI)
|
|
171
|
-
- You need multi-user auth, workspaces, or a database (use `@hachej/boring-core`)
|
|
172
|
-
- You want a full desktop IDE with LSP, debugging, and git (use Cursor or VS Code)
|
|
173
|
-
|
|
174
|
-
---
|
|
175
|
-
|
|
176
|
-
## Troubleshooting
|
|
177
|
-
|
|
178
|
-
| Error | Cause | Fix |
|
|
179
|
-
|-------|-------|-----|
|
|
180
|
-
| `ANTHROPIC_API_KEY not set` | No API key | `export ANTHROPIC_API_KEY=sk-ant-...` before running |
|
|
181
|
-
| `port already in use` | Port 5200 occupied | `PORT=5201 npx @hachej/boring-ui-cli` |
|
|
182
|
-
| Browser doesn't open | `BROWSER=none` or no display | Manually navigate to `http://localhost:5200` |
|
|
183
|
-
| Agent returns errors | Invalid API key | Verify your Anthropic API key is valid and has quota |
|
|
184
|
-
| `workspace root not found` | `BORING_AGENT_WORKSPACE_ROOT` points to non-existent dir | Create the directory or unset the variable to use cwd |
|
|
95
|
+
`build:full` is required from source: the server refuses to start without a
|
|
96
|
+
built frontend under `public/`.
|
|
185
97
|
|
|
186
98
|
---
|
|
187
99
|
|
|
188
|
-
##
|
|
100
|
+
## Two modes
|
|
189
101
|
|
|
190
|
-
- **
|
|
191
|
-
-
|
|
192
|
-
- **
|
|
193
|
-
-
|
|
194
|
-
|
|
195
|
-
- **Only Anthropic Claude**: No OpenAI, Google, or other model providers wired in v1.
|
|
102
|
+
- **Folder mode** (`boring-ui [folder]`) — one folder, one workspace. The fast
|
|
103
|
+
editor-launcher path, like `code .`.
|
|
104
|
+
- **Workspaces mode** (`boring-ui workspaces`) — a persistent local hub serving
|
|
105
|
+
multiple folder-backed workspaces, with a workspace switcher in the UI. The
|
|
106
|
+
registry is a user-local YAML file, not a database.
|
|
196
107
|
|
|
197
|
-
|
|
108
|
+
Both run a Fastify server that serves the prebuilt React/Vite SPA plus the agent
|
|
109
|
+
and workspace API routes against your real filesystem. There is no database.
|
|
198
110
|
|
|
199
|
-
##
|
|
111
|
+
## Plugins
|
|
200
112
|
|
|
201
|
-
|
|
202
|
-
|
|
113
|
+
The CLI discovers plugins from Pi-shaped roots — `~/.pi/agent/extensions/*`
|
|
114
|
+
(global) and `<workspace>/.pi/extensions/*` (workspace-local) — plus
|
|
115
|
+
CLI-bundled defaults (e.g. `@hachej/boring-ask-user`). Authoring is handled by
|
|
116
|
+
the bundled `boring-ui-plugin` CLI:
|
|
203
117
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
**Q: Is my code sent to the cloud?**
|
|
211
|
-
A: Yes — the agent sends file contents and chat messages to the LLM provider (e.g. Anthropic). The filesystem operations run locally on your machine.
|
|
212
|
-
|
|
213
|
-
**Q: How is this different from `npx @hachej/boring-agent`?**
|
|
214
|
-
A: `@hachej/boring-ui-cli` ships the full workbench (file tree, editor, command palette, plugins). `@hachej/boring-agent` is just the agent + chat. The CLI is the batteries-included zero-config entry point.
|
|
118
|
+
```bash
|
|
119
|
+
boring-ui-plugin create <name> --path plugins # npm-package plugin (build step)
|
|
120
|
+
boring-ui-plugin scaffold <name> # workspace runtime plugin (.pi/extensions, hot-reload)
|
|
121
|
+
boring-ui-plugin verify [name]
|
|
122
|
+
boring-ui-plugin test <name>
|
|
123
|
+
```
|
|
215
124
|
|
|
216
|
-
|
|
217
|
-
A: Not directly in v1. The CLI uses the default agent + workspace configuration. For plugin extensibility, build a custom app using `@hachej/boring-workspace` + `@hachej/boring-core`.
|
|
125
|
+
See `@hachej/boring-ui-plugin-cli` for the full plugin authoring workflow.
|
|
218
126
|
|
|
219
127
|
---
|
|
220
128
|
|
|
221
|
-
##
|
|
222
|
-
|
|
223
|
-
`@hachej/boring-ui-cli` is the zero-config entry point. For a full app with:
|
|
224
|
-
- Multi-user authentication
|
|
225
|
-
- Persistent workspaces with Postgres
|
|
226
|
-
- Email invites and password resets
|
|
227
|
-
- Custom domain plugins
|
|
228
|
-
|
|
229
|
-
See the [boring-ui monorepo](https://github.com/hachej/boring-ui) and its packages:
|
|
129
|
+
## Documentation
|
|
230
130
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
| `@hachej/boring-core` | Auth, DB, app factory, multi-user |
|
|
234
|
-
| `@hachej/boring-workspace` | Plugin system, panels, layouts |
|
|
235
|
-
| `@hachej/boring-agent` | Agent runtime, tools, chat UI |
|
|
236
|
-
| `@hachej/boring-ui-kit` | Shared React UI primitives |
|
|
131
|
+
- [`docs/README.md`](./docs/README.md) — architecture and key abstractions
|
|
132
|
+
- [`docs/plans/archive/`](./docs/plans/archive/) — historical design plans (not current docs)
|
|
237
133
|
|
|
238
134
|
---
|
|
239
135
|
|
|
240
|
-
*About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective.
|
|
136
|
+
*About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings.
|
|
241
137
|
|
|
242
138
|
---
|
|
243
139
|
|
package/dist/server/modeApps.js
CHANGED
|
@@ -108,6 +108,7 @@ const RUNTIME_PLUGIN_TRUST_LABEL = "Trusted local runtime plugins";
|
|
|
108
108
|
const RUNTIME_PLUGIN_TRUST_DESCRIPTION = "Loads plugin UI code from trusted local Pi extension roots through the CLI-owned runtime module host.";
|
|
109
109
|
function createRuntimePluginDiagnosticsStore() {
|
|
110
110
|
const byWorkspace = /* @__PURE__ */ new Map();
|
|
111
|
+
const frontErrorsByWorkspace = /* @__PURE__ */ new Map();
|
|
111
112
|
function upsert(workspaceId, pluginId) {
|
|
112
113
|
const workspace = byWorkspace.get(workspaceId) ?? /* @__PURE__ */ new Map();
|
|
113
114
|
byWorkspace.set(workspaceId, workspace);
|
|
@@ -137,6 +138,10 @@ function createRuntimePluginDiagnosticsStore() {
|
|
|
137
138
|
entry.lastErrorCode = void 0;
|
|
138
139
|
entry.lastErrorMessage = void 0;
|
|
139
140
|
entry.lastErrorStage = void 0;
|
|
141
|
+
const storedFrontError = frontErrorsByWorkspace.get(diagnostic.workspaceId)?.get(diagnostic.pluginId);
|
|
142
|
+
if (storedFrontError && diagnostic.revision !== void 0 && storedFrontError.revision < diagnostic.revision) {
|
|
143
|
+
frontErrorsByWorkspace.get(diagnostic.workspaceId)?.delete(diagnostic.pluginId);
|
|
144
|
+
}
|
|
140
145
|
}
|
|
141
146
|
if (diagnostic.stage === "cache") entry.lastRequestAt = now;
|
|
142
147
|
if (diagnostic.stage === "transform" && diagnostic.outcome === "served") {
|
|
@@ -163,8 +168,22 @@ function createRuntimePluginDiagnosticsStore() {
|
|
|
163
168
|
snapshot(workspaceId) {
|
|
164
169
|
return [...byWorkspace.get(workspaceId)?.values() ?? []].map((entry) => ({ ...entry, recent: [...entry.recent] })).sort((a, b) => a.pluginId.localeCompare(b.pluginId));
|
|
165
170
|
},
|
|
171
|
+
recordFrontError(workspaceId, error) {
|
|
172
|
+
const forWorkspace = frontErrorsByWorkspace.get(workspaceId) ?? /* @__PURE__ */ new Map();
|
|
173
|
+
frontErrorsByWorkspace.set(workspaceId, forWorkspace);
|
|
174
|
+
const existing = forWorkspace.get(error.pluginId);
|
|
175
|
+
if (existing && existing.revision > error.revision) return;
|
|
176
|
+
forWorkspace.set(error.pluginId, error);
|
|
177
|
+
},
|
|
178
|
+
clearFrontError(workspaceId, pluginId) {
|
|
179
|
+
frontErrorsByWorkspace.get(workspaceId)?.delete(pluginId);
|
|
180
|
+
},
|
|
181
|
+
frontErrors(workspaceId) {
|
|
182
|
+
return [...frontErrorsByWorkspace.get(workspaceId)?.values() ?? []].sort((a, b) => a.pluginId.localeCompare(b.pluginId));
|
|
183
|
+
},
|
|
166
184
|
disposeWorkspace(workspaceId) {
|
|
167
185
|
byWorkspace.delete(workspaceId);
|
|
186
|
+
frontErrorsByWorkspace.delete(workspaceId);
|
|
168
187
|
}
|
|
169
188
|
};
|
|
170
189
|
}
|
|
@@ -204,11 +223,33 @@ function buildRuntimePluginDiagnosticsResponse(args) {
|
|
|
204
223
|
host: hostEntry
|
|
205
224
|
});
|
|
206
225
|
}
|
|
226
|
+
for (const frontError of args.frontErrors ?? []) {
|
|
227
|
+
const current = byPlugin.get(frontError.pluginId) ?? { id: frontError.pluginId };
|
|
228
|
+
byPlugin.set(frontError.pluginId, {
|
|
229
|
+
...current,
|
|
230
|
+
frontError
|
|
231
|
+
});
|
|
232
|
+
}
|
|
207
233
|
return {
|
|
208
234
|
workspaceId: args.workspaceId,
|
|
209
235
|
plugins: [...byPlugin.values()].sort((a, b) => a.id.localeCompare(b.id))
|
|
210
236
|
};
|
|
211
237
|
}
|
|
238
|
+
function parseFrontErrorReport(pluginId, body) {
|
|
239
|
+
if (typeof body !== "object" || body === null) return null;
|
|
240
|
+
const record = body;
|
|
241
|
+
const message = typeof record.message === "string" ? record.message : "";
|
|
242
|
+
if (!pluginId || !message) return null;
|
|
243
|
+
const revisionRaw = record.revision;
|
|
244
|
+
const revision = typeof revisionRaw === "number" && Number.isFinite(revisionRaw) ? revisionRaw : 0;
|
|
245
|
+
return {
|
|
246
|
+
pluginId,
|
|
247
|
+
revision,
|
|
248
|
+
message,
|
|
249
|
+
...typeof record.url === "string" ? { url: record.url } : {},
|
|
250
|
+
reportedAt: Date.now()
|
|
251
|
+
};
|
|
252
|
+
}
|
|
212
253
|
async function createFolderModeApp(opts) {
|
|
213
254
|
const workspaceRoot = resolve(opts.workspaceRoot);
|
|
214
255
|
const projectName = opts.projectName ?? (basename(workspaceRoot) || "workspace");
|
|
@@ -234,6 +275,10 @@ async function createFolderModeApp(opts) {
|
|
|
234
275
|
logger: false,
|
|
235
276
|
provisionWorkspace: false,
|
|
236
277
|
runtimeProvisioning,
|
|
278
|
+
// The standalone CLI runs on the user's own machine, so ambient skill
|
|
279
|
+
// discovery (workspace + user-global ~/.pi skills) is on. The library
|
|
280
|
+
// default is off (withPiHarnessDefaults) to keep hosted agents isolated.
|
|
281
|
+
pi: { noSkills: false },
|
|
237
282
|
// CLI-bundled internal plugins, resolved to absolute package dirs. This
|
|
238
283
|
// drives the server-side install array (boot-time routes/agentTools);
|
|
239
284
|
// additionalBoringPluginDirs only feeds the asset-manager scan.
|
|
@@ -257,9 +302,17 @@ async function createFolderModeApp(opts) {
|
|
|
257
302
|
workspaceId: FOLDER_RUNTIME_PLUGIN_WORKSPACE_ID,
|
|
258
303
|
loaded: manager?.inspectLoaded() ?? [],
|
|
259
304
|
errors: manager?.getErrors() ?? [],
|
|
260
|
-
host: diagnosticsStore.snapshot(FOLDER_RUNTIME_PLUGIN_WORKSPACE_ID)
|
|
305
|
+
host: diagnosticsStore.snapshot(FOLDER_RUNTIME_PLUGIN_WORKSPACE_ID),
|
|
306
|
+
frontErrors: diagnosticsStore.frontErrors(FOLDER_RUNTIME_PLUGIN_WORKSPACE_ID)
|
|
261
307
|
});
|
|
262
308
|
});
|
|
309
|
+
app.post("/api/v1/agent-plugins/:id/front-error", async (request, reply) => {
|
|
310
|
+
const { id } = request.params;
|
|
311
|
+
const report = parseFrontErrorReport(id, request.body);
|
|
312
|
+
if (!report) return reply.code(400).send({ error: "invalid_front_error_report" });
|
|
313
|
+
diagnosticsStore.recordFrontError(FOLDER_RUNTIME_PLUGIN_WORKSPACE_ID, report);
|
|
314
|
+
return reply.code(204).send();
|
|
315
|
+
});
|
|
263
316
|
app.get("/api/v1/workspace/meta", async () => ({
|
|
264
317
|
workspaceId: "default",
|
|
265
318
|
workspaceRoot,
|
|
@@ -497,6 +550,14 @@ async function createWorkspacesModeApp(opts) {
|
|
|
497
550
|
source: "plugin-preflight",
|
|
498
551
|
message: `${error.code}: ${error.message} (${error.pluginDir})`,
|
|
499
552
|
...error.pluginId ? { pluginId: error.pluginId } : {}
|
|
553
|
+
})),
|
|
554
|
+
// Browser-reported front import failures: the server scan/transform is
|
|
555
|
+
// green, so without these a plugin that never renders looks healthy to
|
|
556
|
+
// the agent. The plugin_diagnostics tool consumes this array.
|
|
557
|
+
...diagnosticsStore.frontErrors(workspace.id).map((error) => ({
|
|
558
|
+
source: "plugin-front",
|
|
559
|
+
message: error.message,
|
|
560
|
+
pluginId: error.pluginId
|
|
500
561
|
}))
|
|
501
562
|
];
|
|
502
563
|
},
|
|
@@ -504,6 +565,9 @@ async function createWorkspacesModeApp(opts) {
|
|
|
504
565
|
const workspace = await requireWorkspace(workspaceId);
|
|
505
566
|
await getLoadedPluginRuntime(workspace);
|
|
506
567
|
return {
|
|
568
|
+
// Same policy as folder mode: the local hub runs on the user's own
|
|
569
|
+
// machine, so ambient skill discovery is on (library default is off).
|
|
570
|
+
noSkills: false,
|
|
507
571
|
additionalSkillPaths: [join(workspaceRoot, ".agents", "skills")],
|
|
508
572
|
packages: [],
|
|
509
573
|
extensionPaths: [],
|
|
@@ -527,9 +591,18 @@ async function createWorkspacesModeApp(opts) {
|
|
|
527
591
|
workspaceId: workspace.id,
|
|
528
592
|
loaded: runtime.manager.inspectLoaded(),
|
|
529
593
|
errors: runtime.manager.getErrors(),
|
|
530
|
-
host: diagnosticsStore.snapshot(workspace.id)
|
|
594
|
+
host: diagnosticsStore.snapshot(workspace.id),
|
|
595
|
+
frontErrors: diagnosticsStore.frontErrors(workspace.id)
|
|
531
596
|
});
|
|
532
597
|
});
|
|
598
|
+
app.post("/api/v1/agent-plugins/:id/front-error", async (request, reply) => {
|
|
599
|
+
const workspace = await workspaceFromRequest(request);
|
|
600
|
+
const { id } = request.params;
|
|
601
|
+
const report = parseFrontErrorReport(id, request.body);
|
|
602
|
+
if (!report) return reply.code(400).send({ error: "invalid_front_error_report" });
|
|
603
|
+
diagnosticsStore.recordFrontError(workspace.id, report);
|
|
604
|
+
return reply.code(204).send();
|
|
605
|
+
});
|
|
533
606
|
app.get("/api/v1/agent-plugins", async (request) => {
|
|
534
607
|
const workspace = await workspaceFromRequest(request);
|
|
535
608
|
const runtime = await getLoadedPluginRuntime(workspace);
|
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
resolveDefaultWorkspacePluginPackagePaths
|
|
11
11
|
} from "@hachej/boring-workspace/app/server";
|
|
12
12
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
resolvePluginSourceScopePaths,
|
|
14
|
+
resolveRegisteredPluginSourceDirs
|
|
15
15
|
} from "@hachej/boring-ui-plugin-cli";
|
|
16
16
|
function resolveBoringUiCliPackageRoot() {
|
|
17
17
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -41,8 +41,8 @@ function resolveCliBoringPluginDirs(workspaceRoot, options = {}) {
|
|
|
41
41
|
const globalScope = resolvePluginSourceScopePaths("global", { globalRoot: globalAgentRoot });
|
|
42
42
|
const localScope = resolvePluginSourceScopePaths("local", { workspaceRoot: resolvedWorkspaceRoot });
|
|
43
43
|
const packageSources = [
|
|
44
|
-
...
|
|
45
|
-
...
|
|
44
|
+
...resolveRegisteredPluginSourceDirs(globalScope).map((dir) => ({ ...dir, scope: "global" })),
|
|
45
|
+
...resolveRegisteredPluginSourceDirs(localScope).map((dir) => ({ ...dir, scope: "local" }))
|
|
46
46
|
];
|
|
47
47
|
const includeDefaultPackages = options.includeDefaultPackages ?? true;
|
|
48
48
|
const roots = [
|
|
@@ -56,6 +56,7 @@ function resolveCliBoringPluginDirs(workspaceRoot, options = {}) {
|
|
|
56
56
|
...packageSources.map((record) => ({
|
|
57
57
|
rootDir: record.rootDir,
|
|
58
58
|
kind: "external",
|
|
59
|
+
registered: true,
|
|
59
60
|
...record.scope === "local" ? { workspaceId: resolvedWorkspaceRoot } : {}
|
|
60
61
|
}))
|
|
61
62
|
];
|
|
@@ -8,6 +8,7 @@ import react from "@vitejs/plugin-react";
|
|
|
8
8
|
import { ErrorCode } from "@hachej/boring-agent/shared";
|
|
9
9
|
import ts from "typescript";
|
|
10
10
|
import { createServer } from "vite";
|
|
11
|
+
import { init as initCjsLexer, parse as parseCjsExports } from "cjs-module-lexer";
|
|
11
12
|
const PLUGIN_FRONT_RUNTIME_BASE_PATH = "/api/v1/agent-plugins/runtime";
|
|
12
13
|
const HOST_VIRTUAL_SINGLETON_MODULES = [
|
|
13
14
|
"react",
|
|
@@ -817,6 +818,86 @@ function runtimeAssetContentType(path) {
|
|
|
817
818
|
return "application/octet-stream";
|
|
818
819
|
}
|
|
819
820
|
}
|
|
821
|
+
const CJS_REQUIRE_PLACEHOLDER = "__boringCjsRequire";
|
|
822
|
+
let cjsLexerReady;
|
|
823
|
+
function ensureCjsLexer() {
|
|
824
|
+
if (!cjsLexerReady) cjsLexerReady = initCjsLexer();
|
|
825
|
+
return cjsLexerReady;
|
|
826
|
+
}
|
|
827
|
+
function hasEsmSyntax(sourceText) {
|
|
828
|
+
const sourceFile = ts.createSourceFile("cjs-probe.js", sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
|
829
|
+
let esm = false;
|
|
830
|
+
const visit = (node) => {
|
|
831
|
+
if (esm) return;
|
|
832
|
+
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node) || ts.isExportAssignment(node) || ts.isFunctionDeclaration(node) && node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) || (ts.isVariableStatement(node) || ts.isClassDeclaration(node)) && node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
833
|
+
esm = true;
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
ts.forEachChild(node, visit);
|
|
837
|
+
};
|
|
838
|
+
visit(sourceFile);
|
|
839
|
+
return esm;
|
|
840
|
+
}
|
|
841
|
+
function looksLikeCommonJs(sourceText) {
|
|
842
|
+
if (hasEsmSyntax(sourceText)) return false;
|
|
843
|
+
return /\b(?:module\.exports|exports\.|exports\[)/.test(sourceText) || /\brequire\s*\(/.test(sourceText);
|
|
844
|
+
}
|
|
845
|
+
function collectRequireSpecifiers(sourceFile) {
|
|
846
|
+
const specifiers = /* @__PURE__ */ new Set();
|
|
847
|
+
const visit = (node) => {
|
|
848
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length === 1 && ts.isStringLiteralLike(node.arguments[0])) {
|
|
849
|
+
specifiers.add(node.arguments[0].text);
|
|
850
|
+
}
|
|
851
|
+
ts.forEachChild(node, visit);
|
|
852
|
+
};
|
|
853
|
+
visit(sourceFile);
|
|
854
|
+
return [...specifiers];
|
|
855
|
+
}
|
|
856
|
+
async function cjsDependencyToEsm(sourceText, modulePath) {
|
|
857
|
+
await ensureCjsLexer();
|
|
858
|
+
const sourceFile = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
|
859
|
+
const requireSpecifiers = collectRequireSpecifiers(sourceFile);
|
|
860
|
+
const importLines = [];
|
|
861
|
+
const requireMapEntries = [];
|
|
862
|
+
requireSpecifiers.forEach((specifier, index) => {
|
|
863
|
+
const binding = `${CJS_REQUIRE_PLACEHOLDER}_${index}`;
|
|
864
|
+
importLines.push(`import * as ${binding} from ${JSON.stringify(specifier)};`);
|
|
865
|
+
requireMapEntries.push(` [${JSON.stringify(specifier)}]: ${binding},`);
|
|
866
|
+
});
|
|
867
|
+
let exportNames = [];
|
|
868
|
+
try {
|
|
869
|
+
const parsed = parseCjsExports(sourceText, modulePath);
|
|
870
|
+
exportNames = [...parsed.exports];
|
|
871
|
+
} catch {
|
|
872
|
+
exportNames = [];
|
|
873
|
+
}
|
|
874
|
+
const safeExportNames = exportNames.filter(
|
|
875
|
+
(name) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) && name !== "default" && name !== "__esModule"
|
|
876
|
+
);
|
|
877
|
+
const namedExportLines = safeExportNames.map(
|
|
878
|
+
(name) => `export const ${name} = ${JSON.stringify(name)} in __boringCjsExports ? __boringCjsExports[${JSON.stringify(name)}] : undefined;`
|
|
879
|
+
);
|
|
880
|
+
return [
|
|
881
|
+
...importLines,
|
|
882
|
+
`const ${CJS_REQUIRE_PLACEHOLDER}_modules = {`,
|
|
883
|
+
...requireMapEntries,
|
|
884
|
+
"};",
|
|
885
|
+
`function ${CJS_REQUIRE_PLACEHOLDER}(id) {`,
|
|
886
|
+
` const ns = ${CJS_REQUIRE_PLACEHOLDER}_modules[id];`,
|
|
887
|
+
` if (!ns) throw new Error("runtime plugin CommonJS require could not be resolved: " + id);`,
|
|
888
|
+
" const def = ns.default;",
|
|
889
|
+
" if (def !== undefined && (ns.__esModule || typeof def !== 'object' || Object.keys(ns).length === 1)) return def;",
|
|
890
|
+
" return ns.default !== undefined ? ns.default : ns;",
|
|
891
|
+
"}",
|
|
892
|
+
"const __boringCjsModule = { exports: {} };",
|
|
893
|
+
`(function (module, exports, require) {`,
|
|
894
|
+
sourceText,
|
|
895
|
+
`})(__boringCjsModule, __boringCjsModule.exports, ${CJS_REQUIRE_PLACEHOLDER});`,
|
|
896
|
+
"const __boringCjsExports = __boringCjsModule.exports;",
|
|
897
|
+
"export default __boringCjsExports;",
|
|
898
|
+
...namedExportLines
|
|
899
|
+
].join("\n");
|
|
900
|
+
}
|
|
820
901
|
function runtimeAssetModuleCode(path, bytes) {
|
|
821
902
|
const dataUrl = `data:${runtimeAssetContentType(path)};base64,${Buffer.from(bytes).toString("base64")}`;
|
|
822
903
|
return `export default ${JSON.stringify(dataUrl)};`;
|
|
@@ -1093,6 +1174,13 @@ async function createPluginFrontRuntimeHost(options = {}) {
|
|
|
1093
1174
|
return runtimeAssetModuleCode(resolvedPath2, await readFile(resolvedPath2));
|
|
1094
1175
|
}
|
|
1095
1176
|
const sourceText2 = await readFile(resolvedPath2, "utf8");
|
|
1177
|
+
const extension = extname(resolvedPath2).toLowerCase();
|
|
1178
|
+
const isCommonJs = extension === ".cjs" || extension !== ".mjs" && looksLikeCommonJs(sourceText2);
|
|
1179
|
+
if (isCommonJs) {
|
|
1180
|
+
const interop = await cjsDependencyToEsm(sourceText2, resolvedPath2);
|
|
1181
|
+
validateSourceImports(interop, resolvedPath2, basePath);
|
|
1182
|
+
return interop;
|
|
1183
|
+
}
|
|
1096
1184
|
validateSourceImports(sourceText2, resolvedPath2, basePath);
|
|
1097
1185
|
return sourceText2;
|
|
1098
1186
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-ui-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.42",
|
|
4
4
|
"description": "Turn an agent into an app",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -15,15 +15,16 @@
|
|
|
15
15
|
"@fastify/compress": "^8.3.1",
|
|
16
16
|
"@fastify/static": "^9.1.1",
|
|
17
17
|
"@vitejs/plugin-react": "^4.0.0",
|
|
18
|
+
"cjs-module-lexer": "^2.0.0",
|
|
18
19
|
"fastify": "^5.4.0",
|
|
19
20
|
"lucide-react": "^1.8.0",
|
|
20
21
|
"typescript": "^5.8.3",
|
|
21
22
|
"vite": "^6.0.0",
|
|
22
|
-
"@hachej/boring-agent": "0.1.
|
|
23
|
-
"@hachej/boring-
|
|
24
|
-
"@hachej/boring-
|
|
25
|
-
"@hachej/boring-ui-
|
|
26
|
-
"@hachej/boring-
|
|
23
|
+
"@hachej/boring-agent": "0.1.42",
|
|
24
|
+
"@hachej/boring-ask-user": "0.1.42",
|
|
25
|
+
"@hachej/boring-workspace": "0.1.42",
|
|
26
|
+
"@hachej/boring-ui-kit": "0.1.42",
|
|
27
|
+
"@hachej/boring-ui-plugin-cli": "0.1.42"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@tailwindcss/vite": "^4.0.0",
|