@traisetech/autopilot 2.3.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 +28 -1
- package/README.md +215 -202
- package/bin/autopilot.js +9 -2
- 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/guide.js +63 -0
- 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
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
const fs = require('fs-extra');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { getGitStats, calculateMetrics } = require('./insights');
|
|
4
|
-
const logger = require('../utils/logger');
|
|
5
|
-
const crypto = require('crypto');
|
|
6
|
-
|
|
7
|
-
// Default API URL (can be overridden by env)
|
|
8
|
-
const DEFAULT_API_URL = 'https://autopilot-cli.vercel.app';
|
|
9
|
-
|
|
10
|
-
async function calculateFocusTime(repoPath) {
|
|
11
|
-
const logPath = path.join(repoPath, 'autopilot.log');
|
|
12
|
-
if (!await fs.pathExists(logPath)) return 0;
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
const content = await fs.readFile(logPath, 'utf8');
|
|
16
|
-
const lines = content.split('\n').filter(l => l.trim());
|
|
17
|
-
let totalMs = 0;
|
|
18
|
-
|
|
19
|
-
for (const line of lines) {
|
|
20
|
-
try {
|
|
21
|
-
const entry = JSON.parse(line);
|
|
22
|
-
if (entry.type === 'FOCUS_SESSION_END' && entry.totalActiveMs) {
|
|
23
|
-
totalMs += entry.totalActiveMs;
|
|
24
|
-
}
|
|
25
|
-
} catch (e) {
|
|
26
|
-
// ignore bad lines
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return Math.round(totalMs / 60000); // minutes
|
|
31
|
-
} catch (error) {
|
|
32
|
-
logger.warn(`Failed to parse autopilot.log: ${error.message}`);
|
|
33
|
-
return 0;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function leaderboard(options) {
|
|
38
|
-
const apiUrl = process.env.AUTOPILOT_API_URL || DEFAULT_API_URL;
|
|
39
|
-
|
|
40
|
-
if (options.sync) {
|
|
41
|
-
await syncLeaderboard(apiUrl, options);
|
|
42
|
-
} else {
|
|
43
|
-
logger.info(`Opening leaderboard at ${apiUrl}/leaderboard...`);
|
|
44
|
-
const { default: open } = await import('open');
|
|
45
|
-
await open(`${apiUrl}/leaderboard`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function syncLeaderboard(apiUrl, options) {
|
|
50
|
-
try {
|
|
51
|
-
const repoPath = options.cwd || process.cwd();
|
|
52
|
-
logger.info('Calculating stats for leaderboard sync...');
|
|
53
|
-
|
|
54
|
-
const commits = await getGitStats(repoPath);
|
|
55
|
-
if (commits.length === 0) {
|
|
56
|
-
logger.warn('No git history found. Cannot sync stats.');
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const metrics = calculateMetrics(commits);
|
|
61
|
-
|
|
62
|
-
// Get user info (git config)
|
|
63
|
-
const git = require('../core/git');
|
|
64
|
-
const { stdout: username } = await git.runGit(repoPath, ['config', 'user.name']);
|
|
65
|
-
const { stdout: email } = await git.runGit(repoPath, ['config', 'user.email']);
|
|
66
|
-
|
|
67
|
-
const userEmail = email.trim() || 'unknown';
|
|
68
|
-
const userName = username.trim() || 'Anonymous';
|
|
69
|
-
|
|
70
|
-
// Anonymize ID using hash
|
|
71
|
-
const userId = crypto.createHash('sha256').update(userEmail).digest('hex').substring(0, 12);
|
|
72
|
-
|
|
73
|
-
// Get focus time from logs (or fallback to git stats proxy)
|
|
74
|
-
const logFocusMinutes = await calculateFocusTime(repoPath);
|
|
75
|
-
const gitFocusMinutes = Math.round(metrics.totalAdditions / 10);
|
|
76
|
-
const focusMinutes = logFocusMinutes > 0 ? logFocusMinutes : gitFocusMinutes;
|
|
77
|
-
|
|
78
|
-
const stats = {
|
|
79
|
-
id: userId,
|
|
80
|
-
username: userName, // Display name (can be public)
|
|
81
|
-
score: metrics.quality.score * 100 + metrics.totalCommits * 10, // Example scoring
|
|
82
|
-
commits: metrics.totalCommits,
|
|
83
|
-
focusMinutes: focusMinutes,
|
|
84
|
-
streak: metrics.streak.current
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
logger.info(`Syncing stats for ${stats.username} (ID: ${userId})...`);
|
|
88
|
-
logger.info('Note: Only metrics are shared. No code or file contents are transmitted.');
|
|
89
|
-
|
|
90
|
-
const response = await fetch(`${apiUrl}/api/leaderboard/sync`, {
|
|
91
|
-
method: 'POST',
|
|
92
|
-
headers: { 'Content-Type': 'application/json' },
|
|
93
|
-
body: JSON.stringify(stats)
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
if (!response.ok) {
|
|
97
|
-
let errorDetail = '';
|
|
98
|
-
try {
|
|
99
|
-
const errJson = await response.json();
|
|
100
|
-
errorDetail = errJson.details || errJson.error || '';
|
|
101
|
-
} catch (e) {
|
|
102
|
-
// Not a JSON error
|
|
103
|
-
}
|
|
104
|
-
throw new Error(`Server responded with ${response.status}${errorDetail ? ': ' + errorDetail : ''}`);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const data = await response.json();
|
|
108
|
-
logger.success(`Successfully synced! You are currently ranked #${data.rank}.`);
|
|
109
|
-
|
|
110
|
-
} catch (error) {
|
|
111
|
-
logger.error(`Failed to sync leaderboard: ${error.message}`);
|
|
112
|
-
logger.info('Make sure the docs server is running (npm run dev in autopilot-docs)');
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
module.exports = { leaderboard, syncLeaderboard };
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { getGitStats, calculateMetrics } = require('./insights');
|
|
4
|
+
const logger = require('../utils/logger');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
|
|
7
|
+
// Default API URL (can be overridden by env)
|
|
8
|
+
const DEFAULT_API_URL = 'https://autopilot-cli.vercel.app';
|
|
9
|
+
|
|
10
|
+
async function calculateFocusTime(repoPath) {
|
|
11
|
+
const logPath = path.join(repoPath, 'autopilot.log');
|
|
12
|
+
if (!await fs.pathExists(logPath)) return 0;
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const content = await fs.readFile(logPath, 'utf8');
|
|
16
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
17
|
+
let totalMs = 0;
|
|
18
|
+
|
|
19
|
+
for (const line of lines) {
|
|
20
|
+
try {
|
|
21
|
+
const entry = JSON.parse(line);
|
|
22
|
+
if (entry.type === 'FOCUS_SESSION_END' && entry.totalActiveMs) {
|
|
23
|
+
totalMs += entry.totalActiveMs;
|
|
24
|
+
}
|
|
25
|
+
} catch (e) {
|
|
26
|
+
// ignore bad lines
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return Math.round(totalMs / 60000); // minutes
|
|
31
|
+
} catch (error) {
|
|
32
|
+
logger.warn(`Failed to parse autopilot.log: ${error.message}`);
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function leaderboard(options) {
|
|
38
|
+
const apiUrl = process.env.AUTOPILOT_API_URL || DEFAULT_API_URL;
|
|
39
|
+
|
|
40
|
+
if (options.sync) {
|
|
41
|
+
await syncLeaderboard(apiUrl, options);
|
|
42
|
+
} else {
|
|
43
|
+
logger.info(`Opening leaderboard at ${apiUrl}/leaderboard...`);
|
|
44
|
+
const { default: open } = await import('open');
|
|
45
|
+
await open(`${apiUrl}/leaderboard`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function syncLeaderboard(apiUrl, options) {
|
|
50
|
+
try {
|
|
51
|
+
const repoPath = options.cwd || process.cwd();
|
|
52
|
+
logger.info('Calculating stats for leaderboard sync...');
|
|
53
|
+
|
|
54
|
+
const commits = await getGitStats(repoPath);
|
|
55
|
+
if (commits.length === 0) {
|
|
56
|
+
logger.warn('No git history found. Cannot sync stats.');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const metrics = calculateMetrics(commits);
|
|
61
|
+
|
|
62
|
+
// Get user info (git config)
|
|
63
|
+
const git = require('../core/git');
|
|
64
|
+
const { stdout: username } = await git.runGit(repoPath, ['config', 'user.name']);
|
|
65
|
+
const { stdout: email } = await git.runGit(repoPath, ['config', 'user.email']);
|
|
66
|
+
|
|
67
|
+
const userEmail = email.trim() || 'unknown';
|
|
68
|
+
const userName = username.trim() || 'Anonymous';
|
|
69
|
+
|
|
70
|
+
// Anonymize ID using hash
|
|
71
|
+
const userId = crypto.createHash('sha256').update(userEmail).digest('hex').substring(0, 12);
|
|
72
|
+
|
|
73
|
+
// Get focus time from logs (or fallback to git stats proxy)
|
|
74
|
+
const logFocusMinutes = await calculateFocusTime(repoPath);
|
|
75
|
+
const gitFocusMinutes = Math.round(metrics.totalAdditions / 10);
|
|
76
|
+
const focusMinutes = logFocusMinutes > 0 ? logFocusMinutes : gitFocusMinutes;
|
|
77
|
+
|
|
78
|
+
const stats = {
|
|
79
|
+
id: userId,
|
|
80
|
+
username: userName, // Display name (can be public)
|
|
81
|
+
score: metrics.quality.score * 100 + metrics.totalCommits * 10, // Example scoring
|
|
82
|
+
commits: metrics.totalCommits,
|
|
83
|
+
focusMinutes: focusMinutes,
|
|
84
|
+
streak: metrics.streak.current
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
logger.info(`Syncing stats for ${stats.username} (ID: ${userId})...`);
|
|
88
|
+
logger.info('Note: Only metrics are shared. No code or file contents are transmitted.');
|
|
89
|
+
|
|
90
|
+
const response = await fetch(`${apiUrl}/api/leaderboard/sync`, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: { 'Content-Type': 'application/json' },
|
|
93
|
+
body: JSON.stringify(stats)
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
let errorDetail = '';
|
|
98
|
+
try {
|
|
99
|
+
const errJson = await response.json();
|
|
100
|
+
errorDetail = errJson.details || errJson.error || '';
|
|
101
|
+
} catch (e) {
|
|
102
|
+
// Not a JSON error
|
|
103
|
+
}
|
|
104
|
+
throw new Error(`Server responded with ${response.status}${errorDetail ? ': ' + errorDetail : ''}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const data = await response.json();
|
|
108
|
+
logger.success(`Successfully synced! You are currently ranked #${data.rank}.`);
|
|
109
|
+
|
|
110
|
+
} catch (error) {
|
|
111
|
+
logger.error(`Failed to sync leaderboard: ${error.message}`);
|
|
112
|
+
logger.info('Make sure the docs server is running (npm run dev in autopilot-docs)');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = { leaderboard, syncLeaderboard };
|
package/src/commands/pause.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
const logger = require('../utils/logger');
|
|
2
|
-
const StateManager = require('../core/state');
|
|
3
|
-
|
|
4
|
-
function pauseCommand(reason) {
|
|
5
|
-
const root = process.cwd();
|
|
6
|
-
const stateManager = new StateManager(root);
|
|
7
|
-
|
|
8
|
-
if (stateManager.isPaused()) {
|
|
9
|
-
logger.warn('Autopilot is already paused.');
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const pauseReason = typeof reason === 'string' ? reason : 'User paused';
|
|
14
|
-
stateManager.pause(pauseReason);
|
|
15
|
-
logger.success(`Autopilot paused: "${pauseReason}"`);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = pauseCommand;
|
|
1
|
+
const logger = require('../utils/logger');
|
|
2
|
+
const StateManager = require('../core/state');
|
|
3
|
+
|
|
4
|
+
function pauseCommand(reason) {
|
|
5
|
+
const root = process.cwd();
|
|
6
|
+
const stateManager = new StateManager(root);
|
|
7
|
+
|
|
8
|
+
if (stateManager.isPaused()) {
|
|
9
|
+
logger.warn('Autopilot is already paused.');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const pauseReason = typeof reason === 'string' ? reason : 'User paused';
|
|
14
|
+
stateManager.pause(pauseReason);
|
|
15
|
+
logger.success(`Autopilot paused: "${pauseReason}"`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = pauseCommand;
|
package/src/commands/preset.js
CHANGED
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workflow Presets Command
|
|
3
|
-
* Built by Praise Masunga (PraiseTechzw)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs-extra');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const logger = require('../utils/logger');
|
|
9
|
-
const { getConfigPath } = require('../utils/paths');
|
|
10
|
-
const { DEFAULT_CONFIG } = require('../config/defaults');
|
|
11
|
-
|
|
12
|
-
const PRESETS = {
|
|
13
|
-
'safe-team': {
|
|
14
|
-
description: 'Safe configuration for team collaboration',
|
|
15
|
-
config: {
|
|
16
|
-
teamMode: true,
|
|
17
|
-
pullBeforePush: true,
|
|
18
|
-
conflictStrategy: 'abort',
|
|
19
|
-
preventSecrets: true,
|
|
20
|
-
commitMessageMode: 'smart',
|
|
21
|
-
debounceSeconds: 30,
|
|
22
|
-
minSecondsBetweenCommits: 300
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
'solo-speed': {
|
|
26
|
-
description: 'Fast-paced configuration for solo developers',
|
|
27
|
-
config: {
|
|
28
|
-
teamMode: false,
|
|
29
|
-
pullBeforePush: false,
|
|
30
|
-
commitMessageMode: 'simple',
|
|
31
|
-
debounceSeconds: 5,
|
|
32
|
-
minSecondsBetweenCommits: 60,
|
|
33
|
-
autoPush: true
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
'strict-ci': {
|
|
37
|
-
description: 'Strict configuration ensuring quality checks pass',
|
|
38
|
-
config: {
|
|
39
|
-
requireChecks: true,
|
|
40
|
-
checks: ['npm test', 'npm run lint'],
|
|
41
|
-
preventSecrets: true,
|
|
42
|
-
preCommitChecks: {
|
|
43
|
-
secrets: true,
|
|
44
|
-
fileSize: true,
|
|
45
|
-
lint: true,
|
|
46
|
-
test: true
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
async function listPresets() {
|
|
53
|
-
logger.section('📋 Available Workflow Presets');
|
|
54
|
-
|
|
55
|
-
Object.entries(PRESETS).forEach(([name, preset]) => {
|
|
56
|
-
console.log(`\n ${logger.colors.cyan(name)}`);
|
|
57
|
-
console.log(` ${preset.description}`);
|
|
58
|
-
});
|
|
59
|
-
console.log('');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async function applyPreset(name) {
|
|
63
|
-
if (!name) {
|
|
64
|
-
logger.error('Please specify a preset name.');
|
|
65
|
-
await listPresets();
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const preset = PRESETS[name];
|
|
70
|
-
if (!preset) {
|
|
71
|
-
logger.error(`Preset '${name}' not found.`);
|
|
72
|
-
await listPresets();
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const repoPath = process.cwd();
|
|
77
|
-
const configPath = getConfigPath(repoPath);
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
// Read existing config or use defaults
|
|
81
|
-
let currentConfig = DEFAULT_CONFIG;
|
|
82
|
-
if (await fs.pathExists(configPath)) {
|
|
83
|
-
currentConfig = await fs.readJson(configPath);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Merge preset config
|
|
87
|
-
const newConfig = {
|
|
88
|
-
...currentConfig,
|
|
89
|
-
...preset.config
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
await fs.writeJson(configPath, newConfig, { spaces: 2 });
|
|
93
|
-
logger.success(`Applied preset '${name}' successfully!`);
|
|
94
|
-
logger.info(`Updated .autopilotrc.json with ${Object.keys(preset.config).length} settings.`);
|
|
95
|
-
|
|
96
|
-
} catch (error) {
|
|
97
|
-
logger.error(`Failed to apply preset: ${error.message}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async function presetCommand(command, name) {
|
|
102
|
-
switch (command) {
|
|
103
|
-
case 'list':
|
|
104
|
-
await listPresets();
|
|
105
|
-
break;
|
|
106
|
-
case 'apply':
|
|
107
|
-
await applyPreset(name);
|
|
108
|
-
break;
|
|
109
|
-
default:
|
|
110
|
-
// If first arg is a known preset name, treat it as apply
|
|
111
|
-
if (PRESETS[command]) {
|
|
112
|
-
await applyPreset(command);
|
|
113
|
-
} else {
|
|
114
|
-
logger.error(`Unknown command or preset: ${command}`);
|
|
115
|
-
console.log('Usage: autopilot preset [list|apply] <name>');
|
|
116
|
-
console.log(' autopilot preset <name>');
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
module.exports = presetCommand;
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Presets Command
|
|
3
|
+
* Built by Praise Masunga (PraiseTechzw)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs-extra');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const logger = require('../utils/logger');
|
|
9
|
+
const { getConfigPath } = require('../utils/paths');
|
|
10
|
+
const { DEFAULT_CONFIG } = require('../config/defaults');
|
|
11
|
+
|
|
12
|
+
const PRESETS = {
|
|
13
|
+
'safe-team': {
|
|
14
|
+
description: 'Safe configuration for team collaboration',
|
|
15
|
+
config: {
|
|
16
|
+
teamMode: true,
|
|
17
|
+
pullBeforePush: true,
|
|
18
|
+
conflictStrategy: 'abort',
|
|
19
|
+
preventSecrets: true,
|
|
20
|
+
commitMessageMode: 'smart',
|
|
21
|
+
debounceSeconds: 30,
|
|
22
|
+
minSecondsBetweenCommits: 300
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
'solo-speed': {
|
|
26
|
+
description: 'Fast-paced configuration for solo developers',
|
|
27
|
+
config: {
|
|
28
|
+
teamMode: false,
|
|
29
|
+
pullBeforePush: false,
|
|
30
|
+
commitMessageMode: 'simple',
|
|
31
|
+
debounceSeconds: 5,
|
|
32
|
+
minSecondsBetweenCommits: 60,
|
|
33
|
+
autoPush: true
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
'strict-ci': {
|
|
37
|
+
description: 'Strict configuration ensuring quality checks pass',
|
|
38
|
+
config: {
|
|
39
|
+
requireChecks: true,
|
|
40
|
+
checks: ['npm test', 'npm run lint'],
|
|
41
|
+
preventSecrets: true,
|
|
42
|
+
preCommitChecks: {
|
|
43
|
+
secrets: true,
|
|
44
|
+
fileSize: true,
|
|
45
|
+
lint: true,
|
|
46
|
+
test: true
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
async function listPresets() {
|
|
53
|
+
logger.section('📋 Available Workflow Presets');
|
|
54
|
+
|
|
55
|
+
Object.entries(PRESETS).forEach(([name, preset]) => {
|
|
56
|
+
console.log(`\n ${logger.colors.cyan(name)}`);
|
|
57
|
+
console.log(` ${preset.description}`);
|
|
58
|
+
});
|
|
59
|
+
console.log('');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function applyPreset(name) {
|
|
63
|
+
if (!name) {
|
|
64
|
+
logger.error('Please specify a preset name.');
|
|
65
|
+
await listPresets();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const preset = PRESETS[name];
|
|
70
|
+
if (!preset) {
|
|
71
|
+
logger.error(`Preset '${name}' not found.`);
|
|
72
|
+
await listPresets();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const repoPath = process.cwd();
|
|
77
|
+
const configPath = getConfigPath(repoPath);
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
// Read existing config or use defaults
|
|
81
|
+
let currentConfig = DEFAULT_CONFIG;
|
|
82
|
+
if (await fs.pathExists(configPath)) {
|
|
83
|
+
currentConfig = await fs.readJson(configPath);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Merge preset config
|
|
87
|
+
const newConfig = {
|
|
88
|
+
...currentConfig,
|
|
89
|
+
...preset.config
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
await fs.writeJson(configPath, newConfig, { spaces: 2 });
|
|
93
|
+
logger.success(`Applied preset '${name}' successfully!`);
|
|
94
|
+
logger.info(`Updated .autopilotrc.json with ${Object.keys(preset.config).length} settings.`);
|
|
95
|
+
|
|
96
|
+
} catch (error) {
|
|
97
|
+
logger.error(`Failed to apply preset: ${error.message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function presetCommand(command, name) {
|
|
102
|
+
switch (command) {
|
|
103
|
+
case 'list':
|
|
104
|
+
await listPresets();
|
|
105
|
+
break;
|
|
106
|
+
case 'apply':
|
|
107
|
+
await applyPreset(name);
|
|
108
|
+
break;
|
|
109
|
+
default:
|
|
110
|
+
// If first arg is a known preset name, treat it as apply
|
|
111
|
+
if (PRESETS[command]) {
|
|
112
|
+
await applyPreset(command);
|
|
113
|
+
} else {
|
|
114
|
+
logger.error(`Unknown command or preset: ${command}`);
|
|
115
|
+
console.log('Usage: autopilot preset [list|apply] <name>');
|
|
116
|
+
console.log(' autopilot preset <name>');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = presetCommand;
|
package/src/commands/resume.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
const logger = require('../utils/logger');
|
|
2
|
-
const StateManager = require('../core/state');
|
|
3
|
-
|
|
4
|
-
function resumeCommand() {
|
|
5
|
-
const root = process.cwd();
|
|
6
|
-
const stateManager = new StateManager(root);
|
|
7
|
-
|
|
8
|
-
if (!stateManager.isPaused()) {
|
|
9
|
-
logger.warn('Autopilot is already running.');
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
stateManager.resume();
|
|
14
|
-
logger.success('Autopilot resumed.');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
module.exports = resumeCommand;
|
|
1
|
+
const logger = require('../utils/logger');
|
|
2
|
+
const StateManager = require('../core/state');
|
|
3
|
+
|
|
4
|
+
function resumeCommand() {
|
|
5
|
+
const root = process.cwd();
|
|
6
|
+
const stateManager = new StateManager(root);
|
|
7
|
+
|
|
8
|
+
if (!stateManager.isPaused()) {
|
|
9
|
+
logger.warn('Autopilot is already running.');
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
stateManager.resume();
|
|
14
|
+
logger.success('Autopilot resumed.');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = resumeCommand;
|
package/src/commands/start.js
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command: start
|
|
3
|
-
* Starts the Autopilot watcher in the foreground
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const process = require('process');
|
|
7
|
-
const logger = require('../utils/logger');
|
|
8
|
-
const Watcher = require('../core/watcher');
|
|
9
|
-
const { getRunningPid } = require('../utils/process');
|
|
10
|
-
|
|
11
|
-
const start = async (options) => {
|
|
12
|
-
const repoPath = process.cwd();
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
// Check if already running
|
|
16
|
-
const runningPid = await getRunningPid(repoPath);
|
|
17
|
-
if (runningPid) {
|
|
18
|
-
logger.warn(`Autopilot is already running (PID: ${runningPid})`);
|
|
19
|
-
logger.info('Run "autopilot stop" to stop the current instance.');
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Initialize watcher
|
|
24
|
-
const watcher = new Watcher(repoPath);
|
|
25
|
-
|
|
26
|
-
logger.section('Starting Autopilot');
|
|
27
|
-
logger.info('Press Ctrl+C to stop, or run "autopilot stop" in another terminal.');
|
|
28
|
-
|
|
29
|
-
// Start watching
|
|
30
|
-
await watcher.start();
|
|
31
|
-
|
|
32
|
-
// Keep process alive is handled by chokidar being persistent
|
|
33
|
-
// The watcher handles process signals for cleanup
|
|
34
|
-
|
|
35
|
-
} catch (error) {
|
|
36
|
-
logger.error(`Failed to start autopilot: ${error.message}`);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
module.exports = start;
|
|
1
|
+
/**
|
|
2
|
+
* Command: start
|
|
3
|
+
* Starts the Autopilot watcher in the foreground
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const process = require('process');
|
|
7
|
+
const logger = require('../utils/logger');
|
|
8
|
+
const Watcher = require('../core/watcher');
|
|
9
|
+
const { getRunningPid } = require('../utils/process');
|
|
10
|
+
|
|
11
|
+
const start = async (options) => {
|
|
12
|
+
const repoPath = process.cwd();
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
// Check if already running
|
|
16
|
+
const runningPid = await getRunningPid(repoPath);
|
|
17
|
+
if (runningPid) {
|
|
18
|
+
logger.warn(`Autopilot is already running (PID: ${runningPid})`);
|
|
19
|
+
logger.info('Run "autopilot stop" to stop the current instance.');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Initialize watcher
|
|
24
|
+
const watcher = new Watcher(repoPath);
|
|
25
|
+
|
|
26
|
+
logger.section('Starting Autopilot');
|
|
27
|
+
logger.info('Press Ctrl+C to stop, or run "autopilot stop" in another terminal.');
|
|
28
|
+
|
|
29
|
+
// Start watching
|
|
30
|
+
await watcher.start();
|
|
31
|
+
|
|
32
|
+
// Keep process alive is handled by chokidar being persistent
|
|
33
|
+
// The watcher handles process signals for cleanup
|
|
34
|
+
|
|
35
|
+
} catch (error) {
|
|
36
|
+
logger.error(`Failed to start autopilot: ${error.message}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
module.exports = start;
|