Commit graph

118 commits

Author SHA1 Message Date
Lahfir
f28dd95555 refactor(ffi): audit and remove unjustified #[allow(dead_code)] annotations
Every reachable pub(crate) helper (convert/ helpers, tree/flatten::flatten_tree,
error::{set_last_error, clear_last_error, last_error_code, error_code_to_result})
now stands without the annotation — these are all called from #[no_mangle]
entrypoints and rustc sees them correctly.

Move the three test-only last_error readers (last_error_message_str,
last_error_suggestion_str, last_error_platform_detail_str) plus the
MessageSource::to_owned_string helper into #[cfg(test)] scope. They were
only ever used by tests; having them stand at module scope required the
annotation and muddied the public-surface read.

Zero #[allow(dead_code)] remains in crates/ffi/src/. Tree still compiles,
27 tests still pass, cbindgen header symbol set unchanged.
2026-04-16 03:29:37 -07:00
Lahfir
95c3da2352 refactor(ffi): extract adapter from lib.rs, use explicit re-exports
Move AdAdapter + build_adapter + ad_adapter_create / ad_adapter_destroy /
ad_check_permissions into a dedicated crates/ffi/src/adapter.rs matching
the crates/macos/src/adapter.rs single-responsibility pattern.

lib.rs is now a module-declaration + explicit re-export shell. The
`pub use types::*` wildcard is replaced with one `pub use` per type so
adding a new type requires a deliberate re-export (prevents accidental
ABI expansion). Narrow most modules to pub(crate) — only error and
types stay pub because cbindgen scans them for public types.

Every file under 400 LOC (max 225). No behavior or ABI change; symbol
set identical.

Closes the structural portion of Unit R. Remaining Unit R cleanup
(c_to_str unbound lifetime + dead_code audit) lands next.
2026-04-16 03:27:03 -07:00
Lahfir
a49c35165e refactor(ffi): split screenshot.rs and surfaces.rs into submodules
- screenshot/{capture,free}.rs: ad_screenshot + ad_free_image
- surfaces/list.rs: ad_list_surfaces + ad_free_surfaces

Drop the last unused convert::surface re-exports; convert/mod.rs now
only re-exports rect_to_c (the single consumer sits inside tree/flatten
and benefits from the shortened path).

No behavior or ABI change; symbol set identical, ordering-only cbindgen diff.
2026-04-16 03:26:01 -07:00
Lahfir
29bde8dd03 refactor(ffi): split input.rs into input/ submodules
Move input.rs into input/{clipboard,mouse,drag}.rs.
- clipboard.rs: ad_get_clipboard + ad_set_clipboard + ad_clear_clipboard + ad_free_string
- mouse.rs: mouse_button_from_c + ad_mouse_event + tests
- drag.rs: ad_drag + tests

Drop unused convert::{c_to_str, free_c_string, string_to_c} re-exports
from convert/mod.rs; all callers now hit crate::convert::string::
directly.

No behavior or ABI change.
2026-04-16 03:25:05 -07:00
Lahfir
690d275484 refactor(ffi): split windows.rs into windows/ submodules
Move windows.rs into windows/{to_core,list,focus,free_one,op}.rs.
- to_core.rs: ad_window_to_core helper (pub(crate) re-exported so
  tree/get.rs keeps its existing crate::windows::ad_window_to_core path)
- list.rs: ad_list_windows + ad_free_windows
- focus.rs: ad_focus_window
- free_one.rs: ad_free_window (single-struct free from ad_launch_app;
  Unit 5b renames to ad_release_window_fields)
- op.rs: ad_window_op

Remove now-unused convert::{window_info_to_c, free_window_info_fields}
re-exports.

No behavior or ABI change. Header symbol set identical.
2026-04-16 03:23:56 -07:00
Lahfir
7463123095 refactor(ffi): split apps.rs into apps/ submodules
Move apps.rs into apps/{list,launch,close}.rs — one concern per file.
Drop the now-unused convert::{app_info_to_c, free_app_info_fields}
re-exports from convert/mod.rs (apps/list.rs imports from the concrete
convert::app submodule directly).

