Skip to content

Commit efe7459

Browse files
raludiclaude
andcommitted
Strip telemetry, replace Python bridge with Swift, add bypass mode and terminal jump
- Remove Mixpanel analytics SDK and all tracking calls - Remove Sparkle auto-updater framework - Replace Python hook script (claude-island-state.py) with compiled Swift CLI binary embedded at Contents/Helpers/claude-island-bridge - Add shell launcher at ~/.claude-island/bin/ (same pattern as Vibe Island) - Add per-session bypass mode (auto-approve all tool permissions) - Add terminal jump: single-click session to bring terminal to front via AppleScript (works with iTerm2, Terminal.app, Ghostty, tmux) - Add context window remaining % display per session via statusLine API - Add statusLine script installation for real-time context usage reporting - Update HookInstaller to use Swift bridge and auto-migrate from old Python hooks - Update README documenting all fork changes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0c92dfc commit efe7459

17 files changed

Lines changed: 681 additions & 999 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ releases/
4242
# Xcode backup
4343
*.xcodeproj.backup/
4444
CLAUDE.md
45+
ClaudeIslandBridge/claude-island-bridge

ClaudeIsland.xcodeproj/project.pbxproj

Lines changed: 29 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
FD0F42E72EE7ABA400980302 /* Mixpanel in Frameworks */ = {isa = PBXBuildFile; productRef = FD0F42E62EE7ABA400980302 /* Mixpanel */; };
1110
FDA49F7D2EE11E3C00F9612E /* Markdown in Frameworks */ = {isa = PBXBuildFile; productRef = FD33FD722EDF32D7002A6548 /* Markdown */; };
12-
FDA49F7E2EE11E3C00F9612E /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = FDA49F7F2EE11E3C00F9612E /* Sparkle */; };
1311
/* End PBXBuildFile section */
1412

1513
/* Begin PBXFileReference section */
@@ -42,9 +40,7 @@
4240
isa = PBXFrameworksBuildPhase;
4341
buildActionMask = 2147483647;
4442
files = (
45-
FD0F42E72EE7ABA400980302 /* Mixpanel in Frameworks */,
4643
FDA49F7D2EE11E3C00F9612E /* Markdown in Frameworks */,
47-
FDA49F7E2EE11E3C00F9612E /* Sparkle in Frameworks */,
4844
);
4945
runOnlyForDeploymentPostprocessing = 0;
5046
};
@@ -77,6 +73,7 @@
7773
FD33FD592EDF32D7002A6548 /* Sources */,
7874
FD33FD5A2EDF32D7002A6548 /* Frameworks */,
7975
FD33FD5B2EDF32D7002A6548 /* Resources */,
76+
AA00000000000001 /* Build Bridge Binary */,
8077
);
8178
buildRules = (
8279
);
@@ -88,8 +85,6 @@
8885
name = ClaudeIsland;
8986
packageProductDependencies = (
9087
FD33FD722EDF32D7002A6548 /* Markdown */,
91-
FDA49F7F2EE11E3C00F9612E /* Sparkle */,
92-
FD0F42E62EE7ABA400980302 /* Mixpanel */,
9388
);
9489
productName = ClaudeIsland;
9590
productReference = FD33FD5D2EDF32D7002A6548 /* Claude Island.app */;
@@ -121,8 +116,6 @@
121116
minimizedProjectReferenceProxies = 1;
122117
packageReferences = (
123118
FD33FD712EDF32D7002A6548 /* XCRemoteSwiftPackageReference "swift-markdown" */,
124-
FDA49F802EE11E3C00F9612E /* XCRemoteSwiftPackageReference "Sparkle" */,
125-
FD0F42E52EE7ABA400980302 /* XCRemoteSwiftPackageReference "mixpanel-swift" */,
126119
);
127120
preferredProjectObjectVersion = 77;
128121
productRefGroup = FD33FD5E2EDF32D7002A6548 /* Products */;
@@ -154,6 +147,29 @@
154147
};
155148
/* End PBXSourcesBuildPhase section */
156149

