maiass 5.8.0 → 5.8.5

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.
@@ -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, 'No AI API key found. Creating anonymous subscription...');
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': 'nodemaiass',
67
- 'X-Client-Version': '5.7.5'
52
+ 'X-Client-Name': getClientName(),
53
+ 'X-Client-Version': getClientVersion()
68
54
  },
69
55
  body: JSON.stringify({
70
56
  machine_fingerprint: machineFingerprint
@@ -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, 'No AI API key found. Creating anonymous subscription...');
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': 'nodemaiass',
136
- 'X-Client-Version': '5.7.5'
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': 'nodemaiass',
380
- 'X-Client-Version': '5.7.5',
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
- if (silent) {
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
- if (silent) {
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
- if (silent) {
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
- if (silent) {
754
- // In silent mode, automatically push
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
- if (silent) {
820
- // In silent mode, automatically stage
850
+ const autoStage = process.env.MAIASS_AUTO_STAGE_UNSTAGED === 'true';
851
+ if (silent || autoStage) {
821
852
  reply = 'y';
822
- console.log('🔄 |)) Automatically staging changes (silent mode)');
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.Blue('🐛 |)) ');
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
@@ -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
- if (silent) {
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 tagReleases = process.env.MAIASS_TAG_RELEASES || 'ask';
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
- switch (tagReleases.toLowerCase()) {
680
- case 'all':
681
- return { shouldTag: true, reason: 'MAIASS_TAG_RELEASES=all', needsPrompt: false };
682
- case 'none':
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
 
@@ -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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Storing ${varName} in secure storage service: ${serviceName}`);
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
- log.debug(SYMBOLS.WARNING, '[MAIASS DEBUG] secret-tool not available, secure storage not supported on this system');
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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Successfully stored ${varName} in secure storage`);
66
+ logger.debug(`Successfully stored ${varName} in secure storage`);
68
67
  }
69
68
  return true;
70
69
  } catch (error) {
71
70
  if (debugMode) {
72
- log.debug(SYMBOLS.WARNING, `[MAIASS DEBUG] Failed to store ${varName} in secure storage: ${error.message}`);
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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Retrieving ${varName} from secure storage service: ${serviceName}`);
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
- log.debug(SYMBOLS.WARNING, '[MAIASS DEBUG] secret-tool not available, secure storage not supported on this system');
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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Successfully retrieved ${varName} from secure storage`);
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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] ${varName} not found in secure storage (this is normal for first run)`);
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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Removing ${varName} from secure storage service: ${serviceName}`);
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
- log.debug(SYMBOLS.WARNING, '[MAIASS DEBUG] secret-tool not available, secure storage not supported on this system');
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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Successfully removed ${varName} from secure storage`);
165
+ logger.debug(`Successfully removed ${varName} from secure storage`);
167
166
  }
168
167
  return true;
169
168
  } catch (error) {
170
169
  if (debugMode) {
171
- log.debug(SYMBOLS.WARNING, `[MAIASS DEBUG] Failed to remove ${varName} from secure storage: ${error.message}`);
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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Using secure storage service name: ${serviceName} (host: ${host})`);
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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Checking ${varName}: value="${envValue}", type=${typeof envValue}, empty=${!envValue || envValue.trim() === ''}`);
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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Environment token appears invalid, checking secure storage`);
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
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Replaced invalid environment token with secure storage token`);
216
+ logger.debug('Replaced invalid environment token with secure storage token');
218
217
  }
219
218
  } else {
220
219
  if (debugMode) {
221
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] Loaded ${varName} from secure storage`);
220
+ logger.debug(`Loaded ${varName} from secure storage`);
222
221
  }
223
222
  }
224
223
  }
225
224
  } else if (debugMode) {
226
- log.debug(SYMBOLS.INFO, `[MAIASS DEBUG] ${varName} already set in environment, skipping secure storage`);
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!'));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "maiass",
3
3
  "type": "module",
4
- "version": "5.8.0",
4
+ "version": "5.8.5",
5
5
  "description": "MAIASS - Modular AI-Augmented Semantic Scribe - Intelligent Git workflow automation",
6
6
  "main": "maiass.mjs",
7
7
  "bin": {