Skip to content

feat(macos): support native title exclusion flags#128

Open
TimeToBuildBob wants to merge 2 commits into
ActivityWatch:masterfrom
TimeToBuildBob:bob-aw-watcher-window-121
Open

feat(macos): support native title exclusion flags#128
TimeToBuildBob wants to merge 2 commits into
ActivityWatch:masterfrom
TimeToBuildBob:bob-aw-watcher-window-121

Conversation

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

Summary

  • pass exclude-title settings from Python into the Swift watcher process
  • parse the new CLI flags in macos.swift and apply regex-based title exclusion before heartbeats
  • add focused tests for the Python-side Swift launcher contract

Testing

  • PYTHONPATH=/tmp/bob-aw-watcher-window-121 uv run pytest tests/test_main.py -q
  • python3 -m py_compile aw_watcher_window/init.py aw_watcher_window/main.py aw_watcher_window/macos_cli.py

Closes #121

@greptile-apps

greptile-apps Bot commented May 28, 2026

Copy link
Copy Markdown

Greptile Summary

This PR wires macOS title-exclusion settings from the Python layer into the Swift watcher subprocess, introducing a new build_swift_command helper in macos_cli.py and corresponding CLI argument parsing (--exclude-title, --exclude-titles) in macos.swift.

  • macos_cli.py: new module that constructs the Swift subprocess command list, emitting one --exclude-titles <pattern> pair per regex, matching the Swift parser's one-value-per-flag contract.
  • macos.swift: adds global excludeTitle/excludeTitlePatterns state, a parseOptionalArguments function for the new flags, and applies titleShouldBeExcluded before each heartbeat.
  • tests/test_main.py: new unit tests covering the baseline (no filters) and the full flag-passing case for build_swift_command.

Confidence Score: 5/5

Safe to merge; the Python↔Swift command-building contract is correct and well-tested, and the one flaw found is a pattern-array accumulation on accessibility-permission retries that does not affect correctness.

All changed code paths are straightforward: the Python side builds a shell command list and the Swift side parses it. The only issue found is that parseOptionalArguments appends to the global excludeTitlePatterns on every start() retry, causing duplicate entries after repeated accessibility-permission checks — but duplicated patterns still produce the correct exclusion result.

aw_watcher_window/macos.swift — specifically the interaction between parseOptionalArguments and the start() retry loop

Important Files Changed

Filename Overview
aw_watcher_window/macos.swift Adds global excludeTitle/excludeTitlePatterns state, CLI argument parsing, and regex-based title filtering before heartbeat; pattern accumulation on start() retry is the one notable flaw
aw_watcher_window/macos_cli.py New helper that builds the Swift subprocess command list from Python args; correctly emits one --exclude-titles flag per pattern to match the Swift parser contract
aw_watcher_window/main.py Delegates Swift subprocess command construction to build_swift_command, forwarding exclude_title and exclude_titles args correctly; non-macOS heartbeat_loop path unchanged
aw_watcher_window/init.py Wraps main() with a deferred import to avoid eager loading of aw_client/aw_core at package import time
tests/test_main.py New focused unit tests for build_swift_command covering both the baseline (no filters) and the flag-passing case

Sequence Diagram

sequenceDiagram
    participant PY as main.py (Python)
    participant CLI as macos_cli.py
    participant SW as macos.swift (Swift)

    PY->>CLI: build_swift_command(binpath, addr, bucket, host, client, exclude_title, exclude_titles)
    CLI-->>PY: [binpath, addr, bucket, host, client, --exclude-title?, --exclude-titles pat1, ...]
    PY->>SW: subprocess.Popen(command)
    SW->>SW: start() — parse positional args
    SW->>SW: parseOptionalArguments(args[5:])
    Note over SW: compiles each pattern to NSRegularExpression
    loop Every window focus / poll
        SW->>SW: getWindowData()
        SW->>SW: "excludeTitle || titleShouldBeExcluded(title)?"
        alt excluded
            SW->>SW: "data.title = excluded"
        end
        SW->>SW: sendHeartbeat(heartbeat)
    end
Loading

Reviews (2): Last reviewed commit: "fix(macos/swift): rename catch binding t..." | Re-trigger Greptile

Comment on lines +145 to +152
func compileExcludeTitlePattern(_ pattern: String) -> NSRegularExpression {
do {
return try NSRegularExpression(pattern: pattern, options: [.caseInsensitive])
} catch {
error("Invalid regex pattern: \(pattern)")
exit(1)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Swift's implicit catch binding shadows the error() logging function. In a bare catch { } clause Swift automatically binds the caught value to an implicit constant also named error. Inside this block error is of type Error (not callable), so error("Invalid regex pattern: …") is a compile-time type error and the Swift binary will fail to build. Give the caught value an explicit name to avoid the shadowing.

Suggested change
func compileExcludeTitlePattern(_ pattern: String) -> NSRegularExpression {
do {
return try NSRegularExpression(pattern: pattern, options: [.caseInsensitive])
} catch {
error("Invalid regex pattern: \(pattern)")
exit(1)
}
}
func compileExcludeTitlePattern(_ pattern: String) -> NSRegularExpression {
do {
return try NSRegularExpression(pattern: pattern, options: [.caseInsensitive])
} catch let regexError {
error("Invalid regex pattern: \(pattern)\(regexError.localizedDescription)")
exit(1)
}
}

Comment on lines +1 to +4
def main():
from .main import main as _main

return _main()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Lazy import may surprise entry-point callers

The wrapper function is needed to avoid pulling in aw_client/aw_core when the package is imported (e.g. during test collection), but it changes the identity of the exported symbol: aw_watcher_window.main.__module__ is now aw_watcher_window instead of aw_watcher_window.main, and attributes like __doc__ from the real main are no longer visible. A comment explaining why the deferred import exists would help future readers understand the intent.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Swift's implicit 'error' variable in catch blocks shadows the custom
error() function defined in the same file, causing the compiler error:
'cannot call value of non-function type any Error'.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(macos/swift): support exclude_title/exclude_titles natively in Swift binary

1 participant