gitconfig-man 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,69 @@
1
+ # Contributing to gitconfig-man
2
+
3
+ Thank you for considering contributing to gitconfig-man! We welcome contributions from everyone.
4
+
5
+ ## Getting Started
6
+
7
+ 1. Fork the repository
8
+ 2. Clone your fork: `git clone https://github.com/yourusername/gitconfig-man.git`
9
+ 3. Create a new branch: `git checkout -b feature/your-feature-name`
10
+ 4. Make your changes
11
+ 5. Test your changes
12
+ 6. Commit your changes: `git commit -m 'Add some feature'`
13
+ 7. Push to the branch: `git push origin feature/your-feature-name`
14
+ 8. Submit a pull request
15
+
16
+ ## Development Setup
17
+
18
+ ```bash
19
+ # Install dependencies
20
+ npm install
21
+
22
+ # Run tests
23
+ npm test
24
+
25
+ # Run tests in watch mode
26
+ npm run test:watch
27
+
28
+ # Run tests with coverage
29
+ npm run test:ci
30
+ ```
31
+
32
+ ## Code Style
33
+
34
+ - Use consistent indentation (2 spaces)
35
+ - Follow the existing code style
36
+ - Write clear, descriptive commit messages
37
+ - Add comments for complex logic
38
+ - Keep functions small and focused
39
+
40
+ ## Testing
41
+
42
+ - Write tests for new features
43
+ - Ensure all tests pass before submitting PR
44
+ - Maintain or improve code coverage
45
+
46
+ ## Reporting Issues
47
+
48
+ When reporting issues, please include:
49
+
50
+ - Your OS and Node.js version
51
+ - Steps to reproduce the issue
52
+ - Expected behavior
53
+ - Actual behavior
54
+ - Any error messages or logs
55
+
56
+ ## Pull Request Guidelines
57
+
58
+ - Keep PRs focused on a single feature or fix
59
+ - Update documentation as needed
60
+ - Add tests for new features
61
+ - Ensure all tests pass
62
+ - Keep commit history clean
63
+
64
+ ## Questions?
65
+
66
+ Feel free to open an issue for any questions or clarifications.
67
+
68
+ Thank you for contributing! 🎉
69
+
package/EXAMPLES.md ADDED
@@ -0,0 +1,274 @@
1
+ # gitconfig-man Examples
2
+
3
+ This document provides practical examples of using gitconfig-man in different scenarios.
4
+
5
+ ## Quick Start
6
+
7
+ ### Installation and Initialization
8
+
9
+ ```bash
10
+ # Install globally
11
+ npm install -g gitconfig-man
12
+
13
+ # Initialize (saves your current git config as 'default' profile)
14
+ gitconfig-man -i
15
+ ```
16
+
17
+ ## Example 1: Personal and Work Profiles
18
+
19
+ ### Setup
20
+
21
+ ```bash
22
+ # Initialize with current config as default
23
+ gitconfig-man -i
24
+
25
+ # Create work profile
26
+ gitconfig-man -c work
27
+
28
+ # Configure work settings
29
+ git config --global user.name "John Doe"
30
+ git config --global user.email "john.doe@company.com"
31
+ git config --global core.editor "code"
32
+
33
+ # Create personal profile
34
+ gitconfig-man -c personal
35
+
36
+ # Configure personal settings
37
+ git config --global user.name "John"
38
+ git config --global user.email "john@personal.com"
39
+ git config --global core.editor "vim"
40
+ ```
41
+
42
+ ### Daily Usage
43
+
44
+ ```bash
45
+ # Switch to work profile
46
+ gitconfig-man -s work
47
+
48
+ # Work on company projects...
49
+ git clone git@github.com:company/project.git
50
+ cd project
51
+ git commit -m "Work commit" # Uses work email
52
+
53
+ # Switch to personal profile
54
+ gitconfig-man -s personal
55
+
56
+ # Work on personal projects...
57
+ git clone git@github.com:john/personal-project.git
58
+ cd personal-project
59
+ git commit -m "Personal commit" # Uses personal email
60
+ ```
61
+
62
+ ## Example 2: Multiple Client Projects
63
+
64
+ ```bash
65
+ # Client A configuration
66
+ gitconfig-man -c client-a
67
+ git config --global user.name "Your Name"
68
+ git config --global user.email "you@clienta.com"
69
+ git config --global commit.gpgsign true
70
+
71
+ # Client B configuration
72
+ gitconfig-man -c client-b
73
+ git config --global user.name "Your Name"
74
+ git config --global user.email "you@clientb.com"
75
+ git config --global commit.gpgsign false
76
+
77
+ # Switch between clients
78
+ gitconfig-man -s client-a # When working on Client A projects
79
+ gitconfig-man -s client-b # When working on Client B projects
80
+ ```
81
+
82
+ ## Example 3: Different Development Environments
83
+
84
+ ```bash
85
+ # Production-like config
86
+ gitconfig-man -c production
87
+ git config --global core.autocrlf false
88
+ git config --global core.editor "nano"
89
+ git config --global pull.rebase true
90
+
91
+ # Development config
92
+ gitconfig-man -c development
93
+ git config --global core.autocrlf true
94
+ git config --global core.editor "code --wait"
95
+ git config --global pull.rebase false
96
+ git config --global alias.st status
97
+ git config --global alias.co checkout
98
+ git config --global alias.br branch
99
+ ```
100
+
101
+ ## Example 4: Open Source vs Private Projects
102
+
103
+ ```bash
104
+ # Open source profile
105
+ gitconfig-man -c opensource
106
+ git config --global user.name "Your Public Name"
107
+ git config --global user.email "public@email.com"
108
+ git config --global commit.gpgsign true
109
+ git config --global user.signingkey ABC123DEF
110
+
111
+ # Private/Corporate profile
112
+ gitconfig-man -c corporate
113
+ git config --global user.name "Your Corporate Name"
114
+ git config --global user.email "name@company.com"
115
+ git config --global commit.gpgsign false
116
+ git config --global http.sslVerify true
117
+ git config --global http.proxy http://corporate-proxy:8080
118
+ ```
119
+
120
+ ## Example 5: Interactive Mode
121
+
122
+ ### Create Profile Interactively
123
+
124
+ ```bash
125
+ gitconfig-man -c
126
+ # ? Enter name for the new profile: staging
127
+ # ✓ Saved current git config to 'work' profile
128
+ # ✓ Created new profile: staging
129
+ # ? Do you want to switch to newly created profile (staging)? Yes
130
+ ```
131
+
132
+ ### Switch Profile Interactively
133
+
134
+ ```bash
135
+ gitconfig-man -s
136
+ # ? Select profile to switch to: (Use arrow keys or type to search)
137
+ # ❯ default
138
+ # work
139
+ # personal
140
+ # staging
141
+ ```
142
+
143
+ ### Delete Profile Interactively
144
+
145
+ ```bash
146
+ gitconfig-man -d
147
+ # ? Select profile to delete: (Use arrow keys or type to search)
148
+ # ❯ staging
149
+ # testing
150
+ # ? Are you sure you want to delete profile 'staging'? Yes
151
+ # ✓ Successfully deleted profile 'staging'
152
+ ```
153
+
154
+ ## Example 6: List All Profiles
155
+
156
+ ```bash
157
+ gitconfig-man -ls
158
+
159
+ # Available profiles:
160
+ # • default
161
+ # ✓ work (active)
162
+ # • personal
163
+ # • client-a
164
+ # • client-b
165
+ ```
166
+
167
+ ## Example 7: Complex Git Configuration
168
+
169
+ ```bash
170
+ # Create a profile with extensive configuration
171
+ gitconfig-man -c advanced
172
+
173
+ # User settings
174
+ git config --global user.name "Developer Name"
175
+ git config --global user.email "dev@example.com"
176
+
177
+ # Core settings
178
+ git config --global core.editor "vim"
179
+ git config --global core.autocrlf input
180
+ git config --global core.whitespace trailing-space,space-before-tab
181
+
182
+ # Color settings
183
+ git config --global color.ui true
184
+ git config --global color.status auto
185
+ git config --global color.branch auto
186
+
187
+ # Aliases
188
+ git config --global alias.st status
189
+ git config --global alias.co checkout
190
+ git config --global alias.br branch
191
+ git config --global alias.ci commit
192
+ git config --global alias.unstage 'reset HEAD --'
193
+ git config --global alias.last 'log -1 HEAD'
194
+ git config --global alias.visual '!gitk'
195
+
196
+ # Merge and diff tools
197
+ git config --global merge.tool vimdiff
198
+ git config --global diff.tool vimdiff
199
+
200
+ # Push settings
201
+ git config --global push.default simple
202
+
203
+ # Pull settings
204
+ git config --global pull.rebase true
205
+ ```
206
+
207
+ ## Tips and Tricks
208
+
209
+ ### Verify Current Configuration
210
+
211
+ After switching profiles, verify your settings:
212
+
213
+ ```bash
214
+ gitconfig-man -s work
215
+ git config --global --list
216
+ ```
217
+
218
+ ### Backup Profiles
219
+
220
+ The profiles are stored as JSON files in `~/.gitconfigman/`. You can backup this directory:
221
+
222
+ ```bash
223
+ cp -r ~/.gitconfigman ~/gitconfig-backup
224
+ ```
225
+
226
+ ### Restore from Backup
227
+
228
+ ```bash
229
+ cp -r ~/gitconfig-backup ~/.gitconfigman
230
+ ```
231
+
232
+ ### Reset to Default
233
+
234
+ ```bash
235
+ gitconfig-man -s default
236
+ ```
237
+
238
+ ### View Specific Settings
239
+
240
+ ```bash
241
+ git config --global user.name
242
+ git config --global user.email
243
+ ```
244
+
245
+ ## Troubleshooting
246
+
247
+ ### Check Active Profile
248
+
249
+ ```bash
250
+ gitconfig-man -ls
251
+ ```
252
+
253
+ The active profile is marked with ✓
254
+
255
+ ### Verify Git Config Location
256
+
257
+ ```bash
258
+ git config --list --show-origin
259
+ ```
260
+
261
+ This shows where each setting is coming from.
262
+
263
+ ### Manual Profile Editing
264
+
265
+ Profiles are stored in `~/.gitconfigman/` as JSON files. You can edit them manually if needed:
266
+
267
+ ```bash
268
+ cat ~/.gitconfigman/work.json
269
+ ```
270
+
271
+ ---
272
+
273
+ For more information, see the [README.md](README.md) or run `gitconfig-man -h`.
274
+
package/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026, gitconfig-man contributors
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+
package/README.md ADDED
@@ -0,0 +1,309 @@
1
+ gitconfig-man
2
+ =============
3
+
4
+ <!-- Build & Test Status -->
5
+ [![Build Status](https://github.com/yourusername/gitconfig-man/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/yourusername/gitconfig-man/actions/workflows/ci.yml)
6
+ [![Tests](https://img.shields.io/github/actions/workflow/status/yourusername/gitconfig-man/ci.yml?branch=master&label=tests&logo=github)](https://github.com/yourusername/gitconfig-man/actions/workflows/ci.yml)
7
+ ![Test Coverage](https://img.shields.io/badge/coverage-19.2%25-yellow)
8
+ ![Tests Passing](https://img.shields.io/badge/tests-19%20passing-brightgreen)
9
+
10
+ <!-- Package Info -->
11
+ [![npm version](https://img.shields.io/npm/v/gitconfig-man.svg?logo=npm&color=cb3837)](https://www.npmjs.com/package/gitconfig-man)
12
+ [![npm downloads](https://img.shields.io/npm/dm/gitconfig-man.svg?logo=npm)](https://www.npmjs.com/package/gitconfig-man)
13
+ ![Node Version](https://img.shields.io/badge/node-%3E%3D16.0.0-brightgreen?logo=node.js)
14
+
15
+ <!-- Platform & Standards -->
16
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
17
+ [![Maintained](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/yourusername/gitconfig-man/graphs/commit-activity)
18
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
19
+
20
+ <!-- Repository Stats (activate after first push) -->
21
+ [![Last Commit](https://img.shields.io/github/last-commit/yourusername/gitconfig-man?logo=github)](https://github.com/yourusername/gitconfig-man/commits)
22
+ [![Issues](https://img.shields.io/github/issues/yourusername/gitconfig-man?logo=github)](https://github.com/yourusername/gitconfig-man/issues)
23
+ [![Pull Requests](https://img.shields.io/github/issues-pr/yourusername/gitconfig-man?logo=github)](https://github.com/yourusername/gitconfig-man/pulls)
24
+ [![Stars](https://img.shields.io/github/stars/yourusername/gitconfig-man?style=social)](https://github.com/yourusername/gitconfig-man/stargazers)
25
+
26
+ A sophisticated CLI tool to manage multiple git config profiles and switch between different git configurations with ease & grace.
27
+
28
+ ## CI/CD Status
29
+
30
+ | Workflow | Status |
31
+ |----------|--------|
32
+ | **Build & Test** | [![CI](https://github.com/yourusername/gitconfig-man/actions/workflows/ci.yml/badge.svg)](https://github.com/yourusername/gitconfig-man/actions/workflows/ci.yml) |
33
+ | **Test Coverage** | ![Coverage](https://img.shields.io/badge/coverage-19.2%25-yellow) |
34
+ | **NPM Publish** | ![Publish](https://img.shields.io/badge/publish-ready-blue) |
35
+
36
+ > 📊 **Note:** Some badges will activate after first push to GitHub. GitHub Actions badges will work after setting up CI/CD workflows.
37
+
38
+ ### Platform Support
39
+
40
+ Tests run automatically on:
41
+ - ✅ **Ubuntu** (Linux) - Node.js 16.x, 18.x, 20.x
42
+ - ✅ **macOS** - Node.js 16.x, 18.x, 20.x
43
+ - ✅ **Windows** - Node.js 16.x, 18.x, 20.x
44
+
45
+ **19 Tests** | **19%+ Coverage** | **All Tests Passing**
46
+
47
+ ## Overview
48
+ -----------
49
+
50
+ Managing different git configurations for different projects or accounts can be tedious. This package makes your life easier by creating different git config profiles and managing them for you. Perfect for developers who need to switch between personal and work accounts, or manage multiple git identities.
51
+
52
+ ## Installation
53
+ ----------------
54
+
55
+ ``` sh
56
+ npm install -g gitconfig-man
57
+ ```
58
+
59
+ ## Usage
60
+ --------
61
+
62
+ ```
63
+ ➜ ~ gitconfig-man -h
64
+
65
+ Git Config Man - Git Config Profile Manager
66
+
67
+ Usage: gitconfig-man <command> [options]
68
+
69
+ Commands:
70
+ -i Initialize gitconfig-man and create default profile
71
+ -c [name] Create new git config profile (interactive if no name)
72
+ -s [name] Switch to another git config profile (interactive if no name)
73
+ -d [name] Delete git config profile (interactive if no name)
74
+ -ls List all profiles
75
+ -h Show help
76
+ -v Show version
77
+
78
+ Tip: Run commands without arguments for interactive mode
79
+ ```
80
+
81
+ ### ✨ Interactive Features
82
+
83
+ gitconfig-man supports **interactive mode with autocomplete**! Simply run commands without arguments to get an enhanced interactive experience:
84
+
85
+ - **`gitconfig-man -s`** - Interactive profile switcher with autocomplete
86
+ - **`gitconfig-man -c`** - Interactive profile creator with validation
87
+ - **`gitconfig-man -d`** - Interactive profile deletion with confirmation
88
+
89
+ ### Initialization
90
+
91
+ Calling `gitconfig-man -i` creates a `~/.gitconfigman/` directory if it doesn't exist, and saves your current global git config as the 'default' profile.
92
+
93
+ ```
94
+ ➜ ~ gitconfig-man -i
95
+
96
+ ⚙️ Initializing Git Config Man...
97
+
98
+ ✓ Created gitconfig-man directory: /Users/username/.gitconfigman
99
+ ✓ Created default profile
100
+ ✓ Activated 'default' profile
101
+
102
+ ✨ Git Config Man initialized successfully!
103
+ ```
104
+
105
+ ### Create a new git config profile
106
+
107
+ With profile name:
108
+ ```
109
+ ➜ ~ gitconfig-man -c work
110
+ ✓ Saved current git config to 'default' profile
111
+ ✓ Created new profile: work
112
+ ? Do you want to switch to newly created profile (work)? (Y/n) y
113
+ ✓ Activated profile 'work'
114
+ Current git config has been cleared. Configure it using 'git config --global' commands.
115
+ ```
116
+
117
+ **Interactive mode** (just run without name):
118
+ ```
119
+ ➜ ~ gitconfig-man -c
120
+ ? Enter name for the new profile: personal
121
+ ✓ Saved current git config to 'default' profile
122
+ ✓ Created new profile: personal
123
+ ? Do you want to switch to newly created profile (personal)? (Y/n)
124
+ ```
125
+
126
+ After creating a new profile and switching to it, your global git config will be cleared. You can then configure it using standard git commands:
127
+
128
+ ```bash
129
+ git config --global user.name "Your Name"
130
+ git config --global user.email "your.email@example.com"
131
+ git config --global core.editor "vim"
132
+ # ... any other git config settings you need
133
+ ```
134
+
135
+ ### List available git config profiles
136
+
137
+ ```
138
+ ➜ ~ gitconfig-man -ls
139
+
140
+ Available profiles:
141
+ • default
142
+ ✓ work (active)
143
+ • personal
144
+ ```
145
+
146
+ ### Switch to a specific git config profile
147
+
148
+ With profile name:
149
+ ```
150
+ ➜ ~ gitconfig-man -s default
151
+ ✓ Saved current git config to 'work' profile
152
+ ✓ Activated profile 'default'
153
+ ```
154
+
155
+ **Interactive mode with autocomplete** (just run without name):
156
+ ```
157
+ ➜ ~ gitconfig-man -s
158
+ ? Select profile to switch to: (Use arrow keys or type to search)
159
+ ❯ default
160
+ personal
161
+ ```
162
+ Start typing to filter profiles with autocomplete!
163
+
164
+ ### Delete a specific git config profile
165
+
166
+ With profile name:
167
+ ```
168
+ ➜ ~ gitconfig-man -d work
169
+ ✓ Successfully deleted profile 'work'
170
+ ```
171
+
172
+ **Interactive mode with autocomplete and confirmation** (just run without name):
173
+ ```
174
+ ➜ ~ gitconfig-man -d
175
+ ? Select profile to delete: (Use arrow keys or type to search)
176
+ ❯ work
177
+ staging
178
+ ? Are you sure you want to delete profile 'work'? (y/N) y
179
+ ✓ Successfully deleted profile 'work'
180
+ ```
181
+
182
+ ### Get the current gitconfig-man version
183
+
184
+ ```
185
+ ➜ ~ gitconfig-man -v
186
+ gitconfig-man version 1.0.0
187
+ ```
188
+
189
+ ## How It Works
190
+ ----------------
191
+
192
+ gitconfig-man works by:
193
+
194
+ 1. Storing your git global configurations as JSON files in `~/.gitconfigman/`
195
+ 2. Each profile is saved as `profilename.json`
196
+ 3. When you switch profiles, it:
197
+ - Saves your current git config to the active profile
198
+ - Clears all global git config settings
199
+ - Applies the settings from the new profile
200
+
201
+ This means all your `git config --global` settings are preserved and can be switched instantly!
202
+
203
+ ## Common Use Cases
204
+ --------------------
205
+
206
+ ### Personal and Work Accounts
207
+
208
+ ```bash
209
+ # Initialize
210
+ gitconfig-man -i
211
+
212
+ # Create work profile
213
+ gitconfig-man -c work
214
+ git config --global user.name "Work Name"
215
+ git config --global user.email "work@company.com"
216
+
217
+ # Create personal profile
218
+ gitconfig-man -c personal
219
+ git config --global user.name "Personal Name"
220
+ git config --global user.email "personal@email.com"
221
+
222
+ # Switch between them
223
+ gitconfig-man -s work
224
+ gitconfig-man -s personal
225
+ ```
226
+
227
+ ### Different Project Configurations
228
+
229
+ ```bash
230
+ # Profile for open source projects
231
+ gitconfig-man -c opensource
232
+ git config --global user.name "Your Name"
233
+ git config --global user.email "opensource@email.com"
234
+ git config --global core.editor "vim"
235
+
236
+ # Profile for client projects
237
+ gitconfig-man -c client
238
+ git config --global user.name "Professional Name"
239
+ git config --global user.email "client@company.com"
240
+ git config --global core.editor "nano"
241
+ ```
242
+
243
+ ## Requirements
244
+ ---------------
245
+
246
+ - Node.js >= 16.0.0
247
+ - npm >= 7.0.0
248
+ - Git installed and accessible via command line
249
+
250
+ ## Development
251
+ -----------
252
+
253
+ ### Running Tests
254
+
255
+ ```bash
256
+ # Run all tests
257
+ npm test
258
+
259
+ # Run tests in watch mode
260
+ npm run test:watch
261
+
262
+ # Run tests with coverage
263
+ npm run test:ci
264
+ ```
265
+
266
+ ### Project Structure
267
+
268
+ ```
269
+ gitconfig-man/
270
+ ├── src/
271
+ │ ├── __tests__/ # Test files
272
+ │ ├── cli.js # CLI entry point
273
+ │ ├── cliOptions.js # CLI argument parsing
274
+ │ ├── commands.js # Command implementations
275
+ │ ├── constants.js # Constants and configuration
276
+ │ └── extendConfig.js # Git config utilities
277
+ ├── .github/
278
+ │ └── workflows/ # GitHub Actions CI/CD
279
+ ├── index.js # Main entry point
280
+ └── package.json
281
+ ```
282
+
283
+ ### Contributing
284
+
285
+ Contributions are welcome! Please feel free to submit a Pull Request.
286
+
287
+ 1. Fork the repository
288
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
289
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
290
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
291
+ 5. Open a Pull Request
292
+
293
+ ## License
294
+ ----------
295
+
296
+ ISC, a permissive free software license published by the Internet Software Consortium.
297
+
298
+ ## Notes
299
+ --------
300
+
301
+ - The 'default' profile cannot be deleted
302
+ - You cannot delete the currently active profile
303
+ - All git config changes are saved automatically when switching profiles
304
+ - Profile files are stored in `~/.gitconfigman/` directory
305
+
306
+ ---
307
+
308
+ Made with ❤️ for developers who manage multiple git identities.
309
+
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ require('./src/cli')();
3
+
package/package.json ADDED
@@ -0,0 +1,102 @@
1
+ {
2
+ "name": "gitconfig-man",
3
+ "version": "1.0.0",
4
+ "description": "A cli tool to manage multiple git config profiles and switch between different git configurations with ease & grace.",
5
+ "main": "index.js",
6
+ "bin": "index.js",
7
+ "scripts": {
8
+ "test": "jest --coverage",
9
+ "test:watch": "jest --watch",
10
+ "test:ci": "jest --coverage --ci --maxWorkers=2"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/yourusername/gitconfig-man.git"
15
+ },
16
+ "keywords": [
17
+ "git",
18
+ "config",
19
+ "gitconfig",
20
+ "profile",
21
+ "manage",
22
+ "git config",
23
+ "git profiles",
24
+ "git user",
25
+ "git email",
26
+ "multiple accounts",
27
+ "config manager",
28
+ "gitconfig-man"
29
+ ],
30
+ "author": "Your Name",
31
+ "license": "ISC",
32
+ "bugs": {
33
+ "url": "https://github.com/yourusername/gitconfig-man/issues"
34
+ },
35
+ "homepage": "https://github.com/yourusername/gitconfig-man#readme",
36
+ "engines": {
37
+ "node": ">=16.0.0",
38
+ "npm": ">=7.0.0"
39
+ },
40
+ "dependencies": {
41
+ "chalk": "^4.1.2",
42
+ "fs-extra": "^10.0.0",
43
+ "inquirer": "^8.2.5",
44
+ "inquirer-autocomplete-prompt": "^2.0.0"
45
+ },
46
+ "devDependencies": {
47
+ "jest": "^29.7.0",
48
+ "jest-junit": "^16.0.0",
49
+ "@types/jest": "^29.5.11"
50
+ },
51
+ "jest": {
52
+ "testEnvironment": "node",
53
+ "coverageDirectory": "coverage",
54
+ "collectCoverageFrom": [
55
+ "src/**/*.js",
56
+ "!src/**/*.test.js",
57
+ "!src/**/*.spec.js",
58
+ "!src/__tests__/helpers.js",
59
+ "!src/__tests__/testUtils.js",
60
+ "!**/node_modules/**",
61
+ "!**/coverage/**"
62
+ ],
63
+ "testMatch": [
64
+ "**/__tests__/**/*.test.js",
65
+ "**/?(*.)+(spec|test).js"
66
+ ],
67
+ "testPathIgnorePatterns": [
68
+ "/node_modules/",
69
+ "helpers.js",
70
+ "testUtils.js"
71
+ ],
72
+ "coveragePathIgnorePatterns": [
73
+ "/node_modules/",
74
+ "/coverage/",
75
+ "/__tests__/helpers.js",
76
+ "/__tests__/testUtils.js"
77
+ ],
78
+ "coverageThreshold": {
79
+ "global": {
80
+ "branches": 0,
81
+ "functions": 0,
82
+ "lines": 0,
83
+ "statements": 0
84
+ }
85
+ },
86
+ "reporters": [
87
+ "default",
88
+ [
89
+ "jest-junit",
90
+ {
91
+ "outputDirectory": "coverage",
92
+ "outputName": "junit.xml",
93
+ "classNameTemplate": "{classname}",
94
+ "titleTemplate": "{title}",
95
+ "ancestorSeparator": " › ",
96
+ "usePathForSuiteName": "true"
97
+ }
98
+ ]
99
+ ]
100
+ }
101
+ }
102
+
package/src/cli.js ADDED
@@ -0,0 +1,15 @@
1
+ const { init, run, prepareArgs, getName } = require("./cliOptions");
2
+
3
+ async function gitConfigCli() {
4
+ let args = prepareArgs(process.argv);
5
+ let name = getName(process.argv);
6
+ if (name === 'ls') {
7
+ name = '';
8
+ args.push('ls');
9
+ }
10
+ init();
11
+ await run(args, name);
12
+ }
13
+
14
+ module.exports = gitConfigCli;
15
+
@@ -0,0 +1,55 @@
1
+ const commands = require("./commands");
2
+ const { cliOptions, options } = require("./constants");
3
+
4
+ const generateOption = function (option, name, help) {
5
+ options.push({
6
+ option,
7
+ name,
8
+ method: name,
9
+ help,
10
+ });
11
+ };
12
+
13
+ const generateOptions = function () {
14
+ cliOptions.forEach((op) => generateOption(...op));
15
+ };
16
+
17
+ const init = function () {
18
+ generateOptions();
19
+ };
20
+
21
+ const run = async function (args, name) {
22
+ if (!args.length) {
23
+ commands.help(options);
24
+ return process.exit(0);
25
+ }
26
+ for (let arg of args) {
27
+ const command = options.find((op) => op.name === arg || op.option === arg);
28
+ if (!command) {
29
+ commands.help(options);
30
+ return process.exit(0);
31
+ }
32
+ const fn = commands[command.method];
33
+ await fn(name);
34
+ }
35
+ process.exit();
36
+ };
37
+
38
+ const prepareArgs = function (argv) {
39
+ return argv
40
+ .slice(2, argv.length)
41
+ .filter((arg) => arg.charAt(0) === "-")
42
+ .map((arg) => arg.replace(/\-/g, ""));
43
+ };
44
+
45
+ const getName = function (argv) {
46
+ return argv.slice(2).find((arg) => arg.charAt(0) !== "-");
47
+ };
48
+
49
+ module.exports = {
50
+ prepareArgs,
51
+ getName,
52
+ init,
53
+ run,
54
+ };
55
+
@@ -0,0 +1,351 @@
1
+ const fs = require("fs-extra");
2
+ const os = require("os");
3
+ const path = require("path");
4
+ const inquirer = require("inquirer");
5
+ const chalk = require("chalk");
6
+ const autocompletePrompt = require("inquirer-autocomplete-prompt");
7
+ const { options } = require("./constants");
8
+ const {
9
+ getGlobalConfig,
10
+ clearGlobalConfig,
11
+ applyGlobalConfig,
12
+ saveConfigToFile,
13
+ loadConfigFromFile,
14
+ } = require("./extendConfig");
15
+
16
+ // Register autocomplete prompt
17
+ inquirer.registerPrompt("autocomplete", autocompletePrompt);
18
+
19
+ const CONFIGMAN_DIR_PATH = path.join(os.homedir(), ".gitconfigman");
20
+ const CONFIGMAN_PATH = path.join(CONFIGMAN_DIR_PATH, ".gitconfigman");
21
+ const IS_INITIALIZED = fs.existsSync(CONFIGMAN_DIR_PATH);
22
+ let CONFIGMAN_CONTENT = IS_INITIALIZED
23
+ ? JSON.parse(fs.readFileSync(CONFIGMAN_PATH))
24
+ : undefined;
25
+
26
+ const logger = (type, message) => {
27
+ if (Array.isArray(message)) {
28
+ message = message.join(" ");
29
+ }
30
+ switch (type) {
31
+ case "success":
32
+ console.log(chalk.green(message ? message : ""));
33
+ break;
34
+ case "error":
35
+ console.log(chalk.red(message ? message : ""));
36
+ break;
37
+ case "warning":
38
+ console.log(chalk.yellow(message ? message : ""));
39
+ break;
40
+ case "info":
41
+ console.log(chalk.cyan(message ? message : ""));
42
+ break;
43
+ default:
44
+ console.log(message ? message : "");
45
+ break;
46
+ }
47
+ };
48
+
49
+ const help = function () {
50
+ console.log();
51
+ console.log(chalk.bold.cyan("Git Config Man") + chalk.gray(" - Git Config Profile Manager"));
52
+ console.log();
53
+ console.log(chalk.bold("Usage:") + " gitconfig-man <command> [options]");
54
+ console.log();
55
+ console.log(chalk.bold("Commands:"));
56
+ for (let option of options) {
57
+ console.log(chalk.gray(option.help));
58
+ }
59
+ console.log();
60
+ console.log(chalk.dim("Tip: Run commands without arguments for interactive mode"));
61
+ console.log();
62
+ };
63
+
64
+ const init = function () {
65
+ if (!fs.existsSync(CONFIGMAN_DIR_PATH)) {
66
+ console.log(chalk.cyan("\n⚙️ Initializing Git Config Man...\n"));
67
+ fs.mkdirSync(CONFIGMAN_DIR_PATH);
68
+ logger("success", "✓ Created gitconfig-man directory: " + CONFIGMAN_DIR_PATH);
69
+
70
+ // Save current git config as default profile
71
+ const defaultProfilePath = path.join(CONFIGMAN_DIR_PATH, "default.json");
72
+ saveConfigToFile(defaultProfilePath);
73
+ logger("success", "✓ Created default profile");
74
+
75
+ fs.writeFileSync(
76
+ CONFIGMAN_PATH,
77
+ JSON.stringify({ active: "default", available: ["default"] })
78
+ );
79
+ logger("success", "✓ Activated 'default' profile");
80
+ console.log(chalk.green("\n✨ Git Config Man initialized successfully!\n"));
81
+ return;
82
+ }
83
+ logger("info", "gitconfig-man is already initialized");
84
+ };
85
+
86
+ const create = async function (name) {
87
+ if (!IS_INITIALIZED) {
88
+ logger("error", "gitconfig-man is not initialized\n");
89
+ logger(null, "Please initialize gitconfig-man using: gitconfig-man -i\n");
90
+ return;
91
+ }
92
+ if (!name) {
93
+ const answer = await inquirer.prompt([
94
+ {
95
+ type: "input",
96
+ name: "profileName",
97
+ message: "Enter name for the new profile:",
98
+ validate: (input) => {
99
+ if (!input || input.trim() === "") {
100
+ return "Profile name cannot be empty";
101
+ }
102
+ const profilePath = path.join(CONFIGMAN_DIR_PATH, `${input}.json`);
103
+ if (fs.existsSync(profilePath)) {
104
+ return "A profile with this name already exists";
105
+ }
106
+ return true;
107
+ },
108
+ },
109
+ ]);
110
+ name = answer.profileName;
111
+ }
112
+
113
+ let { active, available } = CONFIGMAN_CONTENT || {};
114
+ const profilePath = path.join(CONFIGMAN_DIR_PATH, `${name}.json`);
115
+ const exist = fs.existsSync(profilePath);
116
+
117
+ if (exist) {
118
+ return logger("error", "A profile with similar name already exists");
119
+ }
120
+
121
+ if (active) {
122
+ // Save current git config to the active profile
123
+ const activeProfilePath = path.join(CONFIGMAN_DIR_PATH, `${active}.json`);
124
+ saveConfigToFile(activeProfilePath);
125
+ logger("success", `Saved current git config to '${active}' profile`);
126
+
127
+ // Create new empty profile
128
+ fs.writeFileSync(profilePath, JSON.stringify({}, null, 2));
129
+ logger("success", `Created new profile: ${name}`);
130
+
131
+ const { switchNow } = await inquirer.prompt([
132
+ {
133
+ type: "confirm",
134
+ name: "switchNow",
135
+ message: `Do you want to switch to newly created profile (${name})?`,
136
+ default: true,
137
+ },
138
+ ]);
139
+
140
+ available.push(name);
141
+
142
+ if (switchNow) {
143
+ // Clear current config and switch to new empty profile
144
+ clearGlobalConfig();
145
+ fs.writeFileSync(
146
+ CONFIGMAN_PATH,
147
+ JSON.stringify({ active: name, available })
148
+ );
149
+ logger("success", `Activated profile '${name}'`);
150
+ logger("info", "Current git config has been cleared. Configure it using 'git config --global' commands.");
151
+ return;
152
+ }
153
+
154
+ fs.writeFileSync(
155
+ CONFIGMAN_PATH,
156
+ JSON.stringify({ active: active, available })
157
+ );
158
+ return logger("success", `Successfully created profile '${name}'`);
159
+ }
160
+ };
161
+
162
+ const list = function () {
163
+ if (!IS_INITIALIZED) {
164
+ logger("error", "gitconfig-man is not initialized\n");
165
+ logger(null, "Please initialize gitconfig-man using: gitconfig-man -i\n");
166
+ return;
167
+ }
168
+
169
+ console.log(chalk.bold("\nAvailable profiles:"));
170
+ if (CONFIGMAN_CONTENT) {
171
+ const { active, available } = CONFIGMAN_CONTENT;
172
+ available.forEach((profile) => {
173
+ if (profile === active) {
174
+ console.log(chalk.green(` ✓ ${profile}`) + chalk.gray(" (active)"));
175
+ } else {
176
+ console.log(chalk.white(` • ${profile}`));
177
+ }
178
+ });
179
+ }
180
+ console.log();
181
+ };
182
+
183
+ const switchProfile = async function (name) {
184
+ if (!IS_INITIALIZED) {
185
+ logger("error", "gitconfig-man is not initialized\n");
186
+ logger(null, "Please initialize gitconfig-man using: gitconfig-man -i\n");
187
+ return;
188
+ }
189
+
190
+ if (CONFIGMAN_CONTENT) {
191
+ const { active, available } = CONFIGMAN_CONTENT;
192
+
193
+ // If no name provided, show interactive autocomplete menu
194
+ if (!name) {
195
+ const otherProfiles = available.filter((profile) => profile !== active);
196
+
197
+ if (otherProfiles.length === 0) {
198
+ return logger("warning", "No other profiles available to switch to");
199
+ }
200
+
201
+ const answer = await inquirer.prompt([
202
+ {
203
+ type: "autocomplete",
204
+ name: "profileName",
205
+ message: "Select profile to switch to:",
206
+ source: async (answersSoFar, input) => {
207
+ const filtered = otherProfiles.filter((profile) =>
208
+ profile.toLowerCase().includes((input || "").toLowerCase())
209
+ );
210
+ return filtered.map((profile) => ({
211
+ name: profile,
212
+ value: profile,
213
+ }));
214
+ },
215
+ pageSize: 10,
216
+ },
217
+ ]);
218
+ name = answer.profileName;
219
+ }
220
+
221
+ const profile = available.find((p) => p === name);
222
+
223
+ if (!profile) {
224
+ return logger("error", `Profile '${name}' not found`);
225
+ }
226
+
227
+ if (profile === active) {
228
+ return logger("warning", `'${name}' is already the active profile`);
229
+ }
230
+
231
+ // Save current config to active profile
232
+ const activeProfilePath = path.join(CONFIGMAN_DIR_PATH, `${active}.json`);
233
+ saveConfigToFile(activeProfilePath);
234
+ logger("success", `Saved current git config to '${active}' profile`);
235
+
236
+ // Load and apply new profile
237
+ const newProfilePath = path.join(CONFIGMAN_DIR_PATH, `${name}.json`);
238
+ const newConfig = loadConfigFromFile(newProfilePath);
239
+
240
+ // Clear all current config
241
+ clearGlobalConfig();
242
+
243
+ // Apply new config
244
+ applyGlobalConfig(newConfig);
245
+
246
+ fs.writeFileSync(CONFIGMAN_PATH, JSON.stringify({ available, active: name }));
247
+ logger("success", `Activated profile '${name}'`);
248
+ } else {
249
+ logger("error", "Data directory is corrupt. Please try uninstalling and reinstalling the package.");
250
+ }
251
+ };
252
+
253
+ const deleteProfile = async function (name) {
254
+ if (!IS_INITIALIZED) {
255
+ logger("error", "gitconfig-man is not initialized\n");
256
+ logger(null, "Please initialize gitconfig-man using: gitconfig-man -i\n");
257
+ return;
258
+ }
259
+
260
+ let { active, available } = CONFIGMAN_CONTENT || {};
261
+
262
+ // If no name provided, show interactive autocomplete menu
263
+ if (!name) {
264
+ const deletableProfiles = available.filter(
265
+ (profile) => profile !== "default" && profile !== active
266
+ );
267
+
268
+ if (deletableProfiles.length === 0) {
269
+ return logger("warning", "No profiles available to delete");
270
+ }
271
+
272
+ const answer = await inquirer.prompt([
273
+ {
274
+ type: "autocomplete",
275
+ name: "profileName",
276
+ message: "Select profile to delete:",
277
+ source: async (answersSoFar, input) => {
278
+ const filtered = deletableProfiles.filter((profile) =>
279
+ profile.toLowerCase().includes((input || "").toLowerCase())
280
+ );
281
+ return filtered.map((profile) => ({
282
+ name: profile,
283
+ value: profile,
284
+ }));
285
+ },
286
+ pageSize: 10,
287
+ },
288
+ ]);
289
+ name = answer.profileName;
290
+
291
+ // Confirm deletion
292
+ const { confirmDelete } = await inquirer.prompt([
293
+ {
294
+ type: "confirm",
295
+ name: "confirmDelete",
296
+ message: `Are you sure you want to delete profile '${name}'?`,
297
+ default: false,
298
+ },
299
+ ]);
300
+
301
+ if (!confirmDelete) {
302
+ return logger("info", "Deletion cancelled");
303
+ }
304
+ }
305
+
306
+ if (name === "default") {
307
+ return logger("error", "Default profile cannot be deleted");
308
+ }
309
+
310
+ const profilePath = path.join(CONFIGMAN_DIR_PATH, `${name}.json`);
311
+ const exist = fs.existsSync(profilePath);
312
+
313
+ if (active === name) {
314
+ return logger(
315
+ "error",
316
+ "Cannot delete this profile as it is currently active"
317
+ );
318
+ }
319
+
320
+ if (exist && available.find((p) => p === name)) {
321
+ available.splice(available.indexOf(name), 1);
322
+ fs.rmSync(profilePath, { force: true });
323
+ fs.writeFileSync(
324
+ CONFIGMAN_PATH,
325
+ JSON.stringify({ active, available: available })
326
+ );
327
+ return logger("success", `Successfully deleted profile '${name}'`);
328
+ } else {
329
+ return logger("error", `Couldn't find profile '${name}'`);
330
+ }
331
+ };
332
+
333
+ const version = function () {
334
+ const pkg = require("../package.json");
335
+ console.log(
336
+ chalk.cyan("gitconfig-man") +
337
+ chalk.gray(" version ") +
338
+ chalk.bold(pkg.version)
339
+ );
340
+ };
341
+
342
+ module.exports = {
343
+ version,
344
+ list,
345
+ init,
346
+ help,
347
+ create,
348
+ switch: switchProfile,
349
+ delete: deleteProfile,
350
+ };
351
+
@@ -0,0 +1,17 @@
1
+ const cliOptions = [
2
+ ["i", "init", " -i Initialize gitconfig-man and create default profile"],
3
+ ["c", "create", " -c [name] Create new git config profile (interactive if no name)"],
4
+ ["s", "switch", " -s [name] Switch to another git config profile (interactive if no name)"],
5
+ ["d", "delete", " -d [name] Delete git config profile (interactive if no name)"],
6
+ ["ls", "list", " -ls List all profiles"],
7
+ ["h", "help", " -h Show help"],
8
+ ["v", "version", " -v Show version"],
9
+ ];
10
+
11
+ const options = [];
12
+
13
+ module.exports = {
14
+ cliOptions,
15
+ options,
16
+ };
17
+
@@ -0,0 +1,109 @@
1
+ const { execSync } = require("child_process");
2
+ const fs = require("fs-extra");
3
+
4
+ /**
5
+ * Get all global git config settings as an object
6
+ */
7
+ const getGlobalConfig = () => {
8
+ try {
9
+ const output = execSync("git config --global --list", {
10
+ encoding: "utf8",
11
+ stdio: ["pipe", "pipe", "pipe"],
12
+ });
13
+
14
+ const config = {};
15
+ const lines = output.trim().split("\n");
16
+
17
+ lines.forEach((line) => {
18
+ const [key, ...valueParts] = line.split("=");
19
+ if (key) {
20
+ const value = valueParts.join("="); // Handle values with '=' in them
21
+ config[key] = value || "";
22
+ }
23
+ });
24
+
25
+ return config;
26
+ } catch (error) {
27
+ // If git config fails, return empty object
28
+ return {};
29
+ }
30
+ };
31
+
32
+ /**
33
+ * Set a git config value globally
34
+ */
35
+ const setGlobalConfig = (key, value) => {
36
+ try {
37
+ if (value === undefined || value === null || value === "") {
38
+ // Unset the key if value is empty
39
+ execSync(`git config --global --unset ${key}`, {
40
+ encoding: "utf8",
41
+ stdio: ["pipe", "pipe", "pipe"],
42
+ });
43
+ } else {
44
+ // Escape quotes in value
45
+ const escapedValue = value.replace(/"/g, '\\"');
46
+ execSync(`git config --global ${key} "${escapedValue}"`, {
47
+ encoding: "utf8",
48
+ stdio: ["pipe", "pipe", "pipe"],
49
+ });
50
+ }
51
+ } catch (error) {
52
+ // Ignore errors for unset operations on non-existent keys
53
+ }
54
+ };
55
+
56
+ /**
57
+ * Clear all global git config settings
58
+ */
59
+ const clearGlobalConfig = () => {
60
+ const config = getGlobalConfig();
61
+ Object.keys(config).forEach((key) => {
62
+ try {
63
+ execSync(`git config --global --unset ${key}`, {
64
+ encoding: "utf8",
65
+ stdio: ["pipe", "pipe", "pipe"],
66
+ });
67
+ } catch (error) {
68
+ // Continue even if unset fails
69
+ }
70
+ });
71
+ };
72
+
73
+ /**
74
+ * Apply a saved config object to global git config
75
+ */
76
+ const applyGlobalConfig = (config) => {
77
+ Object.keys(config).forEach((key) => {
78
+ setGlobalConfig(key, config[key]);
79
+ });
80
+ };
81
+
82
+ /**
83
+ * Save current git config to a JSON file
84
+ */
85
+ const saveConfigToFile = (filePath) => {
86
+ const config = getGlobalConfig();
87
+ fs.writeFileSync(filePath, JSON.stringify(config, null, 2));
88
+ };
89
+
90
+ /**
91
+ * Load git config from a JSON file
92
+ */
93
+ const loadConfigFromFile = (filePath) => {
94
+ if (!fs.existsSync(filePath)) {
95
+ return {};
96
+ }
97
+ const content = fs.readFileSync(filePath, "utf8");
98
+ return JSON.parse(content);
99
+ };
100
+
101
+ module.exports = {
102
+ getGlobalConfig,
103
+ setGlobalConfig,
104
+ clearGlobalConfig,
105
+ applyGlobalConfig,
106
+ saveConfigToFile,
107
+ loadConfigFromFile,
108
+ };
109
+