aios-core 2.1.4 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/.aios-core/development/tasks/analyze-brownfield.md +456 -456
  2. package/.aios-core/development/tasks/setup-project-docs.md +440 -444
  3. package/.aios-core/infrastructure/scripts/documentation-integrity/brownfield-analyzer.js +501 -501
  4. package/.aios-core/infrastructure/scripts/documentation-integrity/config-generator.js +368 -329
  5. package/.aios-core/infrastructure/scripts/documentation-integrity/deployment-config-loader.js +308 -282
  6. package/.aios-core/infrastructure/scripts/documentation-integrity/doc-generator.js +331 -331
  7. package/.aios-core/infrastructure/scripts/documentation-integrity/gitignore-generator.js +312 -312
  8. package/.aios-core/infrastructure/scripts/documentation-integrity/index.js +74 -74
  9. package/.aios-core/infrastructure/scripts/documentation-integrity/mode-detector.js +389 -358
  10. package/.aios-core/infrastructure/scripts/llm-routing/install-llm-routing.js +6 -6
  11. package/.aios-core/infrastructure/templates/core-config/core-config-brownfield.tmpl.yaml +176 -182
  12. package/.aios-core/infrastructure/templates/core-config/core-config-greenfield.tmpl.yaml +127 -127
  13. package/.aios-core/infrastructure/templates/project-docs/coding-standards-tmpl.md +346 -346
  14. package/.aios-core/infrastructure/templates/project-docs/source-tree-tmpl.md +177 -177
  15. package/.aios-core/infrastructure/templates/project-docs/tech-stack-tmpl.md +267 -267
  16. package/package.json +1 -1
  17. package/packages/installer/src/config/templates/env-template.js +2 -2
  18. package/packages/installer/src/wizard/wizard.js +1 -1
  19. package/packages/installer/tests/integration/environment-configuration.test.js +2 -1
  20. package/packages/installer/tests/unit/env-template.test.js +3 -2
  21. package/src/wizard/index.js +2 -2
  22. package/.aios-core/development/tasks/validate-structure.md +0 -243
  23. package/.aios-core/infrastructure/scripts/source-tree-guardian/index.js +0 -375
  24. package/.aios-core/infrastructure/scripts/source-tree-guardian/manifest-generator.js +0 -410
  25. package/.aios-core/infrastructure/scripts/source-tree-guardian/rules/naming-rules.yaml +0 -285
  26. package/.aios-core/infrastructure/scripts/source-tree-guardian/rules/placement-rules.yaml +0 -262
  27. package/.aios-core/infrastructure/scripts/source-tree-guardian/validator.js +0 -468
