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.
- package/.aios-core/development/tasks/analyze-brownfield.md +456 -456
- package/.aios-core/development/tasks/setup-project-docs.md +440 -444
- package/.aios-core/infrastructure/scripts/documentation-integrity/brownfield-analyzer.js +501 -501
- package/.aios-core/infrastructure/scripts/documentation-integrity/config-generator.js +368 -329
- package/.aios-core/infrastructure/scripts/documentation-integrity/deployment-config-loader.js +308 -282
- package/.aios-core/infrastructure/scripts/documentation-integrity/doc-generator.js +331 -331
- package/.aios-core/infrastructure/scripts/documentation-integrity/gitignore-generator.js +312 -312
- package/.aios-core/infrastructure/scripts/documentation-integrity/index.js +74 -74
- package/.aios-core/infrastructure/scripts/documentation-integrity/mode-detector.js +389 -358
- package/.aios-core/infrastructure/scripts/llm-routing/install-llm-routing.js +6 -6
- package/.aios-core/infrastructure/templates/core-config/core-config-brownfield.tmpl.yaml +176 -182
- package/.aios-core/infrastructure/templates/core-config/core-config-greenfield.tmpl.yaml +127 -127
- package/.aios-core/infrastructure/templates/project-docs/coding-standards-tmpl.md +346 -346
- package/.aios-core/infrastructure/templates/project-docs/source-tree-tmpl.md +177 -177
- package/.aios-core/infrastructure/templates/project-docs/tech-stack-tmpl.md +267 -267
- package/package.json +1 -1
- package/packages/installer/src/config/templates/env-template.js +2 -2
- package/packages/installer/src/wizard/wizard.js +1 -1
- package/packages/installer/tests/integration/environment-configuration.test.js +2 -1
- package/packages/installer/tests/unit/env-template.test.js +3 -2
- package/src/wizard/index.js +2 -2
- package/.aios-core/development/tasks/validate-structure.md +0 -243
- package/.aios-core/infrastructure/scripts/source-tree-guardian/index.js +0 -375
- package/.aios-core/infrastructure/scripts/source-tree-guardian/manifest-generator.js +0 -410
- package/.aios-core/infrastructure/scripts/source-tree-guardian/rules/naming-rules.yaml +0 -285
- package/.aios-core/infrastructure/scripts/source-tree-guardian/rules/placement-rules.yaml +0 -262
- package/.aios-core/infrastructure/scripts/source-tree-guardian/validator.js +0 -468
|
@@ -1,312 +1,312 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gitignore Generator Module
|
|
3
|
-
*
|
|
4
|
-
* Generates .gitignore files based on detected tech stack.
|
|
5
|
-
* Supports brownfield merge mode to preserve existing ignores.
|
|
6
|
-
*
|
|
7
|
-
* @module documentation-integrity/gitignore-generator
|
|
8
|
-
* @version 1.0.0
|
|
9
|
-
* @story 6.9
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
|
|
15
|
-
// Template directory
|
|
16
|
-
const TEMPLATES_DIR = path.join(__dirname, '..', '..', 'templates', 'gitignore');
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Template file names
|
|
20
|
-
* @enum {string}
|
|
21
|
-
*/
|
|
22
|
-
const GitignoreTemplates = {
|
|
23
|
-
AIOS_BASE: 'gitignore-aios-base.tmpl',
|
|
24
|
-
NODE: 'gitignore-node.tmpl',
|
|
25
|
-
PYTHON: 'gitignore-python.tmpl',
|
|
26
|
-
BROWNFIELD_MERGE: 'gitignore-brownfield-merge.tmpl',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Tech stack identifiers
|
|
31
|
-
* @enum {string}
|
|
32
|
-
*/
|
|
33
|
-
const TechStack = {
|
|
34
|
-
NODE: 'node',
|
|
35
|
-
PYTHON: 'python',
|
|
36
|
-
GO: 'go',
|
|
37
|
-
RUST: 'rust',
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Loads a gitignore template
|
|
42
|
-
*
|
|
43
|
-
* @param {string} templateName - Template file name
|
|
44
|
-
* @returns {string} Template content
|
|
45
|
-
* @throws {Error} If template not found
|
|
46
|
-
*/
|
|
47
|
-
function loadGitignoreTemplate(templateName) {
|
|
48
|
-
const templatePath = path.join(TEMPLATES_DIR, templateName);
|
|
49
|
-
|
|
50
|
-
if (!fs.existsSync(templatePath)) {
|
|
51
|
-
throw new Error(`Gitignore template not found: ${templatePath}`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return fs.readFileSync(templatePath, 'utf8');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Detects tech stack from project markers
|
|
59
|
-
*
|
|
60
|
-
* @param {Object} markers - Detected project markers
|
|
61
|
-
* @returns {string[]} List of detected tech stacks
|
|
62
|
-
*/
|
|
63
|
-
function detectTechStacks(markers) {
|
|
64
|
-
const stacks = [];
|
|
65
|
-
|
|
66
|
-
if (markers.hasPackageJson) stacks.push(TechStack.NODE);
|
|
67
|
-
if (markers.hasPythonProject) stacks.push(TechStack.PYTHON);
|
|
68
|
-
if (markers.hasGoMod) stacks.push(TechStack.GO);
|
|
69
|
-
if (markers.hasCargoToml) stacks.push(TechStack.RUST);
|
|
70
|
-
|
|
71
|
-
return stacks;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Gets gitignore templates for detected tech stacks
|
|
76
|
-
*
|
|
77
|
-
* @param {string[]} techStacks - Detected tech stacks
|
|
78
|
-
* @returns {string[]} List of template names to use
|
|
79
|
-
*/
|
|
80
|
-
function getTemplatesForStacks(techStacks) {
|
|
81
|
-
const templates = [GitignoreTemplates.AIOS_BASE];
|
|
82
|
-
|
|
83
|
-
for (const stack of techStacks) {
|
|
84
|
-
switch (stack) {
|
|
85
|
-
case TechStack.NODE:
|
|
86
|
-
templates.push(GitignoreTemplates.NODE);
|
|
87
|
-
break;
|
|
88
|
-
case TechStack.PYTHON:
|
|
89
|
-
templates.push(GitignoreTemplates.PYTHON);
|
|
90
|
-
break;
|
|
91
|
-
// Go and Rust would have their own templates
|
|
92
|
-
// Add when implemented
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return templates;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Generates a complete .gitignore file
|
|
101
|
-
*
|
|
102
|
-
* @param {Object} markers - Detected project markers
|
|
103
|
-
* @param {Object} [options] - Generation options
|
|
104
|
-
* @param {string} [options.projectName] - Project name for header
|
|
105
|
-
* @returns {string} Generated gitignore content
|
|
106
|
-
*/
|
|
107
|
-
function generateGitignore(markers, options = {}) {
|
|
108
|
-
const techStacks = detectTechStacks(markers);
|
|
109
|
-
const templates = getTemplatesForStacks(techStacks);
|
|
110
|
-
|
|
111
|
-
const sections = [];
|
|
112
|
-
|
|
113
|
-
// Add project header
|
|
114
|
-
const projectName = options.projectName || 'Project';
|
|
115
|
-
sections.push(`# ${projectName} .gitignore`);
|
|
116
|
-
sections.push('# Generated by AIOS Documentation Integrity System');
|
|
117
|
-
sections.push(`# Date: ${new Date().toISOString().split('T')[0]}`);
|
|
118
|
-
sections.push(`# Tech Stack: ${techStacks.join(', ') || 'Generic'}`);
|
|
119
|
-
sections.push('');
|
|
120
|
-
|
|
121
|
-
// Load and append each template
|
|
122
|
-
for (const templateName of templates) {
|
|
123
|
-
try {
|
|
124
|
-
const content = loadGitignoreTemplate(templateName);
|
|
125
|
-
sections.push(content);
|
|
126
|
-
sections.push(''); // Add blank line between sections
|
|
127
|
-
} catch (error) {
|
|
128
|
-
console.warn(`Warning: Could not load template ${templateName}: ${error.message}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return sections.join('\n').trim() + '\n';
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Merges AIOS ignores with existing .gitignore
|
|
137
|
-
*
|
|
138
|
-
* @param {string} existingContent - Existing .gitignore content
|
|
139
|
-
* @param {Object} [options] - Merge options
|
|
140
|
-
* @returns {string} Merged gitignore content
|
|
141
|
-
*/
|
|
142
|
-
function mergeGitignore(existingContent, _options = {}) {
|
|
143
|
-
// Check if AIOS section already exists
|
|
144
|
-
if (existingContent.includes('AIOS Integration Section')) {
|
|
145
|
-
console.log('AIOS section already exists in .gitignore, skipping merge');
|
|
146
|
-
return existingContent;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Load merge template
|
|
150
|
-
let mergeSection;
|
|
151
|
-
try {
|
|
152
|
-
mergeSection = loadGitignoreTemplate(GitignoreTemplates.BROWNFIELD_MERGE);
|
|
153
|
-
} catch {
|
|
154
|
-
// Fallback to minimal section
|
|
155
|
-
mergeSection = `
|
|
156
|
-
# ========================================
|
|
157
|
-
# AIOS Integration Section
|
|
158
|
-
# ========================================
|
|
159
|
-
|
|
160
|
-
# AIOS Local Configuration
|
|
161
|
-
.aios-core/local/
|
|
162
|
-
.aios-core/*.local.yaml
|
|
163
|
-
.aios-core/logs/
|
|
164
|
-
.aios-core/cache/
|
|
165
|
-
|
|
166
|
-
# ========================================
|
|
167
|
-
# End of AIOS Integration Section
|
|
168
|
-
# ========================================
|
|
169
|
-
`;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Replace date placeholder
|
|
173
|
-
mergeSection = mergeSection.replace(
|
|
174
|
-
'{{GENERATED_DATE}}',
|
|
175
|
-
new Date().toISOString().split('T')[0],
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
// Append to existing content
|
|
179
|
-
const merged = existingContent.trimEnd() + '\n\n' + mergeSection.trim() + '\n';
|
|
180
|
-
|
|
181
|
-
return merged;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Generates or merges .gitignore for a project
|
|
186
|
-
*
|
|
187
|
-
* @param {string} targetDir - Target directory
|
|
188
|
-
* @param {Object} markers - Detected project markers
|
|
189
|
-
* @param {Object} [options] - Generation options
|
|
190
|
-
* @param {boolean} [options.dryRun] - Don't write file, just return content
|
|
191
|
-
* @param {boolean} [options.merge] - Merge with existing instead of replace
|
|
192
|
-
* @param {string} [options.projectName] - Project name for header
|
|
193
|
-
* @returns {Object} Generation result
|
|
194
|
-
*/
|
|
195
|
-
function generateGitignoreFile(targetDir, markers, options = {}) {
|
|
196
|
-
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
197
|
-
const existingPath = fs.existsSync(gitignorePath);
|
|
198
|
-
|
|
199
|
-
let content;
|
|
200
|
-
let mode;
|
|
201
|
-
|
|
202
|
-
if (existingPath && options.merge !== false) {
|
|
203
|
-
// Brownfield mode - merge with existing
|
|
204
|
-
const existing = fs.readFileSync(gitignorePath, 'utf8');
|
|
205
|
-
content = mergeGitignore(existing, options);
|
|
206
|
-
mode = 'merged';
|
|
207
|
-
} else {
|
|
208
|
-
// Greenfield mode - generate new
|
|
209
|
-
content = generateGitignore(markers, options);
|
|
210
|
-
mode = existingPath ? 'replaced' : 'created';
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const result = {
|
|
214
|
-
success: true,
|
|
215
|
-
path: gitignorePath,
|
|
216
|
-
content,
|
|
217
|
-
mode,
|
|
218
|
-
techStacks: detectTechStacks(markers),
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
if (!options.dryRun) {
|
|
222
|
-
try {
|
|
223
|
-
fs.writeFileSync(gitignorePath, content, 'utf8');
|
|
224
|
-
} catch (error) {
|
|
225
|
-
result.success = false;
|
|
226
|
-
result.error = error.message;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return result;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Checks if a .gitignore file has AIOS integration
|
|
235
|
-
*
|
|
236
|
-
* @param {string} targetDir - Target directory
|
|
237
|
-
* @returns {boolean} True if AIOS section exists
|
|
238
|
-
*/
|
|
239
|
-
function hasAiosIntegration(targetDir) {
|
|
240
|
-
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
241
|
-
|
|
242
|
-
if (!fs.existsSync(gitignorePath)) {
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
247
|
-
return content.includes('AIOS Integration Section') || content.includes('.aios-core/');
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Parses an existing .gitignore into sections
|
|
252
|
-
*
|
|
253
|
-
* @param {string} content - .gitignore content
|
|
254
|
-
* @returns {Object} Parsed sections
|
|
255
|
-
*/
|
|
256
|
-
function parseGitignore(content) {
|
|
257
|
-
const lines = content.split('\n');
|
|
258
|
-
const sections = {
|
|
259
|
-
header: [],
|
|
260
|
-
patterns: [],
|
|
261
|
-
aiosSection: null,
|
|
262
|
-
comments: [],
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
let aiosStart = -1;
|
|
266
|
-
let aiosEnd = -1;
|
|
267
|
-
|
|
268
|
-
for (let i = 0; i < lines.length; i++) {
|
|
269
|
-
const line = lines[i];
|
|
270
|
-
|
|
271
|
-
if (line.includes('AIOS Integration Section')) {
|
|
272
|
-
if (aiosStart === -1) {
|
|
273
|
-
aiosStart = i;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (line.includes('End of AIOS Integration Section')) {
|
|
278
|
-
aiosEnd = i;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (line.startsWith('#') && !line.includes('AIOS')) {
|
|
282
|
-
sections.comments.push({ line: i, content: line });
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (!line.startsWith('#') && line.trim()) {
|
|
286
|
-
sections.patterns.push({ line: i, pattern: line.trim() });
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (aiosStart !== -1) {
|
|
291
|
-
sections.aiosSection = {
|
|
292
|
-
start: aiosStart,
|
|
293
|
-
end: aiosEnd !== -1 ? aiosEnd : lines.length - 1,
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return sections;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
module.exports = {
|
|
301
|
-
loadGitignoreTemplate,
|
|
302
|
-
detectTechStacks,
|
|
303
|
-
getTemplatesForStacks,
|
|
304
|
-
generateGitignore,
|
|
305
|
-
mergeGitignore,
|
|
306
|
-
generateGitignoreFile,
|
|
307
|
-
hasAiosIntegration,
|
|
308
|
-
parseGitignore,
|
|
309
|
-
GitignoreTemplates,
|
|
310
|
-
TechStack,
|
|
311
|
-
TEMPLATES_DIR,
|
|
312
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Gitignore Generator Module
|
|
3
|
+
*
|
|
4
|
+
* Generates .gitignore files based on detected tech stack.
|
|
5
|
+
* Supports brownfield merge mode to preserve existing ignores.
|
|
6
|
+
*
|
|
7
|
+
* @module documentation-integrity/gitignore-generator
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
* @story 6.9
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
// Template directory
|
|
16
|
+
const TEMPLATES_DIR = path.join(__dirname, '..', '..', 'templates', 'gitignore');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Template file names
|
|
20
|
+
* @enum {string}
|
|
21
|
+
*/
|
|
22
|
+
const GitignoreTemplates = {
|
|
23
|
+
AIOS_BASE: 'gitignore-aios-base.tmpl',
|
|
24
|
+
NODE: 'gitignore-node.tmpl',
|
|
25
|
+
PYTHON: 'gitignore-python.tmpl',
|
|
26
|
+
BROWNFIELD_MERGE: 'gitignore-brownfield-merge.tmpl',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Tech stack identifiers
|
|
31
|
+
* @enum {string}
|
|
32
|
+
*/
|
|
33
|
+
const TechStack = {
|
|
34
|
+
NODE: 'node',
|
|
35
|
+
PYTHON: 'python',
|
|
36
|
+
GO: 'go',
|
|
37
|
+
RUST: 'rust',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Loads a gitignore template
|
|
42
|
+
*
|
|
43
|
+
* @param {string} templateName - Template file name
|
|
44
|
+
* @returns {string} Template content
|
|
45
|
+
* @throws {Error} If template not found
|
|
46
|
+
*/
|
|
47
|
+
function loadGitignoreTemplate(templateName) {
|
|
48
|
+
const templatePath = path.join(TEMPLATES_DIR, templateName);
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(templatePath)) {
|
|
51
|
+
throw new Error(`Gitignore template not found: ${templatePath}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return fs.readFileSync(templatePath, 'utf8');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Detects tech stack from project markers
|
|
59
|
+
*
|
|
60
|
+
* @param {Object} markers - Detected project markers
|
|
61
|
+
* @returns {string[]} List of detected tech stacks
|
|
62
|
+
*/
|
|
63
|
+
function detectTechStacks(markers) {
|
|
64
|
+
const stacks = [];
|
|
65
|
+
|
|
66
|
+
if (markers.hasPackageJson) stacks.push(TechStack.NODE);
|
|
67
|
+
if (markers.hasPythonProject) stacks.push(TechStack.PYTHON);
|
|
68
|
+
if (markers.hasGoMod) stacks.push(TechStack.GO);
|
|
69
|
+
if (markers.hasCargoToml) stacks.push(TechStack.RUST);
|
|
70
|
+
|
|
71
|
+
return stacks;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Gets gitignore templates for detected tech stacks
|
|
76
|
+
*
|
|
77
|
+
* @param {string[]} techStacks - Detected tech stacks
|
|
78
|
+
* @returns {string[]} List of template names to use
|
|
79
|
+
*/
|
|
80
|
+
function getTemplatesForStacks(techStacks) {
|
|
81
|
+
const templates = [GitignoreTemplates.AIOS_BASE];
|
|
82
|
+
|
|
83
|
+
for (const stack of techStacks) {
|
|
84
|
+
switch (stack) {
|
|
85
|
+
case TechStack.NODE:
|
|
86
|
+
templates.push(GitignoreTemplates.NODE);
|
|
87
|
+
break;
|
|
88
|
+
case TechStack.PYTHON:
|
|
89
|
+
templates.push(GitignoreTemplates.PYTHON);
|
|
90
|
+
break;
|
|
91
|
+
// Go and Rust would have their own templates
|
|
92
|
+
// Add when implemented
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return templates;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Generates a complete .gitignore file
|
|
101
|
+
*
|
|
102
|
+
* @param {Object} markers - Detected project markers
|
|
103
|
+
* @param {Object} [options] - Generation options
|
|
104
|
+
* @param {string} [options.projectName] - Project name for header
|
|
105
|
+
* @returns {string} Generated gitignore content
|
|
106
|
+
*/
|
|
107
|
+
function generateGitignore(markers, options = {}) {
|
|
108
|
+
const techStacks = detectTechStacks(markers);
|
|
109
|
+
const templates = getTemplatesForStacks(techStacks);
|
|
110
|
+
|
|
111
|
+
const sections = [];
|
|
112
|
+
|
|
113
|
+
// Add project header
|
|
114
|
+
const projectName = options.projectName || 'Project';
|
|
115
|
+
sections.push(`# ${projectName} .gitignore`);
|
|
116
|
+
sections.push('# Generated by AIOS Documentation Integrity System');
|
|
117
|
+
sections.push(`# Date: ${new Date().toISOString().split('T')[0]}`);
|
|
118
|
+
sections.push(`# Tech Stack: ${techStacks.join(', ') || 'Generic'}`);
|
|
119
|
+
sections.push('');
|
|
120
|
+
|
|
121
|
+
// Load and append each template
|
|
122
|
+
for (const templateName of templates) {
|
|
123
|
+
try {
|
|
124
|
+
const content = loadGitignoreTemplate(templateName);
|
|
125
|
+
sections.push(content);
|
|
126
|
+
sections.push(''); // Add blank line between sections
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.warn(`Warning: Could not load template ${templateName}: ${error.message}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return sections.join('\n').trim() + '\n';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Merges AIOS ignores with existing .gitignore
|
|
137
|
+
*
|
|
138
|
+
* @param {string} existingContent - Existing .gitignore content
|
|
139
|
+
* @param {Object} [options] - Merge options
|
|
140
|
+
* @returns {string} Merged gitignore content
|
|
141
|
+
*/
|
|
142
|
+
function mergeGitignore(existingContent, _options = {}) {
|
|
143
|
+
// Check if AIOS section already exists
|
|
144
|
+
if (existingContent.includes('AIOS Integration Section')) {
|
|
145
|
+
console.log('AIOS section already exists in .gitignore, skipping merge');
|
|
146
|
+
return existingContent;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Load merge template
|
|
150
|
+
let mergeSection;
|
|
151
|
+
try {
|
|
152
|
+
mergeSection = loadGitignoreTemplate(GitignoreTemplates.BROWNFIELD_MERGE);
|
|
153
|
+
} catch {
|
|
154
|
+
// Fallback to minimal section
|
|
155
|
+
mergeSection = `
|
|
156
|
+
# ========================================
|
|
157
|
+
# AIOS Integration Section
|
|
158
|
+
# ========================================
|
|
159
|
+
|
|
160
|
+
# AIOS Local Configuration
|
|
161
|
+
.aios-core/local/
|
|
162
|
+
.aios-core/*.local.yaml
|
|
163
|
+
.aios-core/logs/
|
|
164
|
+
.aios-core/cache/
|
|
165
|
+
|
|
166
|
+
# ========================================
|
|
167
|
+
# End of AIOS Integration Section
|
|
168
|
+
# ========================================
|
|
169
|
+
`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Replace date placeholder
|
|
173
|
+
mergeSection = mergeSection.replace(
|
|
174
|
+
'{{GENERATED_DATE}}',
|
|
175
|
+
new Date().toISOString().split('T')[0],
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// Append to existing content
|
|
179
|
+
const merged = existingContent.trimEnd() + '\n\n' + mergeSection.trim() + '\n';
|
|
180
|
+
|
|
181
|
+
return merged;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Generates or merges .gitignore for a project
|
|
186
|
+
*
|
|
187
|
+
* @param {string} targetDir - Target directory
|
|
188
|
+
* @param {Object} markers - Detected project markers
|
|
189
|
+
* @param {Object} [options] - Generation options
|
|
190
|
+
* @param {boolean} [options.dryRun] - Don't write file, just return content
|
|
191
|
+
* @param {boolean} [options.merge] - Merge with existing instead of replace
|
|
192
|
+
* @param {string} [options.projectName] - Project name for header
|
|
193
|
+
* @returns {Object} Generation result
|
|
194
|
+
*/
|
|
195
|
+
function generateGitignoreFile(targetDir, markers, options = {}) {
|
|
196
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
197
|
+
const existingPath = fs.existsSync(gitignorePath);
|
|
198
|
+
|
|
199
|
+
let content;
|
|
200
|
+
let mode;
|
|
201
|
+
|
|
202
|
+
if (existingPath && options.merge !== false) {
|
|
203
|
+
// Brownfield mode - merge with existing
|
|
204
|
+
const existing = fs.readFileSync(gitignorePath, 'utf8');
|
|
205
|
+
content = mergeGitignore(existing, options);
|
|
206
|
+
mode = 'merged';
|
|
207
|
+
} else {
|
|
208
|
+
// Greenfield mode - generate new
|
|
209
|
+
content = generateGitignore(markers, options);
|
|
210
|
+
mode = existingPath ? 'replaced' : 'created';
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const result = {
|
|
214
|
+
success: true,
|
|
215
|
+
path: gitignorePath,
|
|
216
|
+
content,
|
|
217
|
+
mode,
|
|
218
|
+
techStacks: detectTechStacks(markers),
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
if (!options.dryRun) {
|
|
222
|
+
try {
|
|
223
|
+
fs.writeFileSync(gitignorePath, content, 'utf8');
|
|
224
|
+
} catch (error) {
|
|
225
|
+
result.success = false;
|
|
226
|
+
result.error = error.message;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Checks if a .gitignore file has AIOS integration
|
|
235
|
+
*
|
|
236
|
+
* @param {string} targetDir - Target directory
|
|
237
|
+
* @returns {boolean} True if AIOS section exists
|
|
238
|
+
*/
|
|
239
|
+
function hasAiosIntegration(targetDir) {
|
|
240
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
241
|
+
|
|
242
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
247
|
+
return content.includes('AIOS Integration Section') || content.includes('.aios-core/');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Parses an existing .gitignore into sections
|
|
252
|
+
*
|
|
253
|
+
* @param {string} content - .gitignore content
|
|
254
|
+
* @returns {Object} Parsed sections
|
|
255
|
+
*/
|
|
256
|
+
function parseGitignore(content) {
|
|
257
|
+
const lines = content.split('\n');
|
|
258
|
+
const sections = {
|
|
259
|
+
header: [],
|
|
260
|
+
patterns: [],
|
|
261
|
+
aiosSection: null,
|
|
262
|
+
comments: [],
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
let aiosStart = -1;
|
|
266
|
+
let aiosEnd = -1;
|
|
267
|
+
|
|
268
|
+
for (let i = 0; i < lines.length; i++) {
|
|
269
|
+
const line = lines[i];
|
|
270
|
+
|
|
271
|
+
if (line.includes('AIOS Integration Section')) {
|
|
272
|
+
if (aiosStart === -1) {
|
|
273
|
+
aiosStart = i;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (line.includes('End of AIOS Integration Section')) {
|
|
278
|
+
aiosEnd = i;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (line.startsWith('#') && !line.includes('AIOS')) {
|
|
282
|
+
sections.comments.push({ line: i, content: line });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (!line.startsWith('#') && line.trim()) {
|
|
286
|
+
sections.patterns.push({ line: i, pattern: line.trim() });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (aiosStart !== -1) {
|
|
291
|
+
sections.aiosSection = {
|
|
292
|
+
start: aiosStart,
|
|
293
|
+
end: aiosEnd !== -1 ? aiosEnd : lines.length - 1,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return sections;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
module.exports = {
|
|
301
|
+
loadGitignoreTemplate,
|
|
302
|
+
detectTechStacks,
|
|
303
|
+
getTemplatesForStacks,
|
|
304
|
+
generateGitignore,
|
|
305
|
+
mergeGitignore,
|
|
306
|
+
generateGitignoreFile,
|
|
307
|
+
hasAiosIntegration,
|
|
308
|
+
parseGitignore,
|
|
309
|
+
GitignoreTemplates,
|
|
310
|
+
TechStack,
|
|
311
|
+
TEMPLATES_DIR,
|
|
312
|
+
};
|