@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
package/src/commands/doctor.js
CHANGED
|
@@ -1,159 +1,133 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs-extra');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const process = require('process');
|
|
9
|
-
const execa = require('execa');
|
|
10
|
-
const logger = require('../utils/logger');
|
|
11
|
-
const git = require('../core/git');
|
|
12
|
-
|
|
13
|
-
const doctor = async () => {
|
|
14
|
-
const repoPath = process.cwd();
|
|
15
|
-
let issues = 0;
|
|
16
|
-
|
|
17
|
-
logger.section('Autopilot Doctor');
|
|
18
|
-
logger.info('Diagnosing environment...');
|
|
19
|
-
|
|
20
|
-
// 1. Check Git installation
|
|
21
|
-
try {
|
|
22
|
-
const { stdout } = await execa('git', ['--version']);
|
|
23
|
-
logger.success(`Git installed: ${stdout.trim()}`);
|
|
24
|
-
} catch (error) {
|
|
25
|
-
logger.error('Git is not installed or not in PATH.');
|
|
26
|
-
issues++;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// 2. Check valid repository
|
|
30
|
-
try {
|
|
31
|
-
const { stdout } = await execa('git', ['rev-parse', '--is-inside-work-tree'], { cwd: repoPath });
|
|
32
|
-
if (stdout.trim() === 'true') {
|
|
33
|
-
logger.success('Valid Git repository detected');
|
|
34
|
-
} else {
|
|
35
|
-
logger.error('Not inside a Git repository.');
|
|
36
|
-
issues++;
|
|
37
|
-
return; // Stop further checks if not a repo
|
|
38
|
-
}
|
|
39
|
-
} catch (error) {
|
|
40
|
-
logger.error('Not inside a Git repository.');
|
|
41
|
-
issues++;
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// 3. Check remote origin
|
|
46
|
-
try {
|
|
47
|
-
const { stdout } = await execa('git', ['remote', 'get-url', 'origin'], { cwd: repoPath });
|
|
48
|
-
const remoteUrl = stdout.trim();
|
|
49
|
-
logger.success(`Remote 'origin' found: ${remoteUrl}`);
|
|
50
|
-
|
|
51
|
-
// Check remote type
|
|
52
|
-
if (remoteUrl.startsWith('http')) {
|
|
53
|
-
let hasHelper = false;
|
|
54
|
-
try {
|
|
55
|
-
const { stdout: helper } = await execa('git', ['config', '--get', 'credential.helper'], { cwd: repoPath });
|
|
56
|
-
if (helper.trim()) hasHelper = true;
|
|
57
|
-
} catch (e) { /* ignore */ }
|
|
58
|
-
|
|
59
|
-
if (hasHelper) {
|
|
60
|
-
logger.success('Remote uses HTTPS with credential helper configured.');
|
|
61
|
-
} else {
|
|
62
|
-
logger.warn('Remote uses HTTPS. Ensure credential helper is configured for non-interactive push.');
|
|
63
|
-
}
|
|
64
|
-
} else if (remoteUrl.startsWith('git@') || remoteUrl.startsWith('ssh://')) {
|
|
65
|
-
logger.success('Remote uses SSH (recommended).');
|
|
66
|
-
} else {
|
|
67
|
-
logger.info('Remote uses unknown protocol.');
|
|
68
|
-
}
|
|
69
|
-
} catch (error) {
|
|
70
|
-
logger.warn('No remote \'origin\' configured. Auto-push will fail.');
|
|
71
|
-
issues++;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// 4. Check branch name
|
|
75
|
-
const branch = await git.getBranch(repoPath);
|
|
76
|
-
if (branch) {
|
|
77
|
-
logger.info(`Current branch: ${branch}`);
|
|
78
|
-
if (branch === 'main' || branch === 'master') {
|
|
79
|
-
logger.warn('You are on the main/master branch. It is recommended to work on feature branches.');
|
|
80
|
-
issues++;
|
|
81
|
-
}
|
|
82
|
-
} else {
|
|
83
|
-
logger.error('Could not detect current branch.');
|
|
84
|
-
issues++;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 5. Check .env in .gitignore
|
|
88
|
-
const envPath = path.join(repoPath, '.env');
|
|
89
|
-
if (await fs.pathExists(envPath)) {
|
|
90
|
-
try {
|
|
91
|
-
// Check if ignored by git
|
|
92
|
-
await execa('git', ['check-ignore', '.env'], { cwd: repoPath });
|
|
93
|
-
logger.success('.env is properly ignored.');
|
|
94
|
-
} catch (error) {
|
|
95
|
-
// Exit code 1 means not ignored
|
|
96
|
-
logger.warn('SECURITY WARNING: .env file exists but is NOT ignored by git!');
|
|
97
|
-
logger.info('Add .env to your .gitignore file immediately.');
|
|
98
|
-
issues++;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 6. Check remote status (ahead/behind)
|
|
103
|
-
try {
|
|
104
|
-
const remoteStatus = await git.isRemoteAhead(repoPath);
|
|
105
|
-
if (remoteStatus.ok) {
|
|
106
|
-
if (remoteStatus.behind) {
|
|
107
|
-
logger.warn('Your branch is behind remote. You should pull changes before starting autopilot.');
|
|
108
|
-
issues++;
|
|
109
|
-
} else if (remoteStatus.ahead) {
|
|
110
|
-
logger.info('Your branch is ahead of remote. Autopilot will push these changes.');
|
|
111
|
-
} else {
|
|
112
|
-
logger.success('Branch is up to date with remote.');
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
// Could be no upstream configured, which is fine for local-only initially
|
|
116
|
-
logger.info('Could not check remote status (upstream might not be set).');
|
|
117
|
-
}
|
|
118
|
-
} catch (error) {
|
|
119
|
-
logger.info('Skipping remote status check.');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Summary
|
|
123
|
-
console.log(''); // newline
|
|
124
|
-
if (issues === 0) {
|
|
125
|
-
logger.success('Diagnosis complete. No issues found. You are ready to fly! ✈️');
|
|
126
|
-
} else {
|
|
127
|
-
logger.warn(`Diagnosis complete. Found ${issues} potential issue(s).`);
|
|
128
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Autopilot doctor command - Environmental health checks
|
|
3
|
+
* Built by Praise Masunga (PraiseTechzw)
|
|
4
|
+
*/
|
|
129
5
|
|
|
130
|
-
|
|
6
|
+
const execa = require('execa');
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { validateConfig } = require('../core/configValidator');
|
|
10
|
+
const git = require('../core/git');
|
|
11
|
+
const safety = require('../core/safety');
|
|
12
|
+
|
|
13
|
+
async function doctor() {
|
|
14
|
+
const root = process.cwd();
|
|
15
|
+
console.log('\n Autopilot doctor');
|
|
16
|
+
console.log(' ─────────────────────────────');
|
|
17
|
+
|
|
18
|
+
let issues = 0;
|
|
19
|
+
|
|
20
|
+
// 1. Node version >= 18
|
|
21
|
+
const nodeVersion = process.version;
|
|
22
|
+
const major = parseInt(nodeVersion.slice(1).split('.')[0]);
|
|
23
|
+
if (major >= 18) {
|
|
24
|
+
console.log(` [PASS] Node.js version: ${nodeVersion} (>=18 required)`);
|
|
25
|
+
} else {
|
|
26
|
+
console.log(` [FAIL] Node.js version: ${nodeVersion} (>=18 required)`);
|
|
27
|
+
issues++;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 2. Git installed
|
|
31
|
+
try {
|
|
32
|
+
const { stdout } = await execa('git', ['--version']);
|
|
33
|
+
console.log(` [PASS] Git installed: ${stdout.trim()}`);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.log(` [FAIL] Git not found`);
|
|
36
|
+
issues++;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 3. Inside git repo
|
|
131
40
|
try {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
41
|
+
await execa('git', ['rev-parse', '--is-inside-work-tree']);
|
|
42
|
+
console.log(` [PASS] Inside a git repo: yes`);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.log(` [FAIL] Inside a git repo: no`);
|
|
45
|
+
issues++;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 4. .autopilotrc.json exists and valid
|
|
49
|
+
const configPath = path.join(root, '.autopilotrc.json');
|
|
50
|
+
let config = null;
|
|
51
|
+
if (!fs.existsSync(configPath)) {
|
|
52
|
+
console.log(` [FAIL] .autopilotrc.json not found`);
|
|
53
|
+
issues++;
|
|
54
|
+
} else {
|
|
55
|
+
try {
|
|
56
|
+
config = fs.readJsonSync(configPath);
|
|
57
|
+
const validation = validateConfig(config);
|
|
58
|
+
if (validation.valid) {
|
|
59
|
+
console.log(` [PASS] .autopilotrc.json found and valid`);
|
|
144
60
|
} else {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (result.valid) logger.success('Gemini API reachable and key looks valid.');
|
|
148
|
-
else {
|
|
149
|
-
logger.warn(`Gemini API check failed: ${result.error}`);
|
|
150
|
-
issues++;
|
|
151
|
-
}
|
|
61
|
+
console.log(` [FAIL] .autopilotrc.json has errors: ${validation.errors[0]}`);
|
|
62
|
+
issues++;
|
|
152
63
|
}
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.log(` [FAIL] .autopilotrc.json is not valid JSON`);
|
|
66
|
+
issues++;
|
|
153
67
|
}
|
|
154
|
-
} catch (error) {
|
|
155
|
-
// ignore AI check failures
|
|
156
68
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
69
|
+
|
|
70
|
+
// 5. AI API key present
|
|
71
|
+
const geminiKey = config?.aiApiKey || process.env.GEMINI_API_KEY;
|
|
72
|
+
const grokKey = config?.aiApiKey || process.env.GROK_API_KEY; // Actually need to know which one to check
|
|
73
|
+
const hasKey = (config?.aiProvider === 'gemini' && geminiKey) ||
|
|
74
|
+
(config?.aiProvider === 'grok' && grokKey) ||
|
|
75
|
+
(config?.aiProvider === 'none');
|
|
76
|
+
|
|
77
|
+
if (config) {
|
|
78
|
+
if (config.aiProvider === 'none') {
|
|
79
|
+
console.log(` [PASS] Rule-based commit messages enabled (No AI provider)`);
|
|
80
|
+
} else if (!hasKey) {
|
|
81
|
+
console.log(` [FAIL] AI API key missing for provider: ${config.aiProvider}`);
|
|
82
|
+
issues++;
|
|
83
|
+
} else {
|
|
84
|
+
console.log(` [PASS] AI API key present for provider: ${config.aiProvider}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 6. Remote origin reachable (5s timeout)
|
|
89
|
+
try {
|
|
90
|
+
await execa('git', ['ls-remote', 'origin'], { timeout: 5000 });
|
|
91
|
+
const { stdout: remoteUrl } = await execa('git', ['remote', 'get-url', 'origin']);
|
|
92
|
+
console.log(` [PASS] Remote origin reachable: ${remoteUrl.trim()}`);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.log(` [FAIL] Remote origin not reachable (check network or auth)`);
|
|
95
|
+
issues++;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 7. Current branch vs protected branches
|
|
99
|
+
try {
|
|
100
|
+
const branch = await git.getBranch(root);
|
|
101
|
+
if (branch && config) {
|
|
102
|
+
const isProtected = safety.isProtectedBranch(branch, config);
|
|
103
|
+
if (isProtected) {
|
|
104
|
+
console.log(` [FAIL] Current branch "${branch}" is protected — pushes will be blocked`);
|
|
105
|
+
issues++;
|
|
106
|
+
} else {
|
|
107
|
+
console.log(` [PASS] Current branch "${branch}" is not protected`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (err) {}
|
|
111
|
+
|
|
112
|
+
// 8. Retry queue status
|
|
113
|
+
const queuePath = path.join(root, '.autopilot-queue.json');
|
|
114
|
+
if (fs.existsSync(queuePath)) {
|
|
115
|
+
try {
|
|
116
|
+
const queue = fs.readJsonSync(queuePath);
|
|
117
|
+
console.log(` [INFO] Retry queue: ${queue.length} pending jobs`);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
console.log(` [WARN] Could not read .autopilot-queue.json`);
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
console.log(` [PASS] Retry queue: 0 pending jobs`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log(' ─────────────────────────────');
|
|
126
|
+
if (issues > 0) {
|
|
127
|
+
console.log(` ${issues} issues found. Run "autopilot init" to reconfigure.`);
|
|
128
|
+
} else {
|
|
129
|
+
console.log(' Everything looks good! Autopilot is ready to fly.');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = doctor;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const logger = require('../utils/logger');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
|
|
5
|
+
async function guide() {
|
|
6
|
+
const { default: chalk } = await import('chalk');
|
|
7
|
+
|
|
8
|
+
console.log('\n');
|
|
9
|
+
logger.section('🚀 Autopilot Intelligent Guide');
|
|
10
|
+
console.log(chalk.gray('Welcome! Let\'s get you up to speed with Autopilot CLI.\n'));
|
|
11
|
+
|
|
12
|
+
const steps = [
|
|
13
|
+
{
|
|
14
|
+
title: '1. Initialization',
|
|
15
|
+
desc: 'Set up your project with safety rails.',
|
|
16
|
+
cmd: 'autopilot init',
|
|
17
|
+
tip: 'This creates .autopilotrc.json and .autopilotignore.'
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
title: '2. The Watcher',
|
|
21
|
+
desc: 'Start the automation engine.',
|
|
22
|
+
cmd: 'autopilot start',
|
|
23
|
+
tip: 'Runs in the foreground. Press Ctrl+C to stop.'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
title: '3. Real-time Dashboard',
|
|
27
|
+
desc: 'See exactly what Autopilot is doing.',
|
|
28
|
+
cmd: 'autopilot dashboard',
|
|
29
|
+
tip: 'Run this in a separate terminal split for the best experience.'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
title: '4. Productivity Insights',
|
|
33
|
+
desc: 'Analyze your coding habits and quality.',
|
|
34
|
+
cmd: 'autopilot insights',
|
|
35
|
+
tip: 'Try "autopilot insights --export csv" for a detailed report.'
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
title: '5. Global Leaderboard',
|
|
39
|
+
desc: 'See where you rank among other developers.',
|
|
40
|
+
cmd: 'autopilot leaderboard --sync',
|
|
41
|
+
tip: 'Participation is opt-in and anonymized.'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
title: '6. Safety First: Undoing',
|
|
45
|
+
desc: 'Made a mistake? Revert it instantly.',
|
|
46
|
+
cmd: 'autopilot undo',
|
|
47
|
+
tip: 'Keeps your file changes but removes the git commit.'
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
for (const step of steps) {
|
|
52
|
+
console.log(chalk.bold.blue(`\n ${step.title}`));
|
|
53
|
+
console.log(` ${chalk.white(step.desc)}`);
|
|
54
|
+
console.log(` ${chalk.bgBlack.green(' $ ' + step.cmd)}`);
|
|
55
|
+
console.log(` ${chalk.italic.gray(' Tip: ' + step.tip)}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log('\n');
|
|
59
|
+
logger.info('Pro Tip: Run "autopilot doctor" if you encounter any environment issues.');
|
|
60
|
+
console.log('\n');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = guide;
|
package/src/commands/init.js
CHANGED
|
@@ -83,7 +83,7 @@ async function createConfigFile(repoPath, overrides = {}) {
|
|
|
83
83
|
*/
|
|
84
84
|
async function updateGitIgnore(repoPath) {
|
|
85
85
|
const gitIgnorePath = path.join(repoPath, '.gitignore');
|
|
86
|
-
const toIgnore = ['autopilot.log', '.autopilot.pid', '.vscode/'];
|
|
86
|
+
const toIgnore = ['.autopilot.log', '.autopilot.pid', '.autopilot-state.json', '.autopilot/', '.vscode/'];
|
|
87
87
|
let content = '';
|
|
88
88
|
|
|
89
89
|
try {
|
|
@@ -206,16 +206,15 @@ async function initRepo() {
|
|
|
206
206
|
|
|
207
207
|
|
|
208
208
|
const overrides = {
|
|
209
|
-
teamMode: useTeamMode,
|
|
210
209
|
ai: {
|
|
211
|
-
enabled:
|
|
212
|
-
provider: provider,
|
|
213
|
-
apiKey: apiKey,
|
|
214
|
-
grokApiKey: grokApiKey,
|
|
215
|
-
|
|
216
|
-
|
|
210
|
+
enabled: true,
|
|
211
|
+
provider: (customAI.toLowerCase() === 'y') ? provider : "grok",
|
|
212
|
+
apiKey: apiKey || "",
|
|
213
|
+
grokApiKey: grokApiKey || "",
|
|
214
|
+
interactive: interactive,
|
|
215
|
+
model: provider === 'grok' ? "grok-beta" : "gemini-1.5-flash"
|
|
217
216
|
},
|
|
218
|
-
|
|
217
|
+
teamMode: useTeamMode,
|
|
219
218
|
};
|
|
220
219
|
|
|
221
220
|
const created = await createConfigFile(repoPath, overrides);
|