@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/README.md +215 -202
  3. package/bin/autopilot.js +9 -2
  4. package/docs/CONFIGURATION.md +103 -103
  5. package/docs/DESIGN_PRINCIPLES.md +114 -114
  6. package/docs/TEAM-MODE.md +51 -51
  7. package/docs/TROUBLESHOOTING.md +21 -21
  8. package/package.json +75 -69
  9. package/src/commands/config.js +110 -110
  10. package/src/commands/dashboard.mjs +151 -151
  11. package/src/commands/doctor.js +127 -153
  12. package/src/commands/guide.js +63 -0
  13. package/src/commands/init.js +8 -9
  14. package/src/commands/insights.js +237 -237
  15. package/src/commands/leaderboard.js +116 -116
  16. package/src/commands/pause.js +18 -18
  17. package/src/commands/preset.js +121 -121
  18. package/src/commands/resume.js +17 -17
  19. package/src/commands/start.js +41 -41
  20. package/src/commands/status.js +73 -39
  21. package/src/commands/stop.js +58 -50
  22. package/src/commands/undo.js +84 -84
  23. package/src/config/defaults.js +23 -16
  24. package/src/config/ignore.js +14 -31
  25. package/src/config/loader.js +80 -80
  26. package/src/core/commit.js +45 -52
  27. package/src/core/commitMessageGenerator.js +130 -0
  28. package/src/core/configValidator.js +92 -0
  29. package/src/core/events.js +110 -110
  30. package/src/core/focus.js +2 -1
  31. package/src/core/gemini.js +15 -15
  32. package/src/core/git.js +29 -2
  33. package/src/core/history.js +69 -69
  34. package/src/core/notifier.js +61 -0
  35. package/src/core/retryQueue.js +152 -0
  36. package/src/core/safety.js +224 -210
  37. package/src/core/state.js +69 -71
  38. package/src/core/watcher.js +193 -66
  39. package/src/index.js +70 -70
  40. package/src/utils/banner.js +6 -6
  41. package/src/utils/crypto.js +18 -18
  42. package/src/utils/identity.js +41 -41
  43. package/src/utils/logger.js +86 -68
  44. package/src/utils/paths.js +62 -62
  45. package/src/utils/process.js +141 -141
@@ -1,159 +1,133 @@
1
- /**
2
- * Command: doctor
3
- * Diagnoses potential issues with the repository and environment
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
- // 7. AI Connectivity (if enabled)
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
- const { loadConfig } = require('../config/loader');
133
- const config = await loadConfig(repoPath);
134
- if (config?.ai?.enabled) {
135
- logger.section('AI Connectivity');
136
- if (config.ai.provider === 'grok') {
137
- const { validateGrokApiKey } = require('../core/grok');
138
- const result = await validateGrokApiKey(config.ai.grokApiKey);
139
- if (result.valid) logger.success('Grok API reachable and key looks valid.');
140
- else {
141
- logger.warn(`Grok API check failed: ${result.error}`);
142
- issues++;
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
- const { validateApiKey } = require('../core/gemini');
146
- const result = await validateApiKey(config.ai.apiKey);
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
- module.exports = doctor;
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;
@@ -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: useAI,
212
- provider: provider,
213
- apiKey: apiKey,
214
- grokApiKey: grokApiKey,
215
- model: provider === 'grok' ? 'grok-beta' : 'gemini-2.5-flash',
216
- interactive: interactive
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
- commitMessageMode: useAI ? 'ai' : 'smart'
217
+ teamMode: useTeamMode,
219
218
  };
220
219
 
221
220
  const created = await createConfigFile(repoPath, overrides);