@iservu-inc/adf-cli 0.1.6 → 0.3.0

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 (31) hide show
  1. package/.project/chats/complete/2025-10-03_ADF-CLI-QUALITY-BASED-PROGRESS-AND-RESUME.md +399 -0
  2. package/.project/chats/current/2025-10-03_AGENTS-MD-AND-TOOL-GENERATORS.md +699 -0
  3. package/.project/docs/architecture/SYSTEM-DESIGN.md +369 -0
  4. package/.project/docs/frameworks/FRAMEWORK-METHODOLOGIES.md +449 -0
  5. package/.project/docs/goals/PROJECT-VISION.md +112 -0
  6. package/.project/docs/tool-integrations/IDE-CUSTOMIZATIONS.md +578 -0
  7. package/.project/docs/tool-integrations/RESEARCH-FINDINGS.md +828 -0
  8. package/CHANGELOG.md +292 -0
  9. package/jest.config.js +20 -0
  10. package/lib/commands/deploy.js +122 -3
  11. package/lib/commands/init.js +41 -113
  12. package/lib/frameworks/answer-quality-analyzer.js +216 -0
  13. package/lib/frameworks/interviewer.js +447 -0
  14. package/lib/frameworks/output-generators.js +345 -0
  15. package/lib/frameworks/progress-tracker.js +239 -0
  16. package/lib/frameworks/questions.js +664 -0
  17. package/lib/frameworks/session-manager.js +100 -0
  18. package/lib/generators/agents-md-generator.js +388 -0
  19. package/lib/generators/cursor-generator.js +374 -0
  20. package/lib/generators/index.js +98 -0
  21. package/lib/generators/tool-config-generator.js +188 -0
  22. package/lib/generators/vscode-generator.js +403 -0
  23. package/lib/generators/windsurf-generator.js +596 -0
  24. package/package.json +10 -5
  25. package/tests/agents-md-generator.test.js +245 -0
  26. package/tests/answer-quality-analyzer.test.js +173 -0
  27. package/tests/cursor-generator.test.js +326 -0
  28. package/tests/progress-tracker.test.js +205 -0
  29. package/tests/session-manager.test.js +162 -0
  30. package/tests/vscode-generator.test.js +436 -0
  31. package/tests/windsurf-generator.test.js +320 -0
