@traisetech/autopilot 2.4.0 → 2.5.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/CHANGELOG.md +25 -9
- package/README.md +215 -106
- package/bin/autopilot.js +1 -1
- package/docs/CONFIGURATION.md +103 -103
- package/docs/DESIGN_PRINCIPLES.md +114 -114
- package/docs/TEAM-MODE.md +51 -51
- package/docs/TROUBLESHOOTING.md +21 -21
- package/package.json +75 -69
- package/src/commands/config.js +110 -110
- package/src/commands/dashboard.mjs +151 -151
- package/src/commands/doctor.js +127 -153
- package/src/commands/init.js +8 -9
- package/src/commands/insights.js +237 -237
- package/src/commands/leaderboard.js +116 -116
- package/src/commands/pause.js +18 -18
- package/src/commands/preset.js +121 -121
- package/src/commands/resume.js +17 -17
- package/src/commands/start.js +41 -41
- package/src/commands/status.js +73 -39
- package/src/commands/stop.js +58 -50
- package/src/commands/undo.js +84 -84
- package/src/config/defaults.js +23 -16
- package/src/config/ignore.js +14 -31
- package/src/config/loader.js +80 -80
- package/src/core/commit.js +45 -52
- package/src/core/commitMessageGenerator.js +130 -0
- package/src/core/configValidator.js +92 -0
- package/src/core/events.js +110 -110
- package/src/core/focus.js +2 -1
- package/src/core/gemini.js +15 -15
- package/src/core/git.js +29 -2
- package/src/core/history.js +69 -69
- package/src/core/notifier.js +61 -0
- package/src/core/retryQueue.js +152 -0
- package/src/core/safety.js +224 -210
- package/src/core/state.js +69 -71
- package/src/core/watcher.js +193 -66
- package/src/index.js +70 -70
- package/src/utils/banner.js +6 -6
- package/src/utils/crypto.js +18 -18
- package/src/utils/identity.js +41 -41
- package/src/utils/logger.js +86 -68
- package/src/utils/paths.js +62 -62
- package/src/utils/process.js +141 -141
package/src/commands/status.js
CHANGED
|
@@ -1,56 +1,90 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Autopilot status command
|
|
3
|
+
* Built by Praise Masunga (PraiseTechzw)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const fs = require('fs-extra');
|
|
7
7
|
const path = require('path');
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
8
|
+
// const { formatDistanceToNow } = require('date-fns'); // Removed unused dependency
|
|
9
|
+
const git = require('../core/git');
|
|
10
|
+
const safety = require('../core/safety');
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
const
|
|
12
|
+
async function status() {
|
|
13
|
+
const root = process.cwd();
|
|
14
|
+
const statePath = path.join(root, '.autopilot-state.json');
|
|
15
|
+
const queuePath = path.join(root, '.autopilot-queue.json');
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
console.log('\n Autopilot status');
|
|
18
|
+
console.log(' ─────────────────────────────');
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
if (!fs.existsSync(statePath)) {
|
|
21
|
+
console.error('Status: Not Running');
|
|
22
|
+
console.log(' ─────────────────────────────');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
19
25
|
|
|
26
|
+
try {
|
|
27
|
+
const state = fs.readJsonSync(statePath);
|
|
28
|
+
const pid = state.pid;
|
|
29
|
+
let alive = false;
|
|
30
|
+
|
|
20
31
|
if (pid) {
|
|
21
|
-
logger.success(`Status: Running`);
|
|
22
|
-
logger.info(`PID: ${pid}`);
|
|
23
|
-
} else {
|
|
24
|
-
logger.warn('Status: Not Running');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Show recent logs if available
|
|
28
|
-
const logPath = path.join(repoPath, 'autopilot.log');
|
|
29
|
-
if (await fs.pathExists(logPath)) {
|
|
30
|
-
logger.section('Recent Logs');
|
|
31
32
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (lastLines.length > 0) {
|
|
37
|
-
lastLines.forEach(line => console.log(line));
|
|
38
|
-
} else {
|
|
39
|
-
console.log('(Log file is empty)');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
logger.info(`\nFull log: ${logPath}`);
|
|
43
|
-
} catch (error) {
|
|
44
|
-
logger.error(`Could not read log file: ${error.message}`);
|
|
33
|
+
process.kill(pid, 0);
|
|
34
|
+
alive = true;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
alive = false;
|
|
45
37
|
}
|
|
46
|
-
} else {
|
|
47
|
-
logger.info('No log file found.');
|
|
48
38
|
}
|
|
49
39
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
40
|
+
const branch = state.branch || await git.getBranch(root) || 'unknown';
|
|
41
|
+
// Check if protected
|
|
42
|
+
// Read config to get protected branches
|
|
43
|
+
const configPath = path.join(root, '.autopilotrc.json');
|
|
44
|
+
let config = {};
|
|
45
|
+
if (fs.existsSync(configPath)) {
|
|
46
|
+
config = fs.readJsonSync(configPath);
|
|
47
|
+
}
|
|
48
|
+
const isProtected = safety.isProtectedBranch(branch, config);
|
|
49
|
+
|
|
50
|
+
const relativeTime = (ts) => {
|
|
51
|
+
if (!ts) return 'never';
|
|
52
|
+
const diff = Date.now() - ts;
|
|
53
|
+
const mins = Math.floor(diff / 60000);
|
|
54
|
+
if (mins < 1) return 'just now';
|
|
55
|
+
if (mins < 60) return `${mins} min ago`;
|
|
56
|
+
const hours = Math.floor(mins / 60);
|
|
57
|
+
if (hours < 24) return `${hours}h ago`;
|
|
58
|
+
return `${Math.floor(hours / 24)} days ago`;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const uptime = () => {
|
|
62
|
+
if (!state.startedAt) return 'unknown';
|
|
63
|
+
const diff = Date.now() - state.startedAt;
|
|
64
|
+
const hours = Math.floor(diff / 3600000);
|
|
65
|
+
const mins = Math.floor((diff % 3600000) / 60000);
|
|
66
|
+
return `${hours}h ${mins}m`;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
let queueLength = 0;
|
|
70
|
+
if (fs.existsSync(queuePath)) {
|
|
71
|
+
const queue = fs.readJsonSync(queuePath);
|
|
72
|
+
queueLength = queue.length;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(` State: ${alive ? (state.status || 'watching') : 'stopped'}`);
|
|
76
|
+
console.log(` Branch: ${branch}${isProtected ? ' (PROTECTED — push blocked)' : ''}`);
|
|
77
|
+
console.log(` Last commit: ${state.lastCommitHash ? state.lastCommitHash.substring(0, 7) : 'none'} — "${state.lastCommitMessage || 'none'}" (${relativeTime(state.lastCommitAt)})`);
|
|
78
|
+
console.log(` Last push: ${state.lastPushHash ? state.lastPushHash.substring(0, 7) : 'none'} — ${state.lastPushStatus || 'none'} (${relativeTime(state.lastPushAt)})`);
|
|
79
|
+
console.log(` Pending queue: ${queueLength} jobs waiting to push`);
|
|
80
|
+
console.log(` Conflicts: ${state.conflicts || 'none detected'}`);
|
|
81
|
+
console.log(` Watching: ${state.watchPath || root}`);
|
|
82
|
+
console.log(` Uptime: ${uptime()}`);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.log(` Error: Could not read state: ${err.message}`);
|
|
53
85
|
}
|
|
54
|
-
|
|
86
|
+
|
|
87
|
+
console.log(' ─────────────────────────────');
|
|
88
|
+
}
|
|
55
89
|
|
|
56
90
|
module.exports = status;
|
package/src/commands/stop.js
CHANGED
|
@@ -1,50 +1,58 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command: stop
|
|
3
|
-
* Stops the running Autopilot instance
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Command: stop
|
|
3
|
+
* Stops the running Autopilot instance
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const process = require('process');
|
|
9
|
+
const logger = require('../utils/logger');
|
|
10
|
+
const { getRunningPid, removePid } = require('../utils/process');
|
|
11
|
+
|
|
12
|
+
const stop = async () => {
|
|
13
|
+
const repoPath = process.cwd();
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const pid = await getRunningPid(repoPath);
|
|
17
|
+
|
|
18
|
+
if (!pid) {
|
|
19
|
+
logger.info('Autopilot is not running.');
|
|
20
|
+
// Clean up stale PID file just in case
|
|
21
|
+
await removePid(repoPath);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
logger.info(`Stopping Autopilot (PID: ${pid})...`);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// Send SIGTERM to the process
|
|
29
|
+
process.kill(pid, 'SIGTERM');
|
|
30
|
+
|
|
31
|
+
logger.success('Autopilot stopped successfully.');
|
|
32
|
+
|
|
33
|
+
// Cleanup files
|
|
34
|
+
await removePid(repoPath);
|
|
35
|
+
|
|
36
|
+
const statePath = path.join(repoPath, '.autopilot-state.json');
|
|
37
|
+
const logPath = path.join(repoPath, '.autopilot.log');
|
|
38
|
+
|
|
39
|
+
if (fs.existsSync(statePath)) fs.unlinkSync(statePath);
|
|
40
|
+
if (fs.existsSync(logPath)) fs.unlinkSync(logPath);
|
|
41
|
+
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error.code === 'ESRCH') {
|
|
44
|
+
logger.warn('Process not found (stale PID file). Cleaning up...');
|
|
45
|
+
await removePid(repoPath);
|
|
46
|
+
logger.success('Cleaned up stale lock file.');
|
|
47
|
+
} else {
|
|
48
|
+
logger.error(`Failed to stop process: ${error.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logger.error(`Error stopping autopilot: ${error.message}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
module.exports = stop;
|
package/src/commands/undo.js
CHANGED
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
const logger = require('../utils/logger');
|
|
2
|
-
const HistoryManager = require('../core/history');
|
|
3
|
-
const git = require('../core/git');
|
|
4
|
-
|
|
5
|
-
async function undoCommand(options) {
|
|
6
|
-
const root = process.cwd();
|
|
7
|
-
const historyManager = new HistoryManager(root);
|
|
8
|
-
const count = options.count ? parseInt(options.count, 10) : 1;
|
|
9
|
-
|
|
10
|
-
if (isNaN(count) || count < 1) {
|
|
11
|
-
logger.error('Invalid count. Must be a positive integer.');
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
logger.info(`Attempting to undo last ${count > 1 ? count + ' commits' : 'commit'}...`);
|
|
16
|
-
|
|
17
|
-
// Check for dirty working directory
|
|
18
|
-
const hasChanges = await git.hasChanges(root);
|
|
19
|
-
if (hasChanges) {
|
|
20
|
-
logger.warn('Working directory is dirty. Undo might be unsafe.');
|
|
21
|
-
// In a real interactive CLI, we might ask for confirmation here.
|
|
22
|
-
// For now, we proceed with caution or abort if strictly required.
|
|
23
|
-
// We'll assume soft reset is safe for unpushed, revert for pushed.
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
let undoneCount = 0;
|
|
27
|
-
|
|
28
|
-
for (let i = 0; i < count; i++) {
|
|
29
|
-
const lastCommit = historyManager.getLastCommit();
|
|
30
|
-
|
|
31
|
-
if (!lastCommit) {
|
|
32
|
-
logger.warn('No more Autopilot commits found in history.');
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Verify commit exists in git
|
|
37
|
-
const exists = await git.commitExists(root, lastCommit.hash);
|
|
38
|
-
if (!exists) {
|
|
39
|
-
logger.warn(`Commit ${lastCommit.hash} not found in git history. Removing from Autopilot history.`);
|
|
40
|
-
historyManager.removeLastCommit();
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Check if pushed (simplified: if remote branch contains it)
|
|
45
|
-
// For simplicity in this phase, we'll try soft reset first if it's HEAD
|
|
46
|
-
// If it's not HEAD (e.g. manual commits happened on top), we must revert.
|
|
47
|
-
|
|
48
|
-
const headHash = await git.getLatestCommitHash(root);
|
|
49
|
-
|
|
50
|
-
if (headHash === lastCommit.hash) {
|
|
51
|
-
// It's the latest commit, we can try soft reset
|
|
52
|
-
logger.info(`Resetting (soft) ${lastCommit.hash} (${lastCommit.message})...`);
|
|
53
|
-
const result = await git.resetSoft(root, 'HEAD~1');
|
|
54
|
-
if (result.ok) {
|
|
55
|
-
historyManager.removeLastCommit();
|
|
56
|
-
undoneCount++;
|
|
57
|
-
logger.success(`Undid ${lastCommit.hash}`);
|
|
58
|
-
} else {
|
|
59
|
-
logger.error(`Failed to reset ${lastCommit.hash}: ${result.stderr}`);
|
|
60
|
-
break; // Stop on error
|
|
61
|
-
}
|
|
62
|
-
} else {
|
|
63
|
-
// It's buried, or pushed (safer to revert in mixed scenarios)
|
|
64
|
-
logger.info(`Reverting ${lastCommit.hash} (${lastCommit.message})...`);
|
|
65
|
-
const result = await git.revert(root, lastCommit.hash);
|
|
66
|
-
if (result.ok) {
|
|
67
|
-
historyManager.removeLastCommit();
|
|
68
|
-
undoneCount++;
|
|
69
|
-
logger.success(`Reverted ${lastCommit.hash}`);
|
|
70
|
-
} else {
|
|
71
|
-
logger.error(`Failed to revert ${lastCommit.hash}: ${result.stderr}`);
|
|
72
|
-
break; // Stop on error
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (undoneCount > 0) {
|
|
78
|
-
logger.success(`Successfully undid ${undoneCount} commit(s).`);
|
|
79
|
-
} else {
|
|
80
|
-
logger.info('No commits were undone.');
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
module.exports = undoCommand;
|
|
1
|
+
const logger = require('../utils/logger');
|
|
2
|
+
const HistoryManager = require('../core/history');
|
|
3
|
+
const git = require('../core/git');
|
|
4
|
+
|
|
5
|
+
async function undoCommand(options) {
|
|
6
|
+
const root = process.cwd();
|
|
7
|
+
const historyManager = new HistoryManager(root);
|
|
8
|
+
const count = options.count ? parseInt(options.count, 10) : 1;
|
|
9
|
+
|
|
10
|
+
if (isNaN(count) || count < 1) {
|
|
11
|
+
logger.error('Invalid count. Must be a positive integer.');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
logger.info(`Attempting to undo last ${count > 1 ? count + ' commits' : 'commit'}...`);
|
|
16
|
+
|
|
17
|
+
// Check for dirty working directory
|
|
18
|
+
const hasChanges = await git.hasChanges(root);
|
|
19
|
+
if (hasChanges) {
|
|
20
|
+
logger.warn('Working directory is dirty. Undo might be unsafe.');
|
|
21
|
+
// In a real interactive CLI, we might ask for confirmation here.
|
|
22
|
+
// For now, we proceed with caution or abort if strictly required.
|
|
23
|
+
// We'll assume soft reset is safe for unpushed, revert for pushed.
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let undoneCount = 0;
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i < count; i++) {
|
|
29
|
+
const lastCommit = historyManager.getLastCommit();
|
|
30
|
+
|
|
31
|
+
if (!lastCommit) {
|
|
32
|
+
logger.warn('No more Autopilot commits found in history.');
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Verify commit exists in git
|
|
37
|
+
const exists = await git.commitExists(root, lastCommit.hash);
|
|
38
|
+
if (!exists) {
|
|
39
|
+
logger.warn(`Commit ${lastCommit.hash} not found in git history. Removing from Autopilot history.`);
|
|
40
|
+
historyManager.removeLastCommit();
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check if pushed (simplified: if remote branch contains it)
|
|
45
|
+
// For simplicity in this phase, we'll try soft reset first if it's HEAD
|
|
46
|
+
// If it's not HEAD (e.g. manual commits happened on top), we must revert.
|
|
47
|
+
|
|
48
|
+
const headHash = await git.getLatestCommitHash(root);
|
|
49
|
+
|
|
50
|
+
if (headHash === lastCommit.hash) {
|
|
51
|
+
// It's the latest commit, we can try soft reset
|
|
52
|
+
logger.info(`Resetting (soft) ${lastCommit.hash} (${lastCommit.message})...`);
|
|
53
|
+
const result = await git.resetSoft(root, 'HEAD~1');
|
|
54
|
+
if (result.ok) {
|
|
55
|
+
historyManager.removeLastCommit();
|
|
56
|
+
undoneCount++;
|
|
57
|
+
logger.success(`Undid ${lastCommit.hash}`);
|
|
58
|
+
} else {
|
|
59
|
+
logger.error(`Failed to reset ${lastCommit.hash}: ${result.stderr}`);
|
|
60
|
+
break; // Stop on error
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
// It's buried, or pushed (safer to revert in mixed scenarios)
|
|
64
|
+
logger.info(`Reverting ${lastCommit.hash} (${lastCommit.message})...`);
|
|
65
|
+
const result = await git.revert(root, lastCommit.hash);
|
|
66
|
+
if (result.ok) {
|
|
67
|
+
historyManager.removeLastCommit();
|
|
68
|
+
undoneCount++;
|
|
69
|
+
logger.success(`Reverted ${lastCommit.hash}`);
|
|
70
|
+
} else {
|
|
71
|
+
logger.error(`Failed to revert ${lastCommit.hash}: ${result.stderr}`);
|
|
72
|
+
break; // Stop on error
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (undoneCount > 0) {
|
|
78
|
+
logger.success(`Successfully undid ${undoneCount} commit(s).`);
|
|
79
|
+
} else {
|
|
80
|
+
logger.info('No commits were undone.');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = undoCommand;
|
package/src/config/defaults.js
CHANGED
|
@@ -4,29 +4,34 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const DEFAULT_CONFIG = {
|
|
7
|
-
|
|
7
|
+
watchPath: '.',
|
|
8
|
+
debounceMs: 20000,
|
|
9
|
+
aiProvider: 'grok',
|
|
10
|
+
aiApiKey: '',
|
|
11
|
+
ai: {
|
|
12
|
+
enabled: true,
|
|
13
|
+
provider: 'grok',
|
|
14
|
+
apiKey: '',
|
|
15
|
+
grokApiKey: '',
|
|
16
|
+
interactive: true,
|
|
17
|
+
model: 'grok-beta'
|
|
18
|
+
},
|
|
19
|
+
protectedBranches: ['main', 'master', 'production', 'prod', 'release'],
|
|
20
|
+
allowPushToProtected: false,
|
|
21
|
+
notificationsEnabled: true,
|
|
22
|
+
maxRetryAttempts: 5,
|
|
23
|
+
ignorePaths: ['.git', 'node_modules', '.autopilot/', '.autopilot-state.json', '.autopilot.log', '.autopilot-queue.json'],
|
|
24
|
+
|
|
25
|
+
// Legacy/Internal
|
|
8
26
|
minSecondsBetweenCommits: 180,
|
|
9
27
|
autoPush: true,
|
|
10
|
-
blockedBranches: ['main', 'master'],
|
|
11
28
|
requireChecks: false,
|
|
12
29
|
checks: [],
|
|
13
|
-
commitMessageMode: 'ai',
|
|
14
|
-
ai: {
|
|
15
|
-
enabled: true, // Enabled by default
|
|
16
|
-
provider: 'grok', // Grok is the default for our system keys
|
|
17
|
-
apiKey: '',
|
|
18
|
-
grokApiKey: '',
|
|
19
|
-
model: 'grok-beta',
|
|
20
|
-
grokModel: 'grok-beta',
|
|
21
|
-
interactive: true // Prompt user to review AI messages by default
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
// Phase 1: Team Mode
|
|
30
|
+
commitMessageMode: 'ai',
|
|
25
31
|
teamMode: false,
|
|
26
32
|
pullBeforePush: true,
|
|
27
33
|
conflictStrategy: 'abort',
|
|
28
34
|
maxUnpushedCommits: 5,
|
|
29
|
-
// Phase 1: Pre-commit checks
|
|
30
35
|
preCommitChecks: {
|
|
31
36
|
secrets: true,
|
|
32
37
|
fileSize: true,
|
|
@@ -44,8 +49,10 @@ const DEFAULT_IGNORE_PATTERNS = [
|
|
|
44
49
|
'.env.*',
|
|
45
50
|
'coverage/',
|
|
46
51
|
'*.log',
|
|
47
|
-
'autopilot.log',
|
|
52
|
+
'.autopilot.log',
|
|
48
53
|
'.autopilot.pid',
|
|
54
|
+
'.autopilot-state.json',
|
|
55
|
+
'.autopilot/',
|
|
49
56
|
'.DS_Store',
|
|
50
57
|
'.git/',
|
|
51
58
|
'.idea/',
|
package/src/config/ignore.js
CHANGED
|
@@ -54,9 +54,9 @@ const createIgnoreFile = async (repoPath, patterns = []) => {
|
|
|
54
54
|
* @returns {function} Filter function (path => boolean)
|
|
55
55
|
*/
|
|
56
56
|
const createIgnoredFilter = (repoPath, userPatterns = []) => {
|
|
57
|
-
const normalizedRepoPath = normalizePath(repoPath);
|
|
57
|
+
const normalizedRepoPath = normalizePath(repoPath).toLowerCase();
|
|
58
58
|
|
|
59
|
-
// Critical ignores that are ALWAYS enforced
|
|
59
|
+
// Critical ignores that are ALWAYS enforced (lowercase for comparison)
|
|
60
60
|
const criticalPrefixes = [
|
|
61
61
|
'.git',
|
|
62
62
|
'node_modules',
|
|
@@ -71,7 +71,7 @@ const createIgnoredFilter = (repoPath, userPatterns = []) => {
|
|
|
71
71
|
const criticalFiles = [
|
|
72
72
|
'autopilot.log',
|
|
73
73
|
'.autopilot.pid',
|
|
74
|
-
'.
|
|
74
|
+
'.ds_store'
|
|
75
75
|
];
|
|
76
76
|
|
|
77
77
|
const criticalExtensions = [
|
|
@@ -79,18 +79,18 @@ const createIgnoredFilter = (repoPath, userPatterns = []) => {
|
|
|
79
79
|
];
|
|
80
80
|
|
|
81
81
|
return (absolutePath) => {
|
|
82
|
-
// 1.
|
|
83
|
-
|
|
84
|
-
const relativeRaw = path.relative(repoPath, absolutePath);
|
|
82
|
+
// 1. Normalize and lowercase for consistent matching
|
|
83
|
+
const normalizedTarget = normalizePath(absolutePath).toLowerCase();
|
|
85
84
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
// 2. Determine relative path
|
|
86
|
+
let relativePath;
|
|
87
|
+
if (normalizedTarget.startsWith(normalizedRepoPath)) {
|
|
88
|
+
relativePath = normalizedTarget.slice(normalizedRepoPath.length).replace(/^\/+/, '');
|
|
89
|
+
} else {
|
|
90
|
+
// Fallback to path.relative if startsWith fails for some reason
|
|
91
|
+
relativePath = normalizePath(path.relative(repoPath, absolutePath)).toLowerCase();
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
// Normalize to forward slashes for matching
|
|
92
|
-
const relativePath = normalizePath(relativeRaw);
|
|
93
|
-
|
|
94
94
|
// Handle root path case
|
|
95
95
|
if (!relativePath || relativePath === '.') return false;
|
|
96
96
|
|
|
@@ -111,30 +111,13 @@ const createIgnoredFilter = (repoPath, userPatterns = []) => {
|
|
|
111
111
|
if (filename.endsWith(ext)) return true;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
// 4. Check user patterns
|
|
115
|
-
// TODO: Use micromatch if more complex patterns needed, but for now simple matching
|
|
116
|
-
// Note: chokidar handles globs in its 'ignored' option if passed as array,
|
|
117
|
-
// but here we are providing a function.
|
|
118
|
-
|
|
119
|
-
// We can rely on chokidar's glob handling if we pass array, but we are returning a function.
|
|
120
|
-
// If we want to support user globs in this function, we'd need micromatch.
|
|
121
|
-
// However, Chokidar accepts an array of strings/globs/functions.
|
|
122
|
-
// We should probably rely on Chokidar for user patterns if possible,
|
|
123
|
-
// BUT the requirement says "Apply ignore rules in TWO places: a) chokidar ignored function b) internal ignore matcher".
|
|
124
|
-
// So we must handle it here too.
|
|
125
|
-
|
|
126
|
-
// Simple implementation for user patterns:
|
|
114
|
+
// 4. Check user patterns
|
|
127
115
|
for (const pattern of userPatterns) {
|
|
128
|
-
|
|
129
|
-
const cleanPattern = pattern.replace(/^\/+/, '');
|
|
116
|
+
const cleanPattern = pattern.toLowerCase().replace(/^\/+/, '');
|
|
130
117
|
|
|
131
|
-
// Exact match
|
|
132
118
|
if (relativePath === cleanPattern) return true;
|
|
133
|
-
|
|
134
|
-
// Directory match
|
|
135
119
|
if (relativePath.startsWith(cleanPattern + '/')) return true;
|
|
136
120
|
|
|
137
|
-
// Extension match (*.log)
|
|
138
121
|
if (cleanPattern.startsWith('*.')) {
|
|
139
122
|
const ext = cleanPattern.slice(1);
|
|
140
123
|
if (filename.endsWith(ext)) return true;
|