No behavior or ABI change. Header symbol set identical; emission order
shifts per cbindgen's module-path sort (ordering-only diff).
2026-04-16 03:22:49 -07:00
Lahfir
2cf6bb01b7 refactor(ffi): split actions.rs into actions/ submodules
Move actions.rs into actions/{conversion,resolve,execute,result}.rs.
- conversion.rs: direction_from_c + key_combo_from_c + action_from_c + tests
- resolve.rs: ad_resolve_element
- execute.rs: ad_execute_action
- result.rs: action_result_to_c + ad_free_action_result + tests

actions/mod.rs declares submodules; the #[no_mangle] entries remain
discoverable by the cdylib linker and cbindgen without re-exports.
Drop the unused opt_string_to_c re-export from convert/mod.rs (all new
modules import from crate::convert::string:: directly).

No behavior or ABI change. cbindgen header symbol set unchanged;
emission order shifts due to cbindgen's module-path topological sort
(ordering-only diff per Unit R verification criteria).
2026-04-16 03:21:53 -07:00
Lahfir
f3f8c733c2 refactor(ffi): split tree.rs into tree/ submodules, strip inline comments
Move tree.rs into tree/flatten.rs (flatten_tree + flatten_recursive +
strings_to_c_array + its tests), tree/free.rs (free_c_string_array,
free_node_fields, ad_free_tree + null-free test) and tree/get.rs
(ad_get_tree). Drop inline //-comments that restate what the next line
already says.

Behavior unchanged: flatten_tree is still the existing recursive DFS
layout (Unit 4 rewrites to iterative BFS so child ranges address direct
children). cbindgen header byte-identical.
2026-04-16 03:19:49 -07:00
Lahfir
a9661cd69e refactor(ffi): split convert.rs into convert/ submodules
Move convert.rs helpers into convert/{string,rect,window,app,surface}.rs.
Each submodule carries just the helpers for one kind (C-string helpers,
rect conversion, window-info conversion, app-info conversion, surface-info
conversion) plus its focused test(s). convert/mod.rs re-exports the same
public surface so every caller keeps its existing
`use crate::convert::{string_to_c, ...}` paths intact.

No behavior, signature, or ABI change; cbindgen header is byte-identical.
Unit R precondition; Unit 7 later refines string helpers (lossy/NUL-safe).
2026-04-16 03:18:00 -07:00
Lahfir
c2ea56c295 refactor(ffi): split types.rs into one module per type
Move the 28 packed #[repr(C)] structs and #[repr(i32)] enums from
crates/ffi/src/types.rs into crates/ffi/src/types/{point,rect,node,
action,...}.rs, one declaration per file. types/mod.rs carries
explicit pub mod + pub use per type, so callers keep the same
`use crate::types::AdFoo` paths and cbindgen emits a byte-identical
generated header.

Purely mechanical; no behavior, signature, or ABI change. Unit R
precondition for later fix work where individual types are extended
(AdTreeOptions.surface, AdImageBuffer length encapsulation, etc.).
2026-04-16 03:16:57 -07:00
Lahfir
f54087fb3a refactor(ffi): eliminate error.rs unwrap with static CStr fallback
Replace the infallible-today .unwrap() on CString::new fallback with a
MessageSource enum that carries either an owned CString or a 'static
CStr pointer. Interior-NUL payloads now deterministically resolve to
the NUL_BYTE_FALLBACK constant rather than relying on an unwrap that
could panic if the literal ever gained a NUL byte.

Adds a regression test for interior-NUL handling.

