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.
- package/bin/cs-devops-agent +28 -0
- package/docs/RELEASE_NOTES.md +7 -0
- package/package.json +8 -3
- package/scripts/contract-automation/README.md +515 -0
- package/scripts/contract-automation/analyze-with-llm.js +497 -0
- package/scripts/contract-automation/check-compliance.js +567 -0
- package/scripts/contract-automation/generate-contracts.js +592 -0
- package/scripts/contract-automation/validate-commit.js +422 -0
- package/src/credentials-manager.js +108 -0
- package/src/session-coordinator.js +7 -3
- package/src/setup-cs-devops-agent.js +219 -3
|
@@ -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
|
|