150+
/* Begin PBXShellScriptBuildPhase section */
151+
AA00000000000001 /* Build Bridge Binary */ = {
152+
isa = PBXShellScriptBuildPhase;
153+
buildActionMask = 2147483647;
154+
files = (
155+
);
156+
inputFileListPaths = (
157+
);
158+
inputPaths = (
159+
"$(SRCROOT)/ClaudeIslandBridge/main.swift",
160+
);
161+
name = "Build Bridge Binary";
162+
outputFileListPaths = (
163+
);
164+
outputPaths = (
165+
"$(BUILT_PRODUCTS_DIR)/$(PRODUCT_NAME).app/Contents/Helpers/claude-island-bridge",
166+
);
167+
runOnlyForDeploymentPostprocessing = 0;
168+
shellPath = /bin/sh;
169+
shellScript = "set -e\nmkdir -p \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Contents/Helpers\"\nswiftc -O -o \"${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Contents/Helpers/claude-island-bridge\" \"${SRCROOT}/ClaudeIslandBridge/main.swift\"\n";
170+
};
171+
/* End PBXShellScriptBuildPhase section */
172+
157173
/* Begin XCBuildConfiguration section */
158174
FD33FD662EDF32D8002A6548 /* Debug */ = {
159175
isa = XCBuildConfiguration;
@@ -190,7 +206,7 @@
190206
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
191207
COPY_PHASE_STRIP = NO;
192208
DEBUG_INFORMATION_FORMAT = dwarf;
193-
DEVELOPMENT_TEAM = 2DKS5U9LV4;
209+
DEVELOPMENT_TEAM = "";
194210
ENABLE_STRICT_OBJC_MSGSEND = YES;
195211
ENABLE_TESTABILITY = YES;
196212
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -254,7 +270,7 @@
254270
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
255271
COPY_PHASE_STRIP = NO;
256272
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
257-
DEVELOPMENT_TEAM = 2DKS5U9LV4;
273+
DEVELOPMENT_TEAM = "";
258274
ENABLE_NS_ASSERTIONS = NO;
259275
ENABLE_STRICT_OBJC_MSGSEND = YES;
260276
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -284,7 +300,7 @@
284300
CODE_SIGN_STYLE = Automatic;
285301
COMBINE_HIDPI_IMAGES = YES;
286302
CURRENT_PROJECT_VERSION = 3;
287-
DEVELOPMENT_TEAM = 2DKS5U9LV4;
303+
DEVELOPMENT_TEAM = "";
288304
ENABLE_APP_SANDBOX = NO;
289305
ENABLE_HARDENED_RUNTIME = YES;
290306
ENABLE_PREVIEWS = YES;
@@ -319,7 +335,7 @@
319335
CODE_SIGN_STYLE = Automatic;
320336
COMBINE_HIDPI_IMAGES = YES;
321337
CURRENT_PROJECT_VERSION = 3;
322-
DEVELOPMENT_TEAM = 2DKS5U9LV4;
338+
DEVELOPMENT_TEAM = "";
323339
ENABLE_APP_SANDBOX = NO;
324340
ENABLE_HARDENED_RUNTIME = YES;
325341
ENABLE_PREVIEWS = YES;
@@ -369,48 +385,22 @@
369385
/* End XCConfigurationList section */
370386

371387
/* Begin XCRemoteSwiftPackageReference section */
372-
FD0F42E52EE7ABA400980302 /* XCRemoteSwiftPackageReference "mixpanel-swift" */ = {
373-
isa = XCRemoteSwiftPackageReference;
374-
repositoryURL = "https://github.com/mixpanel/mixpanel-swift";
375-
requirement = {
376-
branch = master;
377-
kind = branch;
378-
};
379-
};
380-
FD33FD712EDF32D7002A6548 /* XCRemoteSwiftPackageReference "swift-markdown" */ = {
388+
FD33FD712EDF32D7002A6548 /* XCRemoteSwiftPackageReference "swift-markdown" */ = {
381389
isa = XCRemoteSwiftPackageReference;
382390
repositoryURL = "https://github.com/swiftlang/swift-markdown";
383391
requirement = {
384392
kind = upToNextMajorVersion;
385393
minimumVersion = 0.5.0;
386394
};
387395
};
388-
FDA49F802EE11E3C00F9612E /* XCRemoteSwiftPackageReference "Sparkle" */ = {
389-
isa = XCRemoteSwiftPackageReference;
390-
repositoryURL = "https://github.com/sparkle-project/Sparkle";
391-
requirement = {
392-
kind = upToNextMajorVersion;
393-
minimumVersion = 2.0.0;
394-
};
395-
};
396396
/* End XCRemoteSwiftPackageReference section */
397397

398398
/* Begin XCSwiftPackageProductDependency section */
399-
FD0F42E62EE7ABA400980302 /* Mixpanel */ = {
400-
isa = XCSwiftPackageProductDependency;
401-
package = FD0F42E52EE7ABA400980302 /* XCRemoteSwiftPackageReference "mixpanel-swift" */;
402-
productName = Mixpanel;
403-
};
404399
FD33FD722EDF32D7002A6548 /* Markdown */ = {
405400
isa = XCSwiftPackageProductDependency;
406401
package = FD33FD712EDF32D7002A6548 /* XCRemoteSwiftPackageReference "swift-markdown" */;
407402
productName = Markdown;
408403
};
409-
FDA49F7F2EE11E3C00F9612E /* Sparkle */ = {
410-
isa = XCSwiftPackageProductDependency;
411-
package = FDA49F802EE11E3C00F9612E /* XCRemoteSwiftPackageReference "Sparkle" */;
412-
productName = Sparkle;
413-
};
414404
/* End XCSwiftPackageProductDependency section */
415405
};
416406
rootObject = FD33FD552EDF32D7002A6548 /* Project object */;

ClaudeIsland/App/AppDelegate.swift

Lines changed: 1 addition & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,19 @@
11
import AppKit
2-
import IOKit
3-
import Mixpanel
4-
import Sparkle
52
import SwiftUI
63

74
class AppDelegate: NSObject, NSApplicationDelegate {
85
private var windowManager: WindowManager?
96
private var screenObserver: ScreenObserver?
10-
private var updateCheckTimer: Timer?
117

128
static var shared: AppDelegate?
13-
let updater: SPUUpdater
14-
private let userDriver: NotchUserDriver
159

1610
var windowController: NotchWindowController? {
1711
windowManager?.windowController
1812
}
1913

2014
override init() {
21-
userDriver = NotchUserDriver()
22-
updater = SPUUpdater(
23-
hostBundle: Bundle.main,
24-
applicationBundle: Bundle.main,
25-
userDriver: userDriver,
26-
delegate: nil
27-
)
2815
super.init()
2916
AppDelegate.shared = self
30-
31-
do {
32-
try updater.start()
33-
} catch {
34-
print("Failed to start Sparkle updater: \(error)")
35-
}
3617
}
3718

3819
func applicationDidFinishLaunching(_ notification: Notification) {
@@ -41,32 +22,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
4122
return
4223
}
4324

44-
Mixpanel.initialize(token: "49814c1436104ed108f3fc4735228496")
45-
46-
let distinctId = getOrCreateDistinctId()
47-
Mixpanel.mainInstance().identify(distinctId: distinctId)
48-
49-
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown"
50-
let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "unknown"
51-
let osVersion = Foundation.ProcessInfo.processInfo.operatingSystemVersionString
52-
53-
Mixpanel.mainInstance().registerSuperProperties([
54-
"app_version": version,
55-
"build_number": build,
56-
"macos_version": osVersion
57-
])
58-
59-
fetchAndRegisterClaudeVersion()
60-
61-
Mixpanel.mainInstance().people.set(properties: [
62-
"app_version": version,
63-
"build_number": build,
64-
"macos_version": osVersion
65-
])
66-
67-
Mixpanel.mainInstance().track(event: "App Launched")
68-
Mixpanel.mainInstance().flush()
69-
7025
HookInstaller.installIfNeeded()
7126
NSApplication.shared.setActivationPolicy(.accessory)
7227

@@ -76,106 +31,18 @@ class AppDelegate: NSObject, NSApplicationDelegate {
7631
screenObserver = ScreenObserver { [weak self] in
7732
self?.handleScreenChange()
7833
}
79-
80-
if updater.canCheckForUpdates {
81-
updater.checkForUpdates()
82-
}
83-
84-
updateCheckTimer = Timer.scheduledTimer(withTimeInterval: 3600, repeats: true) { [weak self] _ in
85-
guard let updater = self?.updater, updater.canCheckForUpdates else { return }
86-
updater.checkForUpdates()
87-
}
8834
}
8935

9036
private func handleScreenChange() {
9137
_ = windowManager?.setupNotchWindow()
9238
}
9339

9440
func applicationWillTerminate(_ notification: Notification) {
95-
Mixpanel.mainInstance().flush()
96-
updateCheckTimer?.invalidate()
9741
screenObserver = nil
9842
}
9943

100-
private func getOrCreateDistinctId() -> String {
101-
let key = "mixpanel_distinct_id"
102-
103-
if let existingId = UserDefaults.standard.string(forKey: key) {
104-
return existingId
105-
}
106-
107-
let platformExpert = IOServiceGetMatchingService(
108-
kIOMainPortDefault,
109-
IOServiceMatching("IOPlatformExpertDevice")
110-
)
111-
defer { IOObjectRelease(platformExpert) }
112-
113-
if let uuid = IORegistryEntryCreateCFProperty(
114-
platformExpert,
115-
kIOPlatformUUIDKey as CFString,
116-
kCFAllocatorDefault,
117-
0
118-
)?.takeRetainedValue() as? String {
119-
UserDefaults.standard.set(uuid, forKey: key)
120-
return uuid
121-
}
122-
123-
let newId = UUID().uuidString
124-
UserDefaults.standard.set(newId, forKey: key)
125-
return newId
126-
}
127-
128-
private func fetchAndRegisterClaudeVersion() {
129-
let claudeProjectsDir = FileManager.default.homeDirectoryForCurrentUser
130-
.appendingPathComponent(".claude/projects")
131-
132-
guard let projectDirs = try? FileManager.default.contentsOfDirectory(
133-
at: claudeProjectsDir,
134-
includingPropertiesForKeys: [.contentModificationDateKey],
135-
options: .skipsHiddenFiles
136-
) else { return }
137-
138-
var latestFile: URL?
139-
var latestDate: Date?
140-
141-
for projectDir in projectDirs {
142-
guard let files = try? FileManager.default.contentsOfDirectory(
143-
at: projectDir,
144-
includingPropertiesForKeys: [.contentModificationDateKey],
145-
options: .skipsHiddenFiles
146-
) else { continue }
147-
148-
for file in files where file.pathExtension == "jsonl" && !file.lastPathComponent.hasPrefix("agent-") {
149-
if let attrs = try? file.resourceValues(forKeys: [.contentModificationDateKey]),
150-
let modDate = attrs.contentModificationDate {
151-
if latestDate == nil || modDate > latestDate! {
152-
latestDate = modDate
153-
latestFile = file
154-
}
155-
}
156-
}
157-
}
158-
159-
guard let jsonlFile = latestFile,
160-
let handle = FileHandle(forReadingAtPath: jsonlFile.path) else { return }
161-
defer { try? handle.close() }
162-
163-
let data = handle.readData(ofLength: 8192)
164-
guard let content = String(data: data, encoding: .utf8) else { return }
165-
166-
for line in content.components(separatedBy: .newlines) where !line.isEmpty {
167-
guard let lineData = line.data(using: .utf8),
168-
let json = try? JSONSerialization.jsonObject(with: lineData) as? [String: Any],
169-
let version = json["version"] as? String else { continue }
170-
171-
Mixpanel.mainInstance().registerSuperProperties(["claude_code_version": version])
172-
Mixpanel.mainInstance().people.set(properties: ["claude_code_version": version])
173-
return
174-
}
175-
}
176-
17744
private func ensureSingleInstance() -> Bool {
178-
let bundleID = Bundle.main.bundleIdentifier ?? "com.farouqaldori.ClaudeIsland"
45+
let bundleID = Bundle.main.bundleIdentifier ?? "com.claudeisland"
17946
let runningApps = NSWorkspace.shared.runningApplications.filter {
18047
$0.bundleIdentifier == bundleID
18148
}

ClaudeIsland/Info.plist

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,5 @@
1616
<true/>
1717
<key>NSHumanReadableCopyright</key>
1818
<string>Copyright 2025. All rights reserved.</string>
19-
20-
<!-- Sparkle Update Configuration -->
21-
<key>SUFeedURL</key>
22-
<string>https://claudeisland.com/appcast.xml</string>
23-
<key>SUPublicEDKey</key>
24-
<string>w5NwH5KRfE7LfeZ09J8lWx3UQrWTqUbfH4HHBa1OPRk=</string>
25-
<key>SUEnableAutomaticChecks</key>
26-
<true/>
2719
</dict>
2820
</plist>

ClaudeIsland/Models/SessionState.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ struct SessionState: Equatable, Identifiable, Sendable {
2222
var pid: Int?
2323
var tty: String?
2424
var isInTmux: Bool
25+
var autoApprove: Bool
26+
var remainingPercentage: Int?
2527

2628
// MARK: - State Machine
2729

@@ -71,6 +73,8 @@ struct SessionState: Equatable, Identifiable, Sendable {
7173
pid: Int? = nil,
7274
tty: String? = nil,
7375
isInTmux: Bool = false,
76+
autoApprove: Bool = false,
77+
remainingPercentage: Int? = nil,
7478
phase: SessionPhase = .idle,
7579
chatItems: [ChatHistoryItem] = [],
7680
toolTracker: ToolTracker = ToolTracker(),
@@ -89,6 +93,8 @@ struct SessionState: Equatable, Identifiable, Sendable {
8993
self.pid = pid
9094
self.tty = tty
9195
self.isInTmux = isInTmux
96+
self.autoApprove = autoApprove
97+
self.remainingPercentage = remainingPercentage
9298
self.phase = phase
9399
self.chatItems = chatItems
94100
self.toolTracker = toolTracker

0 commit comments

Comments
 (0)