Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
39b124c
perf(dashboard-popout): park the kanban clock when hidden or timestam…
nwparker Jul 22, 2026
334027c
Update README downloads badge
github-actions[bot] Jul 22, 2026
6ad6241
release: v1.4.151-rc.0
github-actions[bot] Jul 22, 2026
05ec4b7
Update README downloads badge
github-actions[bot] Jul 22, 2026
c4d903f
fix(agent-status): keep Claude in-process teammates visible as idle s…
brennanb2025 Jul 22, 2026
1a9e819
feat(skills): land remaining hybrid stubs (#9846)
brennanb2025 Jul 22, 2026
dfbc2e8
fix(mobile-quick-commands): replay sheet load killed by connection mi…
brennanb2025 Jul 22, 2026
4c2bb50
feat(settings): make Language setting findable by native-language sea…
AmethystLiang Jul 22, 2026
0121f57
fix(agent-status): map codex request_user_input questions to Needs Yo…
brennanb2025 Jul 22, 2026
24b8fcc
fix(mobile): restore the last-open tab when returning to a worktree o…
brennanb2025 Jul 22, 2026
405b9f2
feat(mobile): mount ProtocolBlockScreen when protocol compat is block…
brennanb2025 Jul 22, 2026
56a31a5
Show current branch in Source Control header (#9787)
brennanb2025 Jul 22, 2026
53e018a
fix(terminal): make SSH reconnect UI non-blocking (#9928)
nwparker Jul 22, 2026
8f40419
fix(agent-status): cap the live agent-status map to stop Windows rend…
OrcaWin Jul 22, 2026
d6c9fcd
feat(mobile): surface pairing-auth failures on desktop and mobile (#9…
brennanb2025 Jul 22, 2026
034aeb1
feat(editor): seed Find from selected text (#9982)
AmethystLiang Jul 22, 2026
4468d54
perf(mobile): gate host polling on foreground/background (#9857)
nwparker Jul 22, 2026
76f5b83
fix(mobile): pause session polling in background (#9875)
nwparker Jul 22, 2026
a3d6f84
fix(mobile): pause relative-time clocks when hidden (#9886)
nwparker Jul 22, 2026
c6d2803
perf(mobile): memoize worktree list rows (#9889)
nwparker Jul 22, 2026
11310ee
fix(mobile): keep quick-commands button steady while capabilities loa…
brennanb2025 Jul 22, 2026
17e471c
perf(editor): memoize the markdown preview render pipeline (#9843)
nwparker Jul 22, 2026
8cfb8a2
perf(emulator): park device stream when the window is hidden (#9842)
nwparker Jul 22, 2026
b0e3616
fix: return native promise from awaitClick on Zone.js pages (#9995)
AmethystLiang Jul 22, 2026
4062ed5
perf(worktrees): back off worktree scans for agent-scratch repos (#9985)
brennanb2025 Jul 22, 2026
8dc1ca4
fix(daemon): retire macOS daemons whose login session died (#7936) (#…
brennanb2025 Jul 22, 2026
8b6e530
release: v1.4.151-rc.1
github-actions[bot] Jul 22, 2026
9bdb820
test(e2e): verify Claude is prefilled with issue URL on start (#9980)
AmethystLiang Jul 22, 2026
0d82cc3
Fix legacy worktree lineage projection after stable updates (#9913)
OrcaWin Jul 22, 2026
b606117
Fix paired remote terminal parse backpressure (#9683)
OrcaWin Jul 22, 2026
1d2cd33
fix(deps): resolve Dependabot security alerts (#10006)
nwparker Jul 22, 2026
bc57cf8
fix(sidebar): use automation icon for Hide automation-created filter …
brennanb2025 Jul 22, 2026
a356b9d
fix(worktrees): show CLI-created local worktrees in the sidebar while…
smk508 Jul 22, 2026
b63d4cd
feat(agent-status): question glyph for "needs you" state everywhere (…
brennanb2025 Jul 22, 2026
b65c593
perf(web): pause the runtime heartbeat while the window is hidden (#9…
nwparker Jul 22, 2026
5d3b968
perf(runtime): arm the WS-server heartbeat only while clients are con…
nwparker Jul 22, 2026
6a43f99
perf(mobile): coalesce duplicate concurrent home-screen requests (#9888)
nwparker Jul 22, 2026
01bcc57
perf(mobile): gate dictation setup progress polling on foreground + s…
nwparker Jul 22, 2026
43ae014
Enable accessibility tree (`ax`) command on iOS emulator sessions (#1…
AmethystLiang Jul 23, 2026
db56a9b
fix(types): use concrete Node stat result type (#10017)
brennanb2025 Jul 23, 2026
407d7af
feat(telemetry): classify codex trust-grant fallbacks and attribute g…
brennanb2025 Jul 23, 2026
adc0203
feat(ssh): open SSH workspaces in VS Code Remote-SSH (#10005)
AmethystLiang Jul 23, 2026
9080205
fix(rate-limits): authenticate OpenCode Go through a session cookie j…
bbingz Jul 23, 2026
c8381f3
Preserve Codex [tui] settings across managed CODEX_HOME remirrors (#9…
brennanb2025 Jul 23, 2026
bc301de
fix(worktree-palette): guard undefined review title in matcher (crash…
nwparker Jul 23, 2026
6a8e992
Update README downloads badge
github-actions[bot] Jul 23, 2026
72a0a4d
Revert "Show current branch in Source Control header (#9787)" (#10032)
AmethystLiang Jul 23, 2026
3b86405
fix(terminal): stop overlay fallback measure-fit loop (#10026)
OrcaWin Jul 23, 2026
41751dd
fix(runtime): route HUB-owned SSH worktrees through owning runtime (#…
OrcaWin Jul 23, 2026
7a42271
fix(settings): stop React #185 in Voice speech-model dropdown (crashe…
nwparker Jul 23, 2026
0326594
Update paired Orca servers from the active client (#9839)
OrcaWin Jul 23, 2026
a0944cc
fix(linux): restore Ubuntu 20.04 launch — pin node-pty glibc symbols …
nwparker Jul 23, 2026
6d55c7f
rename: rebrand user-facing Native chat to Chat UI (#10036)
AmethystLiang Jul 23, 2026
3708c4f
fix(mobile): report interrupted native chat sends as delivery-unknown…
AmethystLiang Jul 23, 2026
5b7fdd7
Capture daemon startup logs before window ready in e2e test (#10040)
AmethystLiang Jul 23, 2026
4fce2de
fix(mobile): keep native chat from resizing the covered terminal PTY …
brennanb2025 Jul 23, 2026
0990031
fix(file-explorer): stop italic ignored filenames clipping ".md" to "…
nwparker Jul 23, 2026
bae29fd
fix(native-chat): handle reordering with timestampless transcripts (#…
AmethystLiang Jul 23, 2026
a2e440b
fix(sidebar): identify remote server workspaces (#10061)
OrcaWin Jul 23, 2026
ffe54a5
fix(skills): make skippedReason switch exhaustive (#9594)
Zuz666 Jul 23, 2026
d152039
fix(terminal): never sweep descendants of an already-exited PTY root …
nwparker Jul 23, 2026
059a80b
feat(review): add assignable "Send Review Notes to Agent" shortcut (#…
nwparker Jul 23, 2026
63bd3ff
perf(main): park worktree metadata pollers while the window is hidden…
nwparker Jul 23, 2026
414bdc9
perf(main): gate idle git-common polling + coarse-FS add/remove fix (…
nwparker Jul 23, 2026
8448557
perf(startup): overlap catalog/session disk reads with the worktree s…
nwparker Jul 23, 2026
fc05769
fix(ssh): allow blank user in VS Code authority (#10072)
AmethystLiang Jul 23, 2026
ee6319e
fix(agents): scope Settings agent list and quick-launch menu to the r…
brennanb2025 Jul 23, 2026
4a9affd
fix(emulator): iOS ax via plain-JSON serve-sim helper (supersedes #10…
AmethystLiang Jul 23, 2026
915ca2c
feat(editor): toggle Word Wrap from file tab actions and Alt+Z
innocarpe Jul 23, 2026
1b9c4ce
fix(editor): toggle diffWordWrap for diff surfaces on Alt+Z
innocarpe Jul 25, 2026
d192b28
test(editor): verify word wrap shortcut routing
nwparker Jul 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ docs/**
!docs/reference/
!docs/reference/git-compatibility.md
!docs/reference/headless-linux-server.md
!docs/reference/linux-glibc-compatibility.md

# Stably CLI (only docs/ are tracked)
.stably/*
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Orca targets macOS, Linux, and Windows. Keep all platform-dependent behavior beh
- **Keyboard shortcuts**: Never hardcode `e.metaKey`. Use a platform check (`navigator.userAgent.includes('Mac')`) to pick `metaKey` on Mac and `ctrlKey` on Linux/Windows. Electron menu accelerators should use `CmdOrCtrl`.
- **Shortcut labels in UI**: Display `⌘` / `⇧` on Mac and `Ctrl+` / `Shift+` on other platforms.
- **File paths**: Use `path.join` or Electron/Node path utilities — never assume `/` or `\`.
- **Linux native modules**: keep the glibc floor at Ubuntu 20.04 / glibc 2.31. A module compiled from source on a newer runner can reference symbol versions absent on the floor and crash the app on startup. See [`docs/reference/linux-glibc-compatibility.md`](./docs/reference/linux-glibc-compatibility.md); packaging fails if a bundled native binary needs newer glibc.

## SSH Use Case

Expand Down
12 changes: 10 additions & 2 deletions config/electron-builder.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const {
prunePackagedRuntimeNodeModules,
verifyPackagedMainRuntimeDeps
} = require('./packaged-runtime-node-modules.cjs')
const { verifyLinuxGlibcFloor } = require('./scripts/verify-linux-glibc-floor.cjs')

const isMacRelease = process.env.ORCA_MAC_RELEASE === '1'
const isLinuxArm64Release = process.env.ORCA_LINUX_ARM64_RELEASE === '1'
Expand Down Expand Up @@ -66,9 +67,10 @@ module.exports = {
'!mobile{,/**/*}',
'!native{,/**/*}',
'!skills{,/**/*}',
// Why: authoritative guide markdown is compiled into out/cli; shipping the
// authoring sources too would duplicate content without a runtime consumer.
// Why: guide/stub authoring sources are compiled into runtime artifacts; shipping
// either source tree would duplicate content without a runtime consumer.
'!skill-guides{,/**/*}',
'!skill-stubs{,/**/*}',
'!tests{,/**/*}',
// Why: pr-evidence/ is a local e2e screenshot output (ORCA_CAPTURE_EVIDENCE);
// it is gitignored, but exclude it defensively so a stray local capture at
Expand Down Expand Up @@ -132,6 +134,12 @@ module.exports = {
'node_modules/sherpa-onnx*/**'
],
afterPack: async (context) => {
// Why: a Linux runner-image glibc bump silently shipped a node-pty pty.node
// requiring GLIBC_2.34, crashing the app on startup on Ubuntu 20.04 (#9902).
// Fail packaging if any bundled native binary exceeds the supported floor.
if (context.electronPlatformName === 'linux') {
verifyLinuxGlibcFloor(context.appOutDir)
}
const resourcesDir =
context.electronPlatformName === 'darwin'
? join(
Expand Down
35 changes: 35 additions & 0 deletions config/localization-coverage-allowlist.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,40 @@
"text": "Terminal 1",
"dynamic": false,
"count": 1
},
{
"filePath": "src/renderer/src/components/settings/appearance-search.ts",
"kind": "object-property:keywords",
"text": "语言",
"dynamic": false,
"count": 1
},
{
"filePath": "src/renderer/src/components/settings/appearance-search.ts",
"kind": "object-property:keywords",
"text": "語言",
"dynamic": false,
"count": 1
},
{
"filePath": "src/renderer/src/components/settings/appearance-search.ts",
"kind": "object-property:keywords",
"text": "언어",
"dynamic": false,
"count": 1
},
{
"filePath": "src/renderer/src/components/settings/appearance-search.ts",
"kind": "object-property:keywords",
"text": "言語",
"dynamic": false,
"count": 1
},
{
"filePath": "src/renderer/src/components/settings/appearance-search.ts",
"kind": "object-property:keywords",
"text": "Idioma",
"dynamic": false,
"count": 1
}
]
115 changes: 80 additions & 35 deletions config/patches/node-pty@1.1.0.patch
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git a/binding.gyp b/binding.gyp
index 5f63978b07ab50aaf7523219a2170ec737a6b5db..b3309a07ef99dea7967d7bdd04b9fc3500acacae 100644
index 5f63978b07ab50aaf7523219a2170ec737a6b5db..bbd9e06136e8922f40b5779e35d4fc835f1479ab 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -5,9 +5,6 @@
Expand All @@ -12,6 +12,23 @@ index 5f63978b07ab50aaf7523219a2170ec737a6b5db..b3309a07ef99dea7967d7bdd04b9fc35
'msvs_settings': {
'VCCLCompilerTool': {
'AdditionalOptions': [
@@ -88,6 +85,16 @@
'libraries!': [
'-lutil'
]
+ }],
+ # Orca: pair with the .symver pins in pty.cc. Force the real
+ # libutil.so.1/libpthread.so.0 into DT_NEEDED (gcc's default
+ # --as-needed drops them because the pinned symbols resolve from
+ # libc's compat aliases at build time) so openpty/forkpty/
+ # pthread_sigmask still resolve on Ubuntu 20.04 (glibc 2.31).
+ ['OS=="linux"', {
+ 'ldflags': [
+ '-Wl,--no-as-needed,-l:libutil.so.1,-l:libpthread.so.0,--as-needed'
+ ]
}]
]
}
diff --git a/deps/winpty/src/winpty.gyp b/deps/winpty/src/winpty.gyp
index 1ac5758bedd8cf54f32280dea4e4aeb5afdee30d..e619813759c6f14694838bdfbd0ea5f8360130ef 100644
--- a/deps/winpty/src/winpty.gyp
Expand Down Expand Up @@ -54,6 +71,27 @@ index 1ac5758bedd8cf54f32280dea4e4aeb5afdee30d..e619813759c6f14694838bdfbd0ea5f8
'msvs_settings': {
# Specify this setting here to override a setting from somewhere
# else, such as node's common.gypi.
diff --git a/lib/conpty_console_list_agent.js b/lib/conpty_console_list_agent.js
index 8c4fca9022a6d6f015bca87f61625cde2278f428..0a01730616488119aa21ef441cf3c441e02a974c 100644
--- a/lib/conpty_console_list_agent.js
+++ b/lib/conpty_console_list_agent.js
@@ -10,7 +10,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = require("./utils");
var getConsoleProcessList = utils_1.loadNativeModule('conpty_console_list').module.getConsoleProcessList;
var shellPid = parseInt(process.argv[2], 10);
-var consoleProcessList = getConsoleProcessList(shellPid);
+var consoleProcessList;
+try {
+ consoleProcessList = getConsoleProcessList(shellPid);
+}
+catch (_a) {
+ // Why: AttachConsole can fail after the shell exits; parent already has this fallback.
+ consoleProcessList = [shellPid];
+}
process.send({ consoleProcessList: consoleProcessList });
process.exit(0);
//# sourceMappingURL=conpty_console_list_agent.js.map
\ No newline at end of file
diff --git a/lib/unixTerminal.js b/lib/unixTerminal.js
index 1ec12f796a822c78fba9ad7f6448c3987e325c23..cec8b67aef02f8199e5606a0d257088bf1865877 100644
--- a/lib/unixTerminal.js
Expand All @@ -73,31 +111,12 @@ index 1ec12f796a822c78fba9ad7f6448c3987e325c23..cec8b67aef02f8199e5606a0d257088b
var DEFAULT_FILE = 'sh';
var DEFAULT_NAME = 'xterm';
var DESTROY_SOCKET_TIMEOUT_MS = 200;
diff --git a/lib/conpty_console_list_agent.js b/lib/conpty_console_list_agent.js
index ccc111c9e03a4a661ccfd5d8e8f0ee699571b5dd..f92c6bef7d46dc35c941c87ef186aa46d8ed9c44 100644
--- a/lib/conpty_console_list_agent.js
+++ b/lib/conpty_console_list_agent.js
@@ -9,7 +9,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = require("./utils");
var getConsoleProcessList = utils_1.loadNativeModule('conpty_console_list').module.getConsoleProcessList;
var shellPid = parseInt(process.argv[2], 10);
-var consoleProcessList = getConsoleProcessList(shellPid);
+var consoleProcessList;
+try {
+ consoleProcessList = getConsoleProcessList(shellPid);
+}
+catch (_a) {
+ // Why: AttachConsole can fail after the shell exits; parent already has this fallback.
+ consoleProcessList = [shellPid];
+}
process.send({ consoleProcessList: consoleProcessList });
process.exit(0);
//# sourceMappingURL=conpty_console_list_agent.js.map
diff --git a/src/conpty_console_list_agent.ts b/src/conpty_console_list_agent.ts
index f6a653893e0b9b548c514db29d75599538ee1acb..1d5400489f200ef0161ca687e672e1cc02d29c95 100644
index 181ccabbbe9c4948a9725fb1db907a68e9de01fc..67f31facf85562b67adbfbd04ce28ddd8eeb4a79 100644
--- a/src/conpty_console_list_agent.ts
+++ b/src/conpty_console_list_agent.ts
@@ -11,5 +11,11 @@ import { loadNativeModule } from './utils';
@@ -10,6 +10,12 @@ import { loadNativeModule } from './utils';

const getConsoleProcessList = loadNativeModule('conpty_console_list').module.getConsoleProcessList;
const shellPid = parseInt(process.argv[2], 10);
-const consoleProcessList = getConsoleProcessList(shellPid);
Expand All @@ -111,7 +130,7 @@ index f6a653893e0b9b548c514db29d75599538ee1acb..1d5400489f200ef0161ca687e672e1cc
process.send!({ consoleProcessList });
process.exit(0);
diff --git a/src/unix/pty.cc b/src/unix/pty.cc
index 7b4b9e1f990fbf95b51528bb56dc9717f5b87532..61f39f0cbb91faa2c515f35d2ca850564e6368d9 100644
index 7b4b9e1f990fbf95b51528bb56dc9717f5b87532..383df0c9c48355547c65e6c9bbba593d15c4dd44 100644
--- a/src/unix/pty.cc
+++ b/src/unix/pty.cc
@@ -23,7 +23,9 @@
Expand All @@ -124,7 +143,33 @@ index 7b4b9e1f990fbf95b51528bb56dc9717f5b87532..61f39f0cbb91faa2c515f35d2ca85056
#include <thread>

#include <sys/types.h>
@@ -237,13 +239,23 @@ pty_getproc(int, char *);
@@ -47,6 +49,25 @@
#include <termios.h>
#endif

+/* Orca: glibc 2.32-2.34 relocated pthread_sigmask/openpty/forkpty into libc
+ * under new symbol versions, so building on a newer glibc produces references
+ * (GLIBC_2.32/2.34) absent on Ubuntu 20.04 (glibc 2.31) and the app fails to
+ * launch. Pin these to the pre-merge version glibc still ships as a compat
+ * alias; the binding.gyp ldflags force libutil/libpthread into DT_NEEDED so
+ * those aliases are actually loaded on the target. */
+#if defined(__linux__)
+# if defined(__x86_64__)
+# define ORCA_GLIBC_COMPAT_VERSION "GLIBC_2.2.5"
+# elif defined(__aarch64__)
+# define ORCA_GLIBC_COMPAT_VERSION "GLIBC_2.17"
+# endif
+# ifdef ORCA_GLIBC_COMPAT_VERSION
+__asm__(".symver openpty,openpty@" ORCA_GLIBC_COMPAT_VERSION);
+__asm__(".symver forkpty,forkpty@" ORCA_GLIBC_COMPAT_VERSION);
+__asm__(".symver pthread_sigmask,pthread_sigmask@" ORCA_GLIBC_COMPAT_VERSION);
+# endif
+#endif
+
/* Some platforms name VWERASE and VDISCARD differently */
#if !defined(VWERASE) && defined(VWERSE)
#define VWERASE VWERSE
@@ -237,13 +258,23 @@ pty_getproc(int, char *);
#endif

#if defined(__APPLE__) || defined(__OpenBSD__)
Expand All @@ -149,7 +194,7 @@ index 7b4b9e1f990fbf95b51528bb56dc9717f5b87532..61f39f0cbb91faa2c515f35d2ca85056
#endif

struct DelBuf {
@@ -367,10 +379,11 @@ Napi::Value PtyFork(const Napi::CallbackInfo& info) {
@@ -367,10 +398,11 @@ Napi::Value PtyFork(const Napi::CallbackInfo& info) {
argv[i + 3] = strdup(arg.c_str());
}

Expand All @@ -165,7 +210,7 @@ index 7b4b9e1f990fbf95b51528bb56dc9717f5b87532..61f39f0cbb91faa2c515f35d2ca85056
}
if (pty_nonblock(master) == -1) {
throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
@@ -684,15 +697,73 @@ pty_getproc(int fd, char *tty) {
@@ -684,15 +716,73 @@ pty_getproc(int fd, char *tty) {
#endif

#if defined(__APPLE__)
Expand Down Expand Up @@ -241,25 +286,25 @@ index 7b4b9e1f990fbf95b51528bb56dc9717f5b87532..61f39f0cbb91faa2c515f35d2ca85056

for (; count < 3; count++) {
low_fds[count] = posix_openpt(O_RDWR);
@@ -706,80 +777,118 @@ pty_posix_spawn(char** argv, char** env,
@@ -706,80 +796,118 @@ pty_posix_spawn(char** argv, char** env,
POSIX_SPAWN_SETSID;
*master = posix_openpt(O_RDWR);
if (*master == -1) {
- return;
+ pty_set_spawn_error(err, "posix_openpt", errno);
+ goto done;
+ }
+
+ res = grantpt(*master);
+ if (res == -1) {
+ pty_set_spawn_error(err, "grantpt", errno);
+ goto done;
}

- int res = grantpt(*master) || unlockpt(*master);
+ res = unlockpt(*master);
+ res = grantpt(*master);
if (res == -1) {
- return;
+ pty_set_spawn_error(err, "grantpt", errno);
+ goto done;
+ }
+
+ res = unlockpt(*master);
+ if (res == -1) {
+ pty_set_spawn_error(err, "unlockpt", errno);
+ goto done;
}
Expand Down
55 changes: 50 additions & 5 deletions config/scripts/computer-use-skill-guidance.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { join, resolve } from 'node:path'
import { describe, expect, it } from 'vitest'

const projectDir = resolve(import.meta.dirname, '../..')
const skillPath = join(projectDir, 'skills', 'computer-use', 'SKILL.md')
// Why: computer-use now ships a hybrid discovery stub, so its version-sensitive command
// guidance lives in the authoritative guide source — assert that content there. The
// installable stub projection is checked separately below.
const guidePath = join(projectDir, 'skill-guides', 'computer-use.md')
const stubPath = join(projectDir, 'skills', 'computer-use', 'SKILL.md')

describe('computer-use skill guidance', () => {
it('keeps web-app targeting on the computer-use surface', () => {
const skill = readFileSync(skillPath, 'utf8')
const skill = readFileSync(guidePath, 'utf8')

expect(skill).toContain('Use this skill for desktop UI through `orca computer`')
expect(skill).toContain('operate the desktop browser app/window that contains the page')
Expand All @@ -19,25 +23,66 @@ describe('computer-use skill guidance', () => {
})

it('warns agents to verify browser-hosted form focus before drafting text', () => {
const skill = readFileSync(skillPath, 'utf8')
const skill = readFileSync(guidePath, 'utf8')

expect(skill).toContain('For browser-hosted forms such as Gmail compose')
expect(skill).toContain('verify the focused UI element after each field action')
expect(skill).toContain('Prefer `paste-text` into the verified focused field')
})

it('warns agents about occluded Linux and Windows screenshots', () => {
const skill = readFileSync(skillPath, 'utf8')
const skill = readFileSync(guidePath, 'utf8')

expect(skill).toContain('On Linux and Windows')
expect(skill).toContain('use `--restore-window` so another window does not cover')
expect(skill).toContain('trust the tree over potentially occluded pixels')
})

it('points JSON users to the public accessibility-tree field', () => {
const skill = readFileSync(skillPath, 'utf8')
const skill = readFileSync(guidePath, 'utf8')

expect(skill).toContain('`result.snapshot.treeText`')
expect(skill).not.toContain('`result.elements`')
})
})

describe('computer-use install stub', () => {
it('points at the version-matched guide and preserves the safe resolver', () => {
const stub = readFileSync(stubPath, 'utf8')

expect(stub).toContain('discovery stub')
expect(stub).toContain('ORCA skills get computer-use')
// The safe CLI-resolution contract must survive in the stub, never a bare `orca`.
expect(stub).toContain('ORCA_CLI_COMMAND')
expect(stub).toContain('orca-dev')
expect(stub).toContain('orca-ide')
expect(stub).toContain('GNOME Orca screen reader')
expect(stub).not.toMatch(/^orca /mu)
})

it('gives older binaries a bounded fallback instead of a dead end', () => {
const stub = readFileSync(stubPath, 'utf8').replace(/\s+/gu, ' ')

expect(stub).toContain('explicitly reports that `skills get` is an unknown command')
expect(stub).toContain('do not invent commands')
expect(stub).toContain('ask the user rather than guessing')
})

it('drops the changing command reference from the installable file', () => {
const stub = readFileSync(stubPath, 'utf8')
const guide = readFileSync(guidePath, 'utf8')

// Version-sensitive command detail lives in the binary-served guide now, not here.
expect(stub).not.toContain('result.snapshot.treeText')
expect(stub).not.toContain('--restore-window')
expect(stub.length).toBeLessThan(guide.length)
})

it('keeps the routing frontmatter identical to the guide', () => {
const frontmatter = (text) => /^---\n[\s\S]*?\n---\n/u.exec(text)[0]

expect(frontmatter(readFileSync(stubPath, 'utf8'))).toBe(
frontmatter(readFileSync(guidePath, 'utf8'))
)
})
})
1 change: 1 addition & 0 deletions config/scripts/electron-builder-config.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe('electron-builder config', () => {
'!native{,/**/*}',
'!skills{,/**/*}',
'!skill-guides{,/**/*}',
'!skill-stubs{,/**/*}',
'!resources/skills/**',
'!tests{,/**/*}',
'!pr-evidence{,/**/*}',
Expand Down
11 changes: 10 additions & 1 deletion config/scripts/generate-bundled-skill-guides.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ const GUIDE_ALIASES = {
// Migrating a topic here is effectively one-way — earlier fat installs rely on the stub
// landing to converge — so entries are added as skills convert, never removed. The stub
// body lives in skill-stubs/<topic>.md; the projection reuses the guide's own frontmatter.
const STUB_TOPICS = ['orca-cli']
const STUB_TOPICS = [
'computer-use',
'linear-tickets',
'orca-cli',
'orca-emulator',
'orca-emulator-android',
'orca-linear',
'orca-per-workspace-env',
'orchestration'
]

function normalizeMarkdown(markdown) {
return markdown.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
Expand Down
Loading
Loading