@pellux/goodvibes-tui 0.18.4 → 0.18.8

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 (35) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/README.md +2 -2
  3. package/bin/goodvibes +1 -1
  4. package/bin/goodvibes-daemon +66 -0
  5. package/docs/foundation-artifacts/operator-contract.json +1 -1
  6. package/package.json +14 -7
  7. package/scripts/postinstall.js +220 -0
  8. package/src/agents/orchestrator.ts +1 -1
  9. package/src/input/commands/discovery-runtime.ts +7 -1
  10. package/src/input/commands/local-setup-review.ts +1 -1
  11. package/src/input/commands/local-setup.ts +1 -1
  12. package/src/input/commands/platform-sandbox-qemu.ts +3 -3
  13. package/src/input/commands/platform-sandbox-runtime.ts +5 -5
  14. package/src/input/commands/platform-sandbox-session.ts +1 -1
  15. package/src/mcp/registry.ts +3 -5
  16. package/src/panels/builtin/shared.ts +1 -1
  17. package/src/panels/sandbox-panel.ts +2 -2
  18. package/src/panels/skills-panel.ts +28 -1
  19. package/src/runtime/bootstrap-background.ts +14 -4
  20. package/src/runtime/bootstrap-command-context.ts +1 -1
  21. package/src/runtime/bootstrap-command-parts.ts +1 -1
  22. package/src/runtime/context.ts +1 -1
  23. package/src/runtime/services.ts +7 -3
  24. package/src/runtime/shell-command-services.ts +1 -1
  25. package/src/runtime/shell-command-workspace.ts +1 -1
  26. package/src/tools/control/index.ts +1 -1
  27. package/src/tools/index.ts +7 -7
  28. package/src/version.ts +1 -1
  29. package/docs/README.md +0 -32
  30. package/scripts/postinstall.mjs +0 -203
  31. package/src/runtime/sandbox/backend.ts +0 -291
  32. package/src/runtime/sandbox/manager.ts +0 -364
  33. package/src/runtime/sandbox/provisioning.ts +0 -422
  34. package/src/runtime/sandbox/session-registry.ts +0 -289
  35. package/src/tools/repl/index.ts +0 -318
package/CHANGELOG.md CHANGED
@@ -4,8 +4,156 @@ All notable changes to GoodVibes TUI.
4
4
 
5
5
  ---
6
6
 
