maiass 5.8.0 → 5.8.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/lib/account-info.js +7 -21
- package/lib/bootstrap.js +564 -0
- package/lib/client-info.js +60 -0
- package/lib/commit.js +50 -15
- package/lib/logger.js +1 -1
- package/lib/maiass-pipeline.js +15 -9
- package/lib/secure-storage.js +19 -20
- package/maiass.mjs +16 -0
- package/package.json +1 -1
package/lib/account-info.js
CHANGED
|
@@ -6,6 +6,7 @@ import { SYMBOLS } from './symbols.js';
|
|
|
6
6
|
import { loadEnvironmentConfig } from './config.js';
|
|
7
7
|
import { generateMachineFingerprint } from './machine-fingerprint.js';
|
|
8
8
|
import { retrieveSecureVariable, storeSecureVariable, removeSecureVariable } from './secure-storage.js';
|
|
9
|
+
import { getClientName, getClientVersion } from './client-info.js';
|
|
9
10
|
import { getSingleCharInput } from './input-utils.js';
|
|
10
11
|
import fs from 'fs';
|
|
11
12
|
import path from 'path';
|
|
@@ -25,23 +26,6 @@ function maskToken(token) {
|
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
/**
|
|
29
|
-
* Get client version from package.json
|
|
30
|
-
* @returns {string} Version string
|
|
31
|
-
*/
|
|
32
|
-
function getClientVersion() {
|
|
33
|
-
try {
|
|
34
|
-
const packagePath = path.resolve(process.cwd(), 'package.json');
|
|
35
|
-
if (fs.existsSync(packagePath)) {
|
|
36
|
-
const packageData = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
37
|
-
return packageData.version || '0.0.0';
|
|
38
|
-
}
|
|
39
|
-
} catch (error) {
|
|
40
|
-
// Ignore errors, fallback to default
|
|
41
|
-
}
|
|
42
|
-
return '0.0.0';
|
|
43
|
-
}
|
|
44
|
-
|
|
45
29
|
/**
|
|
46
30
|
* Create anonymous subscription if needed
|
|
47
31
|
* @returns {Promise<string|null>} API token or null
|
|
@@ -50,21 +34,23 @@ async function createAnonymousSubscriptionIfNeeded() {
|
|
|
50
34
|
const debugMode = process.env.MAIASS_DEBUG === 'true';
|
|
51
35
|
|
|
52
36
|
try {
|
|
53
|
-
log.info(SYMBOLS.INFO, '
|
|
54
|
-
|
|
37
|
+
log.info(SYMBOLS.INFO, 'Generating machine fingerprint...');
|
|
55
38
|
const machineFingerprint = generateMachineFingerprint();
|
|
39
|
+
|
|
56
40
|
const endpoint = process.env.MAIASS_AI_HOST || 'https://pound.maiass.net';
|
|
57
41
|
|
|
58
42
|
if (debugMode) {
|
|
59
43
|
log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Creating anonymous subscription at: ${endpoint}/v1/token`);
|
|
60
44
|
}
|
|
61
45
|
|
|
46
|
+
log.info(SYMBOLS.INFO, 'Requesting anonymous subscription...');
|
|
47
|
+
|
|
62
48
|
const response = await fetch(`${endpoint}/v1/token`, {
|
|
63
49
|
method: 'POST',
|
|
64
50
|
headers: {
|
|
65
51
|
'Content-Type': 'application/json',
|
|
66
|
-
'X-Client-Name':
|
|
67
|
-
'X-Client-Version':
|
|
52
|
+
'X-Client-Name': getClientName(),
|
|
53
|
+
'X-Client-Version': getClientVersion()
|
|
68
54
|
},
|
|
69
55
|
body: JSON.stringify({
|
|
70
56
|
machine_fingerprint: machineFingerprint
|
package/lib/bootstrap.js
ADDED
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
// MAIASS Bootstrap Module
|
|
2
|
+
// Interactive project setup and configuration
|
|
3
|
+
// Mirrors bashmaiass lib/core/bootstrap.sh
|
|
4
|
+
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { getSingleCharInput, getLineInput } from './input-utils.js';
|
|
8
|
+
import { log, logger } from './logger.js';
|
|
9
|
+
import { SYMBOLS } from './symbols.js';
|
|
10
|
+
import colors from './colors.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Check if bootstrap is needed
|
|
14
|
+
* @returns {boolean} True if .env.maiass doesn't exist or force bootstrap is set
|
|
15
|
+
*/
|
|
16
|
+
export function needsBootstrap() {
|
|
17
|
+
const forceBootstrap = process.env.MAIASS_FORCE_BOOTSTRAP === 'true';
|
|
18
|
+
const envFileExists = fs.existsSync('.env.maiass');
|
|
19
|
+
|
|
20
|
+
return !envFileExists || forceBootstrap;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Load existing .env.maiass values to use as defaults
|
|
25
|
+
* @returns {Object} Existing configuration values
|
|
26
|
+
*/
|
|
27
|
+
function loadExistingValues() {
|
|
28
|
+
const existing = {};
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync('.env.maiass')) {
|
|
31
|
+
return existing;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const content = fs.readFileSync('.env.maiass', 'utf8');
|
|
36
|
+
const lines = content.split('\n');
|
|
37
|
+
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
const trimmed = line.trim();
|
|
40
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
41
|
+
|
|
42
|
+
const match = trimmed.match(/^([A-Z_]+)=(.*)$/);
|
|
43
|
+
if (match) {
|
|
44
|
+
const [, key, value] = match;
|
|
45
|
+
// Remove quotes if present
|
|
46
|
+
existing[key] = value.replace(/^["']|["']$/g, '');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
logger.debug('Could not load existing .env.maiass:', error.message);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return existing;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Detect project type based on files present
|
|
58
|
+
* @returns {string} Detected project type
|
|
59
|
+
*/
|
|
60
|
+
function detectProjectType() {
|
|
61
|
+
// Check for WordPress
|
|
62
|
+
if (fs.existsSync('wp-config.php') || fs.existsSync('wp-content')) {
|
|
63
|
+
if (fs.existsSync('style.css')) {
|
|
64
|
+
const styleContent = fs.readFileSync('style.css', 'utf8');
|
|
65
|
+
if (styleContent.includes('Theme Name:')) {
|
|
66
|
+
return 'wordpress-theme';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Check for plugin
|
|
70
|
+
const files = fs.readdirSync('.');
|
|
71
|
+
for (const file of files) {
|
|
72
|
+
if (file.endsWith('.php')) {
|
|
73
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
74
|
+
if (content.includes('Plugin Name:')) {
|
|
75
|
+
return 'wordpress-plugin';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return 'wordpress-site';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check for Craft CMS
|
|
83
|
+
if (fs.existsSync('craft') || fs.existsSync('config/general.php')) {
|
|
84
|
+
return 'craft';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Default to bespoke
|
|
88
|
+
return 'bespoke';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Detect version source file
|
|
93
|
+
* @returns {string} Detected version file
|
|
94
|
+
*/
|
|
95
|
+
function detectVersionSource() {
|
|
96
|
+
if (fs.existsSync('package.json')) {
|
|
97
|
+
return 'package.json';
|
|
98
|
+
}
|
|
99
|
+
if (fs.existsSync('composer.json')) {
|
|
100
|
+
return 'composer.json';
|
|
101
|
+
}
|
|
102
|
+
if (fs.existsSync('VERSION')) {
|
|
103
|
+
return 'VERSION';
|
|
104
|
+
}
|
|
105
|
+
return 'package.json'; // Default
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Main bootstrap function
|
|
110
|
+
* @returns {Promise<boolean>} True if bootstrap completed, false if skipped
|
|
111
|
+
*/
|
|
112
|
+
export async function bootstrapProject() {
|
|
113
|
+
const forceBootstrap = process.env.MAIASS_FORCE_BOOTSTRAP === 'true';
|
|
114
|
+
const envFileExists = fs.existsSync('.env.maiass');
|
|
115
|
+
|
|
116
|
+
if (envFileExists && !forceBootstrap) {
|
|
117
|
+
logger.debug('Project already configured (.env.maiass exists)');
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const existing = loadExistingValues();
|
|
122
|
+
const isReconfigure = envFileExists && forceBootstrap;
|
|
123
|
+
|
|
124
|
+
console.log('');
|
|
125
|
+
console.log(colors.BCyan('════════════════════════════════════════════════════════════'));
|
|
126
|
+
if (isReconfigure) {
|
|
127
|
+
console.log(colors.BCyan('🔧 Reconfiguring MAIASS for this project'));
|
|
128
|
+
console.log(colors.Gray('Current values will be shown as defaults.'));
|
|
129
|
+
} else {
|
|
130
|
+
console.log(colors.BCyan('🚀 Setting up MAIASS for this project'));
|
|
131
|
+
console.log(colors.Gray('This is a one-time setup that will make future runs smoother.'));
|
|
132
|
+
}
|
|
133
|
+
console.log(colors.BCyan('════════════════════════════════════════════════════════════'));
|
|
134
|
+
console.log('');
|
|
135
|
+
|
|
136
|
+
const config = {};
|
|
137
|
+
|
|
138
|
+
// Step 1: Configuration file setup
|
|
139
|
+
await setupEnvFile(existing);
|
|
140
|
+
|
|
141
|
+
// Step 2: Project type detection
|
|
142
|
+
config.projectType = await configureProjectType(existing);
|
|
143
|
+
|
|
144
|
+
// Step 3: Version source
|
|
145
|
+
config.versionSource = await configureVersionSource(existing);
|
|
146
|
+
|
|
147
|
+
// Step 4: Features selection
|
|
148
|
+
config.features = await chooseFeatures(existing);
|
|
149
|
+
|
|
150
|
+
// Step 5: Changelog configuration (if full features)
|
|
151
|
+
if (config.features === 'full') {
|
|
152
|
+
config.changelog = await configureChangelog(existing);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Step 6: Branch strategy
|
|
156
|
+
config.branches = await configureBranches(existing);
|
|
157
|
+
|
|
158
|
+
// Step 7: Save configuration
|
|
159
|
+
await saveConfiguration(config);
|
|
160
|
+
|
|
161
|
+
console.log('');
|
|
162
|
+
log.success(SYMBOLS.CHECKMARK, 'Project setup complete!');
|
|
163
|
+
log.info(SYMBOLS.INFO, 'You can modify these settings anytime by editing .env.maiass');
|
|
164
|
+
log.info(SYMBOLS.INFO, "Run 'maiass' again to start using your configured workflow.");
|
|
165
|
+
console.log('');
|
|
166
|
+
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Step 1: Setup environment file
|
|
172
|
+
*/
|
|
173
|
+
async function setupEnvFile(existing) {
|
|
174
|
+
console.log(colors.BCyan('📄 Configuration File Setup'));
|
|
175
|
+
console.log('');
|
|
176
|
+
|
|
177
|
+
if (process.env.MAIASS_FORCE_BOOTSTRAP === 'true') {
|
|
178
|
+
console.log(`${SYMBOLS.INFO} Updating existing .env.maiass file with new configuration.`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
console.log(`${SYMBOLS.INFO} MAIASS uses configuration files to store project settings:`);
|
|
183
|
+
console.log(' • .env.maiass - Team/repo config (tracked in git)');
|
|
184
|
+
console.log(' • .env.maiass.local - Personal settings (gitignored)');
|
|
185
|
+
console.log('');
|
|
186
|
+
|
|
187
|
+
const create = await getSingleCharInput('Create configuration files? [Y/n]: ');
|
|
188
|
+
if (create === 'n') {
|
|
189
|
+
console.log(`${SYMBOLS.WARNING} Setup cancelled. Run with --setup to try again.`);
|
|
190
|
+
process.exit(0);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Step 2: Configure project type
|
|
196
|
+
*/
|
|
197
|
+
async function configureProjectType(existing) {
|
|
198
|
+
console.log('');
|
|
199
|
+
console.log(colors.BCyan('🔍 Project Type Detection'));
|
|
200
|
+
console.log('');
|
|
201
|
+
|
|
202
|
+
const detected = detectProjectType();
|
|
203
|
+
const current = existing.MAIASS_REPO_TYPE || detected;
|
|
204
|
+
|
|
205
|
+
console.log('MAIASS can optimize its workflow based on your project type.');
|
|
206
|
+
console.log('This helps determine version file locations and update strategies.');
|
|
207
|
+
console.log('');
|
|
208
|
+
|
|
209
|
+
console.log(`${SYMBOLS.INFO} Detected project type: ${colors.BGreen(detected)}`);
|
|
210
|
+
if (existing.MAIASS_REPO_TYPE) {
|
|
211
|
+
console.log(`${SYMBOLS.INFO} Current setting: ${colors.BGreen(existing.MAIASS_REPO_TYPE)}`);
|
|
212
|
+
}
|
|
213
|
+
console.log('');
|
|
214
|
+
|
|
215
|
+
console.log(colors.BWhite('Available project types:'));
|
|
216
|
+
console.log(` ${colors.BCyan('1)')} bespoke - Standard Node.js/web project (default)`);
|
|
217
|
+
console.log(` ${colors.BCyan('2)')} wordpress-theme - WordPress theme with style.css versioning`);
|
|
218
|
+
console.log(` ${colors.BCyan('3)')} wordpress-plugin - WordPress plugin with main PHP file versioning`);
|
|
219
|
+
console.log(` ${colors.BCyan('4)')} wordpress-site - Full WordPress installation`);
|
|
220
|
+
console.log(` ${colors.BCyan('5)')} craft - Craft CMS project`);
|
|
221
|
+
console.log('');
|
|
222
|
+
|
|
223
|
+
const defaultChoice = ['bespoke', 'wordpress-theme', 'wordpress-plugin', 'wordpress-site', 'craft'].indexOf(current) + 1;
|
|
224
|
+
const choice = await getLineInput(`Select project type [1-5, Enter for ${defaultChoice}=${current}]: `);
|
|
225
|
+
|
|
226
|
+
if (!choice) return current;
|
|
227
|
+
|
|
228
|
+
const types = ['bespoke', 'wordpress-theme', 'wordpress-plugin', 'wordpress-site', 'craft'];
|
|
229
|
+
const index = parseInt(choice) - 1;
|
|
230
|
+
|
|
231
|
+
if (index >= 0 && index < types.length) {
|
|
232
|
+
log.success(SYMBOLS.CHECKMARK, `Project type set to: ${colors.BGreen(types[index])}`);
|
|
233
|
+
return types[index];
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
log.warning(SYMBOLS.WARNING, `Invalid choice, using: ${current}`);
|
|
237
|
+
return current;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Step 3: Configure version source
|
|
242
|
+
*/
|
|
243
|
+
async function configureVersionSource(existing) {
|
|
244
|
+
console.log('');
|
|
245
|
+
console.log(colors.BCyan('📦 Version Source Configuration'));
|
|
246
|
+
console.log('');
|
|
247
|
+
|
|
248
|
+
const detected = detectVersionSource();
|
|
249
|
+
const current = existing.MAIASS_VERSION_PRIMARY_FILE || detected;
|
|
250
|
+
|
|
251
|
+
console.log('MAIASS needs to know where your project version is stored.');
|
|
252
|
+
console.log('This file will be updated automatically when you bump versions.');
|
|
253
|
+
console.log('');
|
|
254
|
+
|
|
255
|
+
console.log(`${SYMBOLS.INFO} Detected version file: ${colors.BGreen(detected)}`);
|
|
256
|
+
if (existing.MAIASS_VERSION_PRIMARY_FILE) {
|
|
257
|
+
console.log(`${SYMBOLS.INFO} Current setting: ${colors.BGreen(existing.MAIASS_VERSION_PRIMARY_FILE)}`);
|
|
258
|
+
}
|
|
259
|
+
console.log('');
|
|
260
|
+
|
|
261
|
+
console.log(colors.Gray('Common options: package.json, composer.json, VERSION, style.css'));
|
|
262
|
+
const file = await getLineInput(`Version source file [Enter for ${current}]: `);
|
|
263
|
+
|
|
264
|
+
const result = file || current;
|
|
265
|
+
if (file) {
|
|
266
|
+
console.log(`${SYMBOLS.CHECKMARK} Version source set to: ${colors.BGreen(result)}`);
|
|
267
|
+
}
|
|
268
|
+
return result;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Step 4: Choose MAIASS features
|
|
273
|
+
*/
|
|
274
|
+
async function chooseFeatures(existing) {
|
|
275
|
+
console.log('');
|
|
276
|
+
console.log(colors.BCyan('⚙️ Feature Selection'));
|
|
277
|
+
console.log('');
|
|
278
|
+
|
|
279
|
+
const currentMode = existing.MAIASS_MODE || 'full';
|
|
280
|
+
const current = currentMode === 'ai_only' ? 'ai_only' : 'full';
|
|
281
|
+
|
|
282
|
+
console.log('Choose which MAIASS features you want to use:');
|
|
283
|
+
console.log('');
|
|
284
|
+
console.log(colors.BWhite('Available modes:'));
|
|
285
|
+
console.log(` ${colors.BCyan('1)')} full - Complete workflow ${colors.Gray('(recommended)')}`);
|
|
286
|
+
console.log(` • AI-powered commit messages`);
|
|
287
|
+
console.log(` • Automatic version bumping`);
|
|
288
|
+
console.log(` • Changelog generation`);
|
|
289
|
+
console.log(` • Branch management`);
|
|
290
|
+
console.log('');
|
|
291
|
+
console.log(` ${colors.BCyan('2)')} ai_only - AI commit messages only`);
|
|
292
|
+
console.log(` • AI-powered commit messages`);
|
|
293
|
+
console.log(` • No version management`);
|
|
294
|
+
console.log(` • No changelog updates`);
|
|
295
|
+
console.log('');
|
|
296
|
+
|
|
297
|
+
if (existing.MAIASS_MODE) {
|
|
298
|
+
console.log(`${SYMBOLS.INFO} Current mode: ${colors.BGreen(current)}`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const defaultChoice = current === 'full' ? '1' : '2';
|
|
302
|
+
const choice = await getLineInput(`Select mode [1-2, Enter for ${defaultChoice}]: `);
|
|
303
|
+
|
|
304
|
+
let result;
|
|
305
|
+
if (choice === '2') result = 'ai_only';
|
|
306
|
+
else if (choice === '1' || !choice) result = 'full';
|
|
307
|
+
else result = current;
|
|
308
|
+
|
|
309
|
+
console.log(`${SYMBOLS.CHECKMARK} Mode set to: ${colors.BGreen(result)}`);
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Step 5: Configure changelog
|
|
315
|
+
*/
|
|
316
|
+
async function configureChangelog(existing) {
|
|
317
|
+
console.log('');
|
|
318
|
+
console.log(colors.BCyan('🧾 Changelog Configuration'));
|
|
319
|
+
console.log('');
|
|
320
|
+
|
|
321
|
+
const config = {
|
|
322
|
+
path: existing.MAIASS_CHANGELOG_PATH || '.',
|
|
323
|
+
name: existing.MAIASS_CHANGELOG_NAME || 'CHANGELOG.md',
|
|
324
|
+
internalEnabled: existing.MAIASS_CHANGELOG_INTERNAL_ENABLED !== 'false',
|
|
325
|
+
internalPath: existing.MAIASS_CHANGELOG_INTERNAL_PATH || '.',
|
|
326
|
+
internalName: existing.MAIASS_CHANGELOG_INTERNAL_NAME || '.CHANGELOG_internal.md'
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
console.log('MAIASS can maintain two changelogs:');
|
|
330
|
+
console.log(` • ${colors.BWhite('Public changelog')} - CHANGELOG.md (for users/clients)`);
|
|
331
|
+
console.log(` • ${colors.BWhite('Internal changelog')} - .CHANGELOG_internal.md (for team)`);
|
|
332
|
+
console.log('');
|
|
333
|
+
console.log(colors.Gray('Internal changelog includes JIRA tickets, commit authors, and detailed notes.'));
|
|
334
|
+
console.log(colors.Gray('It should be excluded from production deployments.'));
|
|
335
|
+
console.log('');
|
|
336
|
+
|
|
337
|
+
if (existing.MAIASS_CHANGELOG_INTERNAL_ENABLED) {
|
|
338
|
+
console.log(`${SYMBOLS.INFO} Current setting: ${config.internalEnabled ? colors.BGreen('enabled') : colors.BRed('disabled')}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Ask about internal changelog
|
|
342
|
+
const enableInternal = await getSingleCharInput(
|
|
343
|
+
`Enable internal changelog? [${config.internalEnabled ? 'Y/n' : 'y/N'}]: `
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
if (enableInternal === 'y' || (enableInternal === '' && config.internalEnabled)) {
|
|
347
|
+
config.internalEnabled = true;
|
|
348
|
+
console.log(`${SYMBOLS.CHECKMARK} Internal changelog enabled`);
|
|
349
|
+
} else if (enableInternal === 'n' || (enableInternal === '' && !config.internalEnabled)) {
|
|
350
|
+
config.internalEnabled = false;
|
|
351
|
+
console.log(`${SYMBOLS.INFO} Internal changelog disabled`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return config;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Step 6: Configure branch strategy
|
|
359
|
+
*/
|
|
360
|
+
async function configureBranches(existing) {
|
|
361
|
+
console.log('');
|
|
362
|
+
console.log(colors.BCyan('🌿 Branch Strategy Configuration'));
|
|
363
|
+
console.log('');
|
|
364
|
+
|
|
365
|
+
const config = {
|
|
366
|
+
main: existing.MAIASS_MAINBRANCH || 'main',
|
|
367
|
+
develop: existing.MAIASS_DEVELOPBRANCH || 'develop',
|
|
368
|
+
staging: existing.MAIASS_STAGINGBRANCH || 'staging'
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
console.log('MAIASS uses a Git Flow-inspired branching strategy:');
|
|
372
|
+
console.log('');
|
|
373
|
+
console.log(` ${colors.BWhite('Development Branch')} (${colors.BCyan('develop')}) - ${colors.BYellow('★ TRUNK BRANCH')}`);
|
|
374
|
+
console.log(` • This is where MAIASS performs version bumps and changelog updates`);
|
|
375
|
+
console.log(` • Must allow direct commits (no PR requirement)`);
|
|
376
|
+
console.log(` • All feature work merges here first`);
|
|
377
|
+
console.log('');
|
|
378
|
+
console.log(` ${colors.BWhite('Main/Production Branch')} (${colors.BCyan('main/master')})`);
|
|
379
|
+
console.log(` • Stable releases only`);
|
|
380
|
+
console.log(` • Updated via merge from develop after testing`);
|
|
381
|
+
console.log(` • Can require PRs for merges`);
|
|
382
|
+
console.log('');
|
|
383
|
+
console.log(` ${colors.BWhite('Staging Branch')} (${colors.BCyan('staging')}) - ${colors.Gray('optional')}`);
|
|
384
|
+
console.log(` • Pre-production testing environment`);
|
|
385
|
+
console.log(` • Sits between develop and main`);
|
|
386
|
+
console.log('');
|
|
387
|
+
console.log(colors.BYellow('IMPORTANT:') + ' MAIASS must run on the development branch to automatically');
|
|
388
|
+
console.log('version and commit changes. PRs should not be required for this branch.');
|
|
389
|
+
console.log('');
|
|
390
|
+
|
|
391
|
+
if (existing.MAIASS_MAINBRANCH) {
|
|
392
|
+
console.log(`${SYMBOLS.INFO} Current branches: ${colors.BGreen(config.main)} / ${colors.BGreen(config.develop)} / ${colors.BGreen(config.staging)}`);
|
|
393
|
+
}
|
|
394
|
+
console.log('');
|
|
395
|
+
|
|
396
|
+
const main = await getLineInput(`Main/production branch [Enter for ${config.main}]: `);
|
|
397
|
+
config.main = main || config.main;
|
|
398
|
+
|
|
399
|
+
const develop = await getLineInput(`Development branch [Enter for ${config.develop}]: `);
|
|
400
|
+
config.develop = develop || config.develop;
|
|
401
|
+
|
|
402
|
+
const staging = await getLineInput(`Staging branch (optional) [Enter for ${config.staging}]: `);
|
|
403
|
+
config.staging = staging || config.staging;
|
|
404
|
+
|
|
405
|
+
console.log(`${SYMBOLS.CHECKMARK} Branches configured: ${colors.BGreen(config.main)} / ${colors.BGreen(config.develop)} / ${colors.BGreen(config.staging)}`);
|
|
406
|
+
|
|
407
|
+
return config;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Step 7: Save configuration to .env.maiass
|
|
412
|
+
*/
|
|
413
|
+
async function saveConfiguration(config) {
|
|
414
|
+
const timestamp = new Date().toISOString();
|
|
415
|
+
|
|
416
|
+
let content = `# MAIASS Configuration
|
|
417
|
+
# Generated on ${timestamp}
|
|
418
|
+
|
|
419
|
+
# Core Settings
|
|
420
|
+
# Verbosity level (brief/normal/verbose)
|
|
421
|
+
#MAIASS_VERBOSITY=normal
|
|
422
|
+
# Enable debug mode
|
|
423
|
+
#MAIASS_DEBUG=false
|
|
424
|
+
|
|
425
|
+
# Project Configuration
|
|
426
|
+
MAIASS_REPO_TYPE=${config.projectType}
|
|
427
|
+
|
|
428
|
+
# Version Management Settings
|
|
429
|
+
MAIASS_VERSION_PRIMARY_FILE=${config.versionSource}
|
|
430
|
+
# Secondary files that should have their versions updated (comma-separated)
|
|
431
|
+
#MAIASS_VERSION_SECONDARY_FILES=
|
|
432
|
+
|
|
433
|
+
`;
|
|
434
|
+
|
|
435
|
+
// Add mode if AI-only
|
|
436
|
+
if (config.features === 'ai_only') {
|
|
437
|
+
content += `# MAIASS Mode (full or ai_only)
|
|
438
|
+
MAIASS_MODE=ai_only
|
|
439
|
+
|
|
440
|
+
`;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Add changelog config if full mode
|
|
444
|
+
if (config.features === 'full' && config.changelog) {
|
|
445
|
+
content += `# Changelog Configuration
|
|
446
|
+
MAIASS_CHANGELOG_PATH=${config.changelog.path}
|
|
447
|
+
MAIASS_CHANGELOG_NAME=${config.changelog.name}
|
|
448
|
+
MAIASS_CHANGELOG_INTERNAL_ENABLED=${config.changelog.internalEnabled}
|
|
449
|
+
`;
|
|
450
|
+
if (config.changelog.internalEnabled) {
|
|
451
|
+
content += `MAIASS_CHANGELOG_INTERNAL_PATH=${config.changelog.internalPath}
|
|
452
|
+
MAIASS_CHANGELOG_INTERNAL_NAME=${config.changelog.internalName}
|
|
453
|
+
`;
|
|
454
|
+
}
|
|
455
|
+
content += '\n';
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Add branch configuration
|
|
459
|
+
content += `# Branch Configuration
|
|
460
|
+
MAIASS_MAINBRANCH=${config.branches.main}
|
|
461
|
+
MAIASS_DEVELOPBRANCH=${config.branches.develop}
|
|
462
|
+
MAIASS_STAGINGBRANCH=${config.branches.staging}
|
|
463
|
+
|
|
464
|
+
`;
|
|
465
|
+
|
|
466
|
+
// Add standard options
|
|
467
|
+
content += `# Auto-Yes Functionality (for CI/CD and non-interactive mode)
|
|
468
|
+
#MAIASS_AUTO_STAGE_UNSTAGED=false
|
|
469
|
+
#MAIASS_AUTO_PUSH_COMMITS=false
|
|
470
|
+
#MAIASS_AUTO_MERGE_TO_DEVELOP=false
|
|
471
|
+
#MAIASS_AUTO_APPROVE_AI_SUGGESTIONS=false
|
|
472
|
+
|
|
473
|
+
# Push Control
|
|
474
|
+
# Options: blank/ask (prompt user - default), 'always' (auto-push), 'never' (skip push)
|
|
475
|
+
#MAIASS_PUSH_ABSTRACTS_TO_REMOTE=
|
|
476
|
+
|
|
477
|
+
# Patch Release Control
|
|
478
|
+
#MAIASS_PATCH_RELEASES=
|
|
479
|
+
|
|
480
|
+
# Version Checking
|
|
481
|
+
#MAIASS_AUTO_UPDATE_CHECK=true
|
|
482
|
+
|
|
483
|
+
# Invalid Token Behavior
|
|
484
|
+
#MAIASS_AI_INVALID_TOKEN_CHOICES=false
|
|
485
|
+
|
|
486
|
+
# AI Configuration
|
|
487
|
+
#MAIASS_AI_MODEL=gpt-4
|
|
488
|
+
#MAIASS_AI_TEMPERATURE=0.8
|
|
489
|
+
#MAIASS_AI_MAX_CHARACTERS=8000
|
|
490
|
+
#MAIASS_AI_MODE=ask
|
|
491
|
+
|
|
492
|
+
# Repository Configuration
|
|
493
|
+
#MAIASS_REPO_PROVIDER=
|
|
494
|
+
#MAIASS_GITHUB_OWNER=
|
|
495
|
+
#MAIASS_GITHUB_REPO=
|
|
496
|
+
#MAIASS_BITBUCKET_WORKSPACE=
|
|
497
|
+
#MAIASS_BITBUCKET_REPO_SLUG=
|
|
498
|
+
|
|
499
|
+
# Pull Request Configuration
|
|
500
|
+
#MAIASS_STAGING_PULLREQUESTS=on
|
|
501
|
+
#MAIASS_MAIN_PULLREQUESTS=on
|
|
502
|
+
|
|
503
|
+
# Logging Configuration
|
|
504
|
+
#MAIASS_LOGGING=true
|
|
505
|
+
#MAIASS_LOG_FILE=maiass.log
|
|
506
|
+
#MAIASS_HIDEGIT=true
|
|
507
|
+
|
|
508
|
+
# Client Identity (for proxy version enforcement)
|
|
509
|
+
#MAIASS_CLIENT_NAME=nodemaiass
|
|
510
|
+
#MAIASS_CLIENT_VERSION=5.8.1
|
|
511
|
+
`;
|
|
512
|
+
|
|
513
|
+
// Write .env.maiass
|
|
514
|
+
fs.writeFileSync('.env.maiass', content, 'utf8');
|
|
515
|
+
log.success(SYMBOLS.CHECKMARK, 'Configuration saved to .env.maiass');
|
|
516
|
+
|
|
517
|
+
// Create .env.maiass.local if it doesn't exist
|
|
518
|
+
if (!fs.existsSync('.env.maiass.local')) {
|
|
519
|
+
const localContent = `# .env.maiass.local - Personal/Local Settings
|
|
520
|
+
# This file is automatically gitignored and never committed.
|
|
521
|
+
# Use this for personal overrides and sensitive data.
|
|
522
|
+
|
|
523
|
+
# Example: Override AI host for local development
|
|
524
|
+
#MAIASS_AI_HOST=http://localhost:8787
|
|
525
|
+
|
|
526
|
+
# Example: Personal debug settings
|
|
527
|
+
#MAIASS_DEBUG=true
|
|
528
|
+
`;
|
|
529
|
+
fs.writeFileSync('.env.maiass.local', localContent, 'utf8');
|
|
530
|
+
log.success(SYMBOLS.CHECKMARK, 'Created .env.maiass.local for personal settings');
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Ensure .gitignore includes .env.maiass.local
|
|
534
|
+
await ensureGitignore();
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Ensure .gitignore includes necessary patterns
|
|
539
|
+
*/
|
|
540
|
+
async function ensureGitignore() {
|
|
541
|
+
let gitignoreContent = '';
|
|
542
|
+
let needsUpdate = false;
|
|
543
|
+
|
|
544
|
+
if (fs.existsSync('.gitignore')) {
|
|
545
|
+
gitignoreContent = fs.readFileSync('.gitignore', 'utf8');
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const patterns = ['.env.maiass.local', '.env.maiass.bak'];
|
|
549
|
+
const missing = patterns.filter(pattern => !gitignoreContent.includes(pattern));
|
|
550
|
+
|
|
551
|
+
if (missing.length > 0) {
|
|
552
|
+
needsUpdate = true;
|
|
553
|
+
if (gitignoreContent && !gitignoreContent.endsWith('\n')) {
|
|
554
|
+
gitignoreContent += '\n';
|
|
555
|
+
}
|
|
556
|
+
gitignoreContent += '\n# MAIASS environment files\n';
|
|
557
|
+
for (const pattern of missing) {
|
|
558
|
+
gitignoreContent += `${pattern}\n`;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
fs.writeFileSync('.gitignore', gitignoreContent, 'utf8');
|
|
562
|
+
log.success(SYMBOLS.CHECKMARK, `Added ${missing.join(', ')} to .gitignore`);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Client information utilities
|
|
2
|
+
// Provides client name and version for API headers
|
|
3
|
+
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
let cachedVersion = null;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get the current version from package.json
|
|
15
|
+
* @returns {string} Version string
|
|
16
|
+
*/
|
|
17
|
+
function getVersionFromPackageJson() {
|
|
18
|
+
if (cachedVersion) {
|
|
19
|
+
return cachedVersion;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
24
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
25
|
+
cachedVersion = packageJson.version || '0.0.0';
|
|
26
|
+
return cachedVersion;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Warning: Could not read version from package.json:', error.message);
|
|
29
|
+
return '0.0.0';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get client name for API headers
|
|
35
|
+
* Uses MAIASS_CLIENT_NAME environment variable or defaults to 'nodemaiass'
|
|
36
|
+
* @returns {string} Client name
|
|
37
|
+
*/
|
|
38
|
+
export function getClientName() {
|
|
39
|
+
return process.env.MAIASS_CLIENT_NAME || 'nodemaiass';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get client version for API headers
|
|
44
|
+
* Priority: MAIASS_CLIENT_VERSION env var > package.json version
|
|
45
|
+
* @returns {string} Client version
|
|
46
|
+
*/
|
|
47
|
+
export function getClientVersion() {
|
|
48
|
+
return process.env.MAIASS_CLIENT_VERSION || getVersionFromPackageJson();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get both client name and version
|
|
53
|
+
* @returns {Object} Object with name and version properties
|
|
54
|
+
*/
|
|
55
|
+
export function getClientInfo() {
|
|
56
|
+
return {
|
|
57
|
+
name: getClientName(),
|
|
58
|
+
version: getClientVersion()
|
|
59
|
+
};
|
|
60
|
+
}
|
package/lib/commit.js
CHANGED
|
@@ -7,6 +7,7 @@ import readline from 'readline';
|
|
|
7
7
|
import { loadEnvironmentConfig } from './config.js';
|
|
8
8
|
import { generateMachineFingerprint } from './machine-fingerprint.js';
|
|
9
9
|
import { storeSecureVariable, retrieveSecureVariable } from './secure-storage.js';
|
|
10
|
+
import { getClientName, getClientVersion } from './client-info.js';
|
|
10
11
|
import { getSingleCharInput, getMultiLineInput } from './input-utils.js';
|
|
11
12
|
import { logCommit } from './devlog.js';
|
|
12
13
|
import colors from './colors.js';
|
|
@@ -118,9 +119,9 @@ async function createAnonymousSubscriptionIfNeeded() {
|
|
|
118
119
|
return existingToken;
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
log.info(SYMBOLS.INFO, '
|
|
122
|
-
|
|
122
|
+
log.info(SYMBOLS.INFO, 'Generating machine fingerprint...');
|
|
123
123
|
const machineFingerprint = generateMachineFingerprint();
|
|
124
|
+
|
|
124
125
|
const endpoint = process.env.MAIASS_AI_HOST || 'https://pound.maiass.net';
|
|
125
126
|
|
|
126
127
|
if (debugMode) {
|
|
@@ -128,12 +129,14 @@ async function createAnonymousSubscriptionIfNeeded() {
|
|
|
128
129
|
log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Machine fingerprint: ${JSON.stringify(machineFingerprint, null, 2)}`);
|
|
129
130
|
}
|
|
130
131
|
|
|
132
|
+
log.info(SYMBOLS.INFO, 'Requesting anonymous subscription...');
|
|
133
|
+
|
|
131
134
|
const response = await fetch(`${endpoint}/v1/token`, {
|
|
132
135
|
method: 'POST',
|
|
133
136
|
headers: {
|
|
134
137
|
'Content-Type': 'application/json',
|
|
135
|
-
'X-Client-Name':
|
|
136
|
-
'X-Client-Version':
|
|
138
|
+
'X-Client-Name': getClientName(),
|
|
139
|
+
'X-Client-Version': getClientVersion()
|
|
137
140
|
},
|
|
138
141
|
body: JSON.stringify({
|
|
139
142
|
machine_fingerprint: machineFingerprint
|
|
@@ -376,8 +379,8 @@ ${gitDiff}`;
|
|
|
376
379
|
'Content-Type': 'application/json',
|
|
377
380
|
'Authorization': `Bearer ${maiassToken}`,
|
|
378
381
|
'X-Machine-Fingerprint': generateMachineFingerprint(),
|
|
379
|
-
'X-Client-Name':
|
|
380
|
-
'X-Client-Version':
|
|
382
|
+
'X-Client-Name': getClientName(),
|
|
383
|
+
'X-Client-Version': getClientVersion(),
|
|
381
384
|
'X-Subscription-ID': process.env.MAIASS_SUBSCRIPTION_ID || ''
|
|
382
385
|
},
|
|
383
386
|
body: JSON.stringify(requestBody)
|
|
@@ -548,8 +551,12 @@ async function getCommitMessage(gitInfo, options = {}) {
|
|
|
548
551
|
case 'ask':
|
|
549
552
|
if (maiassToken) {
|
|
550
553
|
let reply;
|
|
551
|
-
|
|
554
|
+
const autoApprove = process.env.MAIASS_AUTO_APPROVE_AI_SUGGESTIONS === 'true';
|
|
555
|
+
if (silent || autoApprove) {
|
|
552
556
|
log.info('', 'Would you like to use AI to suggest a commit message? [y/N] y');
|
|
557
|
+
if (autoApprove) {
|
|
558
|
+
log.info(SYMBOLS.INFO, 'MAIASS_AUTO_APPROVE_AI_SUGGESTIONS=true: Automatically using AI');
|
|
559
|
+
}
|
|
553
560
|
reply = 'y';
|
|
554
561
|
} else {
|
|
555
562
|
reply = await getSingleCharInput('Would you like to use AI to suggest a commit message? [y/N] ');
|
|
@@ -565,8 +572,12 @@ async function getCommitMessage(gitInfo, options = {}) {
|
|
|
565
572
|
if (anonToken) {
|
|
566
573
|
// Token created successfully, now ask if they want to use AI
|
|
567
574
|
let reply;
|
|
568
|
-
|
|
575
|
+
const autoApprove = process.env.MAIASS_AUTO_APPROVE_AI_SUGGESTIONS === 'true';
|
|
576
|
+
if (silent || autoApprove) {
|
|
569
577
|
log.info('', 'Would you like to use AI to suggest a commit message? [y/N] y');
|
|
578
|
+
if (autoApprove) {
|
|
579
|
+
log.info(SYMBOLS.INFO, 'MAIASS_AUTO_APPROVE_AI_SUGGESTIONS=true: Automatically using AI');
|
|
580
|
+
}
|
|
570
581
|
reply = 'y';
|
|
571
582
|
} else {
|
|
572
583
|
reply = await getSingleCharInput('Would you like to use AI to suggest a commit message? [y/N] ');
|
|
@@ -611,14 +622,18 @@ async function getCommitMessage(gitInfo, options = {}) {
|
|
|
611
622
|
printGradientLine(60);
|
|
612
623
|
|
|
613
624
|
let reply;
|
|
614
|
-
|
|
625
|
+
const autoApprove = process.env.MAIASS_AUTO_APPROVE_AI_SUGGESTIONS === 'true';
|
|
626
|
+
if (silent || autoApprove) {
|
|
615
627
|
log.info('', 'Use this AI suggestion? [Y/n/e=edit] Y');
|
|
628
|
+
if (autoApprove) {
|
|
629
|
+
log.info(SYMBOLS.INFO, 'MAIASS_AUTO_APPROVE_AI_SUGGESTIONS=true: Automatically accepting AI suggestion');
|
|
630
|
+
}
|
|
616
631
|
reply = 'Y';
|
|
617
632
|
} else {
|
|
618
633
|
reply = await getSingleCharInput('Use this AI suggestion? [Y/n/e=edit] ');
|
|
619
634
|
}
|
|
620
635
|
|
|
621
|
-
switch (reply) {
|
|
636
|
+
switch (reply.toLowerCase()) {
|
|
622
637
|
case 'n':
|
|
623
638
|
log.info(SYMBOLS.INFO, 'AI suggestion declined, entering manual mode');
|
|
624
639
|
useAI = false;
|
|
@@ -750,8 +765,24 @@ async function handleStagedCommit(gitInfo, options = {}) {
|
|
|
750
765
|
// Ask about pushing to remote
|
|
751
766
|
if (remoteExists('origin')) {
|
|
752
767
|
let reply;
|
|
753
|
-
|
|
754
|
-
|
|
768
|
+
const autoPush = process.env.MAIASS_AUTO_PUSH_COMMITS === 'true';
|
|
769
|
+
const pushAbstracts = process.env.MAIASS_PUSH_ABSTRACTS_TO_REMOTE || '';
|
|
770
|
+
|
|
771
|
+
// Handle MAIASS_PUSH_ABSTRACTS_TO_REMOTE: 'always', 'never', or blank/'ask'
|
|
772
|
+
if (pushAbstracts === 'always' || autoPush) {
|
|
773
|
+
log.info('', 'Do you want to push this commit to remote? [y/N] y');
|
|
774
|
+
reply = 'y';
|
|
775
|
+
if (pushAbstracts === 'always') {
|
|
776
|
+
console.log('🔄 |)) Automatically pushing to remote (MAIASS_PUSH_ABSTRACTS_TO_REMOTE=always)');
|
|
777
|
+
} else if (autoPush) {
|
|
778
|
+
console.log('🔄 |)) Automatically pushing to remote (MAIASS_AUTO_PUSH_COMMITS=true)');
|
|
779
|
+
}
|
|
780
|
+
} else if (pushAbstracts === 'never') {
|
|
781
|
+
log.info('', 'Do you want to push this commit to remote? [y/N] n');
|
|
782
|
+
console.log('⏭️ |)) Skipping push to remote (MAIASS_PUSH_ABSTRACTS_TO_REMOTE=never)');
|
|
783
|
+
reply = 'n';
|
|
784
|
+
} else if (silent) {
|
|
785
|
+
log.info('', 'Do you want to push this commit to remote? [y/N] y');
|
|
755
786
|
reply = 'y';
|
|
756
787
|
console.log('🔄 |)) Automatically pushing to remote (silent mode)');
|
|
757
788
|
} else {
|
|
@@ -816,10 +847,14 @@ export async function commitThis(options = {}) {
|
|
|
816
847
|
if (status.unstagedCount > 0 || status.untrackedCount > 0) {
|
|
817
848
|
if (!autoStage) {
|
|
818
849
|
let reply;
|
|
819
|
-
|
|
820
|
-
|
|
850
|
+
const autoStage = process.env.MAIASS_AUTO_STAGE_UNSTAGED === 'true';
|
|
851
|
+
if (silent || autoStage) {
|
|
821
852
|
reply = 'y';
|
|
822
|
-
|
|
853
|
+
if (autoStage) {
|
|
854
|
+
console.log('🔄 |)) Automatically staging changes (MAIASS_AUTO_STAGE_UNSTAGED=true)');
|
|
855
|
+
} else {
|
|
856
|
+
console.log('🔄 |)) Automatically staging changes (silent mode)');
|
|
857
|
+
}
|
|
823
858
|
} else {
|
|
824
859
|
reply = await getSingleCharInput('Do you want to stage and commit them? [y/N] ');
|
|
825
860
|
}
|
package/lib/logger.js
CHANGED
|
@@ -216,7 +216,7 @@ export const log = {
|
|
|
216
216
|
if (!debugEnabled) return;
|
|
217
217
|
|
|
218
218
|
const timestamp = new Date().toISOString();
|
|
219
|
-
const debugPrefix = colors.
|
|
219
|
+
const debugPrefix = colors.BSoftPink('|)) ') + colors.Blue('🐛 ');
|
|
220
220
|
const timestampStr = colors.Gray(`[${timestamp}]`);
|
|
221
221
|
|
|
222
222
|
// Ensure we're writing to stderr to avoid mixing with other output
|
package/lib/maiass-pipeline.js
CHANGED
|
@@ -587,8 +587,12 @@ async function handleMergeToDevelop(branchInfo, commitResult, options = {}) {
|
|
|
587
587
|
let reply;
|
|
588
588
|
let tagReply = 'n'; // Default to no tagging for patches
|
|
589
589
|
|
|
590
|
-
|
|
590
|
+
const autoMerge = process.env.MAIASS_AUTO_MERGE_TO_DEVELOP === 'true';
|
|
591
|
+
if (silent || autoMerge) {
|
|
591
592
|
console.log(colors.BCyan(mergePrompt) + colors.BGreen('Y'));
|
|
593
|
+
if (autoMerge) {
|
|
594
|
+
log.info(SYMBOLS.INFO, 'MAIASS_AUTO_MERGE_TO_DEVELOP=true: Automatically merging to develop');
|
|
595
|
+
}
|
|
592
596
|
reply = 'Y';
|
|
593
597
|
if (tagPrompt) {
|
|
594
598
|
console.log(colors.BCyan(tagPrompt) + colors.BGreen('N'));
|
|
@@ -663,7 +667,7 @@ async function handleMergeToDevelop(branchInfo, commitResult, options = {}) {
|
|
|
663
667
|
* @returns {Object} Tagging decision and reason
|
|
664
668
|
*/
|
|
665
669
|
function shouldTagRelease(versionBump, forceTag = false) {
|
|
666
|
-
const
|
|
670
|
+
const patchReleases = process.env.MAIASS_PATCH_RELEASES || '';
|
|
667
671
|
|
|
668
672
|
// CLI flag overrides everything
|
|
669
673
|
if (forceTag) {
|
|
@@ -675,15 +679,17 @@ function shouldTagRelease(versionBump, forceTag = false) {
|
|
|
675
679
|
return { shouldTag: true, reason: 'major/minor release', needsPrompt: false };
|
|
676
680
|
}
|
|
677
681
|
|
|
678
|
-
// Handle patch releases based on configuration
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
return { shouldTag: false, reason: 'MAIASS_TAG_RELEASES=none', needsPrompt: false };
|
|
682
|
+
// Handle patch releases based on MAIASS_PATCH_RELEASES configuration
|
|
683
|
+
// Options: blank/no (skip release branch - default), 'ask' (prompt user), 'always' (create release)
|
|
684
|
+
switch (patchReleases.toLowerCase()) {
|
|
685
|
+
case 'always':
|
|
686
|
+
return { shouldTag: true, reason: 'MAIASS_PATCH_RELEASES=always', needsPrompt: false };
|
|
684
687
|
case 'ask':
|
|
685
|
-
default:
|
|
686
688
|
return { shouldTag: false, reason: 'patch release', needsPrompt: true };
|
|
689
|
+
case 'no':
|
|
690
|
+
case '':
|
|
691
|
+
default:
|
|
692
|
+
return { shouldTag: false, reason: 'MAIASS_PATCH_RELEASES=no (skip release branch)', needsPrompt: false };
|
|
687
693
|
}
|
|
688
694
|
}
|
|
689
695
|
|
package/lib/secure-storage.js
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { execSync } from 'child_process';
|
|
6
6
|
import os from 'os';
|
|
7
|
-
import { log } from './logger.js';
|
|
8
|
-
import { SYMBOLS } from './symbols.js';
|
|
7
|
+
import { log, logger } from './logger.js';
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* Get environment-specific service name for secure storage
|
|
@@ -38,7 +37,7 @@ export function storeSecureVariable(varName, varValue) {
|
|
|
38
37
|
const debugMode = process.env.MAIASS_DEBUG === 'true';
|
|
39
38
|
|
|
40
39
|
if (debugMode) {
|
|
41
|
-
|
|
40
|
+
logger.debug(`Storing ${varName} in secure storage service: ${serviceName}`);
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
try {
|
|
@@ -57,19 +56,19 @@ export function storeSecureVariable(varName, varValue) {
|
|
|
57
56
|
});
|
|
58
57
|
} catch (error) {
|
|
59
58
|
if (debugMode) {
|
|
60
|
-
|
|
59
|
+
logger.debug('secret-tool not available, secure storage not supported on this system');
|
|
61
60
|
}
|
|
62
61
|
return false;
|
|
63
62
|
}
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
if (debugMode) {
|
|
67
|
-
|
|
66
|
+
logger.debug(`Successfully stored ${varName} in secure storage`);
|
|
68
67
|
}
|
|
69
68
|
return true;
|
|
70
69
|
} catch (error) {
|
|
71
70
|
if (debugMode) {
|
|
72
|
-
|
|
71
|
+
logger.debug(`Failed to store ${varName} in secure storage: ${error.message}`);
|
|
73
72
|
}
|
|
74
73
|
return false;
|
|
75
74
|
}
|
|
@@ -85,7 +84,7 @@ export function retrieveSecureVariable(varName) {
|
|
|
85
84
|
const debugMode = process.env.MAIASS_DEBUG === 'true';
|
|
86
85
|
|
|
87
86
|
if (debugMode) {
|
|
88
|
-
|
|
87
|
+
logger.debug(`Retrieving ${varName} from secure storage service: ${serviceName}`);
|
|
89
88
|
}
|
|
90
89
|
|
|
91
90
|
try {
|
|
@@ -107,20 +106,20 @@ export function retrieveSecureVariable(varName) {
|
|
|
107
106
|
}).trim();
|
|
108
107
|
} catch (error) {
|
|
109
108
|
if (debugMode) {
|
|
110
|
-
|
|
109
|
+
logger.debug('secret-tool not available, secure storage not supported on this system');
|
|
111
110
|
}
|
|
112
111
|
return null;
|
|
113
112
|
}
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
if (debugMode && value) {
|
|
117
|
-
|
|
116
|
+
logger.debug(`Successfully retrieved ${varName} from secure storage`);
|
|
118
117
|
}
|
|
119
118
|
|
|
120
119
|
return value || null;
|
|
121
120
|
} catch (error) {
|
|
122
121
|
if (debugMode) {
|
|
123
|
-
|
|
122
|
+
logger.debug(`${varName} not found in secure storage (this is normal for first run)`);
|
|
124
123
|
}
|
|
125
124
|
return null;
|
|
126
125
|
}
|
|
@@ -136,7 +135,7 @@ export function removeSecureVariable(varName) {
|
|
|
136
135
|
const debugMode = process.env.MAIASS_DEBUG === 'true';
|
|
137
136
|
|
|
138
137
|
if (debugMode) {
|
|
139
|
-
|
|
138
|
+
logger.debug(`Removing ${varName} from secure storage service: ${serviceName}`);
|
|
140
139
|
}
|
|
141
140
|
|
|
142
141
|
try {
|
|
@@ -156,19 +155,19 @@ export function removeSecureVariable(varName) {
|
|
|
156
155
|
});
|
|
157
156
|
} catch (error) {
|
|
158
157
|
if (debugMode) {
|
|
159
|
-
|
|
158
|
+
logger.debug('secret-tool not available, secure storage not supported on this system');
|
|
160
159
|
}
|
|
161
160
|
return false;
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
163
|
|
|
165
164
|
if (debugMode) {
|
|
166
|
-
|
|
165
|
+
logger.debug(`Successfully removed ${varName} from secure storage`);
|
|
167
166
|
}
|
|
168
167
|
return true;
|
|
169
168
|
} catch (error) {
|
|
170
169
|
if (debugMode) {
|
|
171
|
-
|
|
170
|
+
logger.debug(`Failed to remove ${varName} from secure storage: ${error.message}`);
|
|
172
171
|
}
|
|
173
172
|
return false;
|
|
174
173
|
}
|
|
@@ -185,14 +184,14 @@ export function loadSecureVariables() {
|
|
|
185
184
|
|
|
186
185
|
if (debugMode) {
|
|
187
186
|
const host = process.env.MAIASS_AI_HOST || 'https://pound.maiass.net';
|
|
188
|
-
|
|
187
|
+
logger.debug(`Using secure storage service name: ${serviceName} (host: ${host})`);
|
|
189
188
|
}
|
|
190
189
|
|
|
191
190
|
secureVars.forEach(varName => {
|
|
192
191
|
const envValue = process.env[varName];
|
|
193
192
|
|
|
194
193
|
if (debugMode) {
|
|
195
|
-
|
|
194
|
+
logger.debug(`Checking ${varName}: value="${envValue}", type=${typeof envValue}, empty=${!envValue || envValue.trim() === ''}`);
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
// Check if we should prefer secure storage over environment variable
|
|
@@ -202,7 +201,7 @@ export function loadSecureVariables() {
|
|
|
202
201
|
if (/^invalid_|^test_|_test$/.test(envValue) || envValue === 'DISABLED' || envValue.trim() === '') {
|
|
203
202
|
preferSecure = true;
|
|
204
203
|
if (debugMode) {
|
|
205
|
-
|
|
204
|
+
logger.debug('Environment token appears invalid, checking secure storage');
|
|
206
205
|
}
|
|
207
206
|
}
|
|
208
207
|
}
|
|
@@ -214,16 +213,16 @@ export function loadSecureVariables() {
|
|
|
214
213
|
process.env[varName] = value;
|
|
215
214
|
if (preferSecure) {
|
|
216
215
|
if (debugMode) {
|
|
217
|
-
|
|
216
|
+
logger.debug('Replaced invalid environment token with secure storage token');
|
|
218
217
|
}
|
|
219
218
|
} else {
|
|
220
219
|
if (debugMode) {
|
|
221
|
-
|
|
220
|
+
logger.debug(`Loaded ${varName} from secure storage`);
|
|
222
221
|
}
|
|
223
222
|
}
|
|
224
223
|
}
|
|
225
224
|
} else if (debugMode) {
|
|
226
|
-
|
|
225
|
+
logger.debug(`${varName} already set in environment, skipping secure storage`);
|
|
227
226
|
}
|
|
228
227
|
});
|
|
229
228
|
}
|
package/maiass.mjs
CHANGED
|
@@ -38,11 +38,17 @@ import { handleVersionCommand } from './lib/version-command.js';
|
|
|
38
38
|
import { handleMaiassCommand } from './lib/maiass-command.js';
|
|
39
39
|
import { handleAccountInfoCommand } from './lib/account-info.js';
|
|
40
40
|
import { SYMBOLS } from './lib/symbols.js';
|
|
41
|
+
import { bootstrapProject, needsBootstrap } from './lib/bootstrap.js';
|
|
41
42
|
|
|
42
43
|
// Simple CLI setup for pkg compatibility
|
|
43
44
|
const args = process.argv.slice(2);
|
|
44
45
|
const firstArg = args[0];
|
|
45
46
|
|
|
47
|
+
// Handle --setup/--bootstrap flag early
|
|
48
|
+
if (args.includes('--setup') || args.includes('--bootstrap')) {
|
|
49
|
+
process.env.MAIASS_FORCE_BOOTSTRAP = 'true';
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
// Check if first argument is a version bump type
|
|
47
53
|
const versionBumpTypes = ['major', 'minor', 'patch'];
|
|
48
54
|
let command = 'maiass'; // Default to maiass workflow
|
|
@@ -99,6 +105,7 @@ if (args.includes('--help') || args.includes('-h') || command === 'help') {
|
|
|
99
105
|
console.log(' --auto Enable all auto-yes functionality (non-interactive mode)');
|
|
100
106
|
console.log(' --commits-only, -c Generate AI commits without version management');
|
|
101
107
|
console.log(' --auto-stage Automatically stage all changes');
|
|
108
|
+
console.log(' --setup, --bootstrap Run interactive project setup');
|
|
102
109
|
console.log(' --help, -h Show this help message');
|
|
103
110
|
console.log(' --version, -v Show version');
|
|
104
111
|
console.log(' --dry-run Run without making changes');
|
|
@@ -109,6 +116,15 @@ if (args.includes('--help') || args.includes('-h') || command === 'help') {
|
|
|
109
116
|
|
|
110
117
|
// Command routing (wrapped in async IIFE to handle async commands)
|
|
111
118
|
(async () => {
|
|
119
|
+
// Run bootstrap if needed (first-time setup)
|
|
120
|
+
if (needsBootstrap()) {
|
|
121
|
+
const completed = await bootstrapProject();
|
|
122
|
+
if (completed) {
|
|
123
|
+
// Bootstrap completed, exit so user can run maiass again
|
|
124
|
+
process.exit(0);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
112
128
|
switch (command) {
|
|
113
129
|
case 'hello':
|
|
114
130
|
console.log(colors.BCyan('Hello from MAIASS!'));
|