git-cracked 1.2.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Trenton Scott
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # git-cracked
2
+
3
+ Keep your GitHub contribution graph green automatically. Commits realistic-looking code changes to a private repo on a schedule — completely free, no API key needed.
4
+
5
+ ---
6
+
7
+ ## Quickstart
8
+
9
+ ### You need
10
+
11
+ - [Node.js v18+](https://nodejs.org)
12
+ - A GitHub account
13
+ - A private repo cloned to your machine (with some source code files in it)
14
+
15
+ ### One command
16
+
17
+ ```bash
18
+ npx git-cracked
19
+ ```
20
+
21
+ Your browser opens automatically. If this is your first time, you'll see a setup page — fill in the path to your private repo and click save. That's it.
22
+
23
+ ### Or install from source
24
+
25
+ ```bash
26
+ git clone https://github.com/trentzx/git-cracked.git
27
+ cd git-cracked
28
+ npm install
29
+ npm start
30
+ ```
31
+
32
+ ### Auto-start on boot *(so it keeps running after restarts)*
33
+
34
+ From a source install:
35
+
36
+ ```bash
37
+ # Windows
38
+ npm run install-windows
39
+
40
+ # macOS
41
+ npm run install-mac
42
+
43
+ # Linux
44
+ npm run install-linux
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Dashboard
50
+
51
+ Once running, open **[http://localhost:4856](http://localhost:4856)** to see:
52
+
53
+ - Total commits (all time + today)
54
+ - Next scheduled commit times with countdown
55
+ - Full commit history — message, file changed, how long ago
56
+ - **Commit Now** button to test instantly
57
+ - Settings page to change your repo, schedule, or branch
58
+
59
+ The dashboard auto-refreshes every 30 seconds.
60
+
61
+ ---
62
+
63
+ ## Changing the schedule
64
+
65
+ Go to [http://localhost:4856/settings](http://localhost:4856/settings) and pick from the dropdown:
66
+
67
+ | Option | Times |
68
+ |---|---|
69
+ | 3× per weekday *(default)* | 9am, 1pm, 4pm |
70
+ | 2× per weekday | 9am, 3pm |
71
+ | 4× per weekday | 9am, 12pm, 3pm, 6pm |
72
+ | 1× per weekday | 10am |
73
+
74
+ All times use your computer's local time zone, so it works the same anywhere in the world.
75
+
76
+ ---
77
+
78
+ ## How it works
79
+
80
+ 1. At each scheduled time, a random source file in your repo is selected
81
+ 2. A small, realistic change is applied — fixing whitespace, normalising quotes, adjusting a constant, sorting imports, etc.
82
+ 3. A commit message is picked from a bank of 130+ realistic conventional commit messages that match the change type
83
+ 4. The commit is pushed to your private repo automatically
84
+
85
+ **Supported file types:** `.js` `.ts` `.jsx` `.tsx` `.mjs` `.py` `.go` `.java` `.rb` `.cs` `.cpp` `.c` `.rs` `.php` `.swift` `.kt` `.scala` `.sh`
86
+
87
+ ---
88
+
89
+ ## Stopping / removing
90
+
91
+ **Kill the process** — just close the terminal, or stop the background service:
92
+
93
+ **Windows** — delete the startup file:
94
+ ```
95
+ del "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\git-cracked.vbs"
96
+ ```
97
+
98
+ **macOS:**
99
+ ```bash
100
+ launchctl unload ~/Library/LaunchAgents/com.gitcracked.autocommit.plist
101
+ rm ~/Library/LaunchAgents/com.gitcracked.autocommit.plist
102
+ ```
103
+
104
+ **Linux:**
105
+ ```bash
106
+ systemctl --user disable --now git-cracked
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Troubleshooting
112
+
113
+ **"No suitable files found to mutate"**
114
+ Your private repo needs real source code files (`.js`, `.py`, `.ts`, etc.) that are at least 5 lines long and not minified.
115
+
116
+ **Commits don't show on my GitHub graph**
117
+ Make sure `git config user.email` in your private repo matches your GitHub account email:
118
+ ```bash
119
+ cd path/to/your/private/repo
120
+ git config user.email "you@example.com"
121
+ ```
122
+
123
+ **Push fails**
124
+ You need GitHub credentials set up for HTTPS. Run `gh auth login` if you have the GitHub CLI, or set up SSH keys.
125
+
126
+ ---
127
+
128
+ ## Security
129
+
130
+ - Your settings live in `~/.git-cracked/` on your machine and are never committed or uploaded
131
+ - No data is sent anywhere — commit messages are generated locally
132
+ - The tool only touches files inside the repo path you configure
133
+
134
+ ---
135
+
136
+ ## Project structure
137
+
138
+ ```
139
+ git-cracked/
140
+ ├── src/
141
+ │ ├── cli.js Entry point — starts app and opens browser
142
+ │ ├── index.js Scheduler
143
+ │ ├── dashboard.js Web dashboard + setup wizard (port 4856)
144
+ │ ├── committer.js Runs mutate → commit → push
145
+ │ ├── mutator.js Applies realistic code changes
146
+ │ ├── messages.js 130+ commit message bank
147
+ │ ├── logger.js Saves activity to activity.json
148
+ │ ├── config.js Loads and validates config.json
149
+ │ └── setup.js CLI setup wizard (alternative to web UI)
150
+ ├── scripts/
151
+ │ ├── install-windows.js
152
+ │ ├── install-mac.js
153
+ │ └── install-linux.js
154
+ ├── config.example.json
155
+ └── package.json
156
+ ```
@@ -0,0 +1,11 @@
1
+ {
2
+ "repoPath": "/absolute/path/to/your/private/repo",
3
+ "branch": "main",
4
+ "remoteName": "origin",
5
+ "pushAfterCommit": true,
6
+ "schedule": [
7
+ "0 9 * * 1-5",
8
+ "0 13 * * 1-5",
9
+ "0 16 * * 1-5"
10
+ ]
11
+ }
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "git-cracked",
3
+ "version": "1.2.0",
4
+ "description": "Auto-commits realistic-looking code changes to keep your GitHub green — free, no API key required",
5
+ "main": "src/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "git-cracked": "src/cli.js"
9
+ },
10
+ "files": [
11
+ "src/",
12
+ "scripts/",
13
+ "config.example.json",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "scripts": {
18
+ "start": "node src/cli.js",
19
+ "commit-now": "node src/index.js --now",
20
+ "setup": "node src/setup.js",
21
+ "install-windows": "node scripts/install-windows.js",
22
+ "install-mac": "node scripts/install-mac.js",
23
+ "install-linux": "node scripts/install-linux.js"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/trentzx/git-cracked.git"
28
+ },
29
+ "homepage": "https://github.com/trentzx/git-cracked#readme",
30
+ "bugs": {
31
+ "url": "https://github.com/trentzx/git-cracked/issues"
32
+ },
33
+ "author": "Trenton Scott",
34
+ "dependencies": {
35
+ "node-cron": "^3.0.3",
36
+ "simple-git": "^3.27.0"
37
+ },
38
+ "engines": {
39
+ "node": ">=18"
40
+ },
41
+ "keywords": [
42
+ "github",
43
+ "contributions",
44
+ "commits",
45
+ "automation",
46
+ "contribution-graph",
47
+ "scheduler"
48
+ ],
49
+ "license": "MIT"
50
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Installs git-cracked as a systemd user service on Linux.
3
+ * Run once with: node scripts/install-linux.js
4
+ * No sudo required — installs as a user service.
5
+ */
6
+ import { execSync } from 'child_process';
7
+ import { writeFileSync, mkdirSync } from 'fs';
8
+ import { fileURLToPath } from 'url';
9
+ import { dirname, join, resolve } from 'path';
10
+ import { homedir } from 'os';
11
+ import { existsSync } from 'fs';
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const projectRoot = resolve(__dirname, '..');
15
+ const entryPoint = join(projectRoot, 'src', 'index.js');
16
+ const nodePath = process.execPath;
17
+ const serviceDir = join(homedir(), '.config', 'systemd', 'user');
18
+ const servicePath = join(serviceDir, 'git-cracked.service');
19
+
20
+ if (!existsSync(join(projectRoot, 'config.json'))) {
21
+ console.error('config.json not found. Run "npm run setup" first.');
22
+ process.exit(1);
23
+ }
24
+
25
+ const unit = `[Unit]
26
+ Description=Git Cracked - automatic GitHub commit scheduler
27
+ After=network.target
28
+
29
+ [Service]
30
+ Type=simple
31
+ ExecStart=${nodePath} ${entryPoint}
32
+ WorkingDirectory=${projectRoot}
33
+ Restart=on-failure
34
+ RestartSec=30
35
+ StandardOutput=append:${projectRoot}/git-cracked.log
36
+ StandardError=append:${projectRoot}/git-cracked.log
37
+
38
+ [Install]
39
+ WantedBy=default.target
40
+ `;
41
+
42
+ mkdirSync(serviceDir, { recursive: true });
43
+ writeFileSync(servicePath, unit);
44
+
45
+ try {
46
+ execSync('systemctl --user daemon-reload');
47
+ execSync('systemctl --user enable git-cracked.service');
48
+ execSync('systemctl --user start git-cracked.service');
49
+ console.log('systemd user service installed and started.');
50
+ console.log('Status: systemctl --user status git-cracked');
51
+ console.log(`Logs: ${projectRoot}/git-cracked.log`);
52
+ console.log('Remove: systemctl --user disable --now git-cracked');
53
+ } catch (err) {
54
+ console.error('Failed to install service:', err.message);
55
+ console.error('Make sure systemd user services are supported on your distro.');
56
+ process.exit(1);
57
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Installs git-cracked as a macOS launchd agent.
3
+ * Run once with: node scripts/install-mac.js
4
+ * No sudo required — installs for the current user only.
5
+ */
6
+ import { execSync } from 'child_process';
7
+ import { writeFileSync, mkdirSync } from 'fs';
8
+ import { fileURLToPath } from 'url';
9
+ import { dirname, join, resolve } from 'path';
10
+ import { homedir } from 'os';
11
+ import { existsSync } from 'fs';
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const projectRoot = resolve(__dirname, '..');
15
+ const entryPoint = join(projectRoot, 'src', 'index.js');
16
+ const nodePath = process.execPath;
17
+ const label = 'com.gitcracked.autocommit';
18
+ const launchAgentsDir = join(homedir(), 'Library', 'LaunchAgents');
19
+ const plistPath = join(launchAgentsDir, `${label}.plist`);
20
+
21
+ if (!existsSync(join(projectRoot, 'config.json'))) {
22
+ console.error('config.json not found. Run "npm run setup" first.');
23
+ process.exit(1);
24
+ }
25
+
26
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
27
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
28
+ <plist version="1.0">
29
+ <dict>
30
+ <key>Label</key>
31
+ <string>${label}</string>
32
+ <key>ProgramArguments</key>
33
+ <array>
34
+ <string>${nodePath}</string>
35
+ <string>${entryPoint}</string>
36
+ </array>
37
+ <key>RunAtLoad</key>
38
+ <true/>
39
+ <key>KeepAlive</key>
40
+ <true/>
41
+ <key>StandardOutPath</key>
42
+ <string>${projectRoot}/git-cracked.log</string>
43
+ <key>StandardErrorPath</key>
44
+ <string>${projectRoot}/git-cracked.log</string>
45
+ <key>WorkingDirectory</key>
46
+ <string>${projectRoot}</string>
47
+ </dict>
48
+ </plist>`;
49
+
50
+ mkdirSync(launchAgentsDir, { recursive: true });
51
+ writeFileSync(plistPath, plist);
52
+
53
+ try {
54
+ // Unload existing agent if running
55
+ execSync(`launchctl unload "${plistPath}" 2>/dev/null || true`);
56
+ execSync(`launchctl load "${plistPath}"`);
57
+ console.log('LaunchAgent installed and started.');
58
+ console.log(`Logs: ${projectRoot}/git-cracked.log`);
59
+ console.log(`To remove: launchctl unload "${plistPath}" && rm "${plistPath}"`);
60
+ } catch (err) {
61
+ console.error('Failed to load agent:', err.message);
62
+ process.exit(1);
63
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Installs git-cracked to the Windows Startup folder.
3
+ * Runs automatically on login. No admin rights required.
4
+ * Run with: node scripts/install-windows.js
5
+ */
6
+ import { execSync } from 'child_process';
7
+ import { writeFileSync } from 'fs';
8
+ import { fileURLToPath } from 'url';
9
+ import { dirname, join, resolve } from 'path';
10
+ import { existsSync } from 'fs';
11
+ import { homedir } from 'os';
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const projectRoot = resolve(__dirname, '..');
15
+ const entryPoint = join(projectRoot, 'src', 'cli.js');
16
+ const nodePath = process.execPath;
17
+
18
+
19
+ // Windows Startup folder — runs for current user, no admin needed
20
+ const startupDir = join(
21
+ homedir(),
22
+ 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup'
23
+ );
24
+ const vbsPath = join(startupDir, 'git-cracked.vbs');
25
+ const logPath = join(projectRoot, 'git-cracked.log');
26
+
27
+ // VBScript launches Node hidden (no console window flashing on login)
28
+ const vbs = `Set WshShell = CreateObject("WScript.Shell")
29
+ WshShell.Run """${nodePath}"" ""${entryPoint}""", 0, False`;
30
+
31
+ writeFileSync(vbsPath, vbs);
32
+
33
+ console.log('git-cracked installed to Startup folder.');
34
+ console.log(`Startup script: ${vbsPath}`);
35
+ console.log(`Logs: ${logPath}`);
36
+ console.log('');
37
+ console.log('It will start automatically on next login.');
38
+ console.log(`To remove: del "${vbsPath}"`);
package/src/ai.js ADDED
@@ -0,0 +1,3 @@
1
+ import { generateCommitMessage } from './messages.js';
2
+
3
+ export { generateCommitMessage };
package/src/cli.js ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'child_process';
3
+ import { existsSync } from 'fs';
4
+ import { join, dirname } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { CONFIG_PATH } from './paths.js';
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const hasConfig = existsSync(CONFIG_PATH);
10
+ const PORT = 4856;
11
+
12
+ // Start the main process
13
+ const child = spawn(process.execPath, [join(__dirname, 'index.js')], {
14
+ stdio: 'inherit',
15
+ env: process.env,
16
+ });
17
+
18
+ child.on('exit', (code) => process.exit(code ?? 0));
19
+
20
+ // Open browser after a short delay to let the server start
21
+ setTimeout(() => {
22
+ const url = `http://localhost:${PORT}${hasConfig ? '' : '/setup'}`;
23
+ openBrowser(url);
24
+ }, 1200);
25
+
26
+ function openBrowser(url) {
27
+ const platform = process.platform;
28
+ const cmd =
29
+ platform === 'win32' ? ['cmd', ['/c', 'start', url]] :
30
+ platform === 'darwin' ? ['open', [url]] :
31
+ ['xdg-open', [url]];
32
+ try {
33
+ spawn(cmd[0], cmd[1], { stdio: 'ignore', detached: true }).unref();
34
+ } catch {
35
+ console.log(`Open your browser to: ${url}`);
36
+ }
37
+ }
@@ -0,0 +1,37 @@
1
+ import simpleGit from 'simple-git';
2
+ import { existsSync } from 'fs';
3
+ import { mutateRepo } from './mutator.js';
4
+ import { generateCommitMessage } from './ai.js';
5
+ import { logCommit } from './logger.js';
6
+
7
+ export async function runCommit(config) {
8
+ if (!existsSync(config.repoPath)) {
9
+ throw new Error(`Repo path does not exist: ${config.repoPath}`);
10
+ }
11
+
12
+ const git = simpleGit(config.repoPath);
13
+
14
+ const isRepo = await git.checkIsRepo().catch(() => false);
15
+ if (!isRepo) {
16
+ throw new Error(`Not a git repository: ${config.repoPath}`);
17
+ }
18
+
19
+ const { changedFiles, description, relPath } = await mutateRepo(config.repoPath);
20
+
21
+ if (changedFiles.length === 0) {
22
+ console.log('No suitable files found to mutate — skipping commit.');
23
+ return;
24
+ }
25
+
26
+ const message = generateCommitMessage(description);
27
+
28
+ await git.add(changedFiles);
29
+ await git.commit(message);
30
+
31
+ if (config.pushAfterCommit) {
32
+ await git.push(config.remoteName, config.branch);
33
+ }
34
+
35
+ logCommit({ message, file: relPath, repoPath: config.repoPath });
36
+ console.log(`Committed: ${message}`);
37
+ }
package/src/config.js ADDED
@@ -0,0 +1,36 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ import { CONFIG_PATH } from './paths.js';
3
+
4
+ export function loadConfig() {
5
+ if (!existsSync(CONFIG_PATH)) {
6
+ console.error('ERROR: No configuration found. Run "npm start" and complete setup in the browser.');
7
+ process.exit(1);
8
+ }
9
+
10
+ let raw;
11
+ try {
12
+ raw = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));
13
+ } catch {
14
+ console.error(`ERROR: ${CONFIG_PATH} is not valid JSON. Delete it and run setup again.`);
15
+ process.exit(1);
16
+ }
17
+
18
+ const errors = [];
19
+ if (!raw.repoPath) errors.push('repoPath is required');
20
+ if (!Array.isArray(raw.schedule) || raw.schedule.length === 0) {
21
+ errors.push('schedule must be a non-empty array');
22
+ }
23
+
24
+ if (errors.length) {
25
+ console.error('ERROR: Invalid config:\n' + errors.map(e => ' - ' + e).join('\n'));
26
+ process.exit(1);
27
+ }
28
+
29
+ return {
30
+ repoPath: raw.repoPath,
31
+ branch: raw.branch ?? 'main',
32
+ remoteName: raw.remoteName ?? 'origin',
33
+ pushAfterCommit: raw.pushAfterCommit !== false,
34
+ schedule: raw.schedule,
35
+ };
36
+ }