7
+ ## [0.18.8] — 2026-04-15
8
+
9
+ ### Canonical SDK Cutover Completion
10
+
11
+ - Switched the TUI to the canonical published SDK line at `@pellux/goodvibes-sdk@0.18.21`
12
+ - Removed the remaining local REPL and sandbox runtime implementations from the TUI and routed those flows through SDK-owned package imports instead
13
+ - Rewired command, MCP, panel, and runtime call sites that were still holding local platform copies so the TUI now consumes the shared SDK surface instead of carrying duplicate implementations
14
+
15
+ ### TUI-Owned Configuration Boundary Fixes
16
+
17
+ - Kept product-owned storage and runtime identity at `.goodvibes/tui/...` by making the TUI pass explicit host configuration into SDK-owned services instead of inheriting SDK defaults
18
+ - Wired cross-session task graphs to the TUI-owned path under `.goodvibes/tui/sessions/task-graph.json`
19
+ - Instantiated the team and worklist tools with explicit `surfaceRoot: 'tui'` host configuration instead of relying on SDK defaults
20
+ - Aligned language-override and secret-store tests with the real TUI-owned configuration boundary
21
+
22
+ ### Release-Path And Test Hardening
23
+
24
+ - Added per-file repo-local temp roots in `scripts/run-tests.ts` so the full TUI test suite no longer collides on shared temp filesystems
25
+ - Reworked tests that still wrote to `/tmp` or shared temp roots so they now use the active temp root or an explicit external directory when validating escape behavior
26
+ - Fixed the intelligence test helper to lazily initialize its runtime singletons, eliminating the module-init cycle that surfaced after the SDK cutover
27
+ - Improved the Skills panel detail path rendering so long skill origins keep the useful suffix visible instead of clipping away the selected file
28
+
29
+ ### Verification
30
+
31
+ - Full typecheck passes: `bun x tsc --noEmit --pretty false`
32
+ - Full test runner passes locally: `bun run test`
33
+ - Architecture gate passes: `bun run architecture:check`
34
+ - Performance gate passes: `bun run perf:check`
35
+ - Eval gate passes: `bun run eval:gate`
36
+ - Foundation artifacts export passes: `bun run foundation:artifacts`
37
+ - Build passes: `bun run build`
38
+ - Publish packaging check passes: `bun run publish:check`
39
+ - Staged npm package rehearsal passes: `bun run publish:dry-run`
40
+ - Staged GitHub Packages rehearsal passes: `bun run publish:dry-run:github`
41
+ - Local package install smoke passes through `postinstall` with staged release artifacts
42
+ - Diff hygiene passes: `git diff --check`
43
+
44
+ ## [0.18.7] — 2026-04-14
45
+
46
+ Superseded before successful public registry release. The release workflow got past the package-install fix, but the REPL tool and REPL test harness were still allocating temp state under constrained temp filesystems, which failed the test phase before publish. The corrected release shipped in `0.18.8`.
47
+
48
+ ### Release Workflow Install Fix
49
+
50
+ - Fixed the TUI package postinstall path so source checkouts no longer try to download release binaries during `bun install`
51
+ - This unblocks CI and release validation jobs, which install repo dependencies before the GitHub Release assets exist
52
+ - Kept the actual packaged install behavior unchanged: published npm and GitHub Packages installs still download the matching TUI and standalone daemon binaries during `postinstall`
53
+
54
+ ### Verification
55
+
56
+ - Full typecheck passes: `bun x tsc --noEmit --pretty false`
57
+ - Publish packaging check passes: `bun run publish:check`
58
+ - Staged npm package rehearsal passes: `bun run publish:dry-run`
59
+ - Staged GitHub Packages rehearsal passes: `bun run publish:dry-run:github`
60
+ - Local package install smoke passes through `postinstall` with staged release artifacts
61
+ - Diff hygiene passes: `git diff --check`
62
+
63
+ ## [0.18.6] — 2026-04-14
64
+
65
+ Superseded before successful public registry release. The repo checkout still ran the binary-download `postinstall` during CI `bun install`, which blocked the tagged release before publish. The corrected release shipped in `0.18.7`.
66
+
67
+ ### Public Package Delivery Fix
68
+
69
+ - Replaced the oversized bundled-binary npm package model with a smaller package that installs the matching TUI and standalone daemon binaries during `postinstall`
70
+ - Kept the package identities as `@pellux/goodvibes-tui` on npmjs and `@mgd34msu/goodvibes-tui` on GitHub Packages while keeping the installed CLI surface as `goodvibes` and `goodvibes-daemon`
71
+ - Kept the runtime fallback path so installs can still run from Bun + source if a platform binary is unavailable, but made the intended install path the version-matched release binaries
72
+
73
+ ### Release Workflow Correction
74
+
75
+ - Reordered the release workflow so the GitHub Release and binary assets are created before npmjs and GitHub Packages publishing, ensuring package installs can fetch release assets immediately after publish
76
+ - Reworked install-smoke validation so the packed npm tarball is tested through the real `postinstall` path that installs both the TUI and daemon binaries for the current platform
77
+ - Hardened publish validation so registry tarballs fail if they accidentally include vendored binaries again or exceed the package-size guardrail
78
+
79
+ ### Verification
80
+
81
+ - Full typecheck passes: `bun x tsc --noEmit --pretty false`
82
+ - Publish packaging check passes: `bun run publish:check`
83
+ - Staged npm package rehearsal passes: `bun run publish:dry-run`
84
+ - Staged GitHub Packages rehearsal passes: `bun run publish:dry-run:github`
85
+ - Local package install smoke passes through `postinstall` with staged release artifacts
86
+ - Diff hygiene passes: `git diff --check`
87
+
88
+ ## [0.18.5] — 2026-04-14
89
+
90
+ Superseded before successful public registry release. The bundled-binary tarball for this version exceeded npmjs and GitHub Packages size limits, and the corrected public package model shipped in `0.18.6`.
91
+
92
+ ### Public Package And Release Correction
93
+
94
+ - Corrected the public packaged release path after the initial `0.18.4` cutover so the shipped npm distribution now matches the actual intended install model
95
+ - Published the TUI on npm as `@pellux/goodvibes-tui` and mirrored the same release to GitHub Packages as `@mgd34msu/goodvibes-tui`
96
+ - Kept the installed CLI surface as `goodvibes` and `goodvibes-daemon` while moving the package identity to the scoped release names
97
+ - Added both launcher bins to the packaged distribution so global installs expose the interactive TUI and the standalone daemon directly on the user path
98
+
99
+ ### Bundled Binary Delivery
100
+
101
+ - Bundled the compiled TUI binaries directly into the npm package for:
102
+ - Linux x64
103
+ - Linux arm64
104
+ - macOS x64
105
+ - macOS arm64
106
+ - Bundled the standalone daemon binaries directly into the npm package for:
107
+ - Linux x64
108
+ - Linux arm64
109
+ - macOS x64
110
+ - macOS arm64
111
+ - Removed the prior install-time binary download behavior so npm installs now use the packaged binaries already present in `vendor/`
112
+ - Added packaged release checksums through `vendor/SHA256SUMS.txt` and release-asset `SHA256SUMS.txt`
113
+
114
+ ### Release Automation And Publish Path Hardening
115
+
116
+ - Added staged vendor-binary packaging so npm and GitHub Packages publishes are built from the same explicit bundled-binary package shape
117
+ - Added a dedicated publish-packaging path for registry release publishing instead of depending on raw repo copies during publish
118
+ - Added a GitHub Packages publish job alongside the npmjs publish job in the release workflow
119
+ - Fixed staged publish rehearsal so local dry-runs validate the real publishable package shape without tripping registry version conflicts or temporary-filesystem quota failures
120
+ - Extended release validation and install smoke coverage to require both launcher bins and both bundled binary families
121
+
122
+ ### Verification
123
+
124
+ - Full typecheck passes: `bun x tsc --noEmit --pretty false`
125
+ - Architecture gate passes: `bun run architecture:check`
126
+ - Performance gate passes: `bun run perf:check`
127
+ - Eval gate passes: `bun run eval:gate`
128
+ - Full test runner passes: `bun run test`
129
+ - Build passes: `bun run build`
130
+ - Foundation artifact export passes: `bun run foundation:artifacts`
131
+ - Vendored binary staging passes: `bun run vendor:stage --clean`
132
+ - Publish packaging check passes: `bun run publish:check`
133
+ - Staged npm package rehearsal passes: `bun run publish:dry-run`
134
+ - Staged GitHub Packages rehearsal passes: `bun run publish:dry-run:github`
135
+ - Diff hygiene passes: `git diff --check`
136
+
7
137
  ## [0.18.4] — 2026-04-14
