@traisetech/autopilot 0.1.7 → 0.1.8
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 +92 -67
- package/README.md +233 -227
- package/bin/autopilot.js +72 -72
- package/package.json +59 -58
- package/src/commands/doctor.js +121 -121
- package/src/commands/init.js +183 -129
- package/src/commands/insights.js +90 -0
- package/src/config/defaults.js +42 -36
- package/src/config/ignore.js +153 -136
- package/src/core/commit.js +321 -308
- package/src/core/focus.js +197 -0
- package/src/core/gemini.js +109 -0
- package/src/core/git.js +180 -177
- package/src/core/watcher.js +362 -277
- package/src/index.js +6 -0
- package/src/integrations/base.js +23 -0
- package/src/integrations/calendar.js +23 -0
- package/src/integrations/manager.js +48 -0
- package/src/utils/update-check.js +151 -151
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Integration Class
|
|
3
|
+
*/
|
|
4
|
+
class Integration {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.config = config;
|
|
7
|
+
this.name = 'base';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async connect() {
|
|
11
|
+
throw new Error('Not implemented');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async onFocusStart(session) {
|
|
15
|
+
// Optional
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async onFocusEnd(session) {
|
|
19
|
+
// Optional
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = Integration;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calendar Integration (Mock)
|
|
3
|
+
*/
|
|
4
|
+
const Integration = require('./base');
|
|
5
|
+
const logger = require('../utils/logger');
|
|
6
|
+
|
|
7
|
+
class CalendarIntegration extends Integration {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
super(config);
|
|
10
|
+
this.name = 'calendar';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async onFocusStart(session) {
|
|
14
|
+
logger.info('📅 Blocking calendar for focus time...');
|
|
15
|
+
// Implementation would go here (e.g. Google Calendar API)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async onFocusEnd(session) {
|
|
19
|
+
logger.info('📅 Releasing calendar block.');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = CalendarIntegration;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Manager
|
|
3
|
+
* Handles communication with external tools
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const logger = require('../utils/logger');
|
|
7
|
+
|
|
8
|
+
class IntegrationManager {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config || {};
|
|
11
|
+
this.integrations = [];
|
|
12
|
+
this.active = false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
register(integration) {
|
|
16
|
+
this.integrations.push(integration);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async notifyFocusStart(file) {
|
|
20
|
+
if (!this.config.integrationsEnabled) return;
|
|
21
|
+
|
|
22
|
+
logger.debug('[Integrations] Notifying focus start...');
|
|
23
|
+
this.active = true;
|
|
24
|
+
|
|
25
|
+
await Promise.all(this.integrations.map(async (integration) => {
|
|
26
|
+
try {
|
|
27
|
+
await integration.onFocusStart({ file, startTime: Date.now() });
|
|
28
|
+
} catch (err) {
|
|
29
|
+
logger.warn(`Integration ${integration.name} failed: ${err.message}`);
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async notifyFocusEnd() {
|
|
35
|
+
if (!this.active) return;
|
|
36
|
+
this.active = false;
|
|
37
|
+
|
|
38
|
+
await Promise.all(this.integrations.map(async (integration) => {
|
|
39
|
+
try {
|
|
40
|
+
await integration.onFocusEnd({ endTime: Date.now() });
|
|
41
|
+
} catch (err) {
|
|
42
|
+
logger.warn(`Integration ${integration.name} failed: ${err.message}`);
|
|
43
|
+
}
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = IntegrationManager;
|
|
@@ -1,151 +1,151 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Update checker utility
|
|
3
|
-
* Checks for new versions on npm registry
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const https = require('https');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const fs = require('fs-extra');
|
|
9
|
-
const { getConfigDir, ensureConfigDir } = require('./paths');
|
|
10
|
-
const pkg = require('../../package.json');
|
|
11
|
-
|
|
12
|
-
const CHECK_INTERVAL = 1000 * 60 * 60 * 24; // 24 hours
|
|
13
|
-
const REGISTRY_URL = 'https://registry.npmjs.org';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Fetch latest version from npm registry
|
|
17
|
-
* @param {string} packageName
|
|
18
|
-
* @returns {Promise<string>}
|
|
19
|
-
*/
|
|
20
|
-
async function getLatestVersion(packageName) {
|
|
21
|
-
return new Promise((resolve, reject) => {
|
|
22
|
-
const url = `${REGISTRY_URL}/${packageName}/latest`;
|
|
23
|
-
|
|
24
|
-
const req = https.get(url, {
|
|
25
|
-
timeout: 1500, // Short timeout to avoid hanging
|
|
26
|
-
headers: {
|
|
27
|
-
'User-Agent': `autopilot-cli/${pkg.version}`
|
|
28
|
-
}
|
|
29
|
-
}, (res) => {
|
|
30
|
-
if (res.statusCode !== 200) {
|
|
31
|
-
res.resume();
|
|
32
|
-
return reject(new Error(`Status code: ${res.statusCode}`));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let data = '';
|
|
36
|
-
res.on('data', (chunk) => data += chunk);
|
|
37
|
-
res.on('end', () => {
|
|
38
|
-
try {
|
|
39
|
-
const json = JSON.parse(data);
|
|
40
|
-
resolve(json.version);
|
|
41
|
-
} catch (e) {
|
|
42
|
-
reject(e);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
req.on('error', reject);
|
|
48
|
-
req.on('timeout', () => {
|
|
49
|
-
req.destroy();
|
|
50
|
-
reject(new Error('Timeout'));
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Compare two semver strings
|
|
57
|
-
* @param {string} latest
|
|
58
|
-
* @param {string} current
|
|
59
|
-
* @returns {boolean} true if latest > current
|
|
60
|
-
*/
|
|
61
|
-
function isNewer(latest, current) {
|
|
62
|
-
if (!latest || !current) return false;
|
|
63
|
-
|
|
64
|
-
const l = latest.split(/[\.-]/).map(n => parseInt(n, 10) || 0);
|
|
65
|
-
const c = current.split(/[\.-]/).map(n => parseInt(n, 10) || 0);
|
|
66
|
-
|
|
67
|
-
// Compare major, minor, patch
|
|
68
|
-
for (let i = 0; i < 3; i++) {
|
|
69
|
-
if (l[i] > c[i]) return true;
|
|
70
|
-
if (l[i] < c[i]) return false;
|
|
71
|
-
}
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function center(text, width) {
|
|
76
|
-
const visibleLen = text.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
77
|
-
const padding = Math.max(0, width - visibleLen);
|
|
78
|
-
const left = Math.floor(padding / 2);
|
|
79
|
-
const right = padding - left;
|
|
80
|
-
return ' '.repeat(left) + text + ' '.repeat(right);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Check for updates and notify user
|
|
85
|
-
*/
|
|
86
|
-
async function checkForUpdate() {
|
|
87
|
-
try {
|
|
88
|
-
await ensureConfigDir();
|
|
89
|
-
const configDir = getConfigDir();
|
|
90
|
-
const cachePath = path.join(configDir, 'update-check.json');
|
|
91
|
-
|
|
92
|
-
let cache = { lastCheck: 0, latestVersion: null, hasUpdate: false };
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
if (await fs.pathExists(cachePath)) {
|
|
96
|
-
cache = await fs.readJson(cachePath);
|
|
97
|
-
}
|
|
98
|
-
} catch (e) {
|
|
99
|
-
// Ignore cache read errors
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const now = Date.now();
|
|
103
|
-
const shouldCheck = !cache.lastCheck || (now - cache.lastCheck > CHECK_INTERVAL);
|
|
104
|
-
|
|
105
|
-
if (shouldCheck) {
|
|
106
|
-
try {
|
|
107
|
-
const latestVersion = await getLatestVersion(pkg.name);
|
|
108
|
-
const hasUpdate = isNewer(latestVersion, pkg.version);
|
|
109
|
-
|
|
110
|
-
cache = {
|
|
111
|
-
lastCheck: now,
|
|
112
|
-
latestVersion,
|
|
113
|
-
hasUpdate
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// Save cache without awaiting to not block if FS is slow?
|
|
117
|
-
// Better to await to ensure it saves.
|
|
118
|
-
await fs.writeJson(cachePath, cache);
|
|
119
|
-
} catch (e) {
|
|
120
|
-
// Failed to check, maybe offline.
|
|
121
|
-
// Just update lastCheck to avoid retrying immediately on next run?
|
|
122
|
-
// Or leave it to retry next time.
|
|
123
|
-
// Let's leave it so we retry next run.
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (cache.hasUpdate && cache.latestVersion) {
|
|
128
|
-
const v = pkg.version;
|
|
129
|
-
const latest = cache.latestVersion;
|
|
130
|
-
|
|
131
|
-
// Boxed notification
|
|
132
|
-
console.log('\n');
|
|
133
|
-
console.log(' â•──────────────────────────────────────────────────╮');
|
|
134
|
-
console.log(' │ │');
|
|
135
|
-
console.log(` │ Update available ${v.dim()} → ${latest.green()} │`);
|
|
136
|
-
console.log(` │ Run ${('npm i -g ' + pkg.name).cyan()} to update │`);
|
|
137
|
-
console.log(' │ │');
|
|
138
|
-
console.log(' ╰──────────────────────────────────────────────────╯');
|
|
139
|
-
console.log('\n');
|
|
140
|
-
}
|
|
141
|
-
} catch (error) {
|
|
142
|
-
// Fail silently
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Add simple color support since we don't have chalk
|
|
147
|
-
String.prototype.green = function() { return `\x1b[32m${this}\x1b[0m`; };
|
|
148
|
-
String.prototype.cyan = function() { return `\x1b[36m${this}\x1b[0m`; };
|
|
149
|
-
String.prototype.dim = function() { return `\x1b[2m${this}\x1b[0m`; };
|
|
150
|
-
|
|
151
|
-
module.exports = { checkForUpdate };
|
|
1
|
+
/**
|
|
2
|
+
* Update checker utility
|
|
3
|
+
* Checks for new versions on npm registry
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs-extra');
|
|
9
|
+
const { getConfigDir, ensureConfigDir } = require('./paths');
|
|
10
|
+
const pkg = require('../../package.json');
|
|
11
|
+
|
|
12
|
+
const CHECK_INTERVAL = 1000 * 60 * 60 * 24; // 24 hours
|
|
13
|
+
const REGISTRY_URL = 'https://registry.npmjs.org';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Fetch latest version from npm registry
|
|
17
|
+
* @param {string} packageName
|
|
18
|
+
* @returns {Promise<string>}
|
|
19
|
+
*/
|
|
20
|
+
async function getLatestVersion(packageName) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const url = `${REGISTRY_URL}/${packageName}/latest`;
|
|
23
|
+
|
|
24
|
+
const req = https.get(url, {
|
|
25
|
+
timeout: 1500, // Short timeout to avoid hanging
|
|
26
|
+
headers: {
|
|
27
|
+
'User-Agent': `autopilot-cli/${pkg.version}`
|
|
28
|
+
}
|
|
29
|
+
}, (res) => {
|
|
30
|
+
if (res.statusCode !== 200) {
|
|
31
|
+
res.resume();
|
|
32
|
+
return reject(new Error(`Status code: ${res.statusCode}`));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let data = '';
|
|
36
|
+
res.on('data', (chunk) => data += chunk);
|
|
37
|
+
res.on('end', () => {
|
|
38
|
+
try {
|
|
39
|
+
const json = JSON.parse(data);
|
|
40
|
+
resolve(json.version);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
reject(e);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
req.on('error', reject);
|
|
48
|
+
req.on('timeout', () => {
|
|
49
|
+
req.destroy();
|
|
50
|
+
reject(new Error('Timeout'));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Compare two semver strings
|
|
57
|
+
* @param {string} latest
|
|
58
|
+
* @param {string} current
|
|
59
|
+
* @returns {boolean} true if latest > current
|
|
60
|
+
*/
|
|
61
|
+
function isNewer(latest, current) {
|
|
62
|
+
if (!latest || !current) return false;
|
|
63
|
+
|
|
64
|
+
const l = latest.split(/[\.-]/).map(n => parseInt(n, 10) || 0);
|
|
65
|
+
const c = current.split(/[\.-]/).map(n => parseInt(n, 10) || 0);
|
|
66
|
+
|
|
67
|
+
// Compare major, minor, patch
|
|
68
|
+
for (let i = 0; i < 3; i++) {
|
|
69
|
+
if (l[i] > c[i]) return true;
|
|
70
|
+
if (l[i] < c[i]) return false;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function center(text, width) {
|
|
76
|
+
const visibleLen = text.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
77
|
+
const padding = Math.max(0, width - visibleLen);
|
|
78
|
+
const left = Math.floor(padding / 2);
|
|
79
|
+
const right = padding - left;
|
|
80
|
+
return ' '.repeat(left) + text + ' '.repeat(right);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check for updates and notify user
|
|
85
|
+
*/
|
|
86
|
+
async function checkForUpdate() {
|
|
87
|
+
try {
|
|
88
|
+
await ensureConfigDir();
|
|
89
|
+
const configDir = getConfigDir();
|
|
90
|
+
const cachePath = path.join(configDir, 'update-check.json');
|
|
91
|
+
|
|
92
|
+
let cache = { lastCheck: 0, latestVersion: null, hasUpdate: false };
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
if (await fs.pathExists(cachePath)) {
|
|
96
|
+
cache = await fs.readJson(cachePath);
|
|
97
|
+
}
|
|
98
|
+
} catch (e) {
|
|
99
|
+
// Ignore cache read errors
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
const shouldCheck = !cache.lastCheck || (now - cache.lastCheck > CHECK_INTERVAL);
|
|
104
|
+
|
|
105
|
+
if (shouldCheck) {
|
|
106
|
+
try {
|
|
107
|
+
const latestVersion = await getLatestVersion(pkg.name);
|
|
108
|
+
const hasUpdate = isNewer(latestVersion, pkg.version);
|
|
109
|
+
|
|
110
|
+
cache = {
|
|
111
|
+
lastCheck: now,
|
|
112
|
+
latestVersion,
|
|
113
|
+
hasUpdate
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Save cache without awaiting to not block if FS is slow?
|
|
117
|
+
// Better to await to ensure it saves.
|
|
118
|
+
await fs.writeJson(cachePath, cache);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
// Failed to check, maybe offline.
|
|
121
|
+
// Just update lastCheck to avoid retrying immediately on next run?
|
|
122
|
+
// Or leave it to retry next time.
|
|
123
|
+
// Let's leave it so we retry next run.
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (cache.hasUpdate && cache.latestVersion) {
|
|
128
|
+
const v = pkg.version;
|
|
129
|
+
const latest = cache.latestVersion;
|
|
130
|
+
|
|
131
|
+
// Boxed notification
|
|
132
|
+
console.log('\n');
|
|
133
|
+
console.log(' â•──────────────────────────────────────────────────╮');
|
|
134
|
+
console.log(' │ │');
|
|
135
|
+
console.log(` │ Update available ${v.dim()} → ${latest.green()} │`);
|
|
136
|
+
console.log(` │ Run ${('npm i -g ' + pkg.name).cyan()} to update │`);
|
|
137
|
+
console.log(' │ │');
|
|
138
|
+
console.log(' ╰──────────────────────────────────────────────────╯');
|
|
139
|
+
console.log('\n');
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
// Fail silently
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Add simple color support since we don't have chalk
|
|
147
|
+
String.prototype.green = function() { return `\x1b[32m${this}\x1b[0m`; };
|
|
148
|
+
String.prototype.cyan = function() { return `\x1b[36m${this}\x1b[0m`; };
|
|
149
|
+
String.prototype.dim = function() { return `\x1b[2m${this}\x1b[0m`; };
|
|
150
|
+
|
|
151
|
+
module.exports = { checkForUpdate };
|