diff --git a/docs/resources/(resources)/macos-settings.mdx b/docs/resources/(resources)/macos-settings.mdx
new file mode 100644
index 0000000..0d3dc0c
--- /dev/null
+++ b/docs/resources/(resources)/macos-settings.mdx
@@ -0,0 +1,135 @@
+---
+title: macos-settings
+description: A reference page for the macos-settings resource
+---
+
+The macos-settings resource manages common macOS system preferences using the built-in `defaults` command. It covers mouse, keyboard, trackpad, and Dock settings — everything you need to reproduce your preferred system configuration on a new Mac.
+
+## Parameters
+
+All sections and their sub-keys are optional. You only need to declare the settings you want to manage.
+
+### `mouse`
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `naturalScrolling` | boolean | `true` | Scroll content in the natural direction (content follows finger). When `false`, uses the traditional scroll direction. |
+| `acceleration` | boolean | `true` | Enable mouse acceleration. When `false`, the cursor moves at a fixed speed regardless of how fast the mouse is moved. |
+| `speed` | number (0–3) | `1.5` | Mouse tracking speed. Higher values make the cursor move farther per physical movement. |
+
+### `keyboard`
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `keyRepeat` | integer | `6` | Rate of key repeat while a key is held. Lower = faster (1 is fastest; 120 effectively disables repeat). |
+| `initialKeyRepeat` | integer | `68` | Delay before key repeat begins (in ticks). Lower = shorter delay (10 minimum). |
+| `pressAndHold` | boolean | `true` | When `true`, holding a key shows the accent character picker. When `false`, the key repeats instead. |
+| `fnKeysAsStandardKeys` | boolean | `false` | When `true`, the F1–F12 keys act as standard function keys; press Fn to trigger special actions (brightness, volume, etc.). |
+| `keyboardNavigation` | boolean | `false` | When `true`, enables Tab-based focus navigation in system dialogs (equivalent to "Keyboard navigation" in System Settings). |
+
+### `trackpad`
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `speed` | number (0–3) | `1.5` | Trackpad tracking speed. Higher values make the cursor move farther per swipe distance. |
+
+### `dock`
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `position` | `"left"` \| `"bottom"` \| `"right"` | `"bottom"` | Position of the Dock on screen. |
+| `iconSize` | integer (16–128) | `48` | Dock icon size in pixels. |
+| `autohide` | boolean | `false` | Automatically hide and show the Dock when the cursor moves near the screen edge. |
+| `autohideDelay` | number | `0.2` | Seconds to wait before showing the Dock when it is hidden. Set to `0` for instant reveal. |
+| `showRecents` | boolean | `true` | Show recently opened apps in a dedicated section of the Dock. |
+| `minimizeEffect` | `"genie"` \| `"scale"` \| `"suck"` | `"genie"` | Window minimize animation style. |
+
+## macOS defaults mapping
+
+The table below shows the underlying `defaults` key used for each friendly parameter name.
+
+| Section | Parameter | Domain | Key |
+|---------|-----------|--------|-----|
+| mouse | `naturalScrolling` | `NSGlobalDomain` | `com.apple.swipescrolldirection` |
+| mouse | `acceleration` | `NSGlobalDomain` | `com.apple.mouse.linear` (inverted) |
+| mouse | `speed` | `NSGlobalDomain` | `com.apple.mouse.scaling` |
+| keyboard | `keyRepeat` | `NSGlobalDomain` | `KeyRepeat` |
+| keyboard | `initialKeyRepeat` | `NSGlobalDomain` | `InitialKeyRepeat` |
+| keyboard | `pressAndHold` | `NSGlobalDomain` | `ApplePressAndHoldEnabled` |
+| keyboard | `fnKeysAsStandardKeys` | `NSGlobalDomain` | `com.apple.keyboard.fnState` |
+| keyboard | `keyboardNavigation` | `NSGlobalDomain` | `AppleKeyboardUIMode` (0/2) |
+| trackpad | `speed` | `NSGlobalDomain` | `com.apple.trackpad.scaling` |
+| dock | `position` | `com.apple.dock` | `orientation` |
+| dock | `iconSize` | `com.apple.dock` | `tilesize` |
+| dock | `autohide` | `com.apple.dock` | `autohide` |
+| dock | `autohideDelay` | `com.apple.dock` | `autohide-delay` |
+| dock | `showRecents` | `com.apple.dock` | `show-recents` |
+| dock | `minimizeEffect` | `com.apple.dock` | `mineffect` |
+
+## Example usage
+
+### Common macOS preferences
+
+```json title="codify.jsonc"
+[
+  {
+    "type": "macos-settings",
+    "os": ["macOS"],
+    "mouse": {
+      "naturalScrolling": true
+    },
+    "keyboard": {
+      "keyRepeat": 2,
+      "initialKeyRepeat": 15,
+      "pressAndHold": false
+    },
+    "dock": {
+      "position": "left",
+      "iconSize": 36,
+      "autohide": true,
+      "showRecents": false
+    }
+  }
+]
+```
+
+### Non-Apple keyboard setup
+
+```json title="codify.jsonc"
+[
+  {
+    "type": "macos-settings",
+    "os": ["macOS"],
+    "mouse": {
+      "naturalScrolling": false,
+      "acceleration": false
+    },
+    "keyboard": {
+      "fnKeysAsStandardKeys": true
+    }
+  }
+]
+```
+
+### Trackpad speed only
+
+```json title="codify.jsonc"
+[
+  {
+    "type": "macos-settings",
+    "os": ["macOS"],
+    "trackpad": {
+      "speed": 2.5
+    }
+  }
+]
+```
+
+## Notes
+
+- This resource is **macOS only** and has no effect on Linux.
+- No software installation is required — `defaults` is a built-in macOS command.
+- Dock settings take effect immediately (the Dock is automatically restarted). Other settings typically take effect the next time you open an application or after logging out.
+- When the resource is removed from your configuration, all managed settings are reset to their macOS system defaults using `defaults delete`.
+- Changes to `fnKeysAsStandardKeys` may require a full system restart to take effect.
+- The `keyRepeat` and `initialKeyRepeat` values use macOS internal tick units, not milliseconds. Smaller values produce faster key repeat.
diff --git a/package-lock.json b/package-lock.json
index b8e9894..1208c79 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,16 +1,16 @@
 {
   "name": "default",
-  "version": "1.1.0-beta.18",
+  "version": "1.5.0-beta.2",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "default",
-      "version": "1.1.0-beta.18",
+      "version": "1.5.0-beta.2",
       "license": "ISC",
       "dependencies": {
-        "@codifycli/plugin-core": "1.1.0-beta23",
-        "@codifycli/schemas": "1.1.0-beta8",
+        "@codifycli/plugin-core": "1.2.0",
+        "@codifycli/schemas": "1.2.0",
         "ajv": "^8.18.0",
         "ajv-formats": "^2.1.1",
         "chalk": "^5.3.0",
@@ -171,12 +171,12 @@
       }
     },
     "node_modules/@codifycli/plugin-core": {
-      "version": "1.1.0-beta23",
-      "resolved": "https://registry.npmjs.org/@codifycli/plugin-core/-/plugin-core-1.1.0-beta23.tgz",
-      "integrity": "sha512-SsL2Ww1z5nXzdoJdWfnSGbei8TDUaeDblb999QdBbAIM8A9QG5hpt9ftc+y6c0genY2pf9UGznRpbt+CAZGYrQ==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@codifycli/plugin-core/-/plugin-core-1.2.0.tgz",
+      "integrity": "sha512-GvGRSZ1xtwF5TiiauV/VUGNnJPQ6TUhtGZfXqnIwCozdPgTFp3AYH49q7Pbd7AYAG+5pnFUa9J4yO6WNUfDeWA==",
       "license": "ISC",
       "dependencies": {
-        "@codifycli/schemas": "^1.1.0-beta8",
+        "@codifycli/schemas": "^1.2.0",
         "@homebridge/node-pty-prebuilt-multiarch": "^0.13.1",
         "ajv": "^8.18.0",
         "ajv-formats": "^2.1.1",
@@ -254,9 +254,9 @@
       }
     },
     "node_modules/@codifycli/schemas": {
-      "version": "1.1.0-beta8",
-      "resolved": "https://registry.npmjs.org/@codifycli/schemas/-/schemas-1.1.0-beta8.tgz",
-      "integrity": "sha512-2PLCPmU2mtDilqx71uQIjpZLnvqSkdSR+BgImN6eRbRWKJcfltBEONPAlRhRU74kAyURpqCfDSLKTYa1MqLxZw==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@codifycli/schemas/-/schemas-1.2.0.tgz",
+      "integrity": "sha512-ZUx8+IsW8ZvBWu+ilbUsK+vE1oMaiDvsDlgQoe92scRZUh1pFMPw6303N1T9BTep+sooRE5X4Y9IRloPQOjRjQ==",
       "license": "ISC",
       "dependencies": {
         "ajv": "^8.18.0"
diff --git a/package.json b/package.json
index 85998a0..88eaedb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "default",
-  "version": "1.4.0",
+  "version": "1.5.0-beta.3",
   "description": "Default plugin for Codify - provides 50+ declarative resources for managing development tools and system configuration across macOS and Linux",
   "main": "dist/index.js",
   "scripts": {
@@ -41,8 +41,8 @@
   "license": "ISC",
   "type": "module",
   "dependencies": {
-    "@codifycli/plugin-core": "1.1.0-beta23",
-    "@codifycli/schemas": "1.1.0-beta8",
+    "@codifycli/plugin-core": "1.2.0",
+    "@codifycli/schemas": "1.2.0",
     "ajv": "^8.18.0",
     "ajv-formats": "^2.1.1",
     "chalk": "^5.3.0",
diff --git a/src/index.ts b/src/index.ts
index 49c6b47..a1da540 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -23,6 +23,7 @@ import { NpmLoginResource } from './resources/javascript/npm/npm-login.js';
 import { FnmResource } from './resources/javascript/fast-node-manager/fast-node-manager.js';
 import { NvmResource } from './resources/javascript/nvm/nvm.js';
 import { Pnpm } from './resources/javascript/pnpm/pnpm.js';
+import { MacosSettingsResource } from './resources/macos/macos-settings/macos-settings-resource.js';
 import { MacportsResource } from './resources/macports/macports.js';
 import { ClaudeCodeResource } from './resources/claude-code/claude-code.js';
 import { ClaudeCodeProjectResource } from './resources/claude-code/claude-code-project.js';
@@ -105,6 +106,7 @@ runPlugin(Plugin.create(
     new Pip(),
     new PipSync(),
     new MacportsResource(),
+    new MacosSettingsResource(),
     new Npm(),
     new NpmLoginResource(),
     new DockerResource(),
diff --git a/src/resources/macos/macos-settings/macos-settings-resource.ts b/src/resources/macos/macos-settings/macos-settings-resource.ts
new file mode 100644
index 0000000..ceab933
--- /dev/null
+++ b/src/resources/macos/macos-settings/macos-settings-resource.ts
@@ -0,0 +1,476 @@
+import {
+  CreatePlan,
+  DestroyPlan,
+  ExampleConfig,
+  ModifyPlan,
+  ParameterChange,
+  Resource,
+  ResourceSettings,
+  SpawnStatus,
+  getPty,
+  z,
+  CodifyCliSender,
+  ApplyNotes,
+} from '@codifycli/plugin-core';
+import { OS } from '@codifycli/schemas';
+
+const mouseSchema = z.object({
+  naturalScrolling: z.boolean().optional(),
+  acceleration: z.boolean().optional(),
+  speed: z.number().min(0).max(3).optional(),
+}).optional();
+
+const keyboardSchema = z.object({
+  keyRepeat: z.number().int().min(1).optional(),
+  initialKeyRepeat: z.number().int().min(10).optional(),
+  pressAndHold: z.boolean().optional(),
+  fnKeysAsStandardKeys: z.boolean().optional(),
+  keyboardNavigation: z.boolean().optional(),
+}).optional();
+
+const trackpadSchema = z.object({
+  speed: z.number().min(0).max(3).optional(),
+}).optional();
+
+const dockSchema = z.object({
+  position: z.enum(['left', 'bottom', 'right']).optional(),
+  iconSize: z.number().int().min(16).max(128).optional(),
+  autohide: z.boolean().optional(),
+  autohideDelay: z.number().min(0).optional(),
+  showRecents: z.boolean().optional(),
+  minimizeEffect: z.enum(['genie', 'scale', 'suck']).optional(),
+}).optional();
+
+export const schema = z.object({
+  mouse: mouseSchema,
+  keyboard: keyboardSchema,
+  trackpad: trackpadSchema,
+  dock: dockSchema,
+});
+
+export type MacosSettingsConfig = z.infer<typeof schema>;
+type MouseConfig = NonNullable<MacosSettingsConfig['mouse']>;
+type KeyboardConfig = NonNullable<MacosSettingsConfig['keyboard']>;
+type TrackpadConfig = NonNullable<MacosSettingsConfig['trackpad']>;
+type DockConfig = NonNullable<MacosSettingsConfig['dock']>;
+
+const defaultConfig: Partial<MacosSettingsConfig> = {
+  mouse: {},
+  keyboard: {},
+  dock: {},
+};
+
+const exampleCommonPrefs: ExampleConfig = {
+  title: 'Common macOS preferences',
+  description: 'Configure natural scrolling, fast key repeat, and a minimal Dock for a consistent setup on any new Mac.',
+  configs: [{
+    type: 'macos-settings',
+    os: ['macOS'],
+    mouse: {
+      naturalScrolling: true,
+    },
+    keyboard: {
+      keyRepeat: 2,
+      initialKeyRepeat: 15,
+      pressAndHold: false,
+    },
+    dock: {
+      position: 'left',
+      iconSize: 36,
+      autohide: true,
+      showRecents: false,
+    },
+  }],
+};
+
+const exampleNonAppleKeyboard: ExampleConfig = {
+  title: 'Non-Apple keyboard setup',
+  description: 'Disable natural scrolling and enable standard function keys for a non-Apple keyboard or mouse.',
+  configs: [{
+    type: 'macos-settings',
+    os: ['macOS'],
+    mouse: {
+      naturalScrolling: false,
+      acceleration: false,
+    },
+    keyboard: {
+      fnKeysAsStandardKeys: true,
+    },
+  }],
+};
+
+export class MacosSettingsResource extends Resource<MacosSettingsConfig> {
+  getSettings(): ResourceSettings<MacosSettingsConfig> {
+    return {
+      id: 'macos-settings',
+      operatingSystems: [OS.Darwin],
+      schema,
+      defaultConfig,
+      exampleConfigs: {
+        example1: exampleCommonPrefs,
+        example2: exampleNonAppleKeyboard,
+      },
+      parameterSettings: {
+        mouse: { type: 'object', canModify: true },
+        keyboard: { type: 'object', canModify: true },
+        trackpad: { type: 'object', canModify: true },
+        dock: { type: 'object', canModify: true },
+      },
+    };
+  }
+
+  override async refresh(parameters: Partial<MacosSettingsConfig>): Promise<Partial<MacosSettingsConfig> | null> {
+    const result: Partial<MacosSettingsConfig> = {};
+    let anyFound = false;
+
+    if (parameters.mouse) {
+      const mouse = await this.readMouseSettings(parameters.mouse);
+      if (mouse !== null) { result.mouse = mouse; anyFound = true; }
+    }
+    if (parameters.keyboard) {
+      const keyboard = await this.readKeyboardSettings(parameters.keyboard);
+      if (keyboard !== null) { result.keyboard = keyboard; anyFound = true; }
+    }
+    if (parameters.trackpad) {
+      const trackpad = await this.readTrackpadSettings(parameters.trackpad);
+      if (trackpad !== null) { result.trackpad = trackpad; anyFound = true; }
+    }
+    if (parameters.dock) {
+      const dock = await this.readDockSettings(parameters.dock);
+      if (dock !== null) { result.dock = dock; anyFound = true; }
+    }
+
+    return anyFound ? result : null;
+  }
+
+  override async create(plan: CreatePlan<MacosSettingsConfig>): Promise<void> {
+    const { desiredConfig } = plan;
+
+    if (desiredConfig.mouse) {
+      await this.applyMouseSettings(desiredConfig.mouse);
+    }
+    if (desiredConfig.keyboard) {
+      await this.applyKeyboardSettings(desiredConfig.keyboard);
+    }
+    if (desiredConfig.trackpad) {
+      await this.applyTrackpadSettings(desiredConfig.trackpad);
+    }
+    if (desiredConfig.dock) {
+      await this.applyDockSettings(desiredConfig.dock);
+    }
+  }
+
+  override async modify(pc: ParameterChange<MacosSettingsConfig>, plan: ModifyPlan<MacosSettingsConfig>): Promise<void> {
+    const { desiredConfig } = plan;
+
+    if (pc.name === 'mouse' && desiredConfig.mouse) {
+      await this.applyMouseSettings(desiredConfig.mouse);
+    } else if (pc.name === 'keyboard' && desiredConfig.keyboard) {
+      await this.applyKeyboardSettings(desiredConfig.keyboard);
+    } else if (pc.name === 'trackpad' && desiredConfig.trackpad) {
+      await this.applyTrackpadSettings(desiredConfig.trackpad);
+    } else if (pc.name === 'dock' && desiredConfig.dock) {
+      await this.applyDockSettings(desiredConfig.dock);
+    }
+  }
+
+  override async destroy(plan: DestroyPlan<MacosSettingsConfig>): Promise<void> {
+    const { currentConfig } = plan;
+    const $ = getPty();
+    let dockChanged = false;
+
+    if (currentConfig.mouse) {
+      await this.deleteMouseSettings(currentConfig.mouse);
+    }
+    if (currentConfig.keyboard) {
+      await this.deleteKeyboardSettings(currentConfig.keyboard);
+    }
+    if (currentConfig.trackpad) {
+      await this.deleteTrackpadSettings(currentConfig.trackpad);
+    }
+    if (currentConfig.dock) {
+      await this.deleteDockSettings(currentConfig.dock);
+      dockChanged = true;
+    }
+
+    if (dockChanged) {
+      await $.spawnSafe('killall Dock');
+    }
+  }
+
+  // ---- Mouse ----
+
+  private async readMouseSettings(desired: MouseConfig): Promise<MouseConfig | null> {
+    const result: MouseConfig = {};
+    let anyFound = false;
+
+    if ('naturalScrolling' in desired) {
+      const v = await this.readBool('NSGlobalDomain', 'com.apple.swipescrolldirection');
+      if (v !== null) { result.naturalScrolling = v; anyFound = true; }
+    }
+    if ('acceleration' in desired) {
+      const linear = await this.readBool('NSGlobalDomain', 'com.apple.mouse.linear');
+      // com.apple.mouse.linear=true means acceleration is DISABLED; invert for user-friendly name
+      if (linear !== null) { result.acceleration = !linear; anyFound = true; }
+    }
+    if ('speed' in desired) {
+      const v = await this.readFloat('NSGlobalDomain', 'com.apple.mouse.scaling');
+      if (v !== null) { result.speed = v; anyFound = true; }
+    }
+
+    return anyFound ? result : null;
+  }
+
+  private async applyMouseSettings(settings: MouseConfig): Promise<void> {
+    const $ = getPty();
+
+    if (settings.naturalScrolling !== undefined) {
+      await $.spawn(`defaults write NSGlobalDomain com.apple.swipescrolldirection -bool ${settings.naturalScrolling}`);
+    }
+    if (settings.acceleration !== undefined) {
+      // linear=true means no acceleration; invert the user-facing boolean
+      await $.spawn(`defaults write NSGlobalDomain com.apple.mouse.linear -bool ${!settings.acceleration}`);
+    }
+    if (settings.speed !== undefined) {
+      await $.spawn(`defaults write NSGlobalDomain com.apple.mouse.scaling -float ${settings.speed}`);
+    }
+
+    await CodifyCliSender.sendApplyNote(ApplyNotes.RESTART_REQUIRED, 'macos-settings')
+  }
+
+  private async deleteMouseSettings(settings: MouseConfig): Promise<void> {
+    const $ = getPty();
+
+    if ('naturalScrolling' in settings) {
+      await $.spawnSafe('defaults delete NSGlobalDomain com.apple.swipescrolldirection');
+    }
+    if ('acceleration' in settings) {
+      await $.spawnSafe('defaults delete NSGlobalDomain com.apple.mouse.linear');
+    }
+    if ('speed' in settings) {
+      await $.spawnSafe('defaults delete NSGlobalDomain com.apple.mouse.scaling');
+    }
+
+    await CodifyCliSender.sendApplyNote(ApplyNotes.RESTART_REQUIRED, 'macos-settings')
+  }
+
+  // ---- Keyboard ----
+
+  private async readKeyboardSettings(desired: KeyboardConfig): Promise<KeyboardConfig | null> {
+    const result: KeyboardConfig = {};
+    let anyFound = false;
+
+    if ('keyRepeat' in desired) {
+      const v = await this.readInt('NSGlobalDomain', 'KeyRepeat');
+      if (v !== null) { result.keyRepeat = v; anyFound = true; }
+    }
+    if ('initialKeyRepeat' in desired) {
+      const v = await this.readInt('NSGlobalDomain', 'InitialKeyRepeat');
+      if (v !== null) { result.initialKeyRepeat = v; anyFound = true; }
+    }
+    if ('pressAndHold' in desired) {
+      const v = await this.readBool('NSGlobalDomain', 'ApplePressAndHoldEnabled');
+      if (v !== null) { result.pressAndHold = v; anyFound = true; }
+    }
+    if ('fnKeysAsStandardKeys' in desired) {
+      const v = await this.readBool('NSGlobalDomain', 'com.apple.keyboard.fnState');
+      if (v !== null) { result.fnKeysAsStandardKeys = v; anyFound = true; }
+    }
+    if ('keyboardNavigation' in desired) {
+      const v = await this.readInt('NSGlobalDomain', 'AppleKeyboardUIMode');
+      // AppleKeyboardUIMode: 0=disabled, 2=enabled
+      if (v !== null) { result.keyboardNavigation = v === 2; anyFound = true; }
+    }
+
+    return anyFound ? result : null;
+  }
+
+  private async applyKeyboardSettings(settings: KeyboardConfig): Promise<void> {
+    const $ = getPty();
+
+    if (settings.keyRepeat !== undefined) {
+      await $.spawn(`defaults write NSGlobalDomain KeyRepeat -int ${settings.keyRepeat}`);
+    }
+    if (settings.initialKeyRepeat !== undefined) {
+      await $.spawn(`defaults write NSGlobalDomain InitialKeyRepeat -int ${settings.initialKeyRepeat}`);
+    }
+    if (settings.pressAndHold !== undefined) {
+      await $.spawn(`defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool ${settings.pressAndHold}`);
+    }
+    if (settings.fnKeysAsStandardKeys !== undefined) {
+      await $.spawn(`defaults write NSGlobalDomain com.apple.keyboard.fnState -bool ${settings.fnKeysAsStandardKeys}`);
+    }
+    if (settings.keyboardNavigation !== undefined) {
+      // Map boolean to the int value macOS expects (0=disabled, 2=enabled)
+      await $.spawn(`defaults write NSGlobalDomain AppleKeyboardUIMode -int ${settings.keyboardNavigation ? 2 : 0}`);
+    }
+
+    await CodifyCliSender.sendApplyNote(ApplyNotes.RESTART_REQUIRED, 'macos-settings')
+  }
+
+  private async deleteKeyboardSettings(settings: KeyboardConfig): Promise<void> {
+    const $ = getPty();
+
+    if ('keyRepeat' in settings) {
+      await $.spawnSafe('defaults delete NSGlobalDomain KeyRepeat');
+    }
+    if ('initialKeyRepeat' in settings) {
+      await $.spawnSafe('defaults delete NSGlobalDomain InitialKeyRepeat');
+    }
+    if ('pressAndHold' in settings) {
+      await $.spawnSafe('defaults delete NSGlobalDomain ApplePressAndHoldEnabled');
+    }
+    if ('fnKeysAsStandardKeys' in settings) {
+      await $.spawnSafe('defaults delete NSGlobalDomain com.apple.keyboard.fnState');
+    }
+    if ('keyboardNavigation' in settings) {
+      await $.spawnSafe('defaults delete NSGlobalDomain AppleKeyboardUIMode');
+    }
+
+    await CodifyCliSender.sendApplyNote(ApplyNotes.RESTART_REQUIRED, 'macos-settings')
+  }
+
+  // ---- Trackpad ----
+
+  private async readTrackpadSettings(desired: TrackpadConfig): Promise<TrackpadConfig | null> {
+    const result: TrackpadConfig = {};
+    let anyFound = false;
+
+    if ('speed' in desired) {
+      const v = await this.readFloat('NSGlobalDomain', 'com.apple.trackpad.scaling');
+      if (v !== null) { result.speed = v; anyFound = true; }
+    }
+
+    return anyFound ? result : null;
+  }
+
+  private async applyTrackpadSettings(settings: TrackpadConfig): Promise<void> {
+    const $ = getPty();
+
+    if (settings.speed !== undefined) {
+      await $.spawn(`defaults write NSGlobalDomain com.apple.trackpad.scaling -float ${settings.speed}`);
+    }
+
+    await CodifyCliSender.sendApplyNote(ApplyNotes.RESTART_REQUIRED, 'macos-settings')
+  }
+
+  private async deleteTrackpadSettings(settings: TrackpadConfig): Promise<void> {
+    const $ = getPty();
+
+    if ('speed' in settings) {
+      await $.spawnSafe('defaults delete NSGlobalDomain com.apple.trackpad.scaling');
+    }
+
+    await CodifyCliSender.sendApplyNote(ApplyNotes.RESTART_REQUIRED, 'macos-settings')
+  }
+
+  // ---- Dock ----
+
+  private async readDockSettings(desired: DockConfig): Promise<DockConfig | null> {
+    const result: DockConfig = {};
+    let anyFound = false;
+
+    if ('position' in desired) {
+      const v = await this.readString('com.apple.dock', 'orientation');
+      if (v !== null) { result.position = v as DockConfig['position']; anyFound = true; }
+    }
+    if ('iconSize' in desired) {
+      const v = await this.readInt('com.apple.dock', 'tilesize');
+      if (v !== null) { result.iconSize = v; anyFound = true; }
+    }
+    if ('autohide' in desired) {
+      const v = await this.readBool('com.apple.dock', 'autohide');
+      if (v !== null) { result.autohide = v; anyFound = true; }
+    }
+    if ('autohideDelay' in desired) {
+      const v = await this.readFloat('com.apple.dock', 'autohide-delay');
+      if (v !== null) { result.autohideDelay = v; anyFound = true; }
+    }
+    if ('showRecents' in desired) {
+      const v = await this.readBool('com.apple.dock', 'show-recents');
+      if (v !== null) { result.showRecents = v; anyFound = true; }
+    }
+    if ('minimizeEffect' in desired) {
+      const v = await this.readString('com.apple.dock', 'mineffect');
+      if (v !== null) { result.minimizeEffect = v as DockConfig['minimizeEffect']; anyFound = true; }
+    }
+
+    return anyFound ? result : null;
+  }
+
+  private async applyDockSettings(settings: DockConfig): Promise<void> {
+    const $ = getPty();
+
+    if (settings.position !== undefined) {
+      await $.spawn(`defaults write com.apple.dock orientation -string "${settings.position}"`);
+    }
+    if (settings.iconSize !== undefined) {
+      await $.spawn(`defaults write com.apple.dock tilesize -int ${settings.iconSize}`);
+    }
+    if (settings.autohide !== undefined) {
+      await $.spawn(`defaults write com.apple.dock autohide -bool ${settings.autohide}`);
+    }
+    if (settings.autohideDelay !== undefined) {
+      await $.spawn(`defaults write com.apple.dock "autohide-delay" -float ${settings.autohideDelay}`);
+    }
+    if (settings.showRecents !== undefined) {
+      await $.spawn(`defaults write com.apple.dock "show-recents" -bool ${settings.showRecents}`);
+    }
+    if (settings.minimizeEffect !== undefined) {
+      await $.spawn(`defaults write com.apple.dock mineffect -string "${settings.minimizeEffect}"`);
+    }
+
+    await $.spawnSafe('killall Dock');
+  }
+
+  private async deleteDockSettings(settings: DockConfig): Promise<void> {
+    const $ = getPty();
+    const keyMap: Record<string, string> = {
+      position: 'orientation',
+      iconSize: 'tilesize',
+      autohide: 'autohide',
+      autohideDelay: 'autohide-delay',
+      showRecents: 'show-recents',
+      minimizeEffect: 'mineffect',
+    };
+
+    for (const [prop, key] of Object.entries(keyMap)) {
+      if (prop in settings) {
+        await $.spawnSafe(`defaults delete com.apple.dock "${key}"`);
+      }
+    }
+  }
+
+  // ---- Low-level defaults read helpers ----
+
+  private async readBool(domain: string, key: string): Promise<boolean | null> {
+    const $ = getPty();
+    const { data, status } = await $.spawnSafe(`defaults read "${domain}" "${key}"`);
+    if (status === SpawnStatus.ERROR) return null;
+    const val = data.trim();
+    return val === '1' || val === 'true' || val === 'YES';
+  }
+
+  private async readInt(domain: string, key: string): Promise<number | null> {
+    const $ = getPty();
+    const { data, status } = await $.spawnSafe(`defaults read "${domain}" "${key}"`);
+    if (status === SpawnStatus.ERROR) return null;
+    const val = parseInt(data.trim(), 10);
+    return isNaN(val) ? null : val;
+  }
+
+  private async readFloat(domain: string, key: string): Promise<number | null> {
+    const $ = getPty();
+    const { data, status } = await $.spawnSafe(`defaults read "${domain}" "${key}"`);
+    if (status === SpawnStatus.ERROR) return null;
+    const val = parseFloat(data.trim());
+    return isNaN(val) ? null : val;
+  }
+
+  private async readString(domain: string, key: string): Promise<string | null> {
+    const $ = getPty();
+    const { data, status } = await $.spawnSafe(`defaults read "${domain}" "${key}"`);
+    if (status === SpawnStatus.ERROR) return null;
+    return data.trim() || null;
+  }
+}
diff --git a/test/macos/macos-settings.test.ts b/test/macos/macos-settings.test.ts
new file mode 100644
index 0000000..01be39b
--- /dev/null
+++ b/test/macos/macos-settings.test.ts
@@ -0,0 +1,140 @@
+import { describe, expect, it } from 'vitest';
+import { PluginTester, testSpawn } from '@codifycli/plugin-test';
+import * as path from 'node:path';
+import { Utils } from '@codifycli/plugin-core';
+
+describe('macos-settings resource integration tests', { skip: !Utils.isMacOS() }, async () => {
+  const pluginPath = path.resolve('./src/index.ts');
+
+  it('Can configure mouse natural scrolling', { timeout: 300000 }, async () => {
+    await PluginTester.fullTest(pluginPath, [
+      {
+        type: 'macos-settings',
+        mouse: {
+          naturalScrolling: false,
+        },
+      },
+    ], {
+      validateApply: async () => {
+        const { data } = await testSpawn('defaults read NSGlobalDomain com.apple.swipescrolldirection');
+        expect(data.trim()).toBe('0');
+      },
+      testModify: {
+        modifiedConfigs: [{
+          type: 'macos-settings',
+          mouse: {
+            naturalScrolling: true,
+          },
+        }],
+        validateModify: async () => {
+          const { data } = await testSpawn('defaults read NSGlobalDomain com.apple.swipescrolldirection');
+          expect(data.trim()).toBe('1');
+        },
+      },
+      validateDestroy: async () => {
+        // After destroy, the key may be deleted (returns error) or reset to default
+        const { data } = await testSpawn('defaults read NSGlobalDomain com.apple.swipescrolldirection');
+        // Default is true (1) when key is absent, or the key was deleted — either is acceptable
+        const val = data.trim();
+        expect(['', '1'].includes(val) || val.includes('does not exist')).toBe(true);
+      },
+    });
+  });
+
+  it('Can configure Dock settings', { timeout: 300000 }, async () => {
+    await PluginTester.fullTest(pluginPath, [
+      {
+        type: 'macos-settings',
+        dock: {
+          autohide: true,
+          iconSize: 36,
+          showRecents: false,
+        },
+      },
+    ], {
+      validateApply: async () => {
+        const { data: autohide } = await testSpawn('defaults read com.apple.dock autohide');
+        expect(autohide.trim()).toBe('1');
+
+        const { data: tilesize } = await testSpawn('defaults read com.apple.dock tilesize');
+        expect(parseInt(tilesize.trim(), 10)).toBe(36);
+
+        const { data: showRecents } = await testSpawn('defaults read com.apple.dock show-recents');
+        expect(showRecents.trim()).toBe('0');
+      },
+      testModify: {
+        modifiedConfigs: [{
+          type: 'macos-settings',
+          dock: {
+            autohide: false,
+            iconSize: 48,
+            showRecents: true,
+          },
+        }],
+        validateModify: async () => {
+          const { data: autohide } = await testSpawn('defaults read com.apple.dock autohide');
+          expect(autohide.trim()).toBe('0');
+
+          const { data: tilesize } = await testSpawn('defaults read com.apple.dock tilesize');
+          expect(parseInt(tilesize.trim(), 10)).toBe(48);
+        },
+      },
+      validateDestroy: async () => {
+        // After destroy, keys should be deleted — reads will fail or return defaults
+        const { data: tilesize } = await testSpawn('defaults read com.apple.dock tilesize');
+        const val = tilesize.trim();
+        const parsed = parseInt(val, 10);
+        // Either deleted (error output or NaN) or reset to system default (48)
+        expect(val.includes('does not exist') || isNaN(parsed) || parsed === 48).toBe(true);
+      },
+    });
+  });
+
+  it('Can configure keyboard settings', { timeout: 300000 }, async () => {
+    await PluginTester.fullTest(pluginPath, [
+      {
+        type: 'macos-settings',
+        keyboard: {
+          keyRepeat: 2,
+          initialKeyRepeat: 15,
+          pressAndHold: false,
+        },
+      },
+    ], {
+      validateApply: async () => {
+        const { data: keyRepeat } = await testSpawn('defaults read NSGlobalDomain KeyRepeat');
+        expect(parseInt(keyRepeat.trim(), 10)).toBe(2);
+
+        const { data: initialKeyRepeat } = await testSpawn('defaults read NSGlobalDomain InitialKeyRepeat');
+        expect(parseInt(initialKeyRepeat.trim(), 10)).toBe(15);
+
+        const { data: pressAndHold } = await testSpawn('defaults read NSGlobalDomain ApplePressAndHoldEnabled');
+        expect(pressAndHold.trim()).toBe('0');
+      },
+      testModify: {
+        modifiedConfigs: [{
+          type: 'macos-settings',
+          keyboard: {
+            keyRepeat: 6,
+            initialKeyRepeat: 68,
+            pressAndHold: true,
+          },
+        }],
+        validateModify: async () => {
+          const { data: keyRepeat } = await testSpawn('defaults read NSGlobalDomain KeyRepeat');
+          expect(parseInt(keyRepeat.trim(), 10)).toBe(6);
+
+          const { data: initialKeyRepeat } = await testSpawn('defaults read NSGlobalDomain InitialKeyRepeat');
+          expect(parseInt(initialKeyRepeat.trim(), 10)).toBe(68);
+        },
+      },
+      validateDestroy: async () => {
+        const { data: keyRepeat } = await testSpawn('defaults read NSGlobalDomain KeyRepeat');
+        const val = keyRepeat.trim();
+        const parsed = parseInt(val, 10);
+        // Either deleted (error output or NaN) or reset to system default (6)
+        expect(val.includes('does not exist') || isNaN(parsed) || parsed === 6).toBe(true);
+      },
+    });
+  });
+});