8
138
 
139
+ ### First Public Distribution And Delivery
140
+
141
+ - Published the TUI under the Pellux scope as `@pellux/goodvibes-tui` while keeping the installed CLI commands as `goodvibes` and `goodvibes-daemon`
142
+ - Added a GitHub Packages mirror as `@mgd34msu/goodvibes-tui`
143
+ - Bundled the compiled TUI and standalone daemon binaries directly into the npm package instead of relying on install-time downloads
144
+ - Added both `goodvibes` and `goodvibes-daemon` launcher bins to the package so global npm installs expose both commands on the user path
145
+ - Expanded the release asset set to ship compiled binaries for:
146
+ - Linux x64
147
+ - Linux arm64
148
+ - macOS x64
149
+ - macOS arm64
150
+ - Expanded the release asset set to ship standalone daemon binaries for:
151
+ - Linux x64
152
+ - Linux arm64
153
+ - macOS x64
154
+ - macOS arm64
155
+ - Added `SHA256SUMS.txt` checksums for the published release binaries
156
+
9
157
  ### Canonical SDK Cutover
10
158
 
11
159
  - Switched `goodvibes-tui` from the temporary beta line to the canonical `@pellux/goodvibes-sdk@0.18.14` package and rewired imports to the canonical SDK entrypoints
@@ -17,6 +165,9 @@ All notable changes to GoodVibes TUI.
17
165
  - Fixed the TUI eval gate to use the SDK-backed eval baseline, formatting, and scorecard exports after the platform extraction removed the old local files
18
166
  - Fixed the TUI performance gate to build CI snapshots against the SDK-backed `surfacePerf` domain shape instead of the pre-extraction local `uiPerf` shape
19
167
  - Fixed the architecture check so it tolerates removed migration targets on clean checkouts instead of crashing when extracted directories no longer exist
168
+ - Fixed the package publish path so release packaging stages vendored binaries deterministically instead of depending on install-time binary download behavior
169
+ - Fixed the release workflow to publish both npmjs and GitHub Packages distributions from the same bundled-binary package shape
170
+ - Fixed the staged publish rehearsal so it packs the final publishable package locally without tripping registry version conflicts or `/tmp` quota issues
20
171
 
21
172
  ### Verification
22
173
 
@@ -27,7 +178,10 @@ All notable changes to GoodVibes TUI.
27
178
  - Full test runner passes: `bun run test`
28
179
  - Build passes: `bun run build`
29
180
  - Foundation artifact export passes: `bun run foundation:artifacts`
181
+ - Vendored binary staging passes: `bun run vendor:stage --clean`
30
182
  - Publish packaging check passes: `bun run publish:check`
183
+ - Staged npm package rehearsal passes: `bun run publish:dry-run`
184
+ - Staged GitHub Packages rehearsal passes: `bun run publish:dry-run:github`
31
185
  - Diff hygiene passes: `git diff --check`
