Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b589ec0
Windows: fix hidden island not waking on mouse hover
JhoanG956 Oct 1, 2026
2bc101b
Windows: stop Vite from watching the Cargo target dir
JhoanG956 Oct 1, 2026
71fb529
Windows: bump @tauri-apps/api and cli to 2.12.1
JhoanG956 Oct 1, 2026
05b072e
Windows: fix ticker steps overlapping on long commands
JhoanG956 Oct 1, 2026
58966da
Windows: close the island when a file drag is abandoned
JhoanG956 Oct 1, 2026
9c60c70
Windows: GitHub CI and pull requests, Google Calendar with reminders
JhoanG956 Oct 1, 2026
2731fd7
Windows: let Haiku use web search in the chat
JhoanG956 Oct 1, 2026
b804dd7
Merge main into the GitHub CI / Google Calendar branch
JhoanG956 Oct 1, 2026
b012ed5
Mac: GitHub CI for the session's branch, pull requests and Google Cal…
JhoanG956 Oct 5, 2026
8ba164a
Merge Louis-CFM/coucou main into the GitHub CI / Google Calendar branch
JhoanG956 Oct 5, 2026
755106e
Merge Louis-CFM/coucou main into the Mac GitHub CI / Google Calendar …
JhoanG956 Oct 5, 2026
f503049
Linux: PKCE for the Google Calendar sign-in without CNG
JhoanG956 Oct 5, 2026
5e821f4
Linux: rename the RFC 7636 test so it doesn't clash with pkce_primitives
JhoanG956 Oct 5, 2026
9dbefa8
Merge Louis-CFM/coucou main (0.1.9) into the GitHub CI / Google Calen…
JhoanG956 Oct 6, 2026
ac206e1
Merge Louis-CFM/coucou main (0.2.0) into the GitHub CI / Google Calen…
JhoanG956 Oct 6, 2026
5643362
Merge Louis-CFM/coucou main (demo mode, license notes) into the GitHu…
JhoanG956 Oct 6, 2026
46da25a
Merge Louis-CFM/coucou main (0.2.1, Hermes Agent) into the GitHub CI …
JhoanG956 Oct 7, 2026
a32d37e
Merge Louis-CFM/coucou main (community fixes) into the GitHub CI / Go…
JhoanG956 Oct 7, 2026
06f3954
Merge Louis-CFM/coucou main (community batch 2) into the GitHub CI / …
JhoanG956 Oct 7, 2026
1bb8219
Merge Louis-CFM/coucou main (Windows and Linux 0.2.0) into the GitHub…
JhoanG956 Oct 7, 2026
bd08d83
Merge Louis-CFM/coucou main (0.2.2) into the GitHub CI / Google Calen…
JhoanG956 Oct 8, 2026
333c454
Merge Louis-CFM/coucou main (terminal sessions) into the GitHub CI / …
JhoanG956 Oct 8, 2026
c76d076
Merge Louis-CFM/coucou main (Spotify pill, sound packs) into the GitH…
JhoanG956 Oct 8, 2026
683a9f2
Merge Louis-CFM/coucou main (pill colours, open on hover) into the Gi…
JhoanG956 Oct 8, 2026
959b98b
Merge Louis-CFM/coucou main (0.2.3, Windows and Linux 0.3.0) into the…
JhoanG956 Oct 8, 2026
36caf6b
Merge Louis-CFM/coucou main (voice wake word) into the GitHub CI / Go…
JhoanG956 Oct 10, 2026
7b8f974
Merge Louis-CFM/coucou main (voice conversation) into the GitHub CI /…
JhoanG956 Oct 10, 2026
69fa113
Merge Louis-CFM/coucou main (voice understanding fixes) into the GitH…
JhoanG956 Oct 10, 2026
35ce979
Merge Louis-CFM/coucou main (voice Jarvis mode) into the GitHub CI / …
JhoanG956 Oct 10, 2026
3deb038
Merge Louis-CFM/coucou main (voice wake feedback) into the GitHub CI …
JhoanG956 Oct 10, 2026
479374b
Merge Louis-CFM/coucou main (voice turn-taking fixes) into the GitHub…
JhoanG956 Oct 10, 2026
5f1b52b
Merge Louis-CFM/coucou main (voice questions about everything) into t…
JhoanG956 Oct 10, 2026
5242421
Merge Louis-CFM/coucou main (voice in English, ElevenLabs) into the G…
JhoanG956 Oct 10, 2026
e48ad61
Merge Louis-CFM/coucou main (voice gender) into the GitHub CI / Googl…
JhoanG956 Oct 10, 2026
dc8678e
Merge Louis-CFM/coucou main (guided voice email) into the GitHub CI /…
JhoanG956 Oct 10, 2026
1171572
Merge Louis-CFM/coucou main (Mochi CPU, voice mail fixes) into the Gi…
JhoanG956 Oct 10, 2026
df61deb
Merge Louis-CFM/coucou main (voice brain, 60 fps Mochi) into the GitH…
JhoanG956 Oct 10, 2026
7411c5c
Merge Louis-CFM/coucou main (0.3.0) into the GitHub CI / Google Calen…
JhoanG956 Oct 11, 2026
8b28c7f
Merge Louis-CFM/coucou main (Coucou for Android) into the GitHub CI /…
JhoanG956 Oct 11, 2026
cd34a3d
Merge Louis-CFM/coucou main (Windows and Linux 0.4.0, Android release…
JhoanG956 Oct 11, 2026
7934133
Merge Louis-CFM/coucou main (voice language, smoother island) into th…
JhoanG956 Oct 11, 2026
1e0d809
Merge Louis-CFM/coucou main (Mochi steady steps) into the GitHub CI /…
JhoanG956 Oct 11, 2026
20eb8c5
Merge Louis-CFM/coucou main (Mail and Crypto pills, approval queue) i…
JhoanG956 Oct 11, 2026
6aafa8b
Merge Louis-CFM/coucou main (Portfolio pill, more chat providers) int…
JhoanG956 Oct 12, 2026
7d53f05
Merge Louis-CFM/coucou main (Android 0.2.0, Portfolio fixes) into the…
JhoanG956 Oct 13, 2026
0641b4a
Merge Louis-CFM/coucou main (Flow dictation) into the GitHub CI / Goo…
JhoanG956 Oct 14, 2026
767b2f0
Merge Louis-CFM/coucou main (Flow dictation fixes) into the GitHub CI…
JhoanG956 Oct 14, 2026
f2618cb
Merge Louis-CFM/coucou main (Coucou Flow window) into the GitHub CI /…
JhoanG956 Oct 14, 2026
2f67288
Merge Louis-CFM/coucou main (Parakeet, Notetaker) into the GitHub CI …
JhoanG956 Oct 14, 2026
5a918c1
Merge Louis-CFM/coucou main (Coucou window) into the GitHub CI / Goog…
JhoanG956 Oct 14, 2026
b48b75a
Merge Louis-CFM/coucou main (Mac 0.4.0) into the GitHub CI / Google C…
JhoanG956 Oct 14, 2026
52d561e
Merge Louis-CFM/coucou main (Windows and Linux 0.5.0) into the GitHub…
JhoanG956 Oct 15, 2026
2133e09
Merge Louis-CFM/coucou main (site and privacy update for Flow) into t…
JhoanG956 Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,18 @@ jobs:
- name: GitHub pulse tests
run: bash scripts/test-github-pulse.sh

- name: GitHub branch CI tests
run: bash scripts/test-github-branch-ci.sh

- name: GitHub activity tests
run: bash scripts/test-github-activity.sh

- name: Wardrobe tests
run: bash scripts/test-wardrobe.sh

- name: Google Calendar tests
run: bash scripts/test-google-calendar.sh

- name: Pill colour tests
run: bash scripts/test-pill-colors.sh

Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

- GitHub: the CI of your Claude Code session's branch, read from its .git folder. It takes the CI row of the GitHub card (macOS, GitHub build) and the CI detail lists it first. Mochi speaks up when a run on that branch finishes or someone reviews one of your pull requests, and pull requests untouched for a month drop off the list — thanks @JhoanG956
- Google Calendar (macOS): a new pill with your next events and Join for the meeting that's on, and a reminder card when Google Calendar would ring; signs in with your own read-only OAuth client — thanks @JhoanG956
- Dragging a file onto the island and dropping it elsewhere (or pressing Escape) puts the island back the way it was, instead of leaving the drop zone open — thanks @JhoanG956
- Shell steps in the ticker drop the `cd <project> &&` prefix, so the command itself shows — thanks @JhoanG956

## Windows and Linux 0.5.0 — October 15, 2026

Windows and Linux catch up with the Mac's 0.3.0 and 0.4.0: the Coucou window, Flow dictation, the Notetaker and « OK Coucou ». All of it free and open source.
Expand Down
282 changes: 153 additions & 129 deletions NotchBuddy/NotchBuddy.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions NotchBuddy/Resources/CoucouAppStore.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
<true/>
<key>com.apple.security.network.client</key>
<true/>
<!-- Google Calendar sign-in: a one-shot listener on 127.0.0.1 receives the OAuth redirect -->
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.files.bookmarks.app-scope</key>
Expand Down
320 changes: 320 additions & 0 deletions NotchBuddy/Resources/Localizable.xcstrings

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions NotchBuddy/Sources/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
StripePoller.shared.start()
CalcomPoller.shared.start()
NotionPoller.shared.start()
GcalPoller.shared.start()
NotificationCenter.default.addObserver(self, selector: #selector(openSettingsFromNotification(_:)),
name: .openFullSettings, object: nil)
// After the greeting ends, fly Mochi back to the desktop if it was there at last quit
Expand Down
69 changes: 66 additions & 3 deletions NotchBuddy/Sources/App/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ final class AppState: ObservableObject {
if !activeIntegrations.contains("integration_github") && oldValue.contains("integration_github") {
githubPulse = nil
githubActivity = nil
githubError = nil
}
}
}
Expand All @@ -377,6 +378,13 @@ final class AppState: ObservableObject {
@Published var githubStats: GitHubStats? = nil
@Published var githubPulse: GitHubPulse? = nil
@Published var githubActivity: GitHubActivity? = nil
@Published var githubError: String? = nil // last pulse API error (nil = ok)

// Google Calendar (populated by GcalPoller). nil = not connected or not loaded yet.
@Published var gcalEvents: [GcalEvent]? = nil
@Published var gcalError: String? = nil
// The event the reminder card is about
@Published var reminder: GcalEvent? = nil

// Stripe (populated by StripePoller)
@Published var stripePayments: [StripePayment] = []
Expand Down Expand Up @@ -640,11 +648,11 @@ final class AppState: ObservableObject {
var sound: String?
for event in events {
switch event {
case .ciFailed, .mainFailed:
case .ciFailed, .mainFailed, .branchCIFailed, .changesRequested:
if level < 3 { level = 3; badge = .error; sound = "error" }
case .reviewRequested:
case .reviewRequested, .reviewCommented:
if level < 2 { level = 2; badge = .finished; sound = "question" }
case .ciPassed:
case .ciPassed, .branchCIPassed, .reviewApproved:
if level < 1 { level = 1; badge = .finished; sound = "finish" }
}
}
Expand Down Expand Up @@ -753,6 +761,61 @@ final class AppState: ObservableObject {
}
}

// MARK: - Integration news (Google Calendar)

private var integrationClearWork: [String: DispatchWorkItem] = [:]

/// Shows what a poller has to say on its pill: state, badge, sound, and the
/// compact island so the badge is seen. A calendar reminder opens its own card.
func announce(_ news: IntegrationNews, for id: String, reminder event: GcalEvent? = nil) {
guard let idx = tasks.firstIndex(where: { $0.id == id }) else { return }
// Something waiting on you (a meeting about to start) reads as a question
// with the amber badge, not as done or broken.
tasks[idx].state = news.attention ? .question : news.success ? .finished : .error
tasks[idx].steps = news.detail.map { [news.label, $0] } ?? [news.label]
tasks[idx].stepIndex = tasks[idx].steps.count - 1
if focusId != id {
tasks[idx].pillBadge = news.attention ? .approval : news.success ? .finished : .error
}

if let event {
// A reminder is a message, not a badge: open a card that says what,
// when and where, with Join / Open. Unless you are in the middle of
// something — the chat, an approval, a file drop — then it waits as
// the amber badge on the Calendar pill.
SoundEngine.shared.play("approval")
let busy: Set<IslandView> = [.prompt, .approval, .question, .upload, .uploading, .choose, .mail]
if mode == .expanded && busy.contains(view) {
NotificationCenter.default.post(name: .hookReveal, object: nil)
} else {
reminder = event
setFocus(id)
if mode == .expanded { view = .reminder }
else { NotificationCenter.default.post(name: .islandAlert, object: IslandView.reminder) }
}
} else {
SoundEngine.shared.play(news.attention ? "question" : news.success ? "finish" : "error")
// Same as the other pollers: show the compact island so the badge is seen,
// but never steal the screen.
NotificationCenter.default.post(name: .hookReveal, object: nil)
}

// Auto-clear the pill after 60 s (the card's data stays).
integrationClearWork[id]?.cancel()
let work = DispatchWorkItem { [weak self] in
guard let self else { return }
self.integrationClearWork[id] = nil
guard let i = self.tasks.firstIndex(where: { $0.id == id }),
[.finished, .error, .question].contains(self.tasks[i].state) else { return }
self.tasks[i].state = .idle
self.tasks[i].steps = []
self.tasks[i].stepIndex = 0
self.tasks[i].pillBadge = nil
}
integrationClearWork[id] = work
DispatchQueue.main.asyncAfter(deadline: .now() + 60, execute: work)
}

}