@@ -1,358 +1,389 @@
1
- /**
2
- * Mode Detector Module
3
- *
4
- * Detects installation mode for AIOS projects with three-mode support:
5
- * - FRAMEWORK_DEV: Contributing to aios-core itself
6
- * - GREENFIELD: New empty project
7
- * - BROWNFIELD: Existing project being integrated
8
- *
9
- * @module documentation-integrity/mode-detector
10
- * @version 1.0.0
11
- * @story 6.9
12
- */
13
-
14
- const fs = require('fs');
15
- const path = require('path');
16
-
17
- /**
18
- * Installation modes supported by AIOS
19
- * @enum {string}
20
- */
21
- const InstallationMode = {
22
- FRAMEWORK_DEV: 'framework-dev',
23
- GREENFIELD: 'greenfield',
24
- BROWNFIELD: 'brownfield',
25
- UNKNOWN: 'unknown',
26
- };
27
-
28
- /**
29
- * Legacy project type mappings (for backward compatibility)
30
- * @enum {string}
31
- */
32
- const LegacyProjectType = {
33
- EXISTING_AIOS: 'EXISTING_AIOS',
34
- GREENFIELD: 'GREENFIELD',
35
- BROWNFIELD: 'BROWNFIELD',
36
- UNKNOWN: 'UNKNOWN',
37
- };
38
-
39
- /**
40
- * Mode descriptions for display in wizard
41
- * @type {Object.<string, Object>}
42
- */
43
- const ModeDescriptions = {
44
- [InstallationMode.FRAMEWORK_DEV]: {
45
- label: '🔧 Framework Development',
46
- hint: 'Developing aios-core itself - uses framework standards, skips project setup',
47
- description: 'For AIOS contributors working on the framework',
48
- },
49
- [InstallationMode.GREENFIELD]: {
50
- label: '🆕 New Project (Greenfield)',
51
- hint: 'Start a fresh project with AIOS - generates project docs, config, and infrastructure',
52
- description: 'Empty directory setup with full scaffolding',
53
- },
54
- [InstallationMode.BROWNFIELD]: {
55
- label: '📂 Existing Project (Brownfield)',
56
- hint: 'Add AIOS to existing project - analyzes current structure and adapts',
57
- description: 'Integration with existing codebase',
58
- },
59
- [InstallationMode.UNKNOWN]: {
60
- label: '❓ Unknown',
61
- hint: 'Could not determine project type - manual selection required',
62
- description: 'Manual mode selection needed',
63
- },
64
- };
65
-
66
- /**
67
- * Detection result with confidence and reasoning
68
- * @typedef {Object} DetectionResult
69
- * @property {string} mode - Detected installation mode
70
- * @property {string} legacyType - Legacy project type for backward compatibility
71
- * @property {number} confidence - Detection confidence (0-100)
72
- * @property {string} reason - Human-readable reason for detection
73
- * @property {Object} markers - Detected markers in the directory
74
- */
75
-
76
- /**
77
- * Detects the installation mode for a target directory
78
- *
79
- * Detection Priority Order:
80
- * 1. FRAMEWORK_DEV - .aios-core/ exists AND is aios-core repo
81
- * 2. GREENFIELD - directory is empty
82
- * 3. BROWNFIELD - has package.json, .git, or other project markers
83
- * 4. UNKNOWN - has files but no recognized markers
84
- *
85
- * @param {string} targetDir - Directory to analyze
86
- * @returns {DetectionResult} Detection result with mode and metadata
87
- * @throws {Error} If directory cannot be accessed
88
- */
89
- function detectInstallationMode(targetDir = process.cwd()) {
90
- // Validate input
91
- if (!targetDir || typeof targetDir !== 'string') {
92
- throw new Error('Invalid targetDir parameter: must be a non-empty string');
93
- }
94
-
95
- const normalizedDir = path.resolve(targetDir);
96
-
97
- // Check if directory exists
98
- if (!fs.existsSync(normalizedDir)) {
99
- throw new Error(`Directory does not exist: ${normalizedDir}`);
100
- }
101
-
102
- // Collect markers
103
- const markers = collectMarkers(normalizedDir);
104
-
105
- // Priority 1: Check for AIOS framework development
106
- if (markers.hasAiosCore && markers.isAiosCoreRepo) {
107
- return {
108
- mode: InstallationMode.FRAMEWORK_DEV,
109
- legacyType: LegacyProjectType.EXISTING_AIOS,
110
- confidence: 100,
111
- reason: 'Detected aios-core repository with .aios-core directory',
112
- markers,
113
- };
114
- }
115
-
116
- // Priority 2: Check for existing AIOS installation (non-framework)
117
- if (markers.hasAiosCore && !markers.isAiosCoreRepo) {
118
- return {
119
- mode: InstallationMode.BROWNFIELD,
120
- legacyType: LegacyProjectType.EXISTING_AIOS,
121
- confidence: 95,
122
- reason: 'AIOS already installed in user project - treating as brownfield update',
123
- markers,
124
- };
125
- }
126
-
127
- // Priority 3: Check for empty directory (greenfield)
128
- if (markers.isEmpty) {
129
- return {
130
- mode: InstallationMode.GREENFIELD,
131
- legacyType: LegacyProjectType.GREENFIELD,
132
- confidence: 100,
133
- reason: 'Empty directory detected',
134
- markers,
135
- };
136
- }
137
-
138
- // Priority 4: Check for project markers (brownfield)
139
- if (
140
- markers.hasPackageJson ||
141
- markers.hasGit ||
142
- markers.hasPythonProject ||
143
- markers.hasGoMod ||
144
- markers.hasCargoToml
145
- ) {
146
- const projectTypes = [];
147
- if (markers.hasPackageJson) projectTypes.push('Node.js');
148
- if (markers.hasPythonProject) projectTypes.push('Python');
149
- if (markers.hasGoMod) projectTypes.push('Go');
150
- if (markers.hasCargoToml) projectTypes.push('Rust');
151
-
152
- return {
153
- mode: InstallationMode.BROWNFIELD,
154
- legacyType: LegacyProjectType.BROWNFIELD,
155
- confidence: 90,
156
- reason: `Existing project detected: ${projectTypes.join(', ') || 'Git repository'}`,
157
- markers,
158
- };
159
- }
160
-
161
- // Priority 5: Directory has files but no recognized markers
162
- return {
163
- mode: InstallationMode.UNKNOWN,
164
- legacyType: LegacyProjectType.UNKNOWN,
165
- confidence: 0,
166
- reason: 'Directory has files but no recognized project markers',
167
- markers,
168
- };
169
- }
170
-
171
- /**
172
- * Collects all relevant markers from a directory
173
- *
174
- * @param {string} targetDir - Directory to scan
175
- * @returns {Object} Object containing all detected markers
176
- */
177
- function collectMarkers(targetDir) {
178
- const dirContents = fs.readdirSync(targetDir);
179
-
180
- return {
181
- // AIOS markers
182
- hasAiosCore: fs.existsSync(path.join(targetDir, '.aios-core')),
183
- isAiosCoreRepo: isAiosCoreRepository(targetDir),
184
-
185
- // Directory state
186
- isEmpty: dirContents.length === 0,
187
- fileCount: dirContents.length,
188
-
189
- // Project markers
190
- hasPackageJson: fs.existsSync(path.join(targetDir, 'package.json')),
191
- hasGit: fs.existsSync(path.join(targetDir, '.git')),
192
-
193
- // Python markers
194
- hasPythonProject:
195
- fs.existsSync(path.join(targetDir, 'requirements.txt')) ||
196
- fs.existsSync(path.join(targetDir, 'pyproject.toml')) ||
197
- fs.existsSync(path.join(targetDir, 'setup.py')),
198
-
199
- // Go markers
200
- hasGoMod: fs.existsSync(path.join(targetDir, 'go.mod')),
201
-
202
- // Rust markers
203
- hasCargoToml: fs.existsSync(path.join(targetDir, 'Cargo.toml')),
204
-
205
- // Existing standards markers
206
- hasEslintrc:
207
- fs.existsSync(path.join(targetDir, '.eslintrc.js')) ||
208
- fs.existsSync(path.join(targetDir, '.eslintrc.json')) ||
209
- fs.existsSync(path.join(targetDir, '.eslintrc.yaml')),
210
- hasPrettierrc:
211
- fs.existsSync(path.join(targetDir, '.prettierrc')) ||
212
- fs.existsSync(path.join(targetDir, '.prettierrc.json')) ||
213
- fs.existsSync(path.join(targetDir, 'prettier.config.js')),
214
- hasTsconfig: fs.existsSync(path.join(targetDir, 'tsconfig.json')),
215
-
216
- // CI/CD markers
217
- hasGithubWorkflows: fs.existsSync(path.join(targetDir, '.github', 'workflows')),
218
- hasGitlabCi: fs.existsSync(path.join(targetDir, '.gitlab-ci.yml')),
219
- };
220
- }
221
-
222
- /**
223
- * Checks if the target directory is the aios-core repository itself
224
- *
225
- * @param {string} targetDir - Directory to check
226
- * @returns {boolean} True if this is the aios-core repository
227
- */
228
- function isAiosCoreRepository(targetDir) {
229
- const packageJsonPath = path.join(targetDir, 'package.json');
230
-
231
- if (!fs.existsSync(packageJsonPath)) {
232
- return false;
233
- }
234
-
235
- try {
236
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
237
- // Check for aios-core package name or workspaces pattern
238
- const isAiosCore =
239
- packageJson.name === '@aios/core' ||
240
- packageJson.name === 'aios-core' ||
241
- (Array.isArray(packageJson.workspaces) && packageJson.workspaces.includes('packages/*'));
242
- return isAiosCore === true;
243
- } catch {
244
- return false;
245
- }
246
- }
247
-
248
- /**
249
- * Maps legacy project type to new installation mode
250
- *
251
- * @param {string} legacyType - Legacy project type (EXISTING_AIOS, GREENFIELD, etc.)
252
- * @returns {string} New installation mode
253
- */
254
- function mapLegacyTypeToMode(legacyType) {
255
- const mapping = {
256
- [LegacyProjectType.EXISTING_AIOS]: InstallationMode.FRAMEWORK_DEV,
257
- [LegacyProjectType.GREENFIELD]: InstallationMode.GREENFIELD,
258
- [LegacyProjectType.BROWNFIELD]: InstallationMode.BROWNFIELD,
259
- [LegacyProjectType.UNKNOWN]: InstallationMode.UNKNOWN,
260
- };
261
-
262
- return mapping[legacyType] || InstallationMode.UNKNOWN;
263
- }
264
-
265
- /**
266
- * Validates user mode selection against auto-detection
267
- *
268
- * @param {string} selectedMode - User-selected mode
269
- * @param {DetectionResult} detected - Auto-detected result
270
- * @returns {Object} Validation result with warnings if mismatch
271
- */
272
- function validateModeSelection(selectedMode, detected) {
273
- const result = {
274
- isValid: true,
275
- warnings: [],
276
- suggestions: [],
277
- };
278
-
279
- // Allow any selection for UNKNOWN detection
280
- if (detected.mode === InstallationMode.UNKNOWN) {
281
- return result;
282
- }
283
-
284
- // Check for mismatches
285
- if (selectedMode !== detected.mode) {
286
- if (selectedMode === InstallationMode.GREENFIELD && !detected.markers.isEmpty) {
287
- result.warnings.push(
288
- 'Selected greenfield but directory is not empty. Existing files may be overwritten.',
289
- );
290
- }
291
-
292
- if (selectedMode === InstallationMode.FRAMEWORK_DEV && !detected.markers.isAiosCoreRepo) {
293
- result.warnings.push(
294
- 'Selected framework-dev but this does not appear to be the aios-core repository.',
295
- );
296
- }
297
-
298
- if (selectedMode === InstallationMode.BROWNFIELD && detected.markers.isEmpty) {
299
- result.warnings.push(
300
- 'Selected brownfield but directory is empty. Consider using greenfield instead.',
301
- );
302
- result.suggestions.push(InstallationMode.GREENFIELD);
303
- }
304
- }
305
-
306
- return result;
307
- }
308
-
309
- /**
310
- * Gets mode options for wizard display
311
- *
312
- * @param {DetectionResult} [detected] - Optional detected result to highlight recommended
313
- * @returns {Array<Object>} Array of mode options for wizard
314
- */
315
- function getModeOptions(detected = null) {
316
- const options = [
317
- {
318
- value: InstallationMode.GREENFIELD,
319
- label: ModeDescriptions[InstallationMode.GREENFIELD].label,
320
- hint: ModeDescriptions[InstallationMode.GREENFIELD].hint,
321
- },
322
- {
323
- value: InstallationMode.BROWNFIELD,
324
- label: ModeDescriptions[InstallationMode.BROWNFIELD].label,
325
- hint: ModeDescriptions[InstallationMode.BROWNFIELD].hint,
326
- },
327
- {
328
- value: InstallationMode.FRAMEWORK_DEV,
329
- label: ModeDescriptions[InstallationMode.FRAMEWORK_DEV].label,
330
- hint: ModeDescriptions[InstallationMode.FRAMEWORK_DEV].hint,
331
- },
332
- ];
333
-
334
- // If we have detection, mark recommended option
335
- if (detected && detected.mode !== InstallationMode.UNKNOWN) {
336
- const recommendedIndex = options.findIndex((opt) => opt.value === detected.mode);
337
- if (recommendedIndex >= 0) {
338
- options[recommendedIndex].hint += ' (Recommended)';
339
- // Move recommended to top
340
- const recommended = options.splice(recommendedIndex, 1)[0];
341
- options.unshift(recommended);
342
- }
343
- }
344
-
345
- return options;
346
- }
347
-
348
- module.exports = {
349
- detectInstallationMode,
350
- collectMarkers,
351
- isAiosCoreRepository,
352
- mapLegacyTypeToMode,
353
- validateModeSelection,
354
- getModeOptions,
355
- InstallationMode,
356
- LegacyProjectType,
357
- ModeDescriptions,
358
- };
1
+ /**
2
+ * Mode Detector Module
3
+ *
4
+ * Detects installation mode for AIOS projects with three-mode support:
5
+ * - FRAMEWORK_DEV: Contributing to aios-core itself
6
+ * - GREENFIELD: New empty project
7
+ * - BROWNFIELD: Existing project being integrated
8
+ *
9
+ * @module documentation-integrity/mode-detector
10
+ * @version 1.0.0
11
+ * @story 6.9
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ /**
18
+ * Installation modes supported by AIOS
19
+ * @enum {string}
20
+ */
21
+ const InstallationMode = {
22
+ FRAMEWORK_DEV: 'framework-dev',
23
+ GREENFIELD: 'greenfield',
24
+ BROWNFIELD: 'brownfield',
25
+ UNKNOWN: 'unknown',
26
+ };
27
+
28
+ /**
29
+ * Legacy project type mappings (for backward compatibility)
30
+ * @enum {string}
31
+ */
32
+ const LegacyProjectType = {
33
+ EXISTING_AIOS: 'EXISTING_AIOS',
34
+ GREENFIELD: 'GREENFIELD',
35
+ BROWNFIELD: 'BROWNFIELD',
36
+ UNKNOWN: 'UNKNOWN',
37
+ };
38
+
39
+ /**
40
+ * Mode descriptions for display in wizard
41
+ * @type {Object.<string, Object>}
42
+ */
43
+ const ModeDescriptions = {
44
+ [InstallationMode.FRAMEWORK_DEV]: {
45
+ label: '🔧 Framework Development',
46
+ hint: 'Developing aios-core itself - uses framework standards, skips project setup',
47
+ description: 'For AIOS contributors working on the framework',
48
+ },
49
+ [InstallationMode.GREENFIELD]: {
50
+ label: '🆕 New Project (Greenfield)',
51
+ hint: 'Start a fresh project with AIOS - generates project docs, config, and infrastructure',
52
+ description: 'Empty directory setup with full scaffolding',
53
+ },
54
+ [InstallationMode.BROWNFIELD]: {
55
+ label: '📂 Existing Project (Brownfield)',
56
+ hint: 'Add AIOS to existing project - analyzes current structure and adapts',
57
+ description: 'Integration with existing codebase',
58
+ },
59
+ [InstallationMode.UNKNOWN]: {
60
+ label: '❓ Unknown',
61
+ hint: 'Could not determine project type - manual selection required',
62
+ description: 'Manual mode selection needed',
63
+ },
64
+ };
65
+
66
+ /**
67
+ * Detection result with confidence and reasoning
68
+ * @typedef {Object} DetectionResult
69
+ * @property {string} mode - Detected installation mode
70
+ * @property {string} legacyType - Legacy project type for backward compatibility
71
+ * @property {number} confidence - Detection confidence (0-100)
72
+ * @property {string} reason - Human-readable reason for detection
73
+ * @property {Object} markers - Detected markers in the directory
74
+ */
75
+
76
+ /**
77
+ * Detects the installation mode for a target directory
78
+ *
79
+ * Detection Priority Order:
80
+ * 1. FRAMEWORK_DEV - .aios-core/ exists AND is aios-core repo
81
+ * 2. GREENFIELD - directory is empty
82
+ * 3. BROWNFIELD - has package.json, .git, or other project markers
83
+ * 4. UNKNOWN - has files but no recognized markers
84
+ *
85
+ * @param {string} targetDir - Directory to analyze
86
+ * @returns {DetectionResult} Detection result with mode and metadata
87
+ * @throws {Error} If directory cannot be accessed
88
+ */
89
+ function detectInstallationMode(targetDir = process.cwd()) {
90
+ // Validate input
91
+ if (!targetDir || typeof targetDir !== 'string') {
92
+ throw new Error('Invalid targetDir parameter: must be a non-empty string');
93
+ }
94
+
95
+ const normalizedDir = path.resolve(targetDir);
96
+
97
+ // Check if directory exists
98
+ if (!fs.existsSync(normalizedDir)) {
99
+ throw new Error(`Directory does not exist: ${normalizedDir}`);
100
+ }
101
+
102
+ // Collect markers
103
+ const markers = collectMarkers(normalizedDir);
104
+
105
+ // Priority 1: Check for AIOS framework development
106
+ if (markers.hasAiosCore && markers.isAiosCoreRepo) {
107
+ return {
108
+ mode: InstallationMode.FRAMEWORK_DEV,
109
+ legacyType: LegacyProjectType.EXISTING_AIOS,
110
+ confidence: 100,
111
+ reason: 'Detected aios-core repository with .aios-core directory',
112
+ markers,
113
+ };
114
+ }
115
+
116
+ // Priority 2: Check for existing AIOS installation (non-framework)
117
+ if (markers.hasAiosCore && !markers.isAiosCoreRepo) {
118
+ return {
119
+ mode: InstallationMode.BROWNFIELD,
120
+ legacyType: LegacyProjectType.EXISTING_AIOS,
121
+ confidence: 95,
122
+ reason: 'AIOS already installed in user project - treating as brownfield update',
123
+ markers,
124
+ };
125
+ }
126
+
127
+ // Priority 3: Check for empty directory (greenfield)
128
+ if (markers.isEmpty) {
129
+ return {
130
+ mode: InstallationMode.GREENFIELD,
131
+ legacyType: LegacyProjectType.GREENFIELD,
132
+ confidence: 100,
133
+ reason: 'Empty directory detected',
134
+ markers,
135
+ };
136
+ }
137
+
138
+ // Priority 4: Check for project markers (brownfield)
139
+ if (
140
+ markers.hasPackageJson ||
141
+ markers.hasGit ||
142
+ markers.hasPythonProject ||
143
+ markers.hasGoMod ||
144
+ markers.hasCargoToml
145
+ ) {
146
+ const projectTypes = [];
147
+ if (markers.hasPackageJson) projectTypes.push('Node.js');
148
+ if (markers.hasPythonProject) projectTypes.push('Python');
149
+ if (markers.hasGoMod) projectTypes.push('Go');
150
+ if (markers.hasCargoToml) projectTypes.push('Rust');
151
+
152
+ return {
153
+ mode: InstallationMode.BROWNFIELD,
154
+ legacyType: LegacyProjectType.BROWNFIELD,
155
+ confidence: 90,
156
+ reason: `Existing project detected: ${projectTypes.join(', ') || 'Git repository'}`,
157
+ markers,
158
+ };
159
+ }
160
+
161
+ // Priority 5: Directory has files but no recognized markers
162
+ return {
163
+ mode: InstallationMode.UNKNOWN,
164
+ legacyType: LegacyProjectType.UNKNOWN,
165
+ confidence: 0,
166
+ reason: 'Directory has files but no recognized project markers',
167
+ markers,
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Collects all relevant markers from a directory
173
+ *
174
+ * @param {string} targetDir - Directory to scan
175
+ * @returns {Object} Object containing all detected markers
176
+ */
177
+ function collectMarkers(targetDir) {
178
+ const dirContents = fs.readdirSync(targetDir);
179
+
180
+ return {
181
+ // AIOS markers
182
+ hasAiosCore: fs.existsSync(path.join(targetDir, '.aios-core')),
183
+ isAiosCoreRepo: isAiosCoreRepository(targetDir),
184
+
185
+ // Directory state
186
+ isEmpty: dirContents.length === 0,
187
+ fileCount: dirContents.length,
188
+
189
+ // Project markers
190
+ hasPackageJson: fs.existsSync(path.join(targetDir, 'package.json')),
191
+ hasGit: fs.existsSync(path.join(targetDir, '.git')),
192
+
193
+ // Python markers
194
+ hasPythonProject:
195
+ fs.existsSync(path.join(targetDir, 'requirements.txt')) ||
196
+ fs.existsSync(path.join(targetDir, 'pyproject.toml')) ||
197
+ fs.existsSync(path.join(targetDir, 'setup.py')),
198
+
199
+ // Go markers
200
+ hasGoMod: fs.existsSync(path.join(targetDir, 'go.mod')),
201
+
202
+ // Rust markers
203
+ hasCargoToml: fs.existsSync(path.join(targetDir, 'Cargo.toml')),
204
+
205
+ // Existing standards markers
206
+ hasEslintrc:
207
+ fs.existsSync(path.join(targetDir, '.eslintrc.js')) ||
208
+ fs.existsSync(path.join(targetDir, '.eslintrc.json')) ||
209
+ fs.existsSync(path.join(targetDir, '.eslintrc.yaml')),
210
+ hasPrettierrc:
211
+ fs.existsSync(path.join(targetDir, '.prettierrc')) ||
212
+ fs.existsSync(path.join(targetDir, '.prettierrc.json')) ||
213
+ fs.existsSync(path.join(targetDir, 'prettier.config.js')),
214
+ hasTsconfig: fs.existsSync(path.join(targetDir, 'tsconfig.json')),
215
+
216
+ // CI/CD markers
217
+ hasGithubWorkflows: fs.existsSync(path.join(targetDir, '.github', 'workflows')),
218
+ hasGitlabCi: fs.existsSync(path.join(targetDir, '.gitlab-ci.yml')),
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Checks if the target directory is the aios-core repository itself
224
+ *
225
+ * @param {string} targetDir - Directory to check
226
+ * @returns {boolean} True if this is the aios-core repository
227
+ */
228
+ function isAiosCoreRepository(targetDir) {
229
+ const packageJsonPath = path.join(targetDir, 'package.json');
230
+
231
+ if (!fs.existsSync(packageJsonPath)) {
232
+ return false;
233
+ }
234
+
235
+ try {
236
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
237
+
238
+ // Primary check: explicit aios-core package names
239
+ if (packageJson.name === '@aios/core' || packageJson.name === 'aios-core') {
240
+ return true;
241
+ }
242
+
243
+ // Secondary check: workspaces pattern + aios-specific marker file
244
+ // This prevents false positives for generic monorepos
245
+ const hasAiosMarker = fs.existsSync(path.join(targetDir, '.aios-core', 'infrastructure'));
246
+ const hasWorkspaces =
247
+ Array.isArray(packageJson.workspaces) && packageJson.workspaces.includes('packages/*');
248
+
249
+ return hasWorkspaces && hasAiosMarker;
250
+ } catch (error) {
251
+ // Log error for debugging but don't throw - return false for safety
252
+ if (process.env.AIOS_DEBUG) {
253
+ console.warn(`[mode-detector] Error checking aios-core repository: ${error.message}`);
254
+ }
255
+ return false;
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Maps legacy project type to new installation mode
261
+ *
262
+ * @deprecated Use detectInstallationMode().mode directly instead.
263
+ * This function cannot distinguish between FRAMEWORK_DEV and BROWNFIELD
264
+ * for EXISTING_AIOS legacy type (both use EXISTING_AIOS but with different modes).
265
+ * For accurate mode detection, use the mode property from detectInstallationMode().
266
+ *
267
+ * @param {string} legacyType - Legacy project type (EXISTING_AIOS, GREENFIELD, etc.)
268
+ * @param {Object} [context] - Optional context for disambiguation
269
+ * @param {boolean} [context.isAiosCoreRepo] - True if this is the aios-core repository
270
+ * @returns {string} New installation mode
271
+ */
272
+ function mapLegacyTypeToMode(legacyType, context = {}) {
273
+ // EXISTING_AIOS is ambiguous: it can be FRAMEWORK_DEV (aios-core repo)
274
+ // or BROWNFIELD (user project with AIOS installed)
275
+ if (legacyType === LegacyProjectType.EXISTING_AIOS) {
276
+ // Use context to disambiguate if provided
277
+ if (context.isAiosCoreRepo === true) {
278
+ return InstallationMode.FRAMEWORK_DEV;
279
+ }
280
+ if (context.isAiosCoreRepo === false) {
281
+ return InstallationMode.BROWNFIELD;
282
+ }
283
+ // Default to BROWNFIELD as it's safer (won't skip project setup)
284
+ return InstallationMode.BROWNFIELD;
285
+ }
286
+
287
+ const mapping = {
288
+ [LegacyProjectType.GREENFIELD]: InstallationMode.GREENFIELD,
289
+ [LegacyProjectType.BROWNFIELD]: InstallationMode.BROWNFIELD,
290
+ [LegacyProjectType.UNKNOWN]: InstallationMode.UNKNOWN,
291
+ };
292
+
293
+ return mapping[legacyType] || InstallationMode.UNKNOWN;
294
+ }
295
+
296
+ /**
297
+ * Validates user mode selection against auto-detection
298
+ *
299
+ * @param {string} selectedMode - User-selected mode
300
+ * @param {DetectionResult} detected - Auto-detected result
301
+ * @returns {Object} Validation result with warnings if mismatch
302
+ */
303
+ function validateModeSelection(selectedMode, detected) {
304
+ const result = {
305
+ isValid: true,
306
+ warnings: [],
307
+ suggestions: [],
308
+ };
309
+
310
+ // Allow any selection for UNKNOWN detection
311
+ if (detected.mode === InstallationMode.UNKNOWN) {
312
+ return result;
313
+ }
314
+
315
+ // Check for mismatches
316
+ if (selectedMode !== detected.mode) {
317
+ if (selectedMode === InstallationMode.GREENFIELD && !detected.markers.isEmpty) {
318
+ result.warnings.push(
319
+ 'Selected greenfield but directory is not empty. Existing files may be overwritten.',
320
+ );
321
+ }
322
+
323
+ if (selectedMode === InstallationMode.FRAMEWORK_DEV && !detected.markers.isAiosCoreRepo) {
324
+ result.warnings.push(
325
+ 'Selected framework-dev but this does not appear to be the aios-core repository.',
326
+ );
327
+ }
328
+
329
+ if (selectedMode === InstallationMode.BROWNFIELD && detected.markers.isEmpty) {
330
+ result.warnings.push(
331
+ 'Selected brownfield but directory is empty. Consider using greenfield instead.',
332
+ );
333
+ result.suggestions.push(InstallationMode.GREENFIELD);
334
+ }
335
+ }
336
+
337
+ return result;
338
+ }
339
+
340
+ /**
341
+ * Gets mode options for wizard display
342
+ *
343
+ * @param {DetectionResult} [detected] - Optional detected result to highlight recommended
344
+ * @returns {Array<Object>} Array of mode options for wizard
345
+ */
346
+ function getModeOptions(detected = null) {
347
+ const options = [
348
+ {
349
+ value: InstallationMode.GREENFIELD,
350
+ label: ModeDescriptions[InstallationMode.GREENFIELD].label,
351
+ hint: ModeDescriptions[InstallationMode.GREENFIELD].hint,
352
+ },
353
+ {
354
+ value: InstallationMode.BROWNFIELD,
355
+ label: ModeDescriptions[InstallationMode.BROWNFIELD].label,
356
+ hint: ModeDescriptions[InstallationMode.BROWNFIELD].hint,
357
+ },
358
+ {
359
+ value: InstallationMode.FRAMEWORK_DEV,
360
+ label: ModeDescriptions[InstallationMode.FRAMEWORK_DEV].label,
361
+ hint: ModeDescriptions[InstallationMode.FRAMEWORK_DEV].hint,
362
+ },
363
+ ];
364
+
365
+ // If we have detection, mark recommended option
366
+ if (detected && detected.mode !== InstallationMode.UNKNOWN) {
367
+ const recommendedIndex = options.findIndex((opt) => opt.value === detected.mode);
368
+ if (recommendedIndex >= 0) {
369
+ options[recommendedIndex].hint += ' (Recommended)';
370
+ // Move recommended to top
371
+ const recommended = options.splice(recommendedIndex, 1)[0];
372
+ options.unshift(recommended);
373
+ }
374
+ }
375
+
376
+ return options;
377
+ }
378
+
379
+ module.exports = {
380
+ detectInstallationMode,
381
+ collectMarkers,
382
+ isAiosCoreRepository,
383
+ mapLegacyTypeToMode,
384
+ validateModeSelection,
385
+ getModeOptions,
386
+ InstallationMode,
387
+ LegacyProjectType,
388
+ ModeDescriptions,
389
+ };