32
186
 
33
187
  ## [0.18.3] — 2026-04-14
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Version](https://img.shields.io/badge/version-0.18.4-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
5
+ [![Version](https://img.shields.io/badge/version-0.18.8-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
6
6
 
7
7
  A terminal-native AI coding, operations, automation, knowledge, and integration console with a typed runtime, omnichannel surfaces, structured memory/knowledge, and a raw ANSI renderer.
8
8
 
@@ -39,7 +39,7 @@ Common entrypoints:
39
39
  Release distribution:
40
40
 
41
41
  - GitHub Releases are the primary distribution path for compiled binaries
42
- - `npm install -g @pellux/goodvibes-tui` is supported on Linux, macOS, and WSL by downloading the matching prebuilt release binary during install
42
+ - `npm install -g @pellux/goodvibes-tui` is supported on Linux, macOS, and WSL; the install script downloads the matching TUI and daemon binaries for the current platform
43
43
  - native Windows is not supported; use WSL on Windows
44
44
 
45
45
  Common paths:
package/bin/goodvibes CHANGED
@@ -62,5 +62,5 @@ console.error(`platform: ${process.platform}-${process.arch}`);
62
62
  console.error(`supported prebuilt targets: ${supported}`);
63
63
  console.error('Either:');
64
64
  console.error(' 1. install Bun and re-run the command, or');
65
- console.error(' 2. reinstall after a matching GitHub release asset exists for this version.');
65
+ console.error(' 2. rerun the package postinstall step so the matching release binary is downloaded.');
66
66
  process.exit(1);
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ import { accessSync, constants } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { spawnSync } from 'node:child_process';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const packageRoot = join(__dirname, '..');
9
+
10
+ if (process.platform === 'win32') {
11
+ console.error('goodvibes-daemon: native Windows is not supported.');
12
+ console.error('Use WSL so the Linux release binary path applies.');
13
+ process.exit(1);
14
+ }
15
+
16
+ function resolveArtifactName(platform, arch) {
17
+ if (platform === 'linux' && arch === 'x64') return 'goodvibes-daemon-linux-x64';
18
+ if (platform === 'linux' && arch === 'arm64') return 'goodvibes-daemon-linux-arm64';
19
+ if (platform === 'darwin' && arch === 'x64') return 'goodvibes-daemon-macos-x64';
20
+ if (platform === 'darwin' && arch === 'arm64') return 'goodvibes-daemon-macos-arm64';
21
+ return null;
22
+ }
23
+
24
+ function isExecutable(path) {
25
+ try {
26
+ accessSync(path, constants.X_OK);
27
+ return true;
28
+ } catch {
29
+ return false;
30
+ }
31
+ }
32
+
33
+ function run(command, args) {
34
+ const child = spawnSync(command, args, { stdio: 'inherit' });
35
+ if (child.error) {
36
+ throw child.error;
37
+ }
38
+ process.exit(child.status ?? 1);
39
+ }
40
+
41
+ const artifactName = resolveArtifactName(process.platform, process.arch);
42
+ const vendoredBinary = artifactName ? join(packageRoot, 'vendor', artifactName) : null;
43
+
44
+ if (vendoredBinary && isExecutable(vendoredBinary)) {
45
+ run(vendoredBinary, process.argv.slice(2));
46
+ }
47
+
48
+ const bunProbe = spawnSync('bun', ['--version'], { stdio: 'ignore' });
49
+ if (bunProbe.status === 0) {
50
+ run('bun', [join(packageRoot, 'src', 'daemon', 'cli.ts'), ...process.argv.slice(2)]);
51
+ }
52
+
53
+ const supported = [
54
+ 'linux-x64',
55
+ 'linux-arm64',
56
+ 'darwin-x64',
57
+ 'darwin-arm64',
58
+ ].join(', ');
59
+
60
+ console.error('goodvibes-daemon: no runnable binary is available.');
61
+ console.error(`platform: ${process.platform}-${process.arch}`);
62
+ console.error(`supported prebuilt targets: ${supported}`);
63
+ console.error('Either:');
64
+ console.error(' 1. install Bun and re-run the command, or');
65
+ console.error(' 2. rerun the package postinstall step so the matching daemon binary is downloaded.');
66
+ process.exit(1);
@@ -3,7 +3,7 @@
3
3
  "product": {
4
4
  "id": "goodvibes",
5
5
  "surface": "operator",
6
- "version": "0.18.4"
6
+ "version": "0.18.8"
7
7
  },
8
8
  "auth": {
9
9
  "modes": [
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-tui",
3
- "version": "0.18.4",
3
+ "version": "0.18.8",
4
4
  "description": "Terminal-native GoodVibes product for coding, operations, automation, knowledge, channels, and daemon-backed control-plane workflows.",
5
5
  "type": "module",
6
6
  "main": "src/main.ts",
7
7
  "bin": {
8
- "goodvibes": "./bin/goodvibes"
8
+ "goodvibes": "bin/goodvibes",
9
+ "goodvibes-daemon": "bin/goodvibes-daemon"
9
10
  },
10
11
  "files": [
11
12
  "bin",
@@ -15,7 +16,7 @@
15
16
  "src",
16
17
  "!src/test",
17
18
  "!src/**/*.test.ts",
18
- "scripts/postinstall.mjs",
19
+ "scripts/postinstall.js",
19
20
  "README.md",
20
21
  "CHANGELOG.md",
21
22
  "docs/foundation-artifacts"
@@ -30,15 +31,21 @@
30
31
  "build:linux-arm64": "bun build src/main.ts --compile --target=bun-linux-arm64 --outfile dist/goodvibes-linux-arm64",
31
32
  "build:macos-x64": "bun build src/main.ts --compile --target=bun-darwin-x64 --outfile dist/goodvibes-macos-x64",
32
33
  "build:macos-arm64": "bun build src/main.ts --compile --target=bun-darwin-arm64 --outfile dist/goodvibes-macos-arm64",
34
+ "build:daemon:linux-x64": "bun build src/daemon/cli.ts --compile --target=bun-linux-x64 --outfile dist/goodvibes-daemon-linux-x64",
35
+ "build:daemon:linux-arm64": "bun build src/daemon/cli.ts --compile --target=bun-linux-arm64 --outfile dist/goodvibes-daemon-linux-arm64",
36
+ "build:daemon:macos-x64": "bun build src/daemon/cli.ts --compile --target=bun-darwin-x64 --outfile dist/goodvibes-daemon-macos-x64",
37
+ "build:daemon:macos-arm64": "bun build src/daemon/cli.ts --compile --target=bun-darwin-arm64 --outfile dist/goodvibes-daemon-macos-arm64",
33
38
  "build:windows": "bun build src/main.ts --compile --target=bun-windows-x64 --outfile dist/goodvibes-windows.exe",
34
39
  "build:all-shell": "bun run build:linux-x64 && bun run build:linux-arm64 && bun run build:macos-x64 && bun run build:macos-arm64 && bun run build:windows",
35
40
  "test": "bun run scripts/run-tests.ts",
36
41
  "version": "bun run scripts/prebuild.ts",
37
- "postinstall": "node scripts/postinstall.mjs",
38
- "postbuild": "node scripts/postinstall.mjs --no-download",
42
+ "postinstall": "node scripts/postinstall.js",
43
+ "postbuild": "node scripts/postinstall.js --no-download",
39
44
  "release": "bun run scripts/release.ts",
40
45
  "release:dry": "bun run scripts/release.ts --dry-run",
41
- "publish:dry-run": "npm pack --dry-run",
46
+ "publish:package": "bun run scripts/publish-package.ts",
47
+ "publish:dry-run": "bun run scripts/publish-package.ts --dry-run",
48
+ "publish:dry-run:github": "GOODVIBES_PUBLIC_PACKAGE_NAME=@mgd34msu/goodvibes-tui GOODVIBES_PUBLISH_REGISTRY=https://npm.pkg.github.com bun run scripts/publish-package.ts --dry-run",
42
49
  "publish:check": "bun run scripts/publish-check.ts",
43
50
  "build:prod": "bun run scripts/build.ts",
44
51
  "build:all": "bun run scripts/build.ts --all",
@@ -81,7 +88,7 @@
81
88
  "@anthropic-ai/vertex-sdk": "^0.16.0",
82
89
  "@ast-grep/napi": "^0.42.0",
83
90
  "@aws/bedrock-token-generator": "^1.1.0",
84
- "@pellux/goodvibes-sdk": "0.18.14",
91
+ "@pellux/goodvibes-sdk": "0.18.21",
85
92
  "bash-language-server": "^5.6.0",
86
93
  "fuse.js": "^7.1.0",
87
94
  "graphql": "^16.13.2",
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env node
2
+ import { chmodSync, copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { createHash } from 'node:crypto';
4
+ import { homedir } from 'node:os';
5
+ import { basename, dirname, join } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const projectRoot = join(__dirname, '..');
10
+ const home = homedir();
11
+ const pkg = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8'));
12
+ const noDownload = process.argv.includes('--no-download') || process.env.GOODVIBES_SKIP_BINARY_DOWNLOAD === '1';
13
+
14
+ function isSourceCheckout() {
15
+ return existsSync(join(projectRoot, '.git')) || existsSync(join(projectRoot, 'bun.lock'));
16
+ }
17
+
18
+ function resolveArtifactNames(platform, arch) {
19
+ if (platform === 'linux' && arch === 'x64') {
20
+ return {
21
+ app: 'goodvibes-linux-x64',
22
+ daemon: 'goodvibes-daemon-linux-x64',
23
+ };
24
+ }
25
+ if (platform === 'linux' && arch === 'arm64') {
26
+ return {
27
+ app: 'goodvibes-linux-arm64',
28
+ daemon: 'goodvibes-daemon-linux-arm64',
29
+ };
30
+ }
31
+ if (platform === 'darwin' && arch === 'x64') {
32
+ return {
33
+ app: 'goodvibes-macos-x64',
34
+ daemon: 'goodvibes-daemon-macos-x64',
35
+ };
36
+ }
37
+ if (platform === 'darwin' && arch === 'arm64') {
38
+ return {
39
+ app: 'goodvibes-macos-arm64',
40
+ daemon: 'goodvibes-daemon-macos-arm64',
41
+ };
42
+ }
43
+ return null;
44
+ }
45
+
46
+ function prepareBinary(path) {
47
+ if (process.platform !== 'win32') {
48
+ chmodSync(path, 0o755);
49
+ }
50
+ }
51
+
52
+ function sha256(buffer) {
53
+ return createHash('sha256').update(buffer).digest('hex');
54
+ }
55
+
56
+ function parseChecksumFile(contents) {
57
+ const checksums = new Map();
58
+ for (const rawLine of contents.split(/\r?\n/)) {
59
+ const line = rawLine.trim();
60
+ if (!line) continue;
61
+ const match = line.match(/^([a-f0-9]{64})\s+\*?(.+)$/i);
62
+ if (!match) continue;
63
+ checksums.set(match[2], match[1].toLowerCase());
64
+ }
65
+ return checksums;
66
+ }
67
+
68
+ function resolveRepositoryBaseUrl() {
69
+ const repositoryUrl = typeof pkg.repository?.url === 'string' ? pkg.repository.url : '';
70
+ const normalized = repositoryUrl
71
+ .replace(/^git\+/, '')
72
+ .replace(/\.git$/, '')
73
+ .replace(/^git@github\.com:/, 'https://github.com/');
74
+ if (!normalized.startsWith('https://github.com/')) {
75
+ throw new Error(`unsupported repository URL for binary downloads: ${repositoryUrl || '(missing)'}`);
76
+ }
77
+ return normalized;
78
+ }
79
+
80
+ async function downloadFile(url, destination) {
81
+ const response = await fetch(url);
82
+ if (!response.ok) {
83
+ throw new Error(`download failed (${response.status}) for ${url}`);
84
+ }
85
+ writeFileSync(destination, Buffer.from(await response.arrayBuffer()));
86
+ }
87
+
88
+ async function downloadText(url) {
89
+ const response = await fetch(url);
90
+ if (!response.ok) {
91
+ throw new Error(`download failed (${response.status}) for ${url}`);
92
+ }
93
+ return await response.text();
94
+ }
95
+
96
+ async function installPlatformBinaries() {
97
+ const artifacts = resolveArtifactNames(process.platform, process.arch);
98
+ if (!artifacts) {
99
+ console.log(`postinstall: no prebuilt binaries for ${process.platform}-${process.arch}; skipping binary install`);
100
+ return;
101
+ }
102
+
103
+ if (noDownload) {
104
+ console.log('postinstall: skipping binary install (--no-download)');
105
+ return;
106
+ }
107
+
108
+ if (isSourceCheckout()) {
109
+ console.log('postinstall: source checkout detected; skipping release-binary install');
110
+ return;
111
+ }
112
+
113
+ const vendorDir = join(projectRoot, 'vendor');
114
+ mkdirSync(vendorDir, { recursive: true });
115
+
116
+ const localSourceDir = process.env.GOODVIBES_ASSET_SOURCE_DIR?.trim();
117
+ if (localSourceDir) {
118
+ for (const artifactName of [artifacts.app, artifacts.daemon]) {
119
+ const sourcePath = join(localSourceDir, artifactName);
120
+ if (!existsSync(sourcePath)) {
121
+ throw new Error(`missing local release artifact for postinstall smoke: ${sourcePath}`);
122
+ }
123
+ const destination = join(vendorDir, artifactName);
124
+ copyFileSync(sourcePath, destination);
125
+ prepareBinary(destination);
126
+ }
127
+ console.log(`postinstall: installed local smoke-test binaries for ${process.platform}-${process.arch}`);
128
+ return;
129
+ }
130
+
131
+ const releaseBaseUrl =
132
+ process.env.GOODVIBES_RELEASE_BASE_URL?.trim() ||
133
+ `${resolveRepositoryBaseUrl()}/releases/download/v${pkg.version}`;
134
+
135
+ const checksumUrl = `${releaseBaseUrl}/SHA256SUMS.txt`;
136
+ const checksumText = await downloadText(checksumUrl);
137
+ writeFileSync(join(vendorDir, 'SHA256SUMS.txt'), checksumText);
138
+ const checksums = parseChecksumFile(checksumText);
139
+
140
+ for (const artifactName of [artifacts.app, artifacts.daemon]) {
141
+ const destination = join(vendorDir, artifactName);
142
+ const tempDestination = `${destination}.download`;
143
+ rmSync(tempDestination, { force: true });
144
+ await downloadFile(`${releaseBaseUrl}/${artifactName}`, tempDestination);
145
+ const actual = sha256(readFileSync(tempDestination));
146
+ const expected = checksums.get(artifactName);
147
+ if (expected && expected !== actual) {
148
+ rmSync(tempDestination, { force: true });
149
+ throw new Error(`checksum mismatch for ${artifactName}: expected ${expected}, got ${actual}`);
150
+ }
151
+ rmSync(destination, { force: true });
152
+ copyFileSync(tempDestination, destination);
153
+ rmSync(tempDestination, { force: true });
154
+ prepareBinary(destination);
155
+ }
156
+
157
+ console.log(`postinstall: installed release binaries for ${process.platform}-${process.arch}`);
158
+ }
159
+
160
+ function deployBundledFiles() {
161
+ const targets = [
162
+ { src: join(projectRoot, '.goodvibes', 'skills'), dest: join(home, '.goodvibes', 'tui', 'skills') },
163
+ { src: join(projectRoot, '.goodvibes', 'agents'), dest: join(home, '.goodvibes', 'tui', 'agents') },
164
+ ];
165
+
166
+ let installed = 0;
167
+ let skipped = 0;
168
+
169
+ for (const { src, dest } of targets) {
170
+ if (!existsSync(src)) continue;
171
+
172
+ const entries = readdirSync(src, { withFileTypes: true });
173
+
174
+ for (const entry of entries) {
175
+ const srcPath = join(src, entry.name);
176
+ const destPath = join(dest, entry.name);
177
+
178
+ if (existsSync(destPath)) {
179
+ skipped++;
180
+ continue;
181
+ }
182
+
183
+ mkdirSync(dest, { recursive: true });
184
+
185
+ if (entry.isDirectory()) {
186
+ cpSync(srcPath, destPath, { recursive: true });
187
+ console.log(` installed: ${entry.name}/`);
188
+ installed++;
189
+ } else if (entry.name.endsWith('.md')) {
190
+ cpSync(srcPath, destPath);
191
+ console.log(` installed: ${entry.name}`);
192
+ installed++;
193
+ }
194
+ }
195
+ }
196
+
197
+ const goodvibesSrc = join(projectRoot, '.goodvibes', 'GOODVIBES.md');
198
+ const goodvibesDest = join(home, '.goodvibes', 'GOODVIBES.md');
199
+ if (existsSync(goodvibesSrc) && !existsSync(goodvibesDest)) {
200
+ mkdirSync(join(home, '.goodvibes'), { recursive: true });
201
+ copyFileSync(goodvibesSrc, goodvibesDest);
202
+ console.log(` installed: ${basename(goodvibesDest)}`);
203
+ installed++;
204
+ } else if (existsSync(goodvibesDest)) {
205
+ skipped++;
206
+ }
207
+
208
+ if (installed > 0 || skipped > 0) {
209
+ console.log(`postinstall: ${installed} installed, ${skipped} already exist (skipped)`);
210
+ } else {
211
+ console.log('postinstall: nothing to deploy');
212
+ }
213
+ }
214
+
215
+ async function main() {
216
+ await installPlatformBinaries();
217
+ deployBundledFiles();
218
+ }
219
+
220
+ await main();
@@ -63,7 +63,7 @@ type AgentOrchestratorToolDeps = {
63
63
  readonly serviceRegistry?: import('../config/service-registry.ts').ServiceRegistry;
64
64
  readonly featureFlags?: Pick<FeatureFlagManager, 'isEnabled'> | null;
65
65
  readonly overflowHandler?: import('@pellux/goodvibes-sdk/platform/tools/shared/overflow').OverflowHandler;
66
- readonly sandboxSessionRegistry: import('../runtime/sandbox/session-registry.ts').SandboxSessionRegistry;
66
+ readonly sandboxSessionRegistry: import('@pellux/goodvibes-sdk/platform/runtime/sandbox/session-registry').SandboxSessionRegistry;
67
67
  readonly workflowServices: ReturnType<typeof import('@pellux/goodvibes-sdk/platform/tools/workflow/index').createWorkflowServices>;
68
68
  };
69
69
 
@@ -38,7 +38,13 @@ export function registerDiscoveryRuntimeCommands(registry: CommandRegistry): voi
38
38
  ctx.print(`[Scan] Warning: failed to register some providers: ${summarizeError(err)}`);
39
39
  }
40
40
 
41
- if (result.servers.length > 0) persistProviders(requireShellPaths(ctx), result.servers);
41
+ if (result.servers.length > 0) {
42
+ const shellPaths = requireShellPaths(ctx);
43
+ persistProviders({
44
+ homeDirectory: shellPaths.homeDirectory,
45
+ surfaceRoot: 'tui',
46
+ }, result.servers);
47
+ }
42
48
  ctx.renderRequest();
43
49
  },
44
50
  });
@@ -2,7 +2,7 @@ import { dirname, join, resolve } from 'node:path';
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import type { CommandContext } from '../command-registry.ts';
4
4
  import { discoverSkills } from '../../panels/skills-panel.ts';
5
- import { buildSandboxReview, isRunningInWsl } from '../../runtime/sandbox/manager.ts';
5
+ import { buildSandboxReview, isRunningInWsl } from '@pellux/goodvibes-sdk/platform/runtime/sandbox/manager';
6
6
  import { renderQemuWrapperTemplate } from '@pellux/goodvibes-sdk/platform/runtime/sandbox/qemu-wrapper-template';
7
7
  import { getPluginDirectories } from '../../plugins/loader.ts';
8
8
  import { listBuiltinSubscriptionProviders } from '../../config/subscription-providers.ts';
@@ -4,7 +4,7 @@ import type { CommandRegistry, CommandContext } from '../command-registry.ts';
4
4
  import type { ConfigKey } from '../../config/index.ts';
5
5
  import { CONFIG_SCHEMA } from '../../config/index.ts';
6
6
  import { listHookPointContracts } from '../../hooks/index.ts';
7
- import { isRunningInWsl } from '../../runtime/sandbox/manager.ts';
7
+ import { isRunningInWsl } from '@pellux/goodvibes-sdk/platform/runtime/sandbox/manager';
8
8
  import { renderQemuWrapperTemplate } from '@pellux/goodvibes-sdk/platform/runtime/sandbox/qemu-wrapper-template';
9
9
  import type { SetupTransferBundle } from './local-setup-transfer.ts';
10
10
  import {
@@ -8,7 +8,7 @@ import {
8
8
  inspectSandboxQemuSetupManifest,
9
9
  loadSandboxQemuSetupManifest,
10
10
  scaffoldSandboxQemuSetupBundle,
11
- } from '../../runtime/sandbox/provisioning.ts';
11
+ } from '@pellux/goodvibes-sdk/platform/runtime/sandbox/provisioning';
12
12
  import { requireShellPaths } from './runtime-services.ts';
13
13
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils/error-display';
14
14
 
@@ -26,7 +26,7 @@ export async function handleSandboxQemuCommand(args: string[], ctx: CommandConte
26
26
  ctx.print('Usage: /sandbox qemu setup <directory>');
27
27
  return true;
28
28
  }
29
- const bundle = scaffoldSandboxQemuSetupBundle(ctx.platform.configManager, shellPaths.workingDirectory, dirArg);
29
+ const bundle = scaffoldSandboxQemuSetupBundle(ctx.platform.configManager, shellPaths.workingDirectory, dirArg, { surfaceRoot: 'tui' });
30
30
  ctx.platform.configManager.setDynamic('sandbox.vmBackend', 'qemu');
31
31
  ctx.platform.configManager.setDynamic('sandbox.qemuExecWrapper', bundle.wrapperPath);
32
32
  ctx.platform.configManager.setDynamic('sandbox.qemuImagePath', bundle.imagePath);
@@ -61,7 +61,7 @@ export async function handleSandboxQemuCommand(args: string[], ctx: CommandConte
61
61
  return true;
62
62
  }
63
63
  try {
64
- const bundle = bootstrapSandboxQemuSetupBundle(ctx.platform.configManager, shellPaths.workingDirectory, dirArg, sizeGb);
64
+ const bundle = bootstrapSandboxQemuSetupBundle(ctx.platform.configManager, shellPaths.workingDirectory, dirArg, sizeGb, { surfaceRoot: 'tui' });
65
65
  ctx.print([
66
66
  `Bootstrapped QEMU sandbox in ${bundle.directory}`,
67
67
  ` wrapper: ${bundle.wrapperPath}`,