// MARK: - Supporting types
Expand Down
3 changes: 3 additions & 0 deletions NotchBuddy/Sources/App/ClaudeService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ final class KeychainStore: @unchecked Sendable {
"stripe-api-key",
"calcom-api-key",
"notion-api-key",
// Google Calendar: the user's own Desktop OAuth client, and the refresh
// token Google returns once they have consented.
"gcal-client-id", "gcal-client-secret", "gcal-refresh-token",
"elevenlabs-api-key",
// Portfolio exchange credentials
"portfolio-binance-key", "portfolio-binance-secret",
Expand Down
7 changes: 6 additions & 1 deletion NotchBuddy/Sources/App/FileDropView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ final class FileDropNSView: NSView {
var onDragUpdated: ((CGPoint) -> Void)?
var onDragExited: (() -> Void)?
var onFilesDropped: (([URL]) -> Void)?
/// A drop with no file in it (text, a link, a file promise): the drag is over all the same.
var onDropRejected: (() -> Void)?

override init(frame: NSRect) {
super.init(frame: frame)
Expand All @@ -34,7 +36,10 @@ final class FileDropNSView: NSView {
guard let urls = sender.draggingPasteboard.readObjects(
forClasses: [NSURL.self],
options: [.urlReadingFileURLsOnly: true]
) as? [URL], !urls.isEmpty else { return false }
) as? [URL], !urls.isEmpty else {
onDropRejected?()
return false
}
onFilesDropped?(urls)
return true
}
Expand Down
Loading
Loading