s9n-devops-agent 2.0.10 → 2.0.11-dev.1

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.
@@ -27,6 +27,7 @@ import path from 'path';
27
27
  import { execSync } from 'child_process';
28
28
  import { fileURLToPath } from 'url';
29
29
  import { dirname } from 'path';
30
+ import { credentialsManager } from './credentials-manager.js';
30
31
  import {
31
32
  colors,
32
33
  status,
@@ -113,9 +114,116 @@ function backupFile(filePath) {
113
114
  return null;
114
115
  }
115
116
 
116
- // ============================================================================
117
+ function setupFolderStructure(projectRoot) {
118
+ log.header();
119
+ log.title('📂 Checking Folder Structure');
120
+
121
+ // Folders defined in folders.md
122
+ const standardFolders = [
123
+ 'src',
124
+ 'test',
125
+ 'docs',
126
+ 'scripts',
127
+ 'deploy_test',
128
+ 'product_requirement_docs',
129
+ 'infrastructure'
130
+ ];
131
+
132
+ const missingFolders = standardFolders.filter(folder => !fs.existsSync(path.join(projectRoot, folder)));
133
+
134
+ if (missingFolders.length === 0) {
135
+ log.info('Standard folder structure already exists.');
136
+ return;
137
+ }
138
+
139
+ log.info('Found missing standard folders:');
140
+ missingFolders.forEach(folder => console.log(` - ${folder}/`));
141
+ console.log();
142
+
143
+ explain(`
144
+ ${colors.bright}Recommended Structure:${colors.reset}
145
+ Standard folders help organize your code, tests, and documentation.
146
+ This structure is compatible with the DevOps Agent's automation tools.
147
+ `);
148
+
149
+ // We can't use await here because this function is synchronous in the flow,
150
+ // but the main flow is async. We should probably make this async or use prompt from ui-utils.
151
+ // However, looking at the code structure, helper functions are sync or async.
152
+ // Let's return the missing folders and handle the prompt in the main function or make this async.
153
+ return missingFolders;
154
+ }
155
+
156
+ function checkContractsExist(projectRoot) {
157
+ const contractsDir = path.join(projectRoot, 'House_Rules_Contracts');
158
+ if (!fs.existsSync(contractsDir)) return false;
159
+
160
+ const requiredContracts = [
161
+ 'FEATURES_CONTRACT.md',
162
+ 'API_CONTRACT.md',
163
+ 'DATABASE_SCHEMA_CONTRACT.md',
164
+ 'SQL_CONTRACT.json',
165
+ 'THIRD_PARTY_INTEGRATIONS.md',
166
+ 'INFRA_CONTRACT.md'
167
+ ];
168
+
169
+ return requiredContracts.every(file => fs.existsSync(path.join(contractsDir, file)));
170
+ }
171
+
172
+ async function generateContracts(projectRoot) {
173
+ log.header();
174
+ log.title('📜 Generating Contracts');
175
+
176
+ const scriptsDir = path.join(projectRoot, 'scripts', 'contract-automation');
177
+ const generateScript = path.join(scriptsDir, 'generate-contracts.js');
178
+ const analyzeScript = path.join(scriptsDir, 'analyze-with-llm.js');
179
+
180
+ if (!fs.existsSync(generateScript)) {
181
+ log.error('Contract generation scripts not found!');
182
+ return;
183
+ }
184
+
185
+ try {
186
+ // 1. Run local scan
187
+ info('Running local codebase scan...');
188
+ execSync(`node "${generateScript}"`, { cwd: projectRoot, stdio: 'inherit' });
189
+ success('Scan complete.');
190
+
191
+ // 2. Run LLM analysis if key exists
192
+ if (credentialsManager.hasGroqApiKey()) {
193
+ info('Enhancing contracts with AI analysis...');
194
+ try {
195
+ // Inject env for the child process
196
+ const env = { ...process.env };
197
+ credentialsManager.injectEnv(); // Ensure current process has it
198
+ // Note: execSync inherits env by default, but explicit is safer if we modified it
199
+ if (!env.GROQ_API_KEY && credentialsManager.getGroqApiKey()) {
200
+ env.GROQ_API_KEY = credentialsManager.getGroqApiKey();
201
+ env.OPENAI_API_KEY = credentialsManager.getGroqApiKey();
202
+ }
203
+
204
+ execSync(`node "${analyzeScript}" --scan-results=House_Rules_Contracts/contract-scan-results.json`, {
205
+ cwd: projectRoot,
206
+ stdio: 'inherit',
207
+ env
208
+ });
209
+ success('AI analysis complete.');
210
+ } catch (err) {
211
+ warn(`AI analysis failed: ${err.message}`);
212
+ warn('Contracts generated but without AI enhancements.');
213
+ }
214
+ } else {
215
+ warn('Skipping AI analysis (no Groq API Key).');
216
+ warn('Run "s9n-devops-agent creds set-groq-key <key>" later to enable this.');
217
+ }
218
+
219
+ } catch (error) {
220
+ log.error(`Contract generation failed: ${error.message}`);
221
+ }
222
+ }
223
+
224
+ // ===========================================================================
117
225
  // SETUP FUNCTIONS
118
- // ============================================================================
226
+ // ===========================================================================
119
227
 
120
228
  async function setupNpmPackages(projectRoot) {
121
229
  log.header();
@@ -865,6 +973,47 @@ function printInstructions(initials) {
865
973
  log.header();
866
974
  }
867
975
 
976
+ async function setupEnvFile(projectRoot) {
977
+ log.header();
978
+ log.title('🔑 Setting up Environment Variables');
979
+
980
+ const envPath = path.join(projectRoot, '.env');
981
+ let envContent = '';
982
+
983
+ if (fs.existsSync(envPath)) {
984
+ envContent = fs.readFileSync(envPath, 'utf8');
985
+ log.info('.env file already exists');
986
+ } else {
987
+ log.info('Creating .env file');
988
+ }
989
+
990
+ // Check for OPENAI_API_KEY
991
+ if (!envContent.includes('OPENAI_API_KEY=')) {
992
+ console.log();
993
+ explain(`
994
+ ${colors.bright}Groq API Key Setup${colors.reset}
995
+ The contract automation features use Groq LLM (via OpenAI compatibility).
996
+ You can enter your API key now, or set it later in the .env file.
997
+ `);
998
+
999
+ const apiKey = await prompt('Enter Groq API Key (leave empty to skip)');
1000
+
1001
+ if (apiKey) {
1002
+ const newLine = envContent.endsWith('\n') || envContent === '' ? '' : '\n';
1003
+ envContent += `${newLine}# Groq API Key for Contract Automation\nOPENAI_API_KEY=${apiKey}\n`;
1004
+ fs.writeFileSync(envPath, envContent);
1005
+ log.success('Added OPENAI_API_KEY to .env');
1006
+ } else {
1007
+ log.warn('Skipped Groq API Key. Contract automation features may not work.');
1008
+ if (!fs.existsSync(envPath)) {
1009
+ fs.writeFileSync(envPath, '# Environment Variables\n');
1010
+ }
1011
+ }
1012
+ } else {
1013
+ log.info('OPENAI_API_KEY is already configured in .env');
1014
+ }
1015
+ }
1016
+
868
1017
  // ============================================================================
869
1018
  // CLEANUP FUNCTIONS
870
1019
  // ============================================================================
@@ -976,6 +1125,37 @@ ${colors.bright}How:${colors.reset} Creates branches like dev_abc_2025-10-31
976
1125
  tip(`Your branches will be named: ${colors.cyan}dev_${initials}_YYYY-MM-DD${colors.reset}`);
977
1126
  console.log();
978
1127
 
1128
+ // Groq API Key Setup
1129
+ sectionTitle('Groq API Key (Contract Automation)');
1130
+ explain(`
1131
+ ${colors.bright}What:${colors.reset} API Key for Groq (llama-3.1-70b-versatile)
1132
+ ${colors.bright}Why:${colors.reset} Required for AI-Optimized Contract Automation System
1133
+ ${colors.bright}Security:${colors.reset} Stored locally in ${colors.yellow}local_deploy/credentials.json${colors.reset} (gitignored)
1134
+ `);
1135
+
1136
+ const hasKey = credentialsManager.hasGroqApiKey();
1137
+ let groqKey = null;
1138
+
1139
+ if (hasKey) {
1140
+ info('Groq API Key is already configured.');
1141
+ const update = await confirm('Do you want to update it?', false);
1142
+ if (update) {
1143
+ groqKey = await prompt('Enter your Groq API Key');
1144
+ }
1145
+ } else {
1146
+ groqKey = await prompt('Enter your Groq API Key (leave empty to skip)');
1147
+ }
1148
+
1149
+ if (groqKey && groqKey.trim()) {
1150
+ credentialsManager.setGroqApiKey(groqKey.trim());
1151
+ success('Groq API Key saved securely.');
1152
+ } else if (!hasKey) {
1153
+ warn('Skipping Groq API Key setup.');
1154
+ warn('NOTE: Contract Automation features (analyze-with-llm.js) will NOT work without this key.');
1155
+ }
1156
+
1157
+ console.log();
1158
+
979
1159
  // Confirm before proceeding
980
1160
  drawSection('Configuration Summary', [
981
1161
  `${status.folder} Branch prefix: ${colors.cyan}dev_${initials}_${colors.reset}`,
@@ -983,7 +1163,9 @@ ${colors.bright}How:${colors.reset} Creates branches like dev_abc_2025-10-31
983
1163
  `${status.checkmark} VS Code settings and tasks`,
984
1164
  `${status.checkmark} NPM packages and scripts`,
985
1165
  `${status.checkmark} Commit message files`,
986
- `${status.checkmark} House rules for AI agents`
1166
+ `${status.checkmark} House rules for AI agents`,
1167
+ `${status.folder} Standard folder structure check`,
1168
+ `${status.checkmark} Contract files check`
987
1169
  ]);
988
1170
  console.log();
989
1171
 
@@ -999,6 +1181,37 @@ ${colors.bright}How:${colors.reset} Creates branches like dev_abc_2025-10-31
999
1181
  console.log();
1000
1182
 
1001
1183
  try {
1184
+ // Check and setup folder structure first
1185
+ const missingFolders = setupFolderStructure(projectRoot);
1186
+ if (missingFolders && missingFolders.length > 0) {
1187
+ const createFolders = await confirm('Create missing standard folders?', true);
1188
+ if (createFolders) {
1189
+ missingFolders.forEach(folder => {
1190
+ ensureDirectoryExists(path.join(projectRoot, folder));
1191
+ log.success(`Created ${folder}/`);
1192
+ });
1193
+ } else {
1194
+ log.warn('Skipping folder creation.');
1195
+ }
1196
+ }
1197
+
1198
+ // Check for contracts
1199
+ if (!checkContractsExist(projectRoot)) {
1200
+ log.header();
1201
+ log.title('📜 Contract Files Missing');
1202
+ explain(`
1203
+ ${colors.bright}Contract System:${colors.reset}
1204
+ This project uses a Contract System to coordinate multiple AI agents.
1205
+ It seems like this is a fresh setup or contracts are missing.
1206
+ We can scan your codebase and generate them now.
1207
+ `);
1208
+
1209
+ const shouldGenerate = await confirm('Generate contract files now?', true);
1210
+ if (shouldGenerate) {
1211
+ await generateContracts(projectRoot);
1212
+ }
1213
+ }
1214
+
1002
1215
  // Run setup steps
1003
1216
  const packageJson = await setupNpmPackages(projectRoot);
1004
1217
  setupVSCodeSettings(projectRoot, initials);
@@ -1006,6 +1219,9 @@ ${colors.bright}How:${colors.reset} Creates branches like dev_abc_2025-10-31
1006
1219
  setupCommitFiles(projectRoot, initials);
1007
1220
  createRunScripts(projectRoot, initials, packageJson);
1008
1221
 
1222
+ // Setup .env with API keys
1223
+ await setupEnvFile(projectRoot);
1224
+
1009
1225
  // Clean up DevOpsAgent files to avoid duplicates
1010
1226
  cleanupDevOpsAgentFiles(projectRoot);
1011
1227