Precondition for Unit 1's panic boundary, which needs a last-error
path that never allocates and never panics.
2026-04-16 03:14:31 -07:00
Lahfir
fe2cf4446d chore: ignore .context/ for local tooling artifacts 2026-04-13 19:11:54 -07:00
Jake Rosoman
88fec35f5f fix(ffi): use dynamic error codes and fix Box::from_raw UB in action result 2026-04-06 14:21:50 +10:00
Jake Rosoman
3cd32d49ef feat(ffi): copy generated header to include/ for distribution 2026-04-06 14:17:23 +10:00
Jake Rosoman
bae931d7c5 style(ffi): apply rustfmt formatting 2026-04-06 14:16:09 +10:00
Jake Rosoman
ecba145827 feat(ffi): add surfaces list and get_tree FFI 2026-04-06 14:14:55 +10:00
Jake Rosoman
15115149a5 feat(ffi): add screenshot capture and image buffer 2026-04-06 14:13:27 +10:00
Jake Rosoman
2adcbb4cc0 feat(ffi): add clipboard, mouse, and drag FFI functions 2026-04-06 14:12:05 +10:00
Jake Rosoman
297dfeb3e6 feat(ffi): add window list, focus, and operations 2026-04-06 14:10:33 +10:00
Jake Rosoman
42c61ff0c1 feat(ffi): add action types, execute_action, resolve_element 2026-04-06 14:08:50 +10:00
Jake Rosoman
80b0e1fbf4 feat(ffi): add app lifecycle FFI functions 2026-04-06 14:06:22 +10:00
Jake Rosoman
34b04e16bc feat(ffi): add tree flattening with depth-first AdNodeTree 2026-04-06 14:04:59 +10:00
Jake Rosoman
b0e42c13a2 feat(ffi): add adapter lifecycle and permissions check 2026-04-06 14:02:09 +10:00
Jake Rosoman
272071b133 feat(ffi): add thread-local error handling with C accessors 2026-04-06 14:00:01 +10:00
Jake Rosoman
c724bc4892 feat(ffi): add primitive, window, app, surface C types with conversions 2026-04-06 13:57:14 +10:00
Jake Rosoman
920e7ef719 feat: scaffold ffi crate with cbindgen 2026-04-06 13:53:27 +10:00
Lahfir
ccc734ecbc docs: add surface-first snapshot rule for overlays
When a sheet, alert, popover, or menu is open, agents should snapshot
the specific surface instead of the full window. Background refs are
irrelevant while an overlay has focus and waste tokens.
2026-03-03 12:34:50 -08:00
github-actions[bot]
eb607e2b8e
chore(main): release 0.1.11 (#17)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-03 09:54:25 -05:00
Lahfir
39b2bc6348 fix: show skill install prompt on all success paths
promptSkillInstall() was only called after fresh download. Now also
shows when binary already exists or comes from AGENT_DESKTOP_BINARY_PATH.
2026-03-03 06:50:02 -08:00
github-actions[bot]
ceb02470c1
chore(main): release 0.1.10 (#16)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-03 09:23:49 -05:00
Lahfir
208af12459 fix: add clawhub login step before sync in CI
clawhub CLI requires explicit login, env var alone is not enough.
2026-03-03 06:22:06 -08:00
github-actions[bot]
4abb4b9484
chore(main): release 0.1.9 (#15)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-03 09:19:22 -05:00
Lahfir
97665203a4
feat: scalable skill architecture with ClawHub auto-publishing (#14)
* feat: restructure skills for ClawHub publishing with CI auto-publish

- Sync core skill to 54 commands (add notification commands)
- Move macOS skill from .claude/skills/ to git-tracked skills/
- Extract Notification Center section to references/notifications.md
- Add ClawHub metadata (version, tags, requirements) to all SKILL.md
- Remove macos.md from core skill (moved to platform skill)
- Create scripts/link-skills.sh for local dev symlinks
- Add publish-skills CI job to release.yml (ClawHub auto-publish)
- Add skill install prompt to npm postinstall

* refactor: consolidate macOS into single agent-desktop skill

Merge agent-desktop-macos back into agent-desktop/references/macos.md
as a single publishable skill. Includes Notification Center content
inline rather than as a separate reference file.
2026-03-03 06:16:15 -08:00
Lahfir
b8149816b7
Merge pull request #13 from lahfir/release-please--branches--main--components--agent-desktop
chore(main): release 0.1.8
2026-03-01 00:56:57 -08:00
github-actions[bot]
b53a545dc9
chore(main): release 0.1.8 2026-03-01 08:36:36 +00:00
Lahfir
4a300c8cb0 feat: implement --compact flag to collapse single-child unnamed nodes
Activate the existing --compact CLI flag (previously a no-op) to reduce
tree verbosity by collapsing pass-through container nodes that carry no
semantic information (no ref, no name, no value, no description, no
states, exactly one child).

Slack with -i --compact: 264 → 214 nodes, ~5,967 → ~5,117 tokens (14%
reduction). All 161 refs preserved. Finder unaffected (3% reduction).

Handles Electron's empty-string-vs-None pattern using is_none_or.
2026-03-01 00:36:20 -08:00
Lahfir
a19c1b5132 feat: add electron/web app compatibility for accessibility tree traversal
Skip depth budget for non-semantic AXGroup/AXGenericElement wrappers with
empty name and value, allowing default --max-depth 10 to find 100+ refs
in Electron apps like Slack and VS Code (previously found only 3).

Raise resolver search depth from 20 to ABSOLUTE_MAX_DEPTH (50) so ref
resolution succeeds for deeply nested Electron elements.

Fix surface detection to check if focused window itself is the target
surface (Electron reports dialogs as focused window, not as children)
and check both AXRole and AXSubrole for surface matching.

Update Phase 2 (Windows) and Phase 3 (Linux) docs with equivalent
web/Electron compatibility patterns for their respective APIs.
2026-02-28 19:03:11 -08:00
Lahfir
4d9daeadce
Merge pull request #12 from lahfir/release-please--branches--main--components--agent-desktop
chore(main): release 0.1.7
2026-02-27 16:25:21 -08:00
github-actions[bot]
614f82bedc
chore(main): release 0.1.7 2026-02-28 00:24:51 +00:00
Lahfir
b1fd368f19
Merge pull request #11 from lahfir/feat/notification-management-macos
feat: add notification management commands (macOS)
2026-02-27 16:24:35 -08:00
Lahfir
37c05cfaf4 chore: add skills-lock.json to gitignore 2026-02-27 16:18:01 -08:00
Lahfir
25a16be888 chore: remove accidentally committed files 2026-02-27 16:17:00 -08:00
Lahfir
3dd9081e81 style: fix formatting in nc_session.rs 2026-02-27 16:16:40 -08:00
Lahfir
3881bc82bd fix(macos): restore frontmost app after notification center interaction
NcSession now captures the frontmost application before opening
Notification Center and reactivates it on close, preventing the
source app (e.g. Script Editor) from stealing focus during dismiss.
2026-02-27 14:30:31 -08:00
Lahfir
27ef4f34c0 fix(macos): remove AXPress from dismiss action list
AXPress "clicks" the notification body (opening the source app) but
does not actually dismiss it. Removing it from the initial action list
forces the code to fall through to the hover-and-close-button strategy,
which reliably removes the notification from Notification Center.
2026-02-27 14:25:15 -08:00
Lahfir
8aa672c668 refactor: resolve code review findings from notification PR
- fix(core): wait --notification uses index-diff detection instead of
  count, passes text filter, checks deadline before sleep
- fix(core): dismiss-all-notifications uses single NC session with
  batch adapter method and reports individual failures
- refactor(macos): extract dismiss_entry helper to eliminate duplicate
  dismiss logic between single and batch operations
- refactor(macos): restrict nc_session visibility to pub(crate), use
  absolute paths for pgrep/osascript, add bounded wait with kill
  fallback
- refactor(macos): single-pass is_notification_group check using
  matches! macro
- refactor: extract notification CLI args and dispatch into separate
  files to keep cli_args.rs and dispatch.rs under 400 LOC
- refactor: rename DismissAllNotificationsArgs to
  DismissAllNotificationsCliArgs for consistency
- fix(core): remove unused timestamp field from NotificationInfo
- fix: remove poll_interval_ms from wait command (hardcode 500ms)
- docs: update phases.md with completed notification status and
  cross-platform reference notes
2026-02-27 12:42:46 -08:00
Lahfir
844e964b82 docs: update phases with notification and tray trait methods 2026-02-27 11:57:18 -08:00
Lahfir
de651737f1 refactor(macos): headless-first dismiss with cursor as last resort
Try AXDismiss/AXRemoveFromParent/AXPress on the notification group
element before falling back to cursor hover. Extract try_dismiss_button
helper. Cursor-based hover only triggers if all headless strategies
fail.
2026-02-27 11:37:47 -08:00
Lahfir
979758538f fix(macos): use pgrep and async osascript for NC lifecycle
CGWindowList requires Screen Recording permission on macOS Sequoia to
read other processes' window names, and SystemUIServer blocks AX access
to its menu bar (crashes with Obj-C foreign exception). Switch to pgrep
for PID lookup and osascript clicking ControlCenter's Clock menu bar
item, spawned async to avoid blocking.
2026-02-27 11:33:36 -08:00
Lahfir
2374330bb1 test: add notification error and filter matching tests
Add error code serialization and notification_not_found tests in core.
Add matches_filters unit tests in macOS list module covering app,
text, combined, and no-filter cases.
2026-02-27 10:53:15 -08:00