mcp-adr-analysis-server 2.7.24 โ 2.7.26
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/README.md +1 -32
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/index.d.ts +0 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +0 -57
- package/dist/src/index.js.map +1 -1
- package/dist/src/tools/bootstrap-validation-loop-tool.d.ts +0 -178
- package/dist/src/tools/bootstrap-validation-loop-tool.d.ts.map +1 -1
- package/dist/src/tools/bootstrap-validation-loop-tool.js +0 -1580
- package/dist/src/tools/bootstrap-validation-loop-tool.js.map +1 -1
- package/dist/src/tools/tool-catalog.d.ts.map +1 -1
- package/dist/src/tools/tool-catalog.js +2 -22
- package/dist/src/tools/tool-catalog.js.map +1 -1
- package/dist/src/utils/ai-executor.d.ts +1 -1
- package/dist/src/utils/ai-executor.js +1 -1
- package/dist/src/utils/config.d.ts +0 -3
- package/dist/src/utils/config.d.ts.map +1 -1
- package/dist/src/utils/config.js +0 -13
- package/dist/src/utils/config.js.map +1 -1
- package/dist/src/utils/research-orchestrator.d.ts +0 -28
- package/dist/src/utils/research-orchestrator.d.ts.map +1 -1
- package/dist/src/utils/research-orchestrator.js +15 -252
- package/dist/src/utils/research-orchestrator.js.map +1 -1
- package/dist/src/utils/server-context-generator.d.ts.map +1 -1
- package/dist/src/utils/server-context-generator.js +0 -1
- package/dist/src/utils/server-context-generator.js.map +1 -1
- package/package.json +1 -3
- package/dist/src/tools/llm-web-search-tool.d.ts +0 -59
- package/dist/src/tools/llm-web-search-tool.d.ts.map +0 -1
- package/dist/src/tools/llm-web-search-tool.js +0 -165
- package/dist/src/tools/llm-web-search-tool.js.map +0 -1
- package/dist/src/utils/dag-executor.d.ts +0 -106
- package/dist/src/utils/dag-executor.d.ts.map +0 -1
- package/dist/src/utils/dag-executor.js +0 -453
- package/dist/src/utils/dag-executor.js.map +0 -1
- package/dist/src/utils/deployment-task-integration.d.ts +0 -260
- package/dist/src/utils/deployment-task-integration.d.ts.map +0 -1
- package/dist/src/utils/deployment-task-integration.js +0 -393
- package/dist/src/utils/deployment-task-integration.js.map +0 -1
- package/dist/src/utils/dynamic-deployment-intelligence.d.ts +0 -117
- package/dist/src/utils/dynamic-deployment-intelligence.d.ts.map +0 -1
- package/dist/src/utils/dynamic-deployment-intelligence.js +0 -445
- package/dist/src/utils/dynamic-deployment-intelligence.js.map +0 -1
- package/dist/src/utils/pattern-contribution-helper.d.ts +0 -60
- package/dist/src/utils/pattern-contribution-helper.d.ts.map +0 -1
- package/dist/src/utils/pattern-contribution-helper.js +0 -308
- package/dist/src/utils/pattern-contribution-helper.js.map +0 -1
- package/dist/src/utils/pattern-loader.d.ts +0 -183
- package/dist/src/utils/pattern-loader.d.ts.map +0 -1
- package/dist/src/utils/pattern-loader.js +0 -198
- package/dist/src/utils/pattern-loader.js.map +0 -1
- package/dist/src/utils/pattern-to-dag-converter.d.ts +0 -62
- package/dist/src/utils/pattern-to-dag-converter.d.ts.map +0 -1
- package/dist/src/utils/pattern-to-dag-converter.js +0 -265
- package/dist/src/utils/pattern-to-dag-converter.js.map +0 -1
- package/dist/src/utils/resource-extractor.d.ts +0 -57
- package/dist/src/utils/resource-extractor.d.ts.map +0 -1
- package/dist/src/utils/resource-extractor.js +0 -323
- package/dist/src/utils/resource-extractor.js.map +0 -1
- package/dist/src/utils/system-card-manager.d.ts +0 -194
- package/dist/src/utils/system-card-manager.d.ts.map +0 -1
- package/dist/src/utils/system-card-manager.js +0 -336
- package/dist/src/utils/system-card-manager.js.map +0 -1
|
@@ -13,1586 +13,9 @@
|
|
|
13
13
|
* This creates a bidirectional feedback loop where ADRs evolve
|
|
14
14
|
* based on real-world deployment experience.
|
|
15
15
|
*/
|
|
16
|
-
import { promises as fs } from 'fs';
|
|
17
|
-
import { exec } from 'node:child_process';
|
|
18
|
-
import { promisify } from 'util';
|
|
19
|
-
import * as path from 'path';
|
|
20
|
-
import { createHash } from 'crypto';
|
|
21
|
-
import { McpAdrError } from '../types/index.js';
|
|
22
|
-
import { EnhancedLogger } from '../utils/enhanced-logging.js';
|
|
23
|
-
import { ResearchOrchestrator } from '../utils/research-orchestrator.js';
|
|
24
|
-
import { MemoryEntityManager } from '../utils/memory-entity-manager.js';
|
|
25
|
-
import { DynamicDeploymentIntelligence, } from '../utils/dynamic-deployment-intelligence.js';
|
|
26
|
-
import generateAdrBootstrapScripts from './adr-bootstrap-validation-tool.js';
|
|
27
16
|
// NEW: Validated Patterns Integration
|
|
28
17
|
import { detectPlatforms } from '../utils/platform-detector.js';
|
|
29
18
|
import { getPattern } from '../utils/validated-pattern-definitions.js';
|
|
30
|
-
import { generatePatternResearchReport } from '../utils/pattern-research-utility.js';
|
|
31
|
-
// NEW: Tool Context Documentation System
|
|
32
|
-
import { ToolContextManager } from '../utils/context-document-manager.js';
|
|
33
|
-
// NEW: Hybrid DAG Architecture
|
|
34
|
-
import { DAGExecutor } from '../utils/dag-executor.js';
|
|
35
|
-
import { PatternToDAGConverter } from '../utils/pattern-to-dag-converter.js';
|
|
36
|
-
import { PatternContributionHelper } from '../utils/pattern-contribution-helper.js';
|
|
37
|
-
import { PatternLoader } from '../utils/pattern-loader.js';
|
|
38
|
-
// NEW: SystemCard for Resource Tracking
|
|
39
|
-
import { SystemCardManager } from '../utils/system-card-manager.js';
|
|
40
|
-
const execAsync = promisify(exec);
|
|
41
|
-
/**
|
|
42
|
-
* Bootstrap Validation Loop orchestrator
|
|
43
|
-
* Supports dependency injection for improved testability
|
|
44
|
-
* @see Issue #310 - Dependency injection for improved testability
|
|
45
|
-
*/
|
|
46
|
-
export class BootstrapValidationLoop {
|
|
47
|
-
logger;
|
|
48
|
-
projectPath;
|
|
49
|
-
adrDirectory;
|
|
50
|
-
researchOrchestrator;
|
|
51
|
-
memoryManager;
|
|
52
|
-
deploymentIntelligence;
|
|
53
|
-
executionHistory = [];
|
|
54
|
-
maxIterations;
|
|
55
|
-
currentIteration = 0;
|
|
56
|
-
deploymentPlan = null;
|
|
57
|
-
// NEW: Validated Patterns fields
|
|
58
|
-
platformDetection = null;
|
|
59
|
-
validatedPattern = null;
|
|
60
|
-
patternResearchReport = null;
|
|
61
|
-
// NEW: Tool Context Documentation
|
|
62
|
-
contextManager;
|
|
63
|
-
// NEW: Hybrid DAG Architecture
|
|
64
|
-
dagExecutor;
|
|
65
|
-
patternLoader;
|
|
66
|
-
patternConverter;
|
|
67
|
-
contributionHelper;
|
|
68
|
-
// NEW: SystemCard Resource Tracking
|
|
69
|
-
systemCardManager;
|
|
70
|
-
/**
|
|
71
|
-
* Constructor with optional dependency injection
|
|
72
|
-
* @param projectPath - Path to the project
|
|
73
|
-
* @param adrDirectory - Path to ADR directory
|
|
74
|
-
* @param maxIterations - Maximum validation iterations
|
|
75
|
-
* @param deps - Optional dependencies for testing (defaults create real instances)
|
|
76
|
-
*/
|
|
77
|
-
constructor(projectPath, adrDirectory, maxIterations = 5, deps = {}) {
|
|
78
|
-
this.logger = deps.logger ?? new EnhancedLogger();
|
|
79
|
-
this.projectPath = projectPath;
|
|
80
|
-
this.adrDirectory = adrDirectory;
|
|
81
|
-
this.maxIterations = maxIterations;
|
|
82
|
-
this.researchOrchestrator =
|
|
83
|
-
deps.researchOrchestrator ?? new ResearchOrchestrator(projectPath, adrDirectory);
|
|
84
|
-
this.memoryManager = deps.memoryManager ?? new MemoryEntityManager();
|
|
85
|
-
this.deploymentIntelligence = new DynamicDeploymentIntelligence(projectPath, adrDirectory);
|
|
86
|
-
this.contextManager = new ToolContextManager(projectPath);
|
|
87
|
-
// Initialize SystemCard for resource tracking (must be before DAGExecutor)
|
|
88
|
-
this.systemCardManager = new SystemCardManager(projectPath);
|
|
89
|
-
// Initialize DAG architecture components
|
|
90
|
-
this.dagExecutor = new DAGExecutor(5, this.systemCardManager); // Max 5 parallel tasks, with resource tracking
|
|
91
|
-
this.patternLoader = new PatternLoader();
|
|
92
|
-
this.patternConverter = new PatternToDAGConverter();
|
|
93
|
-
this.contributionHelper = new PatternContributionHelper();
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Initialize the validation loop
|
|
97
|
-
*/
|
|
98
|
-
async initialize() {
|
|
99
|
-
await this.memoryManager.initialize();
|
|
100
|
-
await this.contextManager.initialize();
|
|
101
|
-
this.logger.info('Bootstrap Validation Loop initialized', 'BootstrapValidationLoop', {
|
|
102
|
-
projectPath: this.projectPath,
|
|
103
|
-
adrDirectory: this.adrDirectory,
|
|
104
|
-
maxIterations: this.maxIterations,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Execute the complete validation loop
|
|
109
|
-
*/
|
|
110
|
-
async executeLoop(args) {
|
|
111
|
-
const { targetEnvironment, autoFix, captureEnvironmentSnapshot, updateAdrsWithLearnings } = args;
|
|
112
|
-
this.logger.info('Starting Bootstrap Validation Loop', 'BootstrapValidationLoop', {
|
|
113
|
-
targetEnvironment,
|
|
114
|
-
autoFix,
|
|
115
|
-
maxIterations: this.maxIterations,
|
|
116
|
-
});
|
|
117
|
-
// STEP 0: Detect platform using Validated Patterns framework
|
|
118
|
-
this.logger.info('๐ Detecting platform type using Validated Patterns...', 'BootstrapValidationLoop');
|
|
119
|
-
this.platformDetection = await detectPlatforms(this.projectPath);
|
|
120
|
-
this.logger.info(`๐ Platform detection complete (confidence: ${(this.platformDetection.confidence * 100).toFixed(0)}%)`, 'BootstrapValidationLoop', {
|
|
121
|
-
primaryPlatform: this.platformDetection.primaryPlatform,
|
|
122
|
-
detectedPlatforms: this.platformDetection.detectedPlatforms.map(p => p.type),
|
|
123
|
-
evidence: this.platformDetection.evidence.length,
|
|
124
|
-
});
|
|
125
|
-
// STEP 0.1: Get validated pattern for detected platform
|
|
126
|
-
if (this.platformDetection.primaryPlatform) {
|
|
127
|
-
this.validatedPattern = getPattern(this.platformDetection.primaryPlatform);
|
|
128
|
-
if (this.validatedPattern) {
|
|
129
|
-
this.logger.info(`โ
Loaded validated pattern: ${this.validatedPattern.name}`, 'BootstrapValidationLoop', {
|
|
130
|
-
version: this.validatedPattern.version,
|
|
131
|
-
lastUpdated: this.validatedPattern.metadata.lastUpdated,
|
|
132
|
-
authoritativeSources: this.validatedPattern.authoritativeSources.length,
|
|
133
|
-
requiredSources: this.validatedPattern.authoritativeSources.filter(s => s.requiredForDeployment).length,
|
|
134
|
-
});
|
|
135
|
-
// STEP 0.2: Generate research report for authoritative sources
|
|
136
|
-
this.patternResearchReport = generatePatternResearchReport(this.platformDetection.primaryPlatform);
|
|
137
|
-
this.logger.info('๐ Generated research report with authoritative source instructions', 'BootstrapValidationLoop');
|
|
138
|
-
this.logger.info(`โ ๏ธ IMPORTANT: LLM should query ${this.validatedPattern.authoritativeSources.filter(s => s.requiredForDeployment).length} REQUIRED authoritative sources before deployment`, 'BootstrapValidationLoop');
|
|
139
|
-
// Log the research report for LLM to see
|
|
140
|
-
this.logger.info('๐ Research Report:\n' + this.patternResearchReport, 'BootstrapValidationLoop');
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
this.logger.warn(`No validated pattern found for ${this.platformDetection.primaryPlatform}, falling back to dynamic intelligence`, 'BootstrapValidationLoop');
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
// STEP 0.3: Generate AI-powered deployment plan (fallback or hybrid approach)
|
|
147
|
-
this.logger.info('๐ค Generating dynamic deployment plan with AI + research...', 'BootstrapValidationLoop');
|
|
148
|
-
this.deploymentPlan = await this.deploymentIntelligence.generateDeploymentPlan();
|
|
149
|
-
this.logger.info(`๐ Deployment plan generated (confidence: ${this.deploymentPlan.confidence})`, 'BootstrapValidationLoop', {
|
|
150
|
-
platforms: this.deploymentPlan.detectedPlatforms,
|
|
151
|
-
recommended: this.deploymentPlan.recommendedPlatform,
|
|
152
|
-
source: this.deploymentPlan.source,
|
|
153
|
-
requiredFiles: this.deploymentPlan.requiredFiles.length,
|
|
154
|
-
});
|
|
155
|
-
// STEP 0.5: Create Bootstrap ADR for human review
|
|
156
|
-
const bootstrapAdrPath = await this.createBootstrapAdr(this.deploymentPlan);
|
|
157
|
-
this.logger.info(`๐ Bootstrap ADR created: ${bootstrapAdrPath}`, 'BootstrapValidationLoop');
|
|
158
|
-
// STEP 0.6: Save context document for future sessions
|
|
159
|
-
const contextDocumentPath = await this.saveBootstrapContext(this.platformDetection, this.validatedPattern, this.deploymentPlan, bootstrapAdrPath);
|
|
160
|
-
this.logger.info(`๐ Context document saved: ${contextDocumentPath}`, 'BootstrapValidationLoop');
|
|
161
|
-
this.logger.info('โธ๏ธ WAITING FOR HUMAN APPROVAL - Review the bootstrap ADR before proceeding', 'BootstrapValidationLoop');
|
|
162
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
163
|
-
// INFRASTRUCTURE LAYER (Hybrid DAG Architecture)
|
|
164
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
165
|
-
// Execute infrastructure DAG before application deployment iterations
|
|
166
|
-
// This runs ONCE and sets up the platform infrastructure
|
|
167
|
-
this.logger.info('๐๏ธ Starting Infrastructure Layer (DAG-based execution)...', 'BootstrapValidationLoop');
|
|
168
|
-
const infrastructureResult = await this.executeInfrastructureDAG(this.platformDetection);
|
|
169
|
-
if (!infrastructureResult.success) {
|
|
170
|
-
const failedTasksList = infrastructureResult.failedTasks.join(', ');
|
|
171
|
-
this.logger.error(`โ Infrastructure Layer failed: ${infrastructureResult.failedTasks.length} tasks failed`, 'BootstrapValidationLoop', undefined, {
|
|
172
|
-
failedTasks: infrastructureResult.failedTasks,
|
|
173
|
-
skippedTasks: infrastructureResult.skippedTasks,
|
|
174
|
-
});
|
|
175
|
-
// Infrastructure failure is critical - cannot proceed to application layer
|
|
176
|
-
throw new McpAdrError(`Infrastructure deployment failed. Failed tasks: ${failedTasksList}`, 'INFRASTRUCTURE_DEPLOYMENT_ERROR');
|
|
177
|
-
}
|
|
178
|
-
this.logger.info(`โ
Infrastructure Layer complete (${infrastructureResult.duration}ms)`, 'BootstrapValidationLoop', {
|
|
179
|
-
executedTasks: infrastructureResult.executedTasks.length,
|
|
180
|
-
duration: infrastructureResult.duration,
|
|
181
|
-
});
|
|
182
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
183
|
-
// APPLICATION LAYER (Phase-based iteration with auto-fix)
|
|
184
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
185
|
-
this.logger.info('๐ Starting Application Layer (iterative deployment with auto-fix)...', 'BootstrapValidationLoop');
|
|
186
|
-
let success = false;
|
|
187
|
-
let finalResult = null;
|
|
188
|
-
const adrUpdates = [];
|
|
189
|
-
for (let i = 0; i < this.maxIterations; i++) {
|
|
190
|
-
this.currentIteration = i + 1;
|
|
191
|
-
this.logger.info(`Iteration ${this.currentIteration}/${this.maxIterations}`, 'BootstrapValidationLoop');
|
|
192
|
-
// Step 1: Generate bootstrap scripts from current ADRs
|
|
193
|
-
const scriptsGenerated = await this.generateBootstrapScripts();
|
|
194
|
-
if (!scriptsGenerated) {
|
|
195
|
-
throw new McpAdrError('Failed to generate bootstrap scripts', 'SCRIPT_GENERATION_ERROR');
|
|
196
|
-
}
|
|
197
|
-
// Step 1.5: Detect and handle missing files
|
|
198
|
-
this.logger.info('Detecting missing files referenced in ADRs...', 'BootstrapValidationLoop');
|
|
199
|
-
const missingFiles = await this.detectMissingFiles();
|
|
200
|
-
if (missingFiles.length > 0) {
|
|
201
|
-
this.logger.warn(`Found ${missingFiles.length} missing files`, 'BootstrapValidationLoop', {
|
|
202
|
-
critical: missingFiles.filter(f => f.severity === 'critical').length,
|
|
203
|
-
errors: missingFiles.filter(f => f.severity === 'error').length,
|
|
204
|
-
});
|
|
205
|
-
// Handle missing files (create templates, add prerequisites)
|
|
206
|
-
const missingFileLearnings = await this.handleMissingFiles(missingFiles);
|
|
207
|
-
// Log missing file handling results
|
|
208
|
-
this.logger.info(`Handled ${missingFileLearnings.length} missing file issues`, 'BootstrapValidationLoop', {
|
|
209
|
-
created: missingFileLearnings.filter(l => l.type === 'success').length,
|
|
210
|
-
prerequisites: missingFileLearnings.filter(l => l.type === 'prerequisite').length,
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
// Step 2: Execute bootstrap.sh with monitoring
|
|
214
|
-
const executionResult = await this.executeBootstrapWithMonitoring(targetEnvironment, captureEnvironmentSnapshot);
|
|
215
|
-
this.executionHistory.push(executionResult);
|
|
216
|
-
// Step 3: Run validation script and capture results
|
|
217
|
-
const validationResult = await this.executeValidationScript(executionResult.executionId);
|
|
218
|
-
executionResult.validationResults = validationResult.validationResults;
|
|
219
|
-
// Step 4: Analyze results and extract learnings
|
|
220
|
-
const learnings = await this.extractLearnings(executionResult, validationResult);
|
|
221
|
-
executionResult.learnings = learnings;
|
|
222
|
-
// Step 5: Store execution in memory system
|
|
223
|
-
await this.storeExecutionInMemory(executionResult);
|
|
224
|
-
// Check if validation passed
|
|
225
|
-
if (validationResult.allPassed) {
|
|
226
|
-
this.logger.info('โ
Validation passed!', 'BootstrapValidationLoop');
|
|
227
|
-
success = true;
|
|
228
|
-
finalResult = executionResult;
|
|
229
|
-
// Update ADRs with successful learnings
|
|
230
|
-
if (updateAdrsWithLearnings) {
|
|
231
|
-
const updates = await this.generateAdrUpdates(learnings, 'success');
|
|
232
|
-
adrUpdates.push(...updates);
|
|
233
|
-
}
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
// Step 6: If auto-fix enabled, update scripts based on learnings
|
|
237
|
-
if (autoFix) {
|
|
238
|
-
this.logger.info('Auto-fixing bootstrap scripts based on failures', 'BootstrapValidationLoop');
|
|
239
|
-
const scriptsUpdated = await this.updateBootstrapScriptsFromLearnings(learnings, executionResult);
|
|
240
|
-
if (!scriptsUpdated) {
|
|
241
|
-
this.logger.warn('Failed to auto-fix scripts, stopping loop', 'BootstrapValidationLoop');
|
|
242
|
-
finalResult = executionResult;
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
// No auto-fix, stop after first iteration
|
|
248
|
-
finalResult = executionResult;
|
|
249
|
-
break;
|
|
250
|
-
}
|
|
251
|
-
finalResult = executionResult;
|
|
252
|
-
}
|
|
253
|
-
// Generate final ADR update proposals
|
|
254
|
-
if (updateAdrsWithLearnings && finalResult) {
|
|
255
|
-
const updates = await this.generateAdrUpdates(finalResult.learnings, success ? 'success' : 'failure');
|
|
256
|
-
adrUpdates.push(...updates);
|
|
257
|
-
}
|
|
258
|
-
// Ensure finalResult exists before proceeding
|
|
259
|
-
if (!finalResult) {
|
|
260
|
-
throw new McpAdrError('No execution result available - bootstrap validation loop failed to execute', 'EXECUTION_ERROR');
|
|
261
|
-
}
|
|
262
|
-
const result = {
|
|
263
|
-
success,
|
|
264
|
-
iterations: this.currentIteration,
|
|
265
|
-
finalResult,
|
|
266
|
-
adrUpdates,
|
|
267
|
-
executionHistory: this.executionHistory,
|
|
268
|
-
requiresHumanApproval: true,
|
|
269
|
-
};
|
|
270
|
-
// Only add optional properties if they have values
|
|
271
|
-
if (this.deploymentPlan) {
|
|
272
|
-
result.deploymentPlan = this.deploymentPlan;
|
|
273
|
-
}
|
|
274
|
-
if (bootstrapAdrPath) {
|
|
275
|
-
result.bootstrapAdrPath = bootstrapAdrPath;
|
|
276
|
-
}
|
|
277
|
-
if (contextDocumentPath) {
|
|
278
|
-
result.contextDocumentPath = contextDocumentPath;
|
|
279
|
-
}
|
|
280
|
-
return result;
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* Create Bootstrap ADR with deployment plan and architecture diagrams
|
|
284
|
-
*/
|
|
285
|
-
async createBootstrapAdr(plan) {
|
|
286
|
-
try {
|
|
287
|
-
const adrContent = `# Bootstrap Deployment Plan
|
|
288
|
-
|
|
289
|
-
## Status
|
|
290
|
-
PROPOSED - Awaiting human approval
|
|
291
|
-
|
|
292
|
-
## Context
|
|
293
|
-
This ADR documents the automated deployment plan generated for this project.
|
|
294
|
-
|
|
295
|
-
**Detected Platforms**: ${plan.detectedPlatforms.join(', ')}
|
|
296
|
-
**Recommended Platform**: ${plan.recommendedPlatform}
|
|
297
|
-
**Confidence**: ${(plan.confidence * 100).toFixed(1)}%
|
|
298
|
-
**Source**: ${plan.source}
|
|
299
|
-
**Generated**: ${plan.timestamp}
|
|
300
|
-
|
|
301
|
-
## Architecture Diagram
|
|
302
|
-
|
|
303
|
-
${plan.architectureDiagram}
|
|
304
|
-
|
|
305
|
-
## Required Files
|
|
306
|
-
|
|
307
|
-
${plan.requiredFiles
|
|
308
|
-
.map(f => `
|
|
309
|
-
### ${f.path}
|
|
310
|
-
- **Purpose**: ${f.purpose}
|
|
311
|
-
- **Required**: ${f.required ? 'Yes' : 'No'}
|
|
312
|
-
- **Secret**: ${f.isSecret ? 'Yes' : 'No'}
|
|
313
|
-
- **Can Auto-Generate**: ${f.canAutoGenerate ? 'Yes' : 'No'}
|
|
314
|
-
- **Best Practice**: ${f.currentBestPractice}
|
|
315
|
-
${f.validationCommand ? `- **Validation**: \`${f.validationCommand}\`` : ''}
|
|
316
|
-
`)
|
|
317
|
-
.join('\n')}
|
|
318
|
-
|
|
319
|
-
## Environment Variables
|
|
320
|
-
|
|
321
|
-
${plan.environmentVariables
|
|
322
|
-
.map(e => `
|
|
323
|
-
- **${e.name}** (${e.required ? 'Required' : 'Optional'})${e.isSecret ? ' ๐' : ''}
|
|
324
|
-
- ${e.purpose}
|
|
325
|
-
${e.defaultValue ? `- Default: \`${e.defaultValue}\`` : ''}
|
|
326
|
-
`)
|
|
327
|
-
.join('\n')}
|
|
328
|
-
|
|
329
|
-
## Deployment Steps
|
|
330
|
-
|
|
331
|
-
${plan.deploymentSteps
|
|
332
|
-
.map(s => `
|
|
333
|
-
### Step ${s.order}: ${s.title}
|
|
334
|
-
**Command**: \`${s.command}\`
|
|
335
|
-
**Description**: ${s.description}
|
|
336
|
-
**Expected Output**: ${s.expectedOutput}
|
|
337
|
-
**Estimated Time**: ${s.estimatedTime}
|
|
338
|
-
|
|
339
|
-
**Troubleshooting**:
|
|
340
|
-
${s.troubleshooting.map(t => `- ${t}`).join('\n')}
|
|
341
|
-
`)
|
|
342
|
-
.join('\n')}
|
|
343
|
-
|
|
344
|
-
## Validation Checks
|
|
345
|
-
|
|
346
|
-
${plan.validationChecks
|
|
347
|
-
.map(c => `
|
|
348
|
-
- **${c.name}** (${c.severity})
|
|
349
|
-
- Command: \`${c.command}\`
|
|
350
|
-
- Expected: ${c.expectedResult}
|
|
351
|
-
`)
|
|
352
|
-
.join('\n')}
|
|
353
|
-
|
|
354
|
-
## Risks
|
|
355
|
-
|
|
356
|
-
${plan.risks
|
|
357
|
-
.map(r => `
|
|
358
|
-
### ${r.risk} (${r.severity})
|
|
359
|
-
- **Likelihood**: ${r.likelihood}
|
|
360
|
-
- **Mitigation**: ${r.mitigation}
|
|
361
|
-
`)
|
|
362
|
-
.join('\n')}
|
|
363
|
-
|
|
364
|
-
## Prerequisites
|
|
365
|
-
|
|
366
|
-
${plan.prerequisites.map(p => `- ${p}`).join('\n')}
|
|
367
|
-
|
|
368
|
-
## Estimated Duration
|
|
369
|
-
${plan.estimatedDuration}
|
|
370
|
-
|
|
371
|
-
## Research Sources
|
|
372
|
-
|
|
373
|
-
${plan.researchSources.map(s => `- ${s}`).join('\n')}
|
|
374
|
-
|
|
375
|
-
## Decision
|
|
376
|
-
|
|
377
|
-
**Status**: โธ๏ธ **AWAITING HUMAN APPROVAL**
|
|
378
|
-
|
|
379
|
-
Please review this deployment plan and:
|
|
380
|
-
1. Verify the recommended platform is appropriate
|
|
381
|
-
2. Check that all required files are identified
|
|
382
|
-
3. Review security considerations (environment variables, secrets)
|
|
383
|
-
4. Validate deployment steps make sense for your environment
|
|
384
|
-
5. Approve or provide feedback for modifications
|
|
385
|
-
|
|
386
|
-
## Consequences
|
|
387
|
-
|
|
388
|
-
### Positive
|
|
389
|
-
- Automated deployment infrastructure
|
|
390
|
-
- Environment-aware configuration
|
|
391
|
-
- Validated deployment process
|
|
392
|
-
|
|
393
|
-
### Negative
|
|
394
|
-
- Initial setup overhead
|
|
395
|
-
- Platform-specific complexity
|
|
396
|
-
- Maintenance requirements
|
|
397
|
-
|
|
398
|
-
---
|
|
399
|
-
*This ADR was auto-generated by the Bootstrap Validation Loop system using AI-powered deployment intelligence.*
|
|
400
|
-
`;
|
|
401
|
-
// Ensure ADR directory exists
|
|
402
|
-
const adrDir = path.isAbsolute(this.adrDirectory)
|
|
403
|
-
? this.adrDirectory
|
|
404
|
-
: path.join(this.projectPath, this.adrDirectory);
|
|
405
|
-
await fs.mkdir(adrDir, { recursive: true });
|
|
406
|
-
// Create absolute path for ADR file
|
|
407
|
-
const adrPath = path.join(adrDir, `bootstrap-deployment-${Date.now()}.md`);
|
|
408
|
-
await fs.writeFile(adrPath, adrContent, 'utf-8');
|
|
409
|
-
return adrPath;
|
|
410
|
-
}
|
|
411
|
-
catch (error) {
|
|
412
|
-
this.logger.error('Failed to create bootstrap ADR', 'BootstrapValidationLoop', error);
|
|
413
|
-
throw error;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* Save bootstrap context document for future sessions
|
|
418
|
-
*/
|
|
419
|
-
async saveBootstrapContext(platformDetection, validatedPattern, deploymentPlan, bootstrapAdrPath) {
|
|
420
|
-
try {
|
|
421
|
-
const contextDoc = {
|
|
422
|
-
metadata: {
|
|
423
|
-
toolName: 'bootstrap_validation_loop',
|
|
424
|
-
toolVersion: '1.0.0',
|
|
425
|
-
generated: new Date().toISOString(),
|
|
426
|
-
projectPath: this.projectPath,
|
|
427
|
-
projectName: path.basename(this.projectPath),
|
|
428
|
-
status: 'success',
|
|
429
|
-
confidence: platformDetection.confidence * 100,
|
|
430
|
-
},
|
|
431
|
-
quickReference: `
|
|
432
|
-
Detected ${platformDetection.primaryPlatform} (${(platformDetection.confidence * 100).toFixed(0)}% confidence).
|
|
433
|
-
${validatedPattern ? `Using validated pattern: ${validatedPattern.name} v${validatedPattern.version}.` : 'Using dynamic AI analysis.'}
|
|
434
|
-
Bootstrap ADR: ${bootstrapAdrPath}
|
|
435
|
-
`.trim(),
|
|
436
|
-
executionSummary: {
|
|
437
|
-
status: 'Platform detected and deployment plan generated',
|
|
438
|
-
confidence: platformDetection.confidence * 100,
|
|
439
|
-
keyFindings: [
|
|
440
|
-
`Primary platform: ${platformDetection.primaryPlatform}`,
|
|
441
|
-
validatedPattern
|
|
442
|
-
? `Validated pattern: ${validatedPattern.name}`
|
|
443
|
-
: 'Dynamic AI deployment plan',
|
|
444
|
-
`Required files: ${deploymentPlan.requiredFiles.length}`,
|
|
445
|
-
`Deployment steps: ${deploymentPlan.deploymentSteps.length}`,
|
|
446
|
-
`Environment variables: ${deploymentPlan.environmentVariables.length}`,
|
|
447
|
-
],
|
|
448
|
-
},
|
|
449
|
-
detectedContext: {
|
|
450
|
-
platform: {
|
|
451
|
-
primary: platformDetection.primaryPlatform,
|
|
452
|
-
all: platformDetection.detectedPlatforms.map(p => p.type),
|
|
453
|
-
confidence: platformDetection.confidence,
|
|
454
|
-
evidence: platformDetection.evidence.slice(0, 10).map(e => ({
|
|
455
|
-
file: e.file,
|
|
456
|
-
indicator: e.indicator,
|
|
457
|
-
weight: e.weight,
|
|
458
|
-
})),
|
|
459
|
-
},
|
|
460
|
-
validatedPattern: validatedPattern
|
|
461
|
-
? {
|
|
462
|
-
name: validatedPattern.name,
|
|
463
|
-
version: validatedPattern.version,
|
|
464
|
-
platformType: validatedPattern.platformType,
|
|
465
|
-
source: 'typescript-builtin', // Could be enhanced to detect YAML vs TS
|
|
466
|
-
sourceHash: this.computePatternHash(validatedPattern),
|
|
467
|
-
loadedAt: new Date().toISOString(),
|
|
468
|
-
baseRepository: validatedPattern.baseCodeRepository.url,
|
|
469
|
-
authoritativeSources: validatedPattern.authoritativeSources.map(s => ({
|
|
470
|
-
type: s.type,
|
|
471
|
-
url: s.url,
|
|
472
|
-
required: s.requiredForDeployment,
|
|
473
|
-
purpose: s.purpose,
|
|
474
|
-
})),
|
|
475
|
-
deploymentPhases: validatedPattern.deploymentPhases.length,
|
|
476
|
-
validationChecks: validatedPattern.validationChecks.length,
|
|
477
|
-
}
|
|
478
|
-
: null,
|
|
479
|
-
deploymentPlan: {
|
|
480
|
-
recommendedPlatform: deploymentPlan.recommendedPlatform,
|
|
481
|
-
confidence: deploymentPlan.confidence,
|
|
482
|
-
source: deploymentPlan.source,
|
|
483
|
-
requiredFiles: deploymentPlan.requiredFiles.map(f => ({
|
|
484
|
-
path: f.path,
|
|
485
|
-
purpose: f.purpose,
|
|
486
|
-
required: f.required,
|
|
487
|
-
})),
|
|
488
|
-
environmentVariables: deploymentPlan.environmentVariables.map(e => ({
|
|
489
|
-
name: e.name,
|
|
490
|
-
required: e.required,
|
|
491
|
-
isSecret: e.isSecret,
|
|
492
|
-
})),
|
|
493
|
-
deploymentSteps: deploymentPlan.deploymentSteps.length,
|
|
494
|
-
estimatedDuration: deploymentPlan.estimatedDuration,
|
|
495
|
-
},
|
|
496
|
-
},
|
|
497
|
-
generatedArtifacts: [bootstrapAdrPath, 'bootstrap.sh', 'validate_bootstrap.sh'],
|
|
498
|
-
keyDecisions: [
|
|
499
|
-
{
|
|
500
|
-
decision: `Use ${platformDetection.primaryPlatform} as deployment platform`,
|
|
501
|
-
rationale: `Detected with ${(platformDetection.confidence * 100).toFixed(0)}% confidence based on project structure and configuration files`,
|
|
502
|
-
alternatives: platformDetection.detectedPlatforms
|
|
503
|
-
.filter(p => p.type !== platformDetection.primaryPlatform)
|
|
504
|
-
.map(p => `${p.type} (${(p.confidence * 100).toFixed(0)}% confidence)`)
|
|
505
|
-
.slice(0, 3),
|
|
506
|
-
},
|
|
507
|
-
],
|
|
508
|
-
learnings: {
|
|
509
|
-
successes: ['Platform detection completed successfully'],
|
|
510
|
-
failures: [],
|
|
511
|
-
recommendations: [
|
|
512
|
-
'Review bootstrap ADR before proceeding with deployment',
|
|
513
|
-
validatedPattern
|
|
514
|
-
? `Consult authoritative sources for ${validatedPattern.name}`
|
|
515
|
-
: 'Validate deployment plan with team',
|
|
516
|
-
],
|
|
517
|
-
environmentSpecific: [],
|
|
518
|
-
},
|
|
519
|
-
relatedDocuments: {
|
|
520
|
-
adrs: [bootstrapAdrPath],
|
|
521
|
-
configs: deploymentPlan.requiredFiles.map(f => f.path),
|
|
522
|
-
otherContexts: [],
|
|
523
|
-
},
|
|
524
|
-
rawData: {
|
|
525
|
-
// Full validated pattern snapshot for reproducibility
|
|
526
|
-
validatedPatternSnapshot: validatedPattern
|
|
527
|
-
? {
|
|
528
|
-
source: 'typescript-builtin', // Could be enhanced to detect YAML vs TS
|
|
529
|
-
hash: this.computePatternHash(validatedPattern),
|
|
530
|
-
timestamp: new Date().toISOString(),
|
|
531
|
-
definition: validatedPattern, // FULL pattern object with all commands, checks, templates
|
|
532
|
-
}
|
|
533
|
-
: null,
|
|
534
|
-
// Deployment plan details
|
|
535
|
-
deploymentPlan: {
|
|
536
|
-
recommendedPlatform: deploymentPlan.recommendedPlatform,
|
|
537
|
-
confidence: deploymentPlan.confidence,
|
|
538
|
-
source: deploymentPlan.source,
|
|
539
|
-
requiredFiles: deploymentPlan.requiredFiles,
|
|
540
|
-
environmentVariables: deploymentPlan.environmentVariables,
|
|
541
|
-
deploymentSteps: deploymentPlan.deploymentSteps,
|
|
542
|
-
estimatedDuration: deploymentPlan.estimatedDuration,
|
|
543
|
-
},
|
|
544
|
-
},
|
|
545
|
-
};
|
|
546
|
-
// Add validated pattern details if available
|
|
547
|
-
if (validatedPattern) {
|
|
548
|
-
contextDoc.keyDecisions.push({
|
|
549
|
-
decision: `Use ${validatedPattern.name} validated pattern`,
|
|
550
|
-
rationale: `Best practice pattern for ${platformDetection.primaryPlatform} deployments. Provides proven deployment workflow and authoritative sources.`,
|
|
551
|
-
alternatives: ['Custom deployment plan', 'Dynamic AI-generated plan'],
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
const contextPath = await this.contextManager.saveContext('bootstrap', contextDoc);
|
|
555
|
-
return contextPath;
|
|
556
|
-
}
|
|
557
|
-
catch (error) {
|
|
558
|
-
this.logger.error('Failed to save bootstrap context', 'BootstrapValidationLoop', error);
|
|
559
|
-
throw error;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* Generate bootstrap scripts from validated pattern (NEW)
|
|
564
|
-
*/
|
|
565
|
-
async generateBootstrapScriptsFromPattern() {
|
|
566
|
-
if (!this.validatedPattern) {
|
|
567
|
-
return false;
|
|
568
|
-
}
|
|
569
|
-
try {
|
|
570
|
-
this.logger.info(`Generating bootstrap scripts from validated pattern: ${this.validatedPattern.name}`, 'BootstrapValidationLoop');
|
|
571
|
-
// Generate bootstrap.sh from pattern's deployment phases
|
|
572
|
-
let bootstrapScript = `#!/bin/bash
|
|
573
|
-
# Bootstrap script generated from ${this.validatedPattern.name} v${this.validatedPattern.version}
|
|
574
|
-
# Pattern source: ${this.validatedPattern.metadata.source}
|
|
575
|
-
# Last updated: ${this.validatedPattern.metadata.lastUpdated}
|
|
576
|
-
# Generated: ${new Date().toISOString()}
|
|
577
|
-
|
|
578
|
-
set -e # Exit on error
|
|
579
|
-
set -u # Exit on undefined variable
|
|
580
|
-
|
|
581
|
-
echo "========================================"
|
|
582
|
-
echo "Bootstrap Deployment - ${this.validatedPattern.platformType}"
|
|
583
|
-
echo "Pattern: ${this.validatedPattern.name}"
|
|
584
|
-
echo "========================================"
|
|
585
|
-
echo ""
|
|
586
|
-
|
|
587
|
-
`;
|
|
588
|
-
// Add each deployment phase
|
|
589
|
-
for (const phase of this.validatedPattern.deploymentPhases) {
|
|
590
|
-
bootstrapScript += `
|
|
591
|
-
# ============================================================================
|
|
592
|
-
# Phase ${phase.order}: ${phase.name}
|
|
593
|
-
# ${phase.description}
|
|
594
|
-
# Estimated duration: ${phase.estimatedDuration}
|
|
595
|
-
# ============================================================================
|
|
596
|
-
|
|
597
|
-
echo "Starting Phase ${phase.order}: ${phase.name}"
|
|
598
|
-
|
|
599
|
-
`;
|
|
600
|
-
// Add commands for this phase
|
|
601
|
-
for (const command of phase.commands) {
|
|
602
|
-
bootstrapScript += `# ${command.description}\n`;
|
|
603
|
-
bootstrapScript += `echo " โ ${command.description}"\n`;
|
|
604
|
-
bootstrapScript += `${command.command}\n\n`;
|
|
605
|
-
}
|
|
606
|
-
bootstrapScript += `echo "โ Phase ${phase.order} complete"\necho ""\n`;
|
|
607
|
-
}
|
|
608
|
-
bootstrapScript += `
|
|
609
|
-
echo "========================================"
|
|
610
|
-
echo "โ
Bootstrap deployment complete!"
|
|
611
|
-
echo "========================================"
|
|
612
|
-
`;
|
|
613
|
-
// Generate validate_bootstrap.sh from pattern's validation checks
|
|
614
|
-
let validationScript = `#!/bin/bash
|
|
615
|
-
# Validation script generated from ${this.validatedPattern.name}
|
|
616
|
-
# Generated: ${new Date().toISOString()}
|
|
617
|
-
|
|
618
|
-
set -e
|
|
619
|
-
|
|
620
|
-
echo "========================================"
|
|
621
|
-
echo "Bootstrap Validation"
|
|
622
|
-
echo "========================================"
|
|
623
|
-
echo ""
|
|
624
|
-
|
|
625
|
-
FAILED_CHECKS=0
|
|
626
|
-
|
|
627
|
-
`;
|
|
628
|
-
for (const check of this.validatedPattern.validationChecks) {
|
|
629
|
-
validationScript += `
|
|
630
|
-
# ${check.name} (${check.severity})
|
|
631
|
-
echo "Checking: ${check.name}"
|
|
632
|
-
if ${check.command}; then
|
|
633
|
-
echo " โ
PASSED: ${check.name}"
|
|
634
|
-
else
|
|
635
|
-
echo " โ FAILED: ${check.name}"
|
|
636
|
-
echo " ${check.failureMessage}"
|
|
637
|
-
echo " Remediation steps:"
|
|
638
|
-
${check.remediationSteps.map(step => ` echo " - ${step}"`).join('\n')}
|
|
639
|
-
FAILED_CHECKS=$((FAILED_CHECKS + 1))
|
|
640
|
-
fi
|
|
641
|
-
echo ""
|
|
642
|
-
|
|
643
|
-
`;
|
|
644
|
-
}
|
|
645
|
-
validationScript += `
|
|
646
|
-
if [ $FAILED_CHECKS -eq 0 ]; then
|
|
647
|
-
echo "========================================"
|
|
648
|
-
echo "โ
All validation checks passed!"
|
|
649
|
-
echo "========================================"
|
|
650
|
-
exit 0
|
|
651
|
-
else
|
|
652
|
-
echo "========================================"
|
|
653
|
-
echo "โ $FAILED_CHECKS validation check(s) failed"
|
|
654
|
-
echo "========================================"
|
|
655
|
-
exit 1
|
|
656
|
-
fi
|
|
657
|
-
`;
|
|
658
|
-
// Write scripts
|
|
659
|
-
const bootstrapPath = path.join(this.projectPath, 'bootstrap.sh');
|
|
660
|
-
await fs.writeFile(bootstrapPath, bootstrapScript, { mode: 0o755 });
|
|
661
|
-
const validationPath = path.join(this.projectPath, 'validate_bootstrap.sh');
|
|
662
|
-
await fs.writeFile(validationPath, validationScript, { mode: 0o755 });
|
|
663
|
-
this.logger.info('Bootstrap scripts generated from validated pattern', 'BootstrapValidationLoop', {
|
|
664
|
-
bootstrapPath,
|
|
665
|
-
validationPath,
|
|
666
|
-
phases: this.validatedPattern.deploymentPhases.length,
|
|
667
|
-
validationChecks: this.validatedPattern.validationChecks.length,
|
|
668
|
-
});
|
|
669
|
-
return true;
|
|
670
|
-
}
|
|
671
|
-
catch (error) {
|
|
672
|
-
this.logger.error('Failed to generate scripts from validated pattern', 'BootstrapValidationLoop', error);
|
|
673
|
-
return false;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Generate bootstrap scripts from current ADRs (FALLBACK)
|
|
678
|
-
*/
|
|
679
|
-
async generateBootstrapScripts() {
|
|
680
|
-
// NEW: Try validated pattern first if available
|
|
681
|
-
if (this.validatedPattern) {
|
|
682
|
-
this.logger.info('๐ฏ Using validated pattern to generate bootstrap scripts', 'BootstrapValidationLoop');
|
|
683
|
-
const patternSuccess = await this.generateBootstrapScriptsFromPattern();
|
|
684
|
-
if (patternSuccess) {
|
|
685
|
-
return true;
|
|
686
|
-
}
|
|
687
|
-
this.logger.warn('Failed to generate from validated pattern, falling back to ADR-based generation', 'BootstrapValidationLoop');
|
|
688
|
-
}
|
|
689
|
-
try {
|
|
690
|
-
this.logger.info('Generating bootstrap scripts from ADRs', 'BootstrapValidationLoop');
|
|
691
|
-
const result = await generateAdrBootstrapScripts({
|
|
692
|
-
projectPath: this.projectPath,
|
|
693
|
-
adrDirectory: this.adrDirectory,
|
|
694
|
-
outputPath: this.projectPath,
|
|
695
|
-
scriptType: 'both',
|
|
696
|
-
includeTests: true,
|
|
697
|
-
includeDeployment: true,
|
|
698
|
-
enableTreeSitterAnalysis: true,
|
|
699
|
-
});
|
|
700
|
-
// Validate result structure
|
|
701
|
-
if (!result?.content?.[0]?.text) {
|
|
702
|
-
throw new McpAdrError('Invalid response from bootstrap script generator - missing content', 'INVALID_RESPONSE');
|
|
703
|
-
}
|
|
704
|
-
// Extract and validate scripts from result
|
|
705
|
-
let response;
|
|
706
|
-
try {
|
|
707
|
-
response = JSON.parse(result.content[0].text);
|
|
708
|
-
}
|
|
709
|
-
catch (parseError) {
|
|
710
|
-
this.logger.error('Failed to parse bootstrap script response as JSON', 'BootstrapValidationLoop', parseError);
|
|
711
|
-
throw new McpAdrError('Malformed JSON response from script generator', 'PARSE_ERROR');
|
|
712
|
-
}
|
|
713
|
-
// Validate response structure and required properties
|
|
714
|
-
if (!response?.scripts) {
|
|
715
|
-
throw new McpAdrError('Invalid response structure - missing scripts property', 'INVALID_RESPONSE');
|
|
716
|
-
}
|
|
717
|
-
if (typeof response.scripts.bootstrap !== 'string' || !response.scripts.bootstrap.trim()) {
|
|
718
|
-
throw new McpAdrError('Invalid or empty bootstrap script in response', 'INVALID_SCRIPT');
|
|
719
|
-
}
|
|
720
|
-
if (typeof response.scripts.validation !== 'string' || !response.scripts.validation.trim()) {
|
|
721
|
-
throw new McpAdrError('Invalid or empty validation script in response', 'INVALID_SCRIPT');
|
|
722
|
-
}
|
|
723
|
-
// Write bootstrap.sh
|
|
724
|
-
const bootstrapPath = path.join(this.projectPath, 'bootstrap.sh');
|
|
725
|
-
await fs.writeFile(bootstrapPath, response.scripts.bootstrap, { mode: 0o755 });
|
|
726
|
-
// Write validate_bootstrap.sh
|
|
727
|
-
const validationPath = path.join(this.projectPath, 'validate_bootstrap.sh');
|
|
728
|
-
await fs.writeFile(validationPath, response.scripts.validation, { mode: 0o755 });
|
|
729
|
-
this.logger.info('Bootstrap scripts generated successfully', 'BootstrapValidationLoop', {
|
|
730
|
-
bootstrapPath,
|
|
731
|
-
validationPath,
|
|
732
|
-
});
|
|
733
|
-
return true;
|
|
734
|
-
}
|
|
735
|
-
catch (error) {
|
|
736
|
-
this.logger.error('Failed to generate bootstrap scripts', 'BootstrapValidationLoop', error);
|
|
737
|
-
return false;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Execute bootstrap.sh with environment monitoring
|
|
742
|
-
*/
|
|
743
|
-
async executeBootstrapWithMonitoring(targetEnvironment, captureSnapshot) {
|
|
744
|
-
const executionId = `bootstrap_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
745
|
-
const timestamp = new Date().toISOString();
|
|
746
|
-
const startTime = Date.now();
|
|
747
|
-
this.logger.info('Executing bootstrap.sh with monitoring', 'BootstrapValidationLoop', {
|
|
748
|
-
executionId,
|
|
749
|
-
targetEnvironment,
|
|
750
|
-
});
|
|
751
|
-
let environmentSnapshot = {};
|
|
752
|
-
// Capture pre-execution environment snapshot
|
|
753
|
-
if (captureSnapshot) {
|
|
754
|
-
environmentSnapshot = await this.captureEnvironmentSnapshot();
|
|
755
|
-
}
|
|
756
|
-
// Execute bootstrap.sh
|
|
757
|
-
const bootstrapPath = path.join(this.projectPath, 'bootstrap.sh');
|
|
758
|
-
let exitCode = 0;
|
|
759
|
-
let stdout = '';
|
|
760
|
-
let stderr = '';
|
|
761
|
-
try {
|
|
762
|
-
const result = await execAsync(`bash "${bootstrapPath}"`, {
|
|
763
|
-
cwd: this.projectPath,
|
|
764
|
-
timeout: 300000, // 5 minute timeout
|
|
765
|
-
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
766
|
-
});
|
|
767
|
-
stdout = result.stdout;
|
|
768
|
-
stderr = result.stderr;
|
|
769
|
-
}
|
|
770
|
-
catch (error) {
|
|
771
|
-
exitCode = error.code || 1;
|
|
772
|
-
stdout = error.stdout || '';
|
|
773
|
-
stderr = error.stderr || error.message || '';
|
|
774
|
-
this.logger.warn('Bootstrap execution failed', 'BootstrapValidationLoop', {
|
|
775
|
-
exitCode,
|
|
776
|
-
stderr: stderr.substring(0, 500),
|
|
777
|
-
});
|
|
778
|
-
}
|
|
779
|
-
const duration = Date.now() - startTime;
|
|
780
|
-
return {
|
|
781
|
-
executionId,
|
|
782
|
-
timestamp,
|
|
783
|
-
success: exitCode === 0,
|
|
784
|
-
duration,
|
|
785
|
-
exitCode,
|
|
786
|
-
stdout,
|
|
787
|
-
stderr,
|
|
788
|
-
environmentSnapshot,
|
|
789
|
-
learnings: [], // Will be populated later
|
|
790
|
-
};
|
|
791
|
-
}
|
|
792
|
-
/**
|
|
793
|
-
* Execute validate_bootstrap.sh and analyze results
|
|
794
|
-
*/
|
|
795
|
-
async executeValidationScript(executionId) {
|
|
796
|
-
this.logger.info('Executing validate_bootstrap.sh', 'BootstrapValidationLoop', { executionId });
|
|
797
|
-
const validationPath = path.join(this.projectPath, 'validate_bootstrap.sh');
|
|
798
|
-
const validationResults = [];
|
|
799
|
-
let allPassed = true;
|
|
800
|
-
try {
|
|
801
|
-
const result = await execAsync(`bash "${validationPath}"`, {
|
|
802
|
-
cwd: this.projectPath,
|
|
803
|
-
timeout: 180000, // 3 minute timeout
|
|
804
|
-
});
|
|
805
|
-
// Parse validation output
|
|
806
|
-
// This is a simplified parser - actual implementation would be more sophisticated
|
|
807
|
-
const lines = result.stdout.split('\n');
|
|
808
|
-
for (const line of lines) {
|
|
809
|
-
if (line.includes('PASSED') || line.includes('FAILED')) {
|
|
810
|
-
const passed = line.includes('PASSED');
|
|
811
|
-
if (!passed)
|
|
812
|
-
allPassed = false;
|
|
813
|
-
validationResults.push({
|
|
814
|
-
checkId: `validation_${validationResults.length}`,
|
|
815
|
-
adrId: 'unknown', // Would be parsed from output
|
|
816
|
-
requirement: line.trim(),
|
|
817
|
-
passed,
|
|
818
|
-
actualState: passed ? 'compliant' : 'non-compliant',
|
|
819
|
-
expectedState: 'compliant',
|
|
820
|
-
confidence: 0.9,
|
|
821
|
-
evidence: [line],
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
catch (error) {
|
|
827
|
-
allPassed = false;
|
|
828
|
-
this.logger.error('Validation script failed', 'BootstrapValidationLoop', error);
|
|
829
|
-
// Record the failure
|
|
830
|
-
validationResults.push({
|
|
831
|
-
checkId: 'validation_execution',
|
|
832
|
-
adrId: 'system',
|
|
833
|
-
requirement: 'Execute validation script',
|
|
834
|
-
passed: false,
|
|
835
|
-
actualState: 'failed',
|
|
836
|
-
expectedState: 'success',
|
|
837
|
-
confidence: 1.0,
|
|
838
|
-
evidence: [error.message],
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
return { allPassed, validationResults };
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Extract learnings from execution results
|
|
845
|
-
*/
|
|
846
|
-
async extractLearnings(executionResult, validationResult) {
|
|
847
|
-
const learnings = [];
|
|
848
|
-
// Analyze failures
|
|
849
|
-
for (const validation of validationResult.validationResults) {
|
|
850
|
-
if (!validation.passed) {
|
|
851
|
-
learnings.push({
|
|
852
|
-
type: 'failure',
|
|
853
|
-
category: 'infrastructure',
|
|
854
|
-
description: `Validation failed: ${validation.requirement}`,
|
|
855
|
-
adrReference: validation.adrId,
|
|
856
|
-
severity: 'error',
|
|
857
|
-
recommendation: await this.generateRecommendation(validation, executionResult),
|
|
858
|
-
evidence: validation.evidence,
|
|
859
|
-
environmentSpecific: true,
|
|
860
|
-
timestamp: new Date().toISOString(),
|
|
861
|
-
});
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
// Analyze stderr for issues
|
|
865
|
-
if (executionResult.stderr) {
|
|
866
|
-
const errorLines = executionResult.stderr.split('\n').filter(line => line.trim());
|
|
867
|
-
for (const errorLine of errorLines) {
|
|
868
|
-
if (errorLine.toLowerCase().includes('error') ||
|
|
869
|
-
errorLine.toLowerCase().includes('failed')) {
|
|
870
|
-
learnings.push({
|
|
871
|
-
type: 'failure',
|
|
872
|
-
category: 'configuration',
|
|
873
|
-
description: errorLine.substring(0, 200),
|
|
874
|
-
severity: 'error',
|
|
875
|
-
recommendation: 'Review error message and adjust bootstrap script',
|
|
876
|
-
evidence: [errorLine],
|
|
877
|
-
environmentSpecific: true,
|
|
878
|
-
timestamp: new Date().toISOString(),
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
// Use ResearchOrchestrator to understand environment state
|
|
884
|
-
try {
|
|
885
|
-
const envCheck = await this.researchOrchestrator.answerResearchQuestion('What is the current state of the deployment environment? List all running services.');
|
|
886
|
-
if (envCheck.confidence > 0.7) {
|
|
887
|
-
learnings.push({
|
|
888
|
-
type: 'success',
|
|
889
|
-
category: 'infrastructure',
|
|
890
|
-
description: `Environment state verified: ${envCheck.answer?.substring(0, 200)}`,
|
|
891
|
-
severity: 'info',
|
|
892
|
-
recommendation: 'Document current environment state in ADRs',
|
|
893
|
-
evidence: [envCheck.answer || 'Environment check completed'],
|
|
894
|
-
environmentSpecific: true,
|
|
895
|
-
timestamp: new Date().toISOString(),
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
catch (error) {
|
|
900
|
-
this.logger.warn('Failed to check environment state', 'BootstrapValidationLoop', error);
|
|
901
|
-
}
|
|
902
|
-
return learnings;
|
|
903
|
-
}
|
|
904
|
-
/**
|
|
905
|
-
* Generate recommendation for a failed validation
|
|
906
|
-
*/
|
|
907
|
-
async generateRecommendation(validation, _executionResult) {
|
|
908
|
-
// Use ResearchOrchestrator to generate context-aware recommendation
|
|
909
|
-
try {
|
|
910
|
-
const question = `How can we fix this deployment issue: ${validation.requirement}?
|
|
911
|
-
Current state: ${validation.actualState}
|
|
912
|
-
Expected state: ${validation.expectedState}
|
|
913
|
-
Evidence: ${validation.evidence.join(', ')}`;
|
|
914
|
-
const answer = await this.researchOrchestrator.answerResearchQuestion(question);
|
|
915
|
-
if (answer.confidence > 0.6 && answer.answer) {
|
|
916
|
-
return answer.answer.substring(0, 500);
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
catch (error) {
|
|
920
|
-
this.logger.warn('Failed to generate AI recommendation', 'BootstrapValidationLoop', error);
|
|
921
|
-
}
|
|
922
|
-
return 'Review validation failure and adjust bootstrap script accordingly';
|
|
923
|
-
}
|
|
924
|
-
/**
|
|
925
|
-
* Update bootstrap scripts based on learnings
|
|
926
|
-
*/
|
|
927
|
-
async updateBootstrapScriptsFromLearnings(learnings, _executionResult) {
|
|
928
|
-
try {
|
|
929
|
-
const bootstrapPath = path.join(this.projectPath, 'bootstrap.sh');
|
|
930
|
-
let bootstrapContent = await fs.readFile(bootstrapPath, 'utf-8');
|
|
931
|
-
// Apply fixes based on learnings
|
|
932
|
-
for (const learning of learnings) {
|
|
933
|
-
if (learning.type === 'failure' && learning.category === 'infrastructure') {
|
|
934
|
-
// Add prerequisite checks
|
|
935
|
-
if (learning.description.toLowerCase().includes('postgres')) {
|
|
936
|
-
const postgresCheck = `
|
|
937
|
-
# Auto-fix: Added PostgreSQL startup check
|
|
938
|
-
if ! pgrep -x "postgres" > /dev/null; then
|
|
939
|
-
echo "Starting PostgreSQL..."
|
|
940
|
-
systemctl start postgresql || brew services start postgresql
|
|
941
|
-
sleep 3
|
|
942
|
-
fi
|
|
943
|
-
`;
|
|
944
|
-
bootstrapContent = bootstrapContent.replace('# Phase 2: Build and Prepare', postgresCheck + '\n# Phase 2: Build and Prepare');
|
|
945
|
-
}
|
|
946
|
-
// Add Docker checks
|
|
947
|
-
if (learning.description.toLowerCase().includes('docker')) {
|
|
948
|
-
const dockerCheck = `
|
|
949
|
-
# Auto-fix: Added Docker check
|
|
950
|
-
if ! docker ps > /dev/null 2>&1; then
|
|
951
|
-
echo "Docker is not running. Please start Docker and retry."
|
|
952
|
-
exit 1
|
|
953
|
-
fi
|
|
954
|
-
`;
|
|
955
|
-
bootstrapContent = dockerCheck + '\n' + bootstrapContent;
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
// Write updated script
|
|
960
|
-
await fs.writeFile(bootstrapPath, bootstrapContent, { mode: 0o755 });
|
|
961
|
-
this.logger.info('Bootstrap script updated with auto-fixes', 'BootstrapValidationLoop', {
|
|
962
|
-
fixesApplied: learnings.filter(l => l.type === 'failure').length,
|
|
963
|
-
});
|
|
964
|
-
return true;
|
|
965
|
-
}
|
|
966
|
-
catch (error) {
|
|
967
|
-
this.logger.error('Failed to update bootstrap scripts', 'BootstrapValidationLoop', error);
|
|
968
|
-
return false;
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
/**
|
|
972
|
-
* Generate ADR update proposals based on learnings
|
|
973
|
-
*/
|
|
974
|
-
async generateAdrUpdates(learnings, outcome) {
|
|
975
|
-
const proposals = [];
|
|
976
|
-
const { discoverAdrsInDirectory } = await import('../utils/adr-discovery.js');
|
|
977
|
-
const adrs = await discoverAdrsInDirectory(this.adrDirectory, this.projectPath, {
|
|
978
|
-
includeContent: true,
|
|
979
|
-
includeTimeline: false,
|
|
980
|
-
});
|
|
981
|
-
for (const adr of adrs.adrs) {
|
|
982
|
-
// Find learnings related to this ADR
|
|
983
|
-
const relatedLearnings = learnings.filter(l => l.adrReference === adr.filename || adr.content?.includes(l.description.substring(0, 50)));
|
|
984
|
-
if (relatedLearnings.length === 0)
|
|
985
|
-
continue;
|
|
986
|
-
// Generate update content
|
|
987
|
-
const updateContent = this.generateDeploymentExperienceSection(relatedLearnings, outcome);
|
|
988
|
-
proposals.push({
|
|
989
|
-
adrPath: path.join(this.adrDirectory, adr.filename || 'unknown.md'),
|
|
990
|
-
adrTitle: adr.title || 'Unknown ADR',
|
|
991
|
-
updateType: 'append',
|
|
992
|
-
sectionToUpdate: 'Deployment Experience',
|
|
993
|
-
proposedContent: updateContent,
|
|
994
|
-
learnings: relatedLearnings,
|
|
995
|
-
confidence: 0.85,
|
|
996
|
-
requiresReview: outcome === 'failure' && relatedLearnings.some(l => l.severity === 'critical'),
|
|
997
|
-
});
|
|
998
|
-
}
|
|
999
|
-
return proposals;
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* Generate deployment experience section content
|
|
1003
|
-
*/
|
|
1004
|
-
generateDeploymentExperienceSection(learnings, outcome) {
|
|
1005
|
-
const timestamp = new Date().toISOString().split('T')[0];
|
|
1006
|
-
let content = `\n## Deployment Experience\n\n`;
|
|
1007
|
-
content += `**Last Updated**: ${timestamp}\n`;
|
|
1008
|
-
content += `**Deployment Outcome**: ${outcome === 'success' ? 'โ
Successful' : 'โ ๏ธ Issues Encountered'}\n\n`;
|
|
1009
|
-
// Group learnings by category
|
|
1010
|
-
const byCategory = {};
|
|
1011
|
-
for (const learning of learnings) {
|
|
1012
|
-
if (!byCategory[learning.category]) {
|
|
1013
|
-
byCategory[learning.category] = [];
|
|
1014
|
-
}
|
|
1015
|
-
byCategory[learning.category].push(learning);
|
|
1016
|
-
}
|
|
1017
|
-
for (const [category, categoryLearnings] of Object.entries(byCategory)) {
|
|
1018
|
-
content += `### ${category.charAt(0).toUpperCase() + category.slice(1)}\n\n`;
|
|
1019
|
-
for (const learning of categoryLearnings) {
|
|
1020
|
-
const icon = learning.type === 'success' ? 'โ
' : learning.type === 'failure' ? 'โ' : 'โน๏ธ';
|
|
1021
|
-
content += `${icon} **${learning.description}**\n`;
|
|
1022
|
-
content += ` - Severity: ${learning.severity}\n`;
|
|
1023
|
-
content += ` - Recommendation: ${learning.recommendation}\n`;
|
|
1024
|
-
if (learning.environmentSpecific) {
|
|
1025
|
-
content += ` - *Environment-specific consideration*\n`;
|
|
1026
|
-
}
|
|
1027
|
-
content += `\n`;
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
return content;
|
|
1031
|
-
}
|
|
1032
|
-
/**
|
|
1033
|
-
* Detect missing files that might break bootstrap
|
|
1034
|
-
*/
|
|
1035
|
-
async detectMissingFiles() {
|
|
1036
|
-
const missingFiles = [];
|
|
1037
|
-
try {
|
|
1038
|
-
// Parse .gitignore patterns
|
|
1039
|
-
const gitignorePatterns = await this.parseGitignore();
|
|
1040
|
-
// Extract file references from ADRs
|
|
1041
|
-
const { discoverAdrsInDirectory } = await import('../utils/adr-discovery.js');
|
|
1042
|
-
const adrs = await discoverAdrsInDirectory(this.adrDirectory, this.projectPath, {
|
|
1043
|
-
includeContent: true,
|
|
1044
|
-
includeTimeline: false,
|
|
1045
|
-
});
|
|
1046
|
-
const fileReferences = await this.extractFileReferencesFromAdrs(adrs.adrs);
|
|
1047
|
-
// Check each referenced file
|
|
1048
|
-
for (const [filePath, referencedBy] of fileReferences.entries()) {
|
|
1049
|
-
const fullPath = path.join(this.projectPath, filePath);
|
|
1050
|
-
try {
|
|
1051
|
-
await fs.access(fullPath);
|
|
1052
|
-
// File exists, continue
|
|
1053
|
-
}
|
|
1054
|
-
catch (error) {
|
|
1055
|
-
// Only treat as missing if it's a "file not found" error
|
|
1056
|
-
// Other errors (permissions, etc.) should be logged but not treated as missing
|
|
1057
|
-
if (error.code === 'ENOENT') {
|
|
1058
|
-
const isIgnored = this.isFileIgnored(filePath, gitignorePatterns);
|
|
1059
|
-
const fileInfo = this.analyzeMissingFile(filePath, referencedBy, isIgnored);
|
|
1060
|
-
missingFiles.push(fileInfo);
|
|
1061
|
-
}
|
|
1062
|
-
else {
|
|
1063
|
-
this.logger.warn(`Error accessing file ${filePath}: ${error.message}`, 'BootstrapValidationLoop');
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
// Check for common missing files
|
|
1068
|
-
const commonFiles = [
|
|
1069
|
-
{ path: '.env', type: 'env', critical: true },
|
|
1070
|
-
{ path: '.env.example', type: 'env', critical: false },
|
|
1071
|
-
{ path: 'config/database.yml', type: 'config', critical: true },
|
|
1072
|
-
{ path: 'config/secrets.yml', type: 'secret', critical: true },
|
|
1073
|
-
{ path: '.npmrc', type: 'config', critical: false },
|
|
1074
|
-
{ path: 'tsconfig.json', type: 'build', critical: false },
|
|
1075
|
-
];
|
|
1076
|
-
for (const common of commonFiles) {
|
|
1077
|
-
const fullPath = path.join(this.projectPath, common.path);
|
|
1078
|
-
try {
|
|
1079
|
-
await fs.access(fullPath);
|
|
1080
|
-
}
|
|
1081
|
-
catch (error) {
|
|
1082
|
-
// Only treat as missing if it's a "file not found" error
|
|
1083
|
-
if (error.code === 'ENOENT') {
|
|
1084
|
-
const isIgnored = this.isFileIgnored(common.path, gitignorePatterns);
|
|
1085
|
-
if (isIgnored || common.critical) {
|
|
1086
|
-
missingFiles.push({
|
|
1087
|
-
filePath: common.path,
|
|
1088
|
-
fileType: common.type,
|
|
1089
|
-
isIgnored,
|
|
1090
|
-
requiredBy: ['bootstrap-process'],
|
|
1091
|
-
severity: common.critical ? 'critical' : 'warning',
|
|
1092
|
-
canCreateTemplate: true,
|
|
1093
|
-
templateContent: this.generateTemplateContent(common.path, common.type),
|
|
1094
|
-
recommendation: isIgnored
|
|
1095
|
-
? `Create ${common.path} from template (file is gitignored)`
|
|
1096
|
-
: `Consider creating ${common.path} for better configuration`,
|
|
1097
|
-
});
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
else {
|
|
1101
|
-
this.logger.warn(`Error accessing common file ${common.path}: ${error.message}`, 'BootstrapValidationLoop');
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
catch (error) {
|
|
1107
|
-
this.logger.warn('Failed to detect missing files', 'BootstrapValidationLoop', error);
|
|
1108
|
-
}
|
|
1109
|
-
return missingFiles;
|
|
1110
|
-
}
|
|
1111
|
-
/**
|
|
1112
|
-
* Parse .gitignore file patterns
|
|
1113
|
-
*/
|
|
1114
|
-
async parseGitignore() {
|
|
1115
|
-
const gitignorePath = path.join(this.projectPath, '.gitignore');
|
|
1116
|
-
const patterns = [];
|
|
1117
|
-
try {
|
|
1118
|
-
const content = await fs.readFile(gitignorePath, 'utf-8');
|
|
1119
|
-
const lines = content.split('\n');
|
|
1120
|
-
for (const line of lines) {
|
|
1121
|
-
const trimmed = line.trim();
|
|
1122
|
-
// Skip comments and empty lines
|
|
1123
|
-
if (trimmed && !trimmed.startsWith('#')) {
|
|
1124
|
-
patterns.push(trimmed);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
catch {
|
|
1129
|
-
// .gitignore doesn't exist or can't be read
|
|
1130
|
-
this.logger.info('No .gitignore file found', 'BootstrapValidationLoop');
|
|
1131
|
-
}
|
|
1132
|
-
return patterns;
|
|
1133
|
-
}
|
|
1134
|
-
/**
|
|
1135
|
-
* Check if a file path matches gitignore patterns
|
|
1136
|
-
*/
|
|
1137
|
-
isFileIgnored(filePath, patterns) {
|
|
1138
|
-
for (const pattern of patterns) {
|
|
1139
|
-
// Simple pattern matching (could be enhanced with minimatch)
|
|
1140
|
-
if (pattern.includes('*')) {
|
|
1141
|
-
// Wildcard pattern
|
|
1142
|
-
const regex = new RegExp(pattern.replace(/\*/g, '.*').replace(/\?/g, '.'));
|
|
1143
|
-
if (regex.test(filePath))
|
|
1144
|
-
return true;
|
|
1145
|
-
}
|
|
1146
|
-
else {
|
|
1147
|
-
// Exact match or directory match
|
|
1148
|
-
if (filePath === pattern || filePath.startsWith(pattern + '/')) {
|
|
1149
|
-
return true;
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
return false;
|
|
1154
|
-
}
|
|
1155
|
-
/**
|
|
1156
|
-
* Extract file references from ADR content
|
|
1157
|
-
*/
|
|
1158
|
-
async extractFileReferencesFromAdrs(adrs) {
|
|
1159
|
-
const references = new Map();
|
|
1160
|
-
// Common file path patterns
|
|
1161
|
-
const filePatterns = [
|
|
1162
|
-
/(?:config|src|lib|dist)\/[a-zA-Z0-9_\-/.]+\.[a-zA-Z0-9]+/g,
|
|
1163
|
-
/\.[a-zA-Z0-9_-]+(?:\.example)?\.(?:json|yml|yaml|env|conf|config)/g,
|
|
1164
|
-
/`([^`]+\.[a-zA-Z0-9]+)`/g, // Files in backticks
|
|
1165
|
-
];
|
|
1166
|
-
for (const adr of adrs) {
|
|
1167
|
-
if (!adr.content)
|
|
1168
|
-
continue;
|
|
1169
|
-
for (const pattern of filePatterns) {
|
|
1170
|
-
const matches = adr.content.matchAll(pattern);
|
|
1171
|
-
for (const match of matches) {
|
|
1172
|
-
const filePath = match[1] || match[0];
|
|
1173
|
-
if (!references.has(filePath)) {
|
|
1174
|
-
references.set(filePath, []);
|
|
1175
|
-
}
|
|
1176
|
-
references.get(filePath).push(adr.title || adr.filename || 'Unknown ADR');
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
return references;
|
|
1181
|
-
}
|
|
1182
|
-
/**
|
|
1183
|
-
* Analyze a missing file and provide recommendations
|
|
1184
|
-
*/
|
|
1185
|
-
analyzeMissingFile(filePath, referencedBy, isIgnored) {
|
|
1186
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
1187
|
-
let fileType = 'unknown';
|
|
1188
|
-
let severity = 'warning';
|
|
1189
|
-
let canCreateTemplate = false;
|
|
1190
|
-
// Determine file type and severity
|
|
1191
|
-
if (filePath.includes('.env') || ext === '.env') {
|
|
1192
|
-
fileType = 'env';
|
|
1193
|
-
severity = isIgnored ? 'critical' : 'error';
|
|
1194
|
-
canCreateTemplate = true;
|
|
1195
|
-
}
|
|
1196
|
-
else if (['.json', '.yml', '.yaml', '.conf'].includes(ext)) {
|
|
1197
|
-
fileType = 'config';
|
|
1198
|
-
severity = 'error';
|
|
1199
|
-
canCreateTemplate = true;
|
|
1200
|
-
}
|
|
1201
|
-
else if (['tsconfig.json', 'webpack.config.js', 'vite.config.ts'].includes(path.basename(filePath))) {
|
|
1202
|
-
fileType = 'build';
|
|
1203
|
-
severity = 'warning';
|
|
1204
|
-
canCreateTemplate = true;
|
|
1205
|
-
}
|
|
1206
|
-
else if (filePath.includes('secret') || filePath.includes('credential')) {
|
|
1207
|
-
fileType = 'secret';
|
|
1208
|
-
severity = 'critical';
|
|
1209
|
-
canCreateTemplate = false; // Never create secrets automatically
|
|
1210
|
-
}
|
|
1211
|
-
const templateContent = canCreateTemplate
|
|
1212
|
-
? this.generateTemplateContent(filePath, fileType)
|
|
1213
|
-
: undefined;
|
|
1214
|
-
return {
|
|
1215
|
-
filePath,
|
|
1216
|
-
fileType,
|
|
1217
|
-
isIgnored,
|
|
1218
|
-
requiredBy: referencedBy,
|
|
1219
|
-
severity,
|
|
1220
|
-
canCreateTemplate,
|
|
1221
|
-
templateContent,
|
|
1222
|
-
recommendation: this.generateMissingFileRecommendation(filePath, fileType, isIgnored, referencedBy),
|
|
1223
|
-
};
|
|
1224
|
-
}
|
|
1225
|
-
/**
|
|
1226
|
-
* Generate template content for missing files
|
|
1227
|
-
*/
|
|
1228
|
-
generateTemplateContent(filePath, _fileType) {
|
|
1229
|
-
const fileName = path.basename(filePath);
|
|
1230
|
-
if (fileName === '.env' || fileName === '.env.example') {
|
|
1231
|
-
return `# Environment Configuration
|
|
1232
|
-
# Generated by Bootstrap Validation Loop
|
|
1233
|
-
# IMPORTANT: Update these values before deployment
|
|
1234
|
-
|
|
1235
|
-
# Application
|
|
1236
|
-
NODE_ENV=development
|
|
1237
|
-
PORT=3000
|
|
1238
|
-
|
|
1239
|
-
# Database
|
|
1240
|
-
DATABASE_URL=postgresql://localhost:5432/myapp
|
|
1241
|
-
DATABASE_POOL_SIZE=10
|
|
1242
|
-
|
|
1243
|
-
# Security
|
|
1244
|
-
JWT_SECRET=CHANGE_ME_IN_PRODUCTION
|
|
1245
|
-
SESSION_SECRET=CHANGE_ME_IN_PRODUCTION
|
|
1246
|
-
|
|
1247
|
-
# External Services
|
|
1248
|
-
API_KEY=your_api_key_here
|
|
1249
|
-
|
|
1250
|
-
# Logging
|
|
1251
|
-
LOG_LEVEL=info
|
|
1252
|
-
`;
|
|
1253
|
-
}
|
|
1254
|
-
if (fileName.endsWith('.json')) {
|
|
1255
|
-
return `{
|
|
1256
|
-
"// NOTE": "Generated template - customize for your project",
|
|
1257
|
-
"name": "project-config",
|
|
1258
|
-
"version": "1.0.0",
|
|
1259
|
-
"description": "Generated configuration file"
|
|
1260
|
-
}
|
|
1261
|
-
`;
|
|
1262
|
-
}
|
|
1263
|
-
if (fileName.endsWith('.yml') || fileName.endsWith('.yaml')) {
|
|
1264
|
-
return `# Generated template - customize for your project
|
|
1265
|
-
# YAML Configuration
|
|
1266
|
-
|
|
1267
|
-
default:
|
|
1268
|
-
setting: value
|
|
1269
|
-
enabled: false
|
|
1270
|
-
|
|
1271
|
-
development:
|
|
1272
|
-
debug: true
|
|
1273
|
-
|
|
1274
|
-
production:
|
|
1275
|
-
debug: false
|
|
1276
|
-
`;
|
|
1277
|
-
}
|
|
1278
|
-
return `# Generated template for ${fileName}
|
|
1279
|
-
# Please customize this file for your project
|
|
1280
|
-
`;
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Generate recommendation for missing file
|
|
1284
|
-
*/
|
|
1285
|
-
generateMissingFileRecommendation(filePath, fileType, isIgnored, referencedBy) {
|
|
1286
|
-
let recommendation = `Missing file: ${filePath}\n`;
|
|
1287
|
-
recommendation += `Referenced by: ${referencedBy.join(', ')}\n`;
|
|
1288
|
-
if (isIgnored) {
|
|
1289
|
-
recommendation += `โ ๏ธ File is gitignored - this is intentional for security\n`;
|
|
1290
|
-
}
|
|
1291
|
-
switch (fileType) {
|
|
1292
|
-
case 'env':
|
|
1293
|
-
recommendation += isIgnored
|
|
1294
|
-
? `ACTION: Create ${filePath} from template or .env.example\n`
|
|
1295
|
-
: `ACTION: Add ${filePath} to .gitignore and create from template\n`;
|
|
1296
|
-
break;
|
|
1297
|
-
case 'secret':
|
|
1298
|
-
recommendation += `โ CRITICAL: Never commit secrets to version control\n`;
|
|
1299
|
-
recommendation += `ACTION: Create ${filePath} manually with secure values\n`;
|
|
1300
|
-
break;
|
|
1301
|
-
case 'config':
|
|
1302
|
-
recommendation += `ACTION: Create configuration file from template\n`;
|
|
1303
|
-
break;
|
|
1304
|
-
case 'build':
|
|
1305
|
-
recommendation += `ACTION: Generate build configuration or copy from similar project\n`;
|
|
1306
|
-
break;
|
|
1307
|
-
default:
|
|
1308
|
-
recommendation += `ACTION: Create file or update ADRs to remove reference\n`;
|
|
1309
|
-
}
|
|
1310
|
-
return recommendation;
|
|
1311
|
-
}
|
|
1312
|
-
/**
|
|
1313
|
-
* Handle missing files - create templates or add to bootstrap prerequisites
|
|
1314
|
-
*/
|
|
1315
|
-
async handleMissingFiles(missingFiles) {
|
|
1316
|
-
const learnings = [];
|
|
1317
|
-
for (const missing of missingFiles) {
|
|
1318
|
-
// Create template files if safe to do so
|
|
1319
|
-
if (missing.canCreateTemplate && missing.templateContent && missing.fileType !== 'secret') {
|
|
1320
|
-
try {
|
|
1321
|
-
const fullPath = path.join(this.projectPath, missing.filePath);
|
|
1322
|
-
const dir = path.dirname(fullPath);
|
|
1323
|
-
// Ensure directory exists
|
|
1324
|
-
await fs.mkdir(dir, { recursive: true });
|
|
1325
|
-
// Create template file
|
|
1326
|
-
await fs.writeFile(fullPath, missing.templateContent, 'utf-8');
|
|
1327
|
-
learnings.push({
|
|
1328
|
-
type: 'success',
|
|
1329
|
-
category: 'configuration',
|
|
1330
|
-
description: `Created template file: ${missing.filePath}`,
|
|
1331
|
-
severity: 'info',
|
|
1332
|
-
recommendation: `Review and customize ${missing.filePath} before deployment`,
|
|
1333
|
-
evidence: [`File created with template content`],
|
|
1334
|
-
environmentSpecific: false,
|
|
1335
|
-
timestamp: new Date().toISOString(),
|
|
1336
|
-
});
|
|
1337
|
-
this.logger.info(`Created template file: ${missing.filePath}`, 'BootstrapValidationLoop');
|
|
1338
|
-
}
|
|
1339
|
-
catch (error) {
|
|
1340
|
-
learnings.push({
|
|
1341
|
-
type: 'failure',
|
|
1342
|
-
category: 'configuration',
|
|
1343
|
-
description: `Failed to create template: ${missing.filePath}`,
|
|
1344
|
-
severity: missing.severity,
|
|
1345
|
-
recommendation: missing.recommendation,
|
|
1346
|
-
evidence: [error.message],
|
|
1347
|
-
environmentSpecific: true,
|
|
1348
|
-
timestamp: new Date().toISOString(),
|
|
1349
|
-
});
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
else {
|
|
1353
|
-
// File cannot be auto-created, add as prerequisite
|
|
1354
|
-
learnings.push({
|
|
1355
|
-
type: 'prerequisite',
|
|
1356
|
-
category: missing.fileType === 'secret' ? 'security' : 'configuration',
|
|
1357
|
-
description: `Missing required file: ${missing.filePath}`,
|
|
1358
|
-
severity: missing.severity,
|
|
1359
|
-
recommendation: missing.recommendation,
|
|
1360
|
-
evidence: missing.requiredBy,
|
|
1361
|
-
environmentSpecific: missing.isIgnored,
|
|
1362
|
-
timestamp: new Date().toISOString(),
|
|
1363
|
-
});
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
return learnings;
|
|
1367
|
-
}
|
|
1368
|
-
/**
|
|
1369
|
-
* Capture environment snapshot
|
|
1370
|
-
*/
|
|
1371
|
-
async captureEnvironmentSnapshot() {
|
|
1372
|
-
const snapshot = {};
|
|
1373
|
-
try {
|
|
1374
|
-
// Docker check
|
|
1375
|
-
try {
|
|
1376
|
-
const docker = await execAsync('docker ps --format "{{json .}}"', { timeout: 5000 });
|
|
1377
|
-
snapshot.docker = {
|
|
1378
|
-
available: true,
|
|
1379
|
-
containers: docker.stdout.split('\n').filter(Boolean),
|
|
1380
|
-
};
|
|
1381
|
-
}
|
|
1382
|
-
catch {
|
|
1383
|
-
snapshot.docker = { available: false };
|
|
1384
|
-
}
|
|
1385
|
-
// Kubernetes check
|
|
1386
|
-
try {
|
|
1387
|
-
const k8s = await execAsync('kubectl get all --all-namespaces -o json', { timeout: 10000 });
|
|
1388
|
-
snapshot.kubernetes = { available: true, resources: JSON.parse(k8s.stdout) };
|
|
1389
|
-
}
|
|
1390
|
-
catch {
|
|
1391
|
-
snapshot.kubernetes = { available: false };
|
|
1392
|
-
}
|
|
1393
|
-
// OpenShift check
|
|
1394
|
-
try {
|
|
1395
|
-
const oc = await execAsync('oc status', { timeout: 5000 });
|
|
1396
|
-
snapshot.openshift = { available: true, status: oc.stdout };
|
|
1397
|
-
}
|
|
1398
|
-
catch {
|
|
1399
|
-
snapshot.openshift = { available: false };
|
|
1400
|
-
}
|
|
1401
|
-
// System info
|
|
1402
|
-
snapshot.systemInfo = {
|
|
1403
|
-
platform: process.platform,
|
|
1404
|
-
arch: process.arch,
|
|
1405
|
-
nodeVersion: process.version,
|
|
1406
|
-
timestamp: new Date().toISOString(),
|
|
1407
|
-
};
|
|
1408
|
-
}
|
|
1409
|
-
catch (error) {
|
|
1410
|
-
this.logger.warn('Failed to capture complete environment snapshot', 'BootstrapValidationLoop', error);
|
|
1411
|
-
}
|
|
1412
|
-
return snapshot;
|
|
1413
|
-
}
|
|
1414
|
-
/**
|
|
1415
|
-
* Store execution result in memory system
|
|
1416
|
-
*/
|
|
1417
|
-
async storeExecutionInMemory(result) {
|
|
1418
|
-
try {
|
|
1419
|
-
await this.memoryManager.upsertEntity({
|
|
1420
|
-
type: 'bootstrap_execution',
|
|
1421
|
-
title: `Bootstrap Execution ${result.executionId} - ${result.success ? 'Success' : 'Failed'}`,
|
|
1422
|
-
description: `Bootstrap execution on ${result.timestamp} with ${result.learnings.length} learnings captured`,
|
|
1423
|
-
tags: [
|
|
1424
|
-
'bootstrap',
|
|
1425
|
-
'deployment',
|
|
1426
|
-
result.success ? 'success' : 'failure',
|
|
1427
|
-
`iteration-${this.currentIteration}`,
|
|
1428
|
-
`learnings-${result.learnings.length}`,
|
|
1429
|
-
`execution-${result.executionId}`,
|
|
1430
|
-
],
|
|
1431
|
-
relationships: [],
|
|
1432
|
-
context: {
|
|
1433
|
-
projectPhase: 'deployment',
|
|
1434
|
-
technicalStack: ['bootstrap', 'validation'],
|
|
1435
|
-
environmentalFactors: [result.success ? 'success' : 'failure'],
|
|
1436
|
-
stakeholders: ['deployment-team', 'infrastructure-team'],
|
|
1437
|
-
},
|
|
1438
|
-
});
|
|
1439
|
-
}
|
|
1440
|
-
catch (error) {
|
|
1441
|
-
this.logger.warn('Failed to store execution in memory', 'BootstrapValidationLoop', error);
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
/**
|
|
1445
|
-
* Compute SHA-256 hash of validated pattern for change detection
|
|
1446
|
-
* @param pattern - Validated pattern to hash
|
|
1447
|
-
* @returns Hex-encoded SHA-256 hash
|
|
1448
|
-
*/
|
|
1449
|
-
/**
|
|
1450
|
-
* Execute Infrastructure Layer using DAG with validated pattern
|
|
1451
|
-
*
|
|
1452
|
-
* This method:
|
|
1453
|
-
* 1. Attempts to load validated pattern from YAML
|
|
1454
|
-
* 2. If pattern exists: Auto-generates DAG tasks from pattern definition
|
|
1455
|
-
* 3. If pattern doesn't exist: Works with user to create GitHub issue
|
|
1456
|
-
* 4. Executes infrastructure DAG with parallel execution where possible
|
|
1457
|
-
*/
|
|
1458
|
-
async executeInfrastructureDAG(platformDetection) {
|
|
1459
|
-
const platform = platformDetection.primaryPlatform || 'unknown';
|
|
1460
|
-
this.logger.info(`๐๏ธ Starting Infrastructure Layer DAG execution for ${platform}`, 'BootstrapValidationLoop');
|
|
1461
|
-
// Try to load validated pattern from YAML
|
|
1462
|
-
let dynamicPattern = await this.patternLoader.loadPattern(platform);
|
|
1463
|
-
// If no pattern exists, work with user to create GitHub issue
|
|
1464
|
-
if (!dynamicPattern) {
|
|
1465
|
-
this.logger.warn(`โ ๏ธ No validated pattern found for platform: ${platform}`, 'BootstrapValidationLoop');
|
|
1466
|
-
// Prompt user for contribution
|
|
1467
|
-
const prompt = this.contributionHelper.promptUserForContribution(platform, platformDetection);
|
|
1468
|
-
this.logger.info(prompt, 'BootstrapValidationLoop');
|
|
1469
|
-
// Create GitHub issue (in production, this would use GitHub API)
|
|
1470
|
-
const issueResult = await this.contributionHelper.createGitHubIssue(platform, platformDetection);
|
|
1471
|
-
const successMessage = this.contributionHelper.generateSuccessMessage(issueResult, platform);
|
|
1472
|
-
this.logger.info(successMessage, 'BootstrapValidationLoop');
|
|
1473
|
-
// Fall back to AI-generated pattern
|
|
1474
|
-
this.logger.info('๐ค Generating fallback pattern using AI...', 'BootstrapValidationLoop');
|
|
1475
|
-
dynamicPattern = await this.generateFallbackPattern(platformDetection);
|
|
1476
|
-
}
|
|
1477
|
-
// Auto-generate infrastructure DAG tasks from validated pattern
|
|
1478
|
-
const tasks = this.patternConverter.buildInfrastructureTasksFromPattern(dynamicPattern, platform);
|
|
1479
|
-
this.logger.info(`๐ Generated ${tasks.length} infrastructure DAG tasks`, 'BootstrapValidationLoop');
|
|
1480
|
-
// Initialize SystemCard before execution
|
|
1481
|
-
const bootstrapId = `${platform}-bootstrap-${Date.now()}`;
|
|
1482
|
-
const detectedPlatform = platformDetection.detectedPlatforms.find(p => p.type === platform);
|
|
1483
|
-
await this.systemCardManager.initialize({
|
|
1484
|
-
systemId: bootstrapId,
|
|
1485
|
-
created: new Date().toISOString(),
|
|
1486
|
-
platform: {
|
|
1487
|
-
type: platform,
|
|
1488
|
-
...(detectedPlatform?.version && { version: detectedPlatform.version }),
|
|
1489
|
-
detectionConfidence: platformDetection.confidence,
|
|
1490
|
-
},
|
|
1491
|
-
bootstrapContext: {
|
|
1492
|
-
bootstrapId,
|
|
1493
|
-
validatedPatternId: dynamicPattern.id,
|
|
1494
|
-
// These will be updated after bootstrap completion
|
|
1495
|
-
},
|
|
1496
|
-
metadata: {
|
|
1497
|
-
tags: ['bootstrap', platform, 'infrastructure'],
|
|
1498
|
-
description: `Bootstrap deployment for ${platform}`,
|
|
1499
|
-
},
|
|
1500
|
-
});
|
|
1501
|
-
this.logger.info('๐ SystemCard initialized for resource tracking', 'BootstrapValidationLoop');
|
|
1502
|
-
// Execute infrastructure DAG
|
|
1503
|
-
const result = await this.dagExecutor.execute(tasks);
|
|
1504
|
-
// Log results
|
|
1505
|
-
if (result.success) {
|
|
1506
|
-
this.logger.info(`โ
Infrastructure Layer complete: ${result.executedTasks.length} tasks executed in ${result.duration}ms`, 'BootstrapValidationLoop');
|
|
1507
|
-
// Extract resources from task results and update SystemCard
|
|
1508
|
-
// Note: Resource extraction will be implemented in next phase
|
|
1509
|
-
// For now, we'll generate cleanup phases based on what was executed
|
|
1510
|
-
const cleanupPhases = await this.systemCardManager.generateCleanupPhases();
|
|
1511
|
-
this.logger.info(`๐งน Generated ${cleanupPhases.length} cleanup phases with ${this.systemCardManager.getResourceCount()} tracked resources`, 'BootstrapValidationLoop');
|
|
1512
|
-
}
|
|
1513
|
-
else {
|
|
1514
|
-
this.logger.error(`โ Infrastructure Layer failed: ${result.failedTasks.length} tasks failed, ${result.skippedTasks.length} skipped`, 'BootstrapValidationLoop');
|
|
1515
|
-
// Log failed tasks
|
|
1516
|
-
for (const taskId of result.failedTasks) {
|
|
1517
|
-
const taskResult = result.taskResults.get(taskId);
|
|
1518
|
-
if (taskResult) {
|
|
1519
|
-
this.logger.error(` โ ${taskId}: ${taskResult.stderr || taskResult.error?.message}`, 'BootstrapValidationLoop');
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
return result;
|
|
1524
|
-
}
|
|
1525
|
-
/**
|
|
1526
|
-
* Generate fallback pattern using AI when validated pattern doesn't exist
|
|
1527
|
-
*/
|
|
1528
|
-
async generateFallbackPattern(detection) {
|
|
1529
|
-
this.logger.warn('โ ๏ธ Generating fallback pattern using AI - less tested than validated patterns', 'BootstrapValidationLoop');
|
|
1530
|
-
// Use existing dynamic deployment intelligence as fallback
|
|
1531
|
-
const plan = await this.deploymentIntelligence.generateDeploymentPlan();
|
|
1532
|
-
// Convert to DynamicPattern format
|
|
1533
|
-
return {
|
|
1534
|
-
version: '1.0-fallback',
|
|
1535
|
-
id: `${detection.primaryPlatform}-ai-generated`,
|
|
1536
|
-
name: `${detection.primaryPlatform} (AI-Generated)`,
|
|
1537
|
-
description: 'Auto-generated deployment pattern - not validated by community',
|
|
1538
|
-
authoritativeSources: [],
|
|
1539
|
-
deploymentPhases: plan.deploymentSteps.map((step, i) => {
|
|
1540
|
-
const prevStep = i > 0 ? plan.deploymentSteps[i - 1] : undefined;
|
|
1541
|
-
return {
|
|
1542
|
-
order: i + 1,
|
|
1543
|
-
name: step.title,
|
|
1544
|
-
description: step.description,
|
|
1545
|
-
estimatedDuration: step.estimatedTime,
|
|
1546
|
-
canParallelize: false,
|
|
1547
|
-
prerequisites: prevStep ? [prevStep.title] : [],
|
|
1548
|
-
commands: [
|
|
1549
|
-
{
|
|
1550
|
-
description: step.description,
|
|
1551
|
-
command: step.command,
|
|
1552
|
-
expectedExitCode: 0,
|
|
1553
|
-
},
|
|
1554
|
-
],
|
|
1555
|
-
};
|
|
1556
|
-
}),
|
|
1557
|
-
validationChecks: plan.validationChecks.map((check, i) => ({
|
|
1558
|
-
id: `check-${i}`,
|
|
1559
|
-
name: check.name,
|
|
1560
|
-
description: check.name,
|
|
1561
|
-
command: check.command,
|
|
1562
|
-
expectedExitCode: 0,
|
|
1563
|
-
severity: check.severity,
|
|
1564
|
-
failureMessage: `Validation failed: ${check.name}`,
|
|
1565
|
-
remediationSteps: [],
|
|
1566
|
-
})),
|
|
1567
|
-
metadata: {
|
|
1568
|
-
source: 'AI-Generated (Fallback)',
|
|
1569
|
-
lastUpdated: new Date().toISOString(),
|
|
1570
|
-
tags: [detection.primaryPlatform || 'unknown', 'ai-generated', 'unvalidated'],
|
|
1571
|
-
},
|
|
1572
|
-
};
|
|
1573
|
-
}
|
|
1574
|
-
computePatternHash(pattern) {
|
|
1575
|
-
try {
|
|
1576
|
-
// Create a deterministic string representation of the pattern
|
|
1577
|
-
const patternString = JSON.stringify({
|
|
1578
|
-
id: pattern.id,
|
|
1579
|
-
name: pattern.name,
|
|
1580
|
-
version: pattern.version,
|
|
1581
|
-
platformType: pattern.platformType,
|
|
1582
|
-
deploymentPhases: pattern.deploymentPhases,
|
|
1583
|
-
validationChecks: pattern.validationChecks,
|
|
1584
|
-
authoritativeSources: pattern.authoritativeSources,
|
|
1585
|
-
baseCodeRepository: pattern.baseCodeRepository,
|
|
1586
|
-
}, null, 2);
|
|
1587
|
-
// Compute SHA-256 hash
|
|
1588
|
-
return createHash('sha256').update(patternString).digest('hex');
|
|
1589
|
-
}
|
|
1590
|
-
catch (error) {
|
|
1591
|
-
this.logger.warn('Failed to compute pattern hash', 'BootstrapValidationLoop', error);
|
|
1592
|
-
return 'hash-computation-failed';
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
19
|
/**
|
|
1597
20
|
* Generate guided execution instructions for LLM
|
|
1598
21
|
* This function returns step-by-step commands for the LLM to execute
|
|
@@ -1952,12 +375,9 @@ ${previousExecutionSuccess ? 'โ
Deployment validation complete!\n\nReview the
|
|
|
1952
375
|
*/
|
|
1953
376
|
export async function bootstrapValidationLoop(args) {
|
|
1954
377
|
const { projectPath = process.cwd(), adrDirectory = 'docs/adrs', targetEnvironment = 'development', maxIterations = 5, autoFix = true, updateAdrsWithLearnings = true, currentIteration = 0, previousExecutionOutput = '', previousExecutionSuccess = false, deploymentCleanupRequested = false, appSelector, } = args;
|
|
1955
|
-
const loop = new BootstrapValidationLoop(projectPath, adrDirectory, maxIterations);
|
|
1956
|
-
await loop.initialize();
|
|
1957
378
|
// GUIDED MODE: Return instructions for LLM to execute commands
|
|
1958
379
|
// This mode tells the LLM what to run and processes the output iteratively
|
|
1959
380
|
return await generateGuidedExecutionInstructions({
|
|
1960
|
-
loop,
|
|
1961
381
|
projectPath,
|
|
1962
382
|
adrDirectory,
|
|
1963
383
|
targetEnvironment,
|