@@ -0,0 +1,374 @@
1
+ const ToolConfigGenerator = require('./tool-config-generator');
2
+
3
+ /**
4
+ * Generator for Cursor AI configuration files
5
+ * Creates .cursor/rules, .cursor/mcp.json, and deprecation notices
6
+ *
7
+ * See: https://docs.cursor.com/
8
+ */
9
+ class CursorGenerator extends ToolConfigGenerator {
10
+ /**
11
+ * Generate all Cursor configuration files
12
+ */
13
+ async generate() {
14
+ await this.initialize();
15
+
16
+ const generated = {
17
+ rules: [],
18
+ mcp: [],
19
+ legacy: []
20
+ };
21
+
22
+ // Generate .cursor/rules (modern, recommended)
23
+ const rulesPath = await this.generateRules();
24
+ generated.rules.push(rulesPath);
25
+
26
+ // Generate .cursor/mcp.json (optional, advanced)
27
+ // Commented out for now - can be enabled later
28
+ // const mcpPath = await this.generateMCP();
29
+ // generated.mcp.push(mcpPath);
30
+
31
+ // Generate .cursorrules deprecation notice (legacy support)
32
+ const legacyPath = await this.generateLegacyNotice();
33
+ generated.legacy.push(legacyPath);
34
+
35
+ return generated;
36
+ }
37
+
38
+ /**
39
+ * Generate .cursor/rules (primary configuration)
40
+ */
41
+ async generateRules() {
42
+ await this.ensureDir('.cursor');
43
+
44
+ const content = this.framework === 'rapid' ?
45
+ this.generateRulesPRP() :
46
+ this.framework === 'balanced' ?
47
+ this.generateRulesBalanced() :
48
+ this.generateRulesBMAD();
49
+
50
+ return await this.writeToProject('.cursor/rules', content);
51
+ }
52
+
53
+ generateRulesPRP() {
54
+ const projectName = this.getProjectName();
55
+ const sections = this.outputs.sections || {};
56
+
57
+ return `# ${projectName} - Cursor Rules
58
+
59
+ You are a senior developer working on ${projectName}.
60
+
61
+ ## Project Goal
62
+
63
+ ${sections['1._goal_definition'] || sections['goal_definition'] || 'See PRP for details'}
64
+
65
+ ## Tech Stack
66
+
67
+ ${this.extractTechStack(sections)}
68
+
69
+ ## Implementation Blueprint
70
+
71
+ ${sections['4._implementation_blueprint'] || sections['implementation_blueprint'] || 'See PRP for details'}
72
+
73
+ ## Success Criteria
74
+
75
+ ${sections['5._validation'] || sections['validation'] || 'See PRP for validation criteria'}
76
+
77
+ ## Before Implementing Features
78
+
79
+ 1. **Read the full PRP**: \`.adf/sessions/${this.getSessionId()}/outputs/prp.md\`
80
+ 2. **Follow the implementation blueprint** exactly as specified
81
+ 3. **Validate against success criteria** before marking complete
82
+ 4. **Ask clarifying questions** if requirements are unclear
83
+
84
+ ## Code Standards
85
+
86
+ - Use the specified tech stack
87
+ - Follow the file structure in the blueprint
88
+ - Include error handling
89
+ - Write tests for new functionality
90
+ - Document complex logic
91
+ - Maintain consistency with existing code
92
+
93
+ ## Testing
94
+
95
+ - Write tests for all new features
96
+ - Ensure tests pass before committing
97
+ - Follow TDD when appropriate
98
+
99
+ ## What to Avoid
100
+
101
+ - Deviating from the implementation blueprint
102
+ - Skipping tests
103
+ - Ignoring success criteria
104
+ - Using technologies not in the tech stack
105
+
106
+ ---
107
+
108
+ *Generated by ADF CLI v${this.getADFVersion()} from PRP framework*
109
+ *This is a .cursor/rules file. The legacy .cursorrules file is deprecated.*
110
+ `;
111
+ }
112
+
113
+ generateRulesBalanced() {
114
+ const projectName = this.getProjectName();
115
+ const constitution = this.outputs.constitution || '';
116
+ const specification = this.outputs.specification || '';
117
+ const plan = this.outputs.plan || '';
118
+
119
+ const principles = this.extractSection(constitution, 'Core Principles');
120
+ const constraints = this.extractSection(constitution, 'Constraints');
121
+ const techStack = this.extractSection(plan, 'Technology Stack');
122
+
123
+ return `# ${projectName} - Cursor Rules
124
+
125
+ You are a senior developer working on ${projectName}.
126
+
127
+ ## Core Principles
128
+
129
+ ${principles || 'Follow best practices'}
130
+
131
+ ## Constraints (Non-Negotiable)
132
+
133
+ ${constraints || 'No specific constraints'}
134
+
135
+ ## Tech Stack
136
+
137
+ ${techStack || 'See technical plan for details'}
138
+
139
+ ## Architecture
140
+
141
+ ${this.extractSection(specification, 'Architecture') || 'See specification.md for complete architecture'}
142
+
143
+ ## Before Implementing
144
+
145
+ 1. **Read the constitution**: \`.adf/sessions/${this.getSessionId()}/outputs/constitution.md\`
146
+ - Understand principles and constraints
147
+ 2. **Review the specification**: \`.adf/sessions/${this.getSessionId()}/outputs/specification.md\`
148
+ - Know what needs to be built
149
+ 3. **Check the technical plan**: \`.adf/sessions/${this.getSessionId()}/outputs/plan.md\`
150
+ - Follow architectural decisions
151
+ 4. **Review tasks**: \`.adf/sessions/${this.getSessionId()}/outputs/tasks.md\`
152
+ - Understand implementation phases
153
+
154
+ ## Code Standards
155
+
156
+ ${this.extractSection(plan, 'Code Style') || this.extractSection(plan, 'Coding Standards') || 'Follow best practices'}
157
+
158
+ - Write tests first (TDD approach)
159
+ - Follow architecture patterns from specification
160
+ - Adhere to constraints (these are non-negotiable)
161
+ - Maintain consistency with code style
162
+
163
+ ## Testing
164
+
165
+ - Comprehensive test coverage
166
+ - Unit tests for logic
167
+ - Integration tests for workflows
168
+ - Follow testing approach in technical plan
169
+
170
+ ## What You MUST NOT Do
171
+
172
+ - Violate any constraints from constitution
173
+ - Deviate from architectural patterns
174
+ - Skip tests
175
+ - Ignore code standards
176
+
177
+ ## Reference Files
178
+
179
+ - **Constitution**: \`.adf/sessions/${this.getSessionId()}/outputs/constitution.md\` (principles & constraints)
180
+ - **Specification**: \`.adf/sessions/${this.getSessionId()}/outputs/specification.md\` (detailed requirements)
181
+ - **Technical Plan**: \`.adf/sessions/${this.getSessionId()}/outputs/plan.md\` (architecture & decisions)
182
+ - **Tasks**: \`.adf/sessions/${this.getSessionId()}/outputs/tasks.md\` (implementation breakdown)
183
+
184
+ ---
185
+
186
+ *Generated by ADF CLI v${this.getADFVersion()} from Balanced framework*
187
+ *This is a .cursor/rules file. The legacy .cursorrules file is deprecated.*
188
+ `;
189
+ }
190
+
191
+ generateRulesBMAD() {
192
+ const projectName = this.getProjectName();
193
+ const prd = this.outputs.prd || '';
194
+ const architecture = this.outputs.architecture || '';
195
+
196
+ return `# ${projectName} - Cursor Rules
197
+
198
+ You are a senior developer working on ${projectName}.
199
+
200
+ ## Product Overview
201
+
202
+ ${this.extractSection(prd, 'Executive Summary') || this.extractSection(prd, 'Overview') || 'See PRD'}
203
+
204
+ ## Goals and Objectives
205
+
206
+ ${this.extractSection(prd, 'Goals and Objectives') || 'See PRD'}
207
+
208
+ ## Technical Requirements
209
+
210
+ ${this.extractSection(prd, 'Technical Requirements') || this.extractSection(prd, 'Technical Architecture') || 'See PRD'}
211
+
212
+ ## System Architecture
213
+
214
+ ${this.extractSection(architecture, 'System Overview') || this.extractSection(architecture, 'Architecture Overview') || 'See architecture.md'}
215
+
216
+ ## Before Implementing
217
+
218
+ 1. **Read the PRD**: \`.adf/sessions/${this.getSessionId()}/outputs/prd.md\`
219
+ - Understand complete product vision
220
+ 2. **Study the architecture**: \`.adf/sessions/${this.getSessionId()}/outputs/architecture.md\`
221
+ - Know the system design and component structure
222
+ 3. **Review user stories**: \`.adf/sessions/${this.getSessionId()}/outputs/stories.md\`
223
+ - Understand user needs and acceptance criteria
224
+
225
+ ## Code Quality Standards
226
+
227
+ - Maintain test coverage >= 80%
228
+ - Follow architectural patterns from architecture.md
229
+ - Implement user stories as specified in stories.md
230
+ - Write clean, maintainable, documented code
231
+ - Handle errors gracefully
232
+ - Consider scalability and performance
233
+
234
+ ## Testing
235
+
236
+ - Write comprehensive tests
237
+ - Unit tests for components
238
+ - Integration tests for workflows
239
+ - E2E tests for user journeys
240
+ - All tests must pass before committing
241
+
242
+ ## Security
243
+
244
+ - Never commit sensitive data (API keys, passwords, tokens)
245
+ - Use environment variables for configuration
246
+ - Validate all user input
247
+ - Follow security best practices
248
+ - See PRD for specific security requirements
249
+
250
+ ## Performance
251
+
252
+ - Follow performance requirements in PRD
253
+ - Optimize critical paths
254
+ - Monitor resource usage
255
+ - See architecture.md for performance patterns
256
+
257
+ ## What You MUST NOT Do
258
+
259
+ - Deviate from architectural decisions without discussion
260
+ - Skip tests
261
+ - Ignore user story acceptance criteria
262
+ - Commit sensitive data
263
+ - Make breaking changes without approval
264
+
265
+ ## Reference Files
266
+
267
+ - **PRD**: \`.adf/sessions/${this.getSessionId()}/outputs/prd.md\` (complete product requirements)
268
+ - **Architecture**: \`.adf/sessions/${this.getSessionId()}/outputs/architecture.md\` (system design)
269
+ - **User Stories**: \`.adf/sessions/${this.getSessionId()}/outputs/stories.md\` (user needs & acceptance criteria)
270
+
271
+ ---
272
+
273
+ *Generated by ADF CLI v${this.getADFVersion()} from BMAD framework*
274
+ *This is a .cursor/rules file. The legacy .cursorrules file is deprecated.*
275
+ `;
276
+ }
277
+
278
+ /**
279
+ * Generate .cursor/mcp.json (Model Context Protocol servers)
280
+ * Optional advanced feature for custom tools
281
+ */
282
+ async generateMCP() {
283
+ await this.ensureDir('.cursor');
284
+
285
+ const mcpConfig = {
286
+ mcpServers: {
287
+ "requirement-checker": {
288
+ command: "node",
289
+ args: ["./tools/requirement-checker.js"],
290
+ env: {
291
+ SESSION_PATH: `.adf/sessions/${this.getSessionId()}`
292
+ }
293
+ }
294
+ }
295
+ };
296
+
297
+ const content = JSON.stringify(mcpConfig, null, 2);
298
+ return await this.writeToProject('.cursor/mcp.json', content);
299
+ }
300
+
301
+ /**
302
+ * Generate .cursorrules with deprecation notice
303
+ * Legacy support - redirect to modern .cursor/rules
304
+ */
305
+ async generateLegacyNotice() {
306
+ const content = `# DEPRECATED: .cursorrules
307
+
308
+ This file format is deprecated by Cursor.
309
+
310
+ **Please use:** \`.cursor/rules\` instead
311
+
312
+ The modern \`.cursor/rules\` file provides:
313
+ - Better organization
314
+ - Project-scoped rules
315
+ - Enhanced functionality
316
+
317
+ ## Migration
318
+
319
+ The ADF CLI has automatically generated:
320
+ - \`.cursor/rules\` - Your project rules (use this)
321
+
322
+ You can delete this .cursorrules file.
323
+
324
+ ---
325
+
326
+ *Generated by ADF CLI v${this.getADFVersion()}*
327
+ `;
328
+
329
+ return await this.writeToProject('.cursorrules', content);
330
+ }
331
+
332
+ /**
333
+ * Extract tech stack from PRP sections
334
+ */
335
+ extractTechStack(sections) {
336
+ const contextualIntelligence = sections['3._contextual_intelligence'] ||
337
+ sections['contextual_intelligence'] || '';
338
+
339
+ const lines = contextualIntelligence.split('\n');
340
+ const techLines = [];
341
+ let inTechSection = false;
342
+
343
+ for (const line of lines) {
344
+ if (line.toLowerCase().includes('tech') ||
345
+ line.toLowerCase().includes('stack') ||
346
+ line.toLowerCase().includes('technology')) {
347
+ inTechSection = true;
348
+ }
349
+ if (inTechSection && line.trim()) {
350
+ techLines.push(line);
351
+ if (techLines.length > 10) break;
352
+ }
353
+ if (line.startsWith('##') && inTechSection && techLines.length > 0) {
354
+ break;
355
+ }
356
+ }
357
+
358
+ return techLines.length > 0 ? techLines.join('\n') : 'See framework outputs for tech stack';
359
+ }
360
+
361
+ /**
362
+ * Get ADF CLI version
363
+ */
364
+ getADFVersion() {
365
+ try {
366
+ const packageJson = require('../../package.json');
367
+ return packageJson.version;
368
+ } catch (error) {
369
+ return '0.3.0';
370
+ }
371
+ }
372
+ }
373
+
374
+ module.exports = CursorGenerator;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Tool Configuration Generators
3
+ * Transform ADF framework outputs into IDE/tool-specific configurations
4
+ */
5
+
6
+ const AgentsMdGenerator = require('./agents-md-generator');
7
+ const WindsurfGenerator = require('./windsurf-generator');
8
+ const CursorGenerator = require('./cursor-generator');
9
+ const VSCodeGenerator = require('./vscode-generator');
10
+ const ToolConfigGenerator = require('./tool-config-generator');
11
+
12
+ /**
13
+ * Generate all tool configurations for a project
14
+ * @param {string} sessionPath - Path to ADF session
15
+ * @param {string} projectPath - Path to project root
16
+ * @param {string} framework - Framework type (rapid/balanced/comprehensive)
17
+ * @param {object} options - Generation options
18
+ * @returns {object} Generated file paths
19
+ */
20
+ async function generateAll(sessionPath, projectPath, framework, options = {}) {
21
+ const generated = {
22
+ universal: [],
23
+ windsurf: [],
24
+ cursor: [],
25
+ vscode: []
26
+ };
27
+
28
+ // Always generate AGENTS.md (universal standard)
29
+ const agentsMd = new AgentsMdGenerator(sessionPath, projectPath, framework);
30
+ const agentsMdPath = await agentsMd.generate();
31
+ generated.universal.push(agentsMdPath);
32
+
33
+ // Generate tool-specific configs based on options
34
+ if (options.tool === 'windsurf' || options.all) {
35
+ const windsurf = new WindsurfGenerator(sessionPath, projectPath, framework);
36
+ const windsurfPaths = await windsurf.generate();
37
+ generated.windsurf = windsurfPaths;
38
+ }
39
+
40
+ if (options.tool === 'cursor' || options.all) {
41
+ const cursor = new CursorGenerator(sessionPath, projectPath, framework);
42
+ const cursorPaths = await cursor.generate();
43
+ generated.cursor = cursorPaths;
44
+ }
45
+
46
+ if (options.tool === 'vscode' || options.all) {
47
+ const vscode = new VSCodeGenerator(sessionPath, projectPath, framework);
48
+ const vscodePaths = await vscode.generate();
49
+ generated.vscode = vscodePaths;
50
+ }
51
+
52
+ return generated;
53
+ }
54
+
55
+ /**
56
+ * Generate only AGENTS.md (universal standard)
57
+ */
58
+ async function generateAgentsMd(sessionPath, projectPath, framework) {
59
+ const generator = new AgentsMdGenerator(sessionPath, projectPath, framework);
60
+ return await generator.generate();
61
+ }
62
+
63
+ /**
64
+ * Generate Windsurf configurations
65
+ */
66
+ async function generateWindsurf(sessionPath, projectPath, framework) {
67
+ const generator = new WindsurfGenerator(sessionPath, projectPath, framework);
68
+ return await generator.generate();
69
+ }
70
+
71
+ /**
72
+ * Generate Cursor configurations
73
+ */
74
+ async function generateCursor(sessionPath, projectPath, framework) {
75
+ const generator = new CursorGenerator(sessionPath, projectPath, framework);
76
+ return await generator.generate();
77
+ }
78
+
79
+ /**
80
+ * Generate VS Code configurations
81
+ */
82
+ async function generateVSCode(sessionPath, projectPath, framework) {
83
+ const generator = new VSCodeGenerator(sessionPath, projectPath, framework);
84
+ return await generator.generate();
85
+ }
86
+
87
+ module.exports = {
88
+ generateAll,
89
+ generateAgentsMd,
90
+ generateWindsurf,
91
+ generateCursor,
92
+ generateVSCode,
93
+ AgentsMdGenerator,
94
+ WindsurfGenerator,
95
+ CursorGenerator,
96
+ VSCodeGenerator,
97
+ ToolConfigGenerator
98
+ };
@@ -0,0 +1,188 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Base class for generating tool-specific configuration files
6
+ * Transforms ADF framework outputs into IDE/tool configurations
7
+ */
8
+ class ToolConfigGenerator {
9
+ constructor(sessionPath, projectPath, framework) {
10
+ this.sessionPath = sessionPath;
11
+ this.projectPath = projectPath;
12
+ this.framework = framework;
13
+ this.outputsPath = path.join(sessionPath, 'outputs');
14
+ this.metadata = null;
15
+ this.outputs = {};
16
+ }
17
+
18
+ /**
19
+ * Load session metadata and framework outputs
20
+ */
21
+ async initialize() {
22
+ // Load metadata
23
+ const metadataPath = path.join(this.sessionPath, '_metadata.json');
24
+ if (await fs.pathExists(metadataPath)) {
25
+ this.metadata = await fs.readJson(metadataPath);
26
+ }
27
+
28
+ // Load framework outputs based on type
29
+ await this.loadFrameworkOutputs();
30
+ }
31
+
32
+ /**
33
+ * Load framework-specific output files
34
+ */
35
+ async loadFrameworkOutputs() {
36
+ if (this.framework === 'rapid') {
37
+ await this.loadPRPOutputs();
38
+ } else if (this.framework === 'balanced') {
39
+ await this.loadBalancedOutputs();
40
+ } else if (this.framework === 'comprehensive') {
41
+ await this.loadBMADOutputs();
42
+ }
43
+ }
44
+
45
+ async loadPRPOutputs() {
46
+ const prpPath = path.join(this.outputsPath, 'prp.md');
47
+ if (await fs.pathExists(prpPath)) {
48
+ this.outputs.prp = await fs.readFile(prpPath, 'utf-8');
49
+ this.outputs.sections = this.parsePRPSections(this.outputs.prp);
50
+ }
51
+ }
52
+
53
+ async loadBalancedOutputs() {
54
+ const files = ['constitution.md', 'specification.md', 'plan.md', 'tasks.md'];
55
+ for (const file of files) {
56
+ const filePath = path.join(this.outputsPath, file);
57
+ if (await fs.pathExists(filePath)) {
58
+ const key = file.replace('.md', '');
59
+ this.outputs[key] = await fs.readFile(filePath, 'utf-8');
60
+ }
61
+ }
62
+ }
63
+
64
+ async loadBMADOutputs() {
65
+ const files = ['prd.md', 'architecture.md', 'stories.md'];
66
+ for (const file of files) {
67
+ const filePath = path.join(this.outputsPath, file);
68
+ if (await fs.pathExists(filePath)) {
69
+ const key = file.replace('.md', '');
70
+ this.outputs[key] = await fs.readFile(filePath, 'utf-8');
71
+ }
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Parse PRP markdown into sections
77
+ */
78
+ parsePRPSections(content) {
79
+ const sections = {};
80
+ const lines = content.split('\n');
81
+ let currentSection = null;
82
+ let currentContent = [];
83
+
84
+ for (const line of lines) {
85
+ // Check for main headings (## Section)
86
+ const headingMatch = line.match(/^##\s+(.+)$/);
87
+ if (headingMatch) {
88
+ // Save previous section
89
+ if (currentSection) {
90
+ sections[currentSection] = currentContent.join('\n').trim();
91
+ }
92
+ // Start new section
93
+ currentSection = headingMatch[1].toLowerCase().replace(/\s+/g, '_');
94
+ currentContent = [];
95
+ } else if (currentSection) {
96
+ currentContent.push(line);
97
+ }
98
+ }
99
+
100
+ // Save last section
101
+ if (currentSection) {
102
+ sections[currentSection] = currentContent.join('\n').trim();
103
+ }
104
+
105
+ return sections;
106
+ }
107
+
108
+ /**
109
+ * Extract project name from metadata or session
110
+ */
111
+ getProjectName() {
112
+ if (this.metadata && this.metadata.projectName) {
113
+ return this.metadata.projectName;
114
+ }
115
+ // Fallback to directory name
116
+ return path.basename(this.projectPath);
117
+ }
118
+
119
+ /**
120
+ * Get session ID
121
+ */
122
+ getSessionId() {
123
+ return path.basename(this.sessionPath);
124
+ }
125
+
126
+ /**
127
+ * Replace template variables in content
128
+ */
129
+ replaceVariables(template, variables) {
130
+ let result = template;
131
+ for (const [key, value] of Object.entries(variables)) {
132
+ const regex = new RegExp(`\\{${key}\\}`, 'g');
133
+ result = result.replace(regex, value || '');
134
+ }
135
+ return result;
136
+ }
137
+
138
+ /**
139
+ * Extract content between markdown headings
140
+ */
141
+ extractSection(content, sectionName) {
142
+ const lines = content.split('\n');
143
+ let inSection = false;
144
+ let sectionContent = [];
145
+
146
+ for (const line of lines) {
147
+ if (line.startsWith('##')) {
148
+ if (inSection) {
149
+ break; // End of our section
150
+ }
151
+ if (line.toLowerCase().includes(sectionName.toLowerCase())) {
152
+ inSection = true;
153
+ continue;
154
+ }
155
+ }
156
+ if (inSection) {
157
+ sectionContent.push(line);
158
+ }
159
+ }
160
+
161
+ return sectionContent.join('\n').trim();
162
+ }
163
+
164
+ /**
165
+ * Write file to project root
166
+ */
167
+ async writeToProject(fileName, content) {
168
+ const filePath = path.join(this.projectPath, fileName);
169
+ await fs.writeFile(filePath, content, 'utf-8');
170
+ return filePath;
171
+ }
172
+
173
+ /**
174
+ * Ensure directory exists
175
+ */
176
+ async ensureDir(dirPath) {
177
+ await fs.ensureDir(path.join(this.projectPath, dirPath));
178
+ }
179
+
180
+ /**
181
+ * Generate all configurations (override in subclasses)
182
+ */
183
+ async generate() {
184
+ throw new Error('generate() must be implemented by subclass');
185
+ }
186
+ }
187
+
188
+ module.exports = ToolConfigGenerator;