···11-import {disableAutomaticSwitching} from '../functions/index.js';
22-33-export async function disable() {
44- await disableAutomaticSwitching();
55-66- console.log('Automatic switching disabled');
77-}
-58
actions/enable.js
···11-import {config} from '../config.js';
22-import {packageJson} from '../constants/index.js';
33-import {
44- enableAutomaticSwitching,
55- getCurrentMode,
66- isTerminalOpen,
77- setTerminalProfile,
88-} from '../functions/index.js';
99-1010-/**
1111- * @param {string} mode
1212- */
1313-function undefinedProfileMessage(mode) {
1414- return `${mode} profile must be specified with --${mode}-profile or previously set with \`${packageJson.name} set-${mode}-mode\``;
1515-}
1616-1717-/**
1818- * @param {object} parameters
1919- * @param {string|undefined} parameters.darkProfile
2020- * @param {string|undefined} parameters.lightProfile
2121- */
2222-export async function enable(parameters) {
2323- if (
2424- [parameters.darkProfile, config.darkProfile].every(
2525- (value) => value === undefined,
2626- )
2727- ) {
2828- throw new Error(undefinedProfileMessage('dark'));
2929- }
3030-3131- if (
3232- [parameters.lightProfile, config.lightProfile].every(
3333- (value) => value === undefined,
3434- )
3535- ) {
3636- throw new Error(undefinedProfileMessage('light'));
3737- }
3838-3939- if (parameters.darkProfile !== undefined) {
4040- config.darkProfile = parameters.darkProfile;
4141- }
4242-4343- if (parameters.lightProfile !== undefined) {
4444- config.lightProfile = parameters.lightProfile;
4545- }
4646-4747- await enableAutomaticSwitching();
4848-4949- if (await isTerminalOpen()) {
5050- const mode = await getCurrentMode();
5151-5252- if (parameters[`${mode}Profile`] !== undefined) {
5353- await setTerminalProfile(config[`${mode}Profile`]);
5454- }
5555- }
5656-5757- console.log('automatic switching enabled');
5858-}
-5
actions/index.js
···11-export {disable} from './disable.js';
22-export {enable} from './enable.js';
33-export {setModeProfile} from './set-mode-profile.js';
44-export {status} from './status.js';
55-export {updateProfile} from './update-profile.js';
-21
actions/set-mode-profile.js
···11-import {config} from '../config.js';
22-import {
33- getCurrentMode,
44- isAutomaticSwitchingEnabled,
55- isTerminalOpen,
66- setTerminalProfile,
77-} from '../functions/index.js';
88-99-export async function setModeProfile({mode, profile}) {
1010- config[`${mode}Profile`] = profile;
1111-1212- if ((await isAutomaticSwitchingEnabled()) && (await isTerminalOpen())) {
1313- const currentMode = await getCurrentMode();
1414-1515- if (currentMode === mode) {
1616- await setTerminalProfile(profile);
1717- }
1818- }
1919-2020- console.log(`${mode} mode profile set to '${profile}'`);
2121-}
···11-import {config} from '../config.js';
22-import {
33- getCurrentMode,
44- isAutomaticSwitchingEnabled,
55- isTerminalOpen,
66- setTerminalProfile,
77-} from '../functions/index.js';
88-99-export async function updateProfile() {
1010- if (!(await isAutomaticSwitchingEnabled()) || !(await isTerminalOpen())) {
1111- return;
1212- }
1313-1414- if (!config.darkProfile) {
1515- throw new Error('Dark profile not set');
1616- }
1717-1818- if (!config.lightProfile) {
1919- throw new Error('Light profile not set');
2020- }
2121-2222- const mode = await getCurrentMode();
2323- const profile = config[`${mode}Profile`];
2424-2525- await setTerminalProfile(profile);
2626-}
+16
changelog.md
···55The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7788+## [Unreleased](https://github.com/patrik-csak/auto-terminal-profile/compare/v6.0.0...HEAD)
99+1010+### Changed
1111+1212+- **BREAKING**: auto-terminal-profile is now a Homebrew formula
1313+ - To migrate from the npm package to the new Homebrew formula:
1414+ 1. Disable automatic switching:
1515+ ```shell
1616+ auto-terminal-profile disable
1717+ ```
1818+ 1. Uninstall the auto-terminal-profile npm package:
1919+ ```shell
2020+ npm uninstall --global auto-terminal-profile
2121+ ```
2222+ 1. Follow the new install and usage instructions in the [readme](readme.md)
2323+824## [6.0.0](https://github.com/patrik-csak/auto-terminal-profile/compare/v5.0.0...v6.0.0) – 2025-05-27
9251026### Added
+6-50
cli.js
···11#! /usr/bin/env node
2233import {program} from 'commander';
44-import {packageJson} from './constants/index.js';
55-import {
66- disable,
77- enable,
88- setModeProfile,
99- status,
1010- updateProfile,
1111-} from './actions/index.js';
44+import {config, update} from './commands/index.js';
55+import {getPackageJson} from './library/index.js';
66+77+const packageJson = await getPackageJson();
128139program
1410 .name(packageJson.name)
1511 .description(packageJson.description)
1612 .version(packageJson.version);
17131818-program
1919- .command('disable')
2020- .description(
2121- 'disable automatic macOS Terminal profile switching based on system dark / light mode',
2222- )
2323- .action(disable);
2424-2525-program
2626- .command('enable')
2727- .description(
2828- 'enable automatic macOS Terminal profile switching based on system dark / light mode',
2929- )
3030- .option(
3131- '--dark-profile <profile>',
3232- 'dark profile name, for example "One Dark"',
3333- )
3434- .option(
3535- '--light-profile <profile>',
3636- 'light profile name, for example "One Light"',
3737- )
3838- .action(enable);
3939-4040-for (const mode of ['dark', 'light']) {
4141- program
4242- .command(`set-${mode}-profile`)
4343- .description(`set the Terminal profile to use in ${mode} mode`)
4444- .argument('<profile>')
4545- .action((profile) => setModeProfile({mode, profile}));
4646-}
4747-4848-program
4949- .command('status')
5050- .description('show status and configuration')
5151- .action(status);
5252-5353-program
5454- .command('update-profile')
5555- .description(
5656- 'update the profile of currently running Terminal windows / tabs',
5757- )
5858- .action(updateProfile);
1414+program.addCommand(config).addCommand(update);
59156060-program.parse();
1616+await program.parseAsync();
+2
commands/config/commands/index.js
···11+export {default as show} from './show.js';
22+export {default as set} from './set/index.js';
+1
commands/config/commands/set/commands/index.js
···11+export {default as setMode} from './set-mode.js';
+37
commands/config/commands/set/commands/set-mode.js
···11+import {Argument, Command} from '@commander-js/extra-typings';
22+import {consola} from 'consola';
33+import {getTerminalProfiles, setTerminalProfile} from 'mac-terminal';
44+import ow from 'ow';
55+import {
66+ getConfig,
77+ getCurrentMode,
88+ modes,
99+} from '../../../../../library/index.js';
1010+1111+/**
1212+ * Make set mode command
1313+ *
1414+ * @param {'dark' | 'light'} mode
1515+ */
1616+export default async function setMode(mode) {
1717+ ow(mode, ow.string.oneOf(modes));
1818+1919+ return new Command(mode)
2020+ .description(`set terminal profile for ${mode}`)
2121+ .addArgument(
2222+ new Argument('<profile>', 'terminal profile name').choices(
2323+ await getTerminalProfiles(),
2424+ ),
2525+ )
2626+ .action(async (profile) => {
2727+ const config = await getConfig();
2828+2929+ config.set(`profiles.${mode}`, profile);
3030+3131+ consola.success('Saved configuration');
3232+3333+ if (mode === (await getCurrentMode())) {
3434+ await setTerminalProfile({profile, setDefault: true});
3535+ }
3636+ });
3737+}
···11-import {readFile} from 'node:fs/promises';
22-import {fileURLToPath} from 'node:url';
33-import process from 'node:process';
44-import pupa from 'pupa';
55-import envPaths from 'env-paths';
66-import {packageJson} from '../constants/index.js';
77-88-export async function getLaunchAgentPlistFileContents() {
99- return pupa(
1010- await readFile(new URL('../templates/launch-agent.xml', import.meta.url), {
1111- encoding: 'utf8',
1212- }),
1313- {
1414- autoTerminalProfilePath: fileURLToPath(
1515- new URL('../cli.js', import.meta.url),
1616- ),
1717- darkModeNotifyPath: fileURLToPath(
1818- new URL(
1919- '../dark-mode-notify/.build/release/dark-mode-notify',
2020- import.meta.url,
2121- ),
2222- ),
2323- logPath: envPaths(packageJson.name).log,
2424- path: process.env.PATH,
2525- },
2626- );
2727-}
-7
functions/index.js
···11-export {enableAutomaticSwitching} from './enable-automatic-switching.js';
22-export {disableAutomaticSwitching} from './disable-automatic-switching.js';
33-export {getCurrentMode} from './get-current-mode.js';
44-export {getLaunchAgentPlistFileContents} from './get-launch-agent-plist-file-contents.js';
55-export {isAutomaticSwitchingEnabled} from './is-automatic-switching-enabled.js';
66-export {isTerminalOpen} from './is-terminal-open.js';
77-export {setTerminalProfile} from './set-terminal-profile.js';
-32
functions/is-automatic-switching-enabled.js
···11-import {existsSync as exists} from 'node:fs';
22-import {execa} from 'execa';
33-import {launchAgentPlistFilePath} from '../constants/index.js';
44-55-/**
66- * @return {Promise<boolean>}
77- */
88-async function isDarkModeNotifyRunning() {
99- const {stdout} = await execa`launchctl list`;
1010-1111- return stdout.includes('ke.bou.dark-mode-notify');
1212-}
1313-1414-/**
1515- * @return {Promise<boolean>}
1616- */
1717-export async function isAutomaticSwitchingEnabled() {
1818- const _isDarkModeNotifyRunning = await isDarkModeNotifyRunning();
1919- const plistFileExists = exists(launchAgentPlistFilePath);
2020-2121- if (_isDarkModeNotifyRunning && !plistFileExists) {
2222- throw new Error(
2323- `Automatic switching is running, but the launch agent plist file (${launchAgentPlistFilePath}) doesn't exist`,
2424- );
2525- } else if (!_isDarkModeNotifyRunning && plistFileExists) {
2626- throw new Error(
2727- `Automatic switching is not running, but the launch agent plist file (${launchAgentPlistFilePath}) exists`,
2828- );
2929- }
3030-3131- return _isDarkModeNotifyRunning && plistFileExists;
3232-}
-18
functions/is-terminal-open.js
···11-import {runAppleScript} from 'run-applescript';
22-33-/**
44- * @returns {Promise<boolean>}
55- */
66-export async function isTerminalOpen() {
77- const result = await runAppleScript(`tell application "System Events"
88- set isOpen to (name of processes) contains "Terminal"
99-end tell
1010-1111-if isOpen then
1212- return "true"
1313-else
1414- return "false"
1515-end if`);
1616-1717- return result === 'true';
1818-}
-11
functions/set-terminal-profile.js
···11-import {
22- setTerminalDefaultProfile as setDefaultProfile,
33- setTerminalProfile as setProfile,
44-} from 'terminal-profile';
55-66-/**
77- * @param {string} profile
88- */
99-export async function setTerminalProfile(profile) {
1010- await Promise.all([setDefaultProfile(profile), setProfile(profile)]);
1111-}
···11+import memoize from 'p-memoize';
22+import {readPackageUp} from 'read-package-up';
33+44+async function getPackageJson() {
55+ const result = await readPackageUp({
66+ cwd: new URL('.', import.meta.url),
77+ normalize: true,
88+ });
99+1010+ if (result.packageJson === undefined) {
1111+ throw new Error('Failed to get package.json');
1212+ }
1313+1414+ return result.packageJson;
1515+}
1616+1717+export default memoize(getPackageJson);
+4
library/index.js
···11+export {default as getConfig} from './get-config.js';
22+export {default as getCurrentMode} from './get-current-mode.js';
33+export {default as getPackageJson} from './get-package-json.js';
44+export {default as modes} from './modes.js';
···11MIT License
2233-Copyright (c) 2022 Patrik Csak
33+Copyright (c) Patrik Csak <p@trikcsak.com> [https://patrikcsak.com]
4455Permission is hereby granted, free of charge, to any person obtaining a copy
66of this software and associated documentation files (the "Software"), to deal
···1818AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121-SOFTWARE.2121+SOFTWARE.
···11# Auto Terminal Profile
2233-Automatically switch [macOS Terminal](https://en.wikipedia.org/wiki/Terminal_%28macOS%29) profiles when the [dark / light appearance mode](https://support.apple.com/guide/mac-help/use-a-light-or-dark-appearance-mchl52e1c2d2/mac) changes
33+Automatically switch [Terminal](https://en.wikipedia.org/wiki/Terminal_%28macOS%29) profiles when macOS [dark/light mode](https://support.apple.com/guide/mac-help/use-a-light-or-dark-appearance-mchl52e1c2d2/mac) changes
4455
6677-## Prerequisites
77+## Requirements
8899-- [Node.js](https://nodejs.org/) 20–24
99+- [Homebrew](https://brew.sh)
10101111## Installation
12121313```shell
1414-npm install --global auto-terminal-profile
1414+brew install patrik-csak/homebrew-tap/auto-terminal-profile
1515```
16161717## Usage
18181919-### Enable automatic profile switching
1919+### 1. Prepare your Terminal profiles
20202121-To get started, enable automatic profile switching and set your preferred dark and light mode profiles:
2121+[Import the Terminal profiles](https://support.apple.com/guide/terminal/import-and-export-terminal-profiles-trml4299c696/mac) you want to use, or continue to the next step if you want to use default profiles
22222323-```shell
2424-auto-terminal-profile enable \
2525- --dark-profile='One Dark' \
2626- --light-profile='One Light'
2727-```
2323+### 2. Configure auto-terminal-profile
28242929-### Switch profile on Terminal startup
3030-3131-Auto Terminal Profile only runs if Terminal is running, so the profile can fall out of sync if the macOS appearance mode changes while Terminal isn’t running.
3232-3333-To sync the Terminal profile to the current macOS appearance mode once:
2525+Set your preferred dark and light mode profiles:
34263527```shell
3636-auto-terminal-profile update-profile
2828+auto-terminal-profile config set
3729```
38303939-To sync the Terminal profile to the current macOS appearance mode when Terminal app is opened, you can add that line to your shell startup script (e.g. `.zshrc`), but it will increase the startup time of new shell sessions:
3131+<details>
3232+<summary><small>You can also set profiles individually:</small></summary>
40334141-```zsh
4242-if [ "$TERM_PROGRAM" = "Apple_Terminal" ]; then
4343- auto-terminal-profile update-profile
4444-fi
3434+```shell
3535+auto-terminal-profile config set dark 'Clear Dark'
3636+auto-terminal-profile config set light 'Clear Light'
4537```
46384747-### Disable automatic profile switching
3939+</details>
4040+4141+### 3. Enable automatic switching
48424943```shell
5050-auto-terminal-profile disable
4444+brew services start auto-terminal-profile
5145```
52465353-### Help
4747+---
4848+4949+### Disable automatic switching
54505551```shell
5656-auto-terminal-profile --help
5252+brew services stop auto-terminal-profile
5753```
5454+5555+---
5656+5757+### Related
5858+5959+- [mac-terminal](https://github.com/patrik-csak/mac-terminal) – Node.js library to control the macOS Terminal app