maiass 5.7.31
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/LICENSE +26 -0
- package/README.md +347 -0
- package/build.js +127 -0
- package/lib/account-info.js +476 -0
- package/lib/colors.js +49 -0
- package/lib/commit.js +885 -0
- package/lib/config-command.js +310 -0
- package/lib/config-manager.js +344 -0
- package/lib/config.js +150 -0
- package/lib/devlog.js +182 -0
- package/lib/env-display.js +162 -0
- package/lib/git-info.js +509 -0
- package/lib/header.js +152 -0
- package/lib/input-utils.js +116 -0
- package/lib/logger.js +285 -0
- package/lib/machine-fingerprint.js +229 -0
- package/lib/maiass-command.js +79 -0
- package/lib/maiass-pipeline.js +1204 -0
- package/lib/maiass-variables.js +152 -0
- package/lib/secure-storage.js +256 -0
- package/lib/symbols.js +200 -0
- package/lib/token-validator.js +184 -0
- package/lib/version-command.js +256 -0
- package/lib/version-manager.js +902 -0
- package/maiass-standalone.cjs +148 -0
- package/maiass.cjs +34 -0
- package/maiass.mjs +167 -0
- package/package.json +45 -0
- package/setup-env.js +83 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// Configuration command handler for MAIASS CLI
|
|
2
|
+
// Implements: nma config [options] [key[=value]]
|
|
3
|
+
|
|
4
|
+
import { log, logger } from './logger.js';
|
|
5
|
+
import colors from './colors.js';
|
|
6
|
+
import { SYMBOLS } from './symbols.js';
|
|
7
|
+
import { MAIASS_VARIABLES } from './maiass-variables.js';
|
|
8
|
+
import {
|
|
9
|
+
getConfigPaths,
|
|
10
|
+
configExists,
|
|
11
|
+
readConfig,
|
|
12
|
+
writeConfig,
|
|
13
|
+
setConfigValue,
|
|
14
|
+
getConfigValue,
|
|
15
|
+
listConfig,
|
|
16
|
+
editConfig,
|
|
17
|
+
validateConfig
|
|
18
|
+
} from './config-manager.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Display configuration values in a formatted table
|
|
22
|
+
* @param {Object} config - Configuration data from listConfig()
|
|
23
|
+
* @param {Object} options - Display options
|
|
24
|
+
*/
|
|
25
|
+
function displayConfig(config, options = {}) {
|
|
26
|
+
const { scope = 'all', showSensitive = false, showPaths = true } = options;
|
|
27
|
+
|
|
28
|
+
logger.header(SYMBOLS.GEAR, 'Configuration Status');
|
|
29
|
+
|
|
30
|
+
if (showPaths) {
|
|
31
|
+
logger.section('Configuration Files:', colors.BBlue);
|
|
32
|
+
|
|
33
|
+
const globalStatus = config.files.global.exists ?
|
|
34
|
+
colors.BGreen(`${SYMBOLS.CHECKMARK} EXISTS`) :
|
|
35
|
+
colors.BYellow(`${SYMBOLS.WARNING} Not found`);
|
|
36
|
+
|
|
37
|
+
const projectStatus = config.files.project.exists ?
|
|
38
|
+
colors.BGreen(`${SYMBOLS.CHECKMARK} EXISTS`) :
|
|
39
|
+
colors.BYellow(`${SYMBOLS.WARNING} Not found`);
|
|
40
|
+
|
|
41
|
+
console.log(` 1. Global Config ${globalStatus}`);
|
|
42
|
+
console.log(` ${colors.Gray(config.files.global.path)}`);
|
|
43
|
+
console.log(` 2. Project Config ${projectStatus}`);
|
|
44
|
+
console.log(` ${colors.Gray(config.files.project.path)}`);
|
|
45
|
+
console.log();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Filter variables based on scope
|
|
49
|
+
let varsToShow = Object.entries(config.merged);
|
|
50
|
+
|
|
51
|
+
if (scope === 'global') {
|
|
52
|
+
varsToShow = varsToShow.filter(([key, info]) => info.source === 'global');
|
|
53
|
+
} else if (scope === 'project') {
|
|
54
|
+
varsToShow = varsToShow.filter(([key, info]) => info.source === 'project');
|
|
55
|
+
} else if (scope === 'set') {
|
|
56
|
+
varsToShow = varsToShow.filter(([key, info]) => info.source !== 'default' && info.source !== 'not_set');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (varsToShow.length === 0) {
|
|
60
|
+
console.log(colors.BYellow(`${SYMBOLS.INFO} No configuration values found for scope: ${scope}`));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
logger.section('Configuration Values:', colors.BBlue);
|
|
65
|
+
|
|
66
|
+
// Group by category for better display
|
|
67
|
+
const categories = {
|
|
68
|
+
'Core System': ['MAIASS_DEBUG', 'MAIASS_VERBOSITY', 'MAIASS_LOGGING', 'MAIASS_BRAND'],
|
|
69
|
+
'AI Integration': ['MAIASS_AI_MODE', 'MAIASS_AI_TOKEN', 'MAIASS_AI_MODEL', 'MAIASS_AI_TEMPERATURE', 'MAIASS_AI_HOST', 'MAIASS_AI_MAX_CHARACTERS', 'MAIASS_AI_COMMIT_MESSAGE_STYLE'],
|
|
70
|
+
'Git Branches': ['MAIASS_DEVELOPBRANCH', 'MAIASS_STAGINGBRANCH', 'MAIASS_MAINBRANCH'],
|
|
71
|
+
'Repository Settings': ['MAIASS_REPO_TYPE', 'MAIASS_GITHUB_OWNER', 'MAIASS_GITHUB_REPO', 'MAIASS_BITBUCKET_WORKSPACE', 'MAIASS_BITBUCKET_REPO_SLUG'],
|
|
72
|
+
'Pull Requests': ['MAIASS_STAGING_PULLREQUESTS', 'MAIASS_MAIN_PULLREQUESTS'],
|
|
73
|
+
'Version Management': ['MAIASS_VERSION_PATH', 'MAIASS_VERSION_PRIMARY_FILE', 'MAIASS_VERSION_PRIMARY_TYPE', 'MAIASS_VERSION_PRIMARY_LINE_START', 'MAIASS_VERSION_SECONDARY_FILES'],
|
|
74
|
+
'Changelog': ['MAIASS_CHANGELOG_PATH', 'MAIASS_CHANGELOG_NAME', 'MAIASS_CHANGELOG_INTERNAL_NAME']
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
Object.entries(categories).forEach(([categoryName, categoryVars]) => {
|
|
78
|
+
const categoryEntries = varsToShow.filter(([key]) => categoryVars.includes(key));
|
|
79
|
+
|
|
80
|
+
if (categoryEntries.length > 0) {
|
|
81
|
+
console.log(colors.BWhite(` ${categoryName}:`));
|
|
82
|
+
console.log();
|
|
83
|
+
|
|
84
|
+
categoryEntries.forEach(([key, info]) => {
|
|
85
|
+
const displayKey = key.replace('MAIASS_', '').toLowerCase();
|
|
86
|
+
const sourceColor = {
|
|
87
|
+
'project local': colors.BCyan,
|
|
88
|
+
'project': colors.BGreen,
|
|
89
|
+
'global': colors.BBlue,
|
|
90
|
+
'default': colors.Gray,
|
|
91
|
+
'not_set': colors.BYellow
|
|
92
|
+
}[info.source] || colors.White;
|
|
93
|
+
|
|
94
|
+
const sourceText = {
|
|
95
|
+
'project local': 'project local',
|
|
96
|
+
'project': 'project',
|
|
97
|
+
'global': 'global',
|
|
98
|
+
'default': 'default',
|
|
99
|
+
'not_set': 'not set'
|
|
100
|
+
}[info.source];
|
|
101
|
+
|
|
102
|
+
let displayValue = info.value || '(not set)';
|
|
103
|
+
if (info.sensitive && !showSensitive && info.value) {
|
|
104
|
+
displayValue = '***' + info.value.slice(-4);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.log(` ${colors.BWhite(displayKey.padEnd(25))} = ${colors.White(displayValue)}`);
|
|
108
|
+
console.log(` ${' '.repeat(25)} ${sourceColor(`→ ${sourceText}`)} ${colors.Gray(`(${info.description})`)}`);
|
|
109
|
+
console.log();
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Show other variables not in categories
|
|
115
|
+
const otherEntries = varsToShow.filter(([key]) =>
|
|
116
|
+
!Object.values(categories).flat().includes(key)
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (otherEntries.length > 0) {
|
|
120
|
+
console.log(colors.BWhite(' Other Settings:'));
|
|
121
|
+
console.log();
|
|
122
|
+
|
|
123
|
+
otherEntries.forEach(([key, info]) => {
|
|
124
|
+
const displayKey = key.replace('MAIASS_', '').toLowerCase();
|
|
125
|
+
const sourceColor = {
|
|
126
|
+
'project local': colors.BCyan,
|
|
127
|
+
'project': colors.BGreen,
|
|
128
|
+
'global': colors.BBlue,
|
|
129
|
+
'default': colors.Gray,
|
|
130
|
+
'not_set': colors.BYellow
|
|
131
|
+
}[info.source] || colors.White;
|
|
132
|
+
|
|
133
|
+
const sourceText = {
|
|
134
|
+
'project local': 'project local',
|
|
135
|
+
'project': 'project',
|
|
136
|
+
'global': 'global',
|
|
137
|
+
'default': 'default',
|
|
138
|
+
'not_set': 'not set'
|
|
139
|
+
}[info.source];
|
|
140
|
+
|
|
141
|
+
let displayValue = info.value || '(not set)';
|
|
142
|
+
if (info.sensitive && !showSensitive && info.value) {
|
|
143
|
+
displayValue = '***' + info.value.slice(-4);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.log(` ${colors.BWhite(displayKey.padEnd(25))} = ${colors.White(displayValue)}`);
|
|
147
|
+
console.log(` ${' '.repeat(25)} ${sourceColor(`→ ${sourceText}`)} ${colors.Gray(`(${info.description})`)}`);
|
|
148
|
+
console.log();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Handle config command
|
|
155
|
+
* @param {Object} args - Command arguments from yargs
|
|
156
|
+
*/
|
|
157
|
+
export async function handleConfigCommand(args) {
|
|
158
|
+
const {
|
|
159
|
+
global: isGlobal,
|
|
160
|
+
project: isProject,
|
|
161
|
+
edit,
|
|
162
|
+
list,
|
|
163
|
+
'show-sensitive': showSensitive,
|
|
164
|
+
'list-vars': listVars,
|
|
165
|
+
key
|
|
166
|
+
} = args;
|
|
167
|
+
|
|
168
|
+
const paths = getConfigPaths();
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
// Handle --list-vars flag
|
|
172
|
+
if (listVars) {
|
|
173
|
+
log.info(SYMBOLS.INFO, 'Available Configuration Variables:');
|
|
174
|
+
log.space();
|
|
175
|
+
|
|
176
|
+
Object.entries(MAIASS_VARIABLES).forEach(([key, def]) => {
|
|
177
|
+
const displayKey = key.replace('MAIASS_', '').toLowerCase();
|
|
178
|
+
const sensitive = def.sensitive ? colors.BYellow(' (sensitive)') : '';
|
|
179
|
+
console.log(` ${colors.BWhite(displayKey.padEnd(30))} - ${colors.Gray(def.description)}${sensitive}`);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
log.space();
|
|
183
|
+
log.blue(SYMBOLS.INFO, 'Usage examples:');
|
|
184
|
+
console.log(` nma config --global maiass_token=your_token_here`);
|
|
185
|
+
console.log(` nma config --project debug=true`);
|
|
186
|
+
console.log(` nma config verbosity`);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Handle --edit flag
|
|
191
|
+
if (edit) {
|
|
192
|
+
const configPath = isGlobal ? paths.global :
|
|
193
|
+
isProject ? paths.project :
|
|
194
|
+
paths.project; // Default to project
|
|
195
|
+
|
|
196
|
+
log.blue(SYMBOLS.INFO, `Editing ${isGlobal ? 'global' : 'project'} configuration...`);
|
|
197
|
+
editConfig(configPath);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Handle key=value assignment
|
|
202
|
+
if (key) {
|
|
203
|
+
const arg = key;
|
|
204
|
+
|
|
205
|
+
if (arg.includes('=')) {
|
|
206
|
+
// Set configuration value
|
|
207
|
+
const [rawKey, ...valueParts] = arg.split('=');
|
|
208
|
+
const key = `MAIASS_${rawKey.toUpperCase()}`;
|
|
209
|
+
const value = valueParts.join('=');
|
|
210
|
+
|
|
211
|
+
// Validate key exists
|
|
212
|
+
if (!MAIASS_VARIABLES[key]) {
|
|
213
|
+
console.error(colors.Red(`${SYMBOLS.CROSS} Unknown configuration variable: ${rawKey}`));
|
|
214
|
+
console.log(colors.BYellow(`${SYMBOLS.INFO} Use --list-vars to see available variables`));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Determine target config file
|
|
219
|
+
const configPath = isGlobal ? paths.global : paths.project;
|
|
220
|
+
const scope = isGlobal ? 'global' : 'project';
|
|
221
|
+
|
|
222
|
+
// Validate value
|
|
223
|
+
const tempConfig = { [key]: value };
|
|
224
|
+
const errors = validateConfig(tempConfig);
|
|
225
|
+
|
|
226
|
+
if (errors.length > 0) {
|
|
227
|
+
console.error(colors.Red(`${SYMBOLS.CROSS} Configuration validation failed:`));
|
|
228
|
+
errors.forEach(error => {
|
|
229
|
+
console.error(colors.Red(` ${error.key}: ${error.error}`));
|
|
230
|
+
if (error.current) {
|
|
231
|
+
console.error(colors.Gray(` Current value: ${error.current}`));
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Set the value
|
|
238
|
+
setConfigValue(key, value, { global: isGlobal });
|
|
239
|
+
|
|
240
|
+
const varDef = MAIASS_VARIABLES[key];
|
|
241
|
+
const displayKey = rawKey.toLowerCase();
|
|
242
|
+
let displayValue = value;
|
|
243
|
+
|
|
244
|
+
if (varDef.sensitive) {
|
|
245
|
+
displayValue = '***' + value.slice(-4);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
log.success(SYMBOLS.CHECKMARK, 'Configuration updated');
|
|
249
|
+
console.log(` ${colors.BWhite(displayKey)} = ${colors.White(displayValue)} ${colors.Gray(`(${scope})`)}`);
|
|
250
|
+
console.log(` ${colors.Gray(`File: ${configPath}`)}`);
|
|
251
|
+
|
|
252
|
+
} else {
|
|
253
|
+
// Get specific configuration value
|
|
254
|
+
const rawKey = arg;
|
|
255
|
+
const key = `MAIASS_${rawKey.toUpperCase()}`;
|
|
256
|
+
|
|
257
|
+
if (!MAIASS_VARIABLES[key]) {
|
|
258
|
+
console.error(colors.Red(`${SYMBOLS.CROSS} Unknown configuration variable: ${rawKey}`));
|
|
259
|
+
console.log(colors.BYellow(`${SYMBOLS.INFO} Use --list-vars to see available variables`));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const valueInfo = getConfigValue(key);
|
|
264
|
+
const varDef = MAIASS_VARIABLES[key];
|
|
265
|
+
|
|
266
|
+
let displayValue = valueInfo.value || '(not set)';
|
|
267
|
+
if (varDef.sensitive && !showSensitive && valueInfo.value) {
|
|
268
|
+
displayValue = '***' + valueInfo.value.slice(-4);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const sourceColor = {
|
|
272
|
+
'project local': colors.BCyan,
|
|
273
|
+
'project': colors.BGreen,
|
|
274
|
+
'global': colors.BBlue,
|
|
275
|
+
'default': colors.Gray,
|
|
276
|
+
'not_set': colors.BYellow
|
|
277
|
+
}[valueInfo.source] || colors.White;
|
|
278
|
+
|
|
279
|
+
log.info(SYMBOLS.INFO, 'Configuration Value:');
|
|
280
|
+
log.space();
|
|
281
|
+
console.log(` ${colors.BWhite(rawKey.toLowerCase())} = ${colors.White(displayValue)}`);
|
|
282
|
+
console.log(` ${sourceColor(`→ ${valueInfo.source}`)} ${colors.Gray(`(${varDef.description})`)}`);
|
|
283
|
+
|
|
284
|
+
if (valueInfo.path) {
|
|
285
|
+
console.log(` ${colors.Gray(`File: ${valueInfo.path}`)}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Default: show configuration
|
|
293
|
+
const config = listConfig({ showSensitive });
|
|
294
|
+
|
|
295
|
+
let scope = 'all';
|
|
296
|
+
if (isGlobal && !isProject) scope = 'global';
|
|
297
|
+
else if (isProject && !isGlobal) scope = 'project';
|
|
298
|
+
else if (list) scope = 'set';
|
|
299
|
+
|
|
300
|
+
displayConfig(config, {
|
|
301
|
+
scope,
|
|
302
|
+
showSensitive,
|
|
303
|
+
showPaths: scope === 'all'
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
} catch (error) {
|
|
307
|
+
console.error(colors.Red(`${SYMBOLS.CROSS} Configuration error: ${error.message}`));
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
// Configuration management for MAIASS
|
|
2
|
+
// Handles global and project-level configuration settings
|
|
3
|
+
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import os from 'os';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
import { log, logger } from './logger.js';
|
|
9
|
+
import colors from './colors.js';
|
|
10
|
+
import { SYMBOLS } from './symbols.js';
|
|
11
|
+
import { MAIASS_VARIABLES } from './maiass-variables.js';
|
|
12
|
+
import { loadEnvironmentConfig } from './config.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get configuration file paths
|
|
16
|
+
* @returns {Object} Configuration file paths
|
|
17
|
+
*/
|
|
18
|
+
export function getConfigPaths() {
|
|
19
|
+
return {
|
|
20
|
+
global: path.join(os.homedir(), '.env.maiass'),
|
|
21
|
+
project: path.resolve(process.cwd(), '.env.maiass'),
|
|
22
|
+
projectDir: process.cwd(),
|
|
23
|
+
homeDir: os.homedir()
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a configuration file exists
|
|
29
|
+
* @param {string} configPath - Path to config file
|
|
30
|
+
* @returns {boolean} True if file exists
|
|
31
|
+
*/
|
|
32
|
+
export function configExists(configPath) {
|
|
33
|
+
return fs.existsSync(configPath);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Read configuration file contents
|
|
38
|
+
* @param {string} configPath - Path to config file
|
|
39
|
+
* @returns {Object} Parsed configuration object
|
|
40
|
+
*/
|
|
41
|
+
export function readConfig(configPath) {
|
|
42
|
+
if (!configExists(configPath)) {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
48
|
+
const config = {};
|
|
49
|
+
|
|
50
|
+
// Parse .env format (KEY=value)
|
|
51
|
+
content.split('\n').forEach(line => {
|
|
52
|
+
line = line.trim();
|
|
53
|
+
if (line && !line.startsWith('#')) {
|
|
54
|
+
const [key, ...valueParts] = line.split('=');
|
|
55
|
+
if (key && valueParts.length > 0) {
|
|
56
|
+
let value = valueParts.join('=').trim();
|
|
57
|
+
|
|
58
|
+
// Remove surrounding quotes if present
|
|
59
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
60
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
61
|
+
value = value.slice(1, -1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
config[key.trim()] = value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return config;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(colors.Red(`${SYMBOLS.CROSS} Error reading config file ${configPath}: ${error.message}`));
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Write configuration to file
|
|
78
|
+
* @param {string} configPath - Path to config file
|
|
79
|
+
* @param {Object} config - Configuration object to write
|
|
80
|
+
* @param {Object} options - Write options
|
|
81
|
+
*/
|
|
82
|
+
export function writeConfig(configPath, config, options = {}) {
|
|
83
|
+
const { backup = true, createDir = true } = options;
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
// Create directory if it doesn't exist
|
|
87
|
+
if (createDir) {
|
|
88
|
+
const dir = path.dirname(configPath);
|
|
89
|
+
if (!fs.existsSync(dir)) {
|
|
90
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Create backup if file exists
|
|
95
|
+
if (backup && configExists(configPath)) {
|
|
96
|
+
const backupPath = `${configPath}.backup.${Date.now()}`;
|
|
97
|
+
fs.copyFileSync(configPath, backupPath);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Generate .env format content
|
|
101
|
+
const lines = [];
|
|
102
|
+
lines.push('# MAIASS Configuration');
|
|
103
|
+
lines.push(`# Generated on ${new Date().toISOString()}`);
|
|
104
|
+
lines.push('');
|
|
105
|
+
|
|
106
|
+
// Group variables by category for better organization
|
|
107
|
+
const categories = {
|
|
108
|
+
'Core': ['MAIASS_DEBUG', 'MAIASS_VERBOSITY', 'MAIASS_LOGGING', 'MAIASS_BRAND'],
|
|
109
|
+
'AI': ['MAIASS_AI_MODE', 'MAIASS_AI_TOKEN', 'MAIASS_AI_MODEL', 'MAIASS_AI_TEMPERATURE', 'MAIASS_AI_ENDPOINT', 'MAIASS_AI_MAX_CHARACTERS', 'MAIASS_AI_COMMIT_MESSAGE_STYLE'],
|
|
110
|
+
'Branches': ['MAIASS_DEVELOPBRANCH', 'MAIASS_STAGINGBRANCH', 'MAIASS_MAINBRANCH'],
|
|
111
|
+
'Repository': ['MAIASS_REPO_TYPE', 'MAIASS_GITHUB_OWNER', 'MAIASS_GITHUB_REPO', 'MAIASS_BITBUCKET_WORKSPACE', 'MAIASS_BITBUCKET_REPO_SLUG'],
|
|
112
|
+
'Pull Requests': ['MAIASS_STAGING_PULLREQUESTS', 'MAIASS_MAIN_PULLREQUESTS'],
|
|
113
|
+
'Versioning': ['MAIASS_VERSION_PATH', 'MAIASS_VERSION_PRIMARY_FILE', 'MAIASS_VERSION_PRIMARY_TYPE', 'MAIASS_VERSION_PRIMARY_LINE_START', 'MAIASS_VERSION_SECONDARY_FILES'],
|
|
114
|
+
'Changelog': ['MAIASS_CHANGELOG_PATH', 'MAIASS_CHANGELOG_NAME', 'MAIASS_CHANGELOG_INTERNAL_NAME'],
|
|
115
|
+
'Other': []
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Write variables by category
|
|
119
|
+
Object.entries(categories).forEach(([categoryName, categoryVars]) => {
|
|
120
|
+
const varsInCategory = Object.keys(config).filter(key =>
|
|
121
|
+
categoryVars.includes(key) || (categoryName === 'Other' && !Object.values(categories).flat().includes(key))
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
if (varsInCategory.length > 0) {
|
|
125
|
+
lines.push(`# ${categoryName} Settings`);
|
|
126
|
+
varsInCategory.forEach(key => {
|
|
127
|
+
const value = config[key];
|
|
128
|
+
const varDef = MAIASS_VARIABLES[key];
|
|
129
|
+
|
|
130
|
+
if (varDef && varDef.description) {
|
|
131
|
+
lines.push(`# ${varDef.description}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Quote values that contain spaces or special characters
|
|
135
|
+
const needsQuotes = /[\s#"'\\]/.test(value);
|
|
136
|
+
const quotedValue = needsQuotes ? `"${value.replace(/"/g, '\\"')}"` : value;
|
|
137
|
+
|
|
138
|
+
lines.push(`${key}=${quotedValue}`);
|
|
139
|
+
});
|
|
140
|
+
lines.push('');
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
fs.writeFileSync(configPath, lines.join('\n'), 'utf8');
|
|
145
|
+
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(colors.Red(`${SYMBOLS.CROSS} Error writing config file ${configPath}: ${error.message}`));
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Set a configuration value
|
|
154
|
+
* @param {string} key - Configuration key
|
|
155
|
+
* @param {string} value - Configuration value
|
|
156
|
+
* @param {Object} options - Options like { global: true }
|
|
157
|
+
*/
|
|
158
|
+
export function setConfigValue(key, value, options = {}) {
|
|
159
|
+
const paths = getConfigPaths();
|
|
160
|
+
const configPath = options.global ? paths.global : paths.project;
|
|
161
|
+
|
|
162
|
+
const config = readConfig(configPath);
|
|
163
|
+
config[key] = value;
|
|
164
|
+
writeConfig(configPath, config);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get a configuration value with fallback hierarchy
|
|
169
|
+
* @param {string} key - Configuration key
|
|
170
|
+
* @returns {Object} Value info with source
|
|
171
|
+
*/
|
|
172
|
+
export function getConfigValue(key) {
|
|
173
|
+
const paths = getConfigPaths();
|
|
174
|
+
|
|
175
|
+
// Check project local config first (highest priority)
|
|
176
|
+
const projectLocalPath = path.resolve(process.cwd(), '.env.maiass.local');
|
|
177
|
+
const projectLocalConfig = readConfig(projectLocalPath);
|
|
178
|
+
if (projectLocalConfig[key] !== undefined) {
|
|
179
|
+
return {
|
|
180
|
+
value: projectLocalConfig[key],
|
|
181
|
+
source: 'project local',
|
|
182
|
+
path: projectLocalPath
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Check project config (second priority)
|
|
187
|
+
const projectConfig = readConfig(paths.project);
|
|
188
|
+
if (projectConfig[key] !== undefined) {
|
|
189
|
+
return {
|
|
190
|
+
value: projectConfig[key],
|
|
191
|
+
source: 'project',
|
|
192
|
+
path: paths.project
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Check global config
|
|
197
|
+
const globalConfig = readConfig(paths.global);
|
|
198
|
+
if (globalConfig[key] !== undefined) {
|
|
199
|
+
return {
|
|
200
|
+
value: globalConfig[key],
|
|
201
|
+
source: 'global',
|
|
202
|
+
path: paths.global
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Check default value
|
|
207
|
+
const varDef = MAIASS_VARIABLES[key];
|
|
208
|
+
if (varDef && varDef.default !== undefined) {
|
|
209
|
+
return {
|
|
210
|
+
value: varDef.default,
|
|
211
|
+
source: 'default',
|
|
212
|
+
path: null
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
value: undefined,
|
|
218
|
+
source: 'not_set',
|
|
219
|
+
path: null
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* List all configuration values with their sources
|
|
225
|
+
* @param {Object} options - Listing options
|
|
226
|
+
* @returns {Object} Configuration listing
|
|
227
|
+
*/
|
|
228
|
+
export function listConfig(options = {}) {
|
|
229
|
+
const { scope = 'all', showSensitive = false } = options;
|
|
230
|
+
const paths = getConfigPaths();
|
|
231
|
+
|
|
232
|
+
const result = {
|
|
233
|
+
global: {},
|
|
234
|
+
project: {},
|
|
235
|
+
merged: {},
|
|
236
|
+
files: {
|
|
237
|
+
global: { exists: configExists(paths.global), path: paths.global },
|
|
238
|
+
project: { exists: configExists(paths.project), path: paths.project }
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// Read config files
|
|
243
|
+
if (result.files.global.exists) {
|
|
244
|
+
result.global = readConfig(paths.global);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (result.files.project.exists) {
|
|
248
|
+
result.project = readConfig(paths.project);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Create merged config with source tracking
|
|
252
|
+
Object.keys(MAIASS_VARIABLES).forEach(key => {
|
|
253
|
+
const valueInfo = getConfigValue(key);
|
|
254
|
+
const varDef = MAIASS_VARIABLES[key];
|
|
255
|
+
|
|
256
|
+
// Skip sensitive variables unless explicitly requested
|
|
257
|
+
if (varDef.sensitive && !showSensitive) {
|
|
258
|
+
if (valueInfo.value) {
|
|
259
|
+
valueInfo.value = '***' + valueInfo.value.slice(-4);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
result.merged[key] = {
|
|
264
|
+
...valueInfo,
|
|
265
|
+
description: varDef.description,
|
|
266
|
+
sensitive: varDef.sensitive || false
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Open configuration file in default editor
|
|
275
|
+
* @param {string} configPath - Path to config file
|
|
276
|
+
*/
|
|
277
|
+
export function editConfig(configPath) {
|
|
278
|
+
try {
|
|
279
|
+
// Create file if it doesn't exist
|
|
280
|
+
if (!configExists(configPath)) {
|
|
281
|
+
writeConfig(configPath, {});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Try to detect and use appropriate editor
|
|
285
|
+
const editor = process.env.EDITOR || process.env.VISUAL || 'nano';
|
|
286
|
+
|
|
287
|
+
log.blue(SYMBOLS.INFO, `Opening ${configPath} in ${editor}...`);
|
|
288
|
+
|
|
289
|
+
execSync(`${editor} "${configPath}"`, {
|
|
290
|
+
stdio: 'inherit',
|
|
291
|
+
cwd: process.cwd()
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
log.success(SYMBOLS.CHECKMARK, 'Configuration file updated');
|
|
295
|
+
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.error(colors.Red(`${SYMBOLS.CROSS} Error opening editor: ${error.message}`));
|
|
298
|
+
log.warning(SYMBOLS.INFO, `You can manually edit: ${configPath}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Validate configuration values
|
|
304
|
+
* @param {Object} config - Configuration object to validate
|
|
305
|
+
* @returns {Array} Array of validation errors
|
|
306
|
+
*/
|
|
307
|
+
export function validateConfig(config) {
|
|
308
|
+
const errors = [];
|
|
309
|
+
|
|
310
|
+
Object.entries(config).forEach(([key, value]) => {
|
|
311
|
+
const varDef = MAIASS_VARIABLES[key];
|
|
312
|
+
|
|
313
|
+
if (!varDef) {
|
|
314
|
+
errors.push({
|
|
315
|
+
key,
|
|
316
|
+
error: 'Unknown configuration variable',
|
|
317
|
+
suggestion: 'Check available variables with: nma config --list-vars'
|
|
318
|
+
});
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Add specific validation rules here as needed
|
|
323
|
+
if (key === 'MAIASS_AI_TEMPERATURE') {
|
|
324
|
+
const temp = parseFloat(value);
|
|
325
|
+
if (isNaN(temp) || temp < 0 || temp > 2) {
|
|
326
|
+
errors.push({
|
|
327
|
+
key,
|
|
328
|
+
error: 'Temperature must be a number between 0 and 2',
|
|
329
|
+
current: value
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (key.includes('PULLREQUESTS') && !['on', 'off'].includes(value)) {
|
|
335
|
+
errors.push({
|
|
336
|
+
key,
|
|
337
|
+
error: 'Pull request setting must be "on" or "off"',
|
|
338
|
+
current: value
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
return errors;
|
|
344
|
+
}
|