s9n-devops-agent 2.0.9 → 2.0.11-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cs-devops-agent +28 -0
- package/package.json +4 -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 +20 -18
- package/src/setup-cs-devops-agent.js +175 -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();
|
|
@@ -976,6 +1084,37 @@ ${colors.bright}How:${colors.reset} Creates branches like dev_abc_2025-10-31
|
|
|
976
1084
|
tip(`Your branches will be named: ${colors.cyan}dev_${initials}_YYYY-MM-DD${colors.reset}`);
|
|
977
1085
|
console.log();
|
|
978
1086
|
|
|
1087
|
+
// Groq API Key Setup
|
|
1088
|
+
sectionTitle('Groq API Key (Contract Automation)');
|
|
1089
|
+
explain(`
|
|
1090
|
+
${colors.bright}What:${colors.reset} API Key for Groq (llama-3.1-70b-versatile)
|
|
1091
|
+
${colors.bright}Why:${colors.reset} Required for AI-Optimized Contract Automation System
|
|
1092
|
+
${colors.bright}Security:${colors.reset} Stored locally in ${colors.yellow}local_deploy/credentials.json${colors.reset} (gitignored)
|
|
1093
|
+
`);
|
|
1094
|
+
|
|
1095
|
+
const hasKey = credentialsManager.hasGroqApiKey();
|
|
1096
|
+
let groqKey = null;
|
|
1097
|
+
|
|
1098
|
+
if (hasKey) {
|
|
1099
|
+
info('Groq API Key is already configured.');
|
|
1100
|
+
const update = await confirm('Do you want to update it?', false);
|
|
1101
|
+
if (update) {
|
|
1102
|
+
groqKey = await prompt('Enter your Groq API Key');
|
|
1103
|
+
}
|
|
1104
|
+
} else {
|
|
1105
|
+
groqKey = await prompt('Enter your Groq API Key (leave empty to skip)');
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
if (groqKey && groqKey.trim()) {
|
|
1109
|
+
credentialsManager.setGroqApiKey(groqKey.trim());
|
|
1110
|
+
success('Groq API Key saved securely.');
|
|
1111
|
+
} else if (!hasKey) {
|
|
1112
|
+
warn('Skipping Groq API Key setup.');
|
|
1113
|
+
warn('NOTE: Contract Automation features (analyze-with-llm.js) will NOT work without this key.');
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
console.log();
|
|
1117
|
+
|
|
979
1118
|
// Confirm before proceeding
|
|
980
1119
|
drawSection('Configuration Summary', [
|
|
981
1120
|
`${status.folder} Branch prefix: ${colors.cyan}dev_${initials}_${colors.reset}`,
|
|
@@ -983,7 +1122,9 @@ ${colors.bright}How:${colors.reset} Creates branches like dev_abc_2025-10-31
|
|
|
983
1122
|
`${status.checkmark} VS Code settings and tasks`,
|
|
984
1123
|
`${status.checkmark} NPM packages and scripts`,
|
|
985
1124
|
`${status.checkmark} Commit message files`,
|
|
986
|
-
`${status.checkmark} House rules for AI agents
|
|
1125
|
+
`${status.checkmark} House rules for AI agents`,
|
|
1126
|
+
`${status.folder} Standard folder structure check`,
|
|
1127
|
+
`${status.checkmark} Contract files check`
|
|
987
1128
|
]);
|
|
988
1129
|
console.log();
|
|
989
1130
|
|
|
@@ -999,6 +1140,37 @@ ${colors.bright}How:${colors.reset} Creates branches like dev_abc_2025-10-31
|
|
|
999
1140
|
console.log();
|
|
1000
1141
|
|
|
1001
1142
|
try {
|
|
1143
|
+
// Check and setup folder structure first
|
|
1144
|
+
const missingFolders = setupFolderStructure(projectRoot);
|
|
1145
|
+
if (missingFolders && missingFolders.length > 0) {
|
|
1146
|
+
const createFolders = await confirm('Create missing standard folders?', true);
|
|
1147
|
+
if (createFolders) {
|
|
1148
|
+
missingFolders.forEach(folder => {
|
|
1149
|
+
ensureDirectoryExists(path.join(projectRoot, folder));
|
|
1150
|
+
log.success(`Created ${folder}/`);
|
|
1151
|
+
});
|
|
1152
|
+
} else {
|
|
1153
|
+
log.warn('Skipping folder creation.');
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// Check for contracts
|
|
1158
|
+
if (!checkContractsExist(projectRoot)) {
|
|
1159
|
+
log.header();
|
|
1160
|
+
log.title('📜 Contract Files Missing');
|
|
1161
|
+
explain(`
|
|
1162
|
+
${colors.bright}Contract System:${colors.reset}
|
|
1163
|
+
This project uses a Contract System to coordinate multiple AI agents.
|
|
1164
|
+
It seems like this is a fresh setup or contracts are missing.
|
|
1165
|
+
We can scan your codebase and generate them now.
|
|
1166
|
+
`);
|
|
1167
|
+
|
|
1168
|
+
const shouldGenerate = await confirm('Generate contract files now?', true);
|
|
1169
|
+
if (shouldGenerate) {
|
|
1170
|
+
await generateContracts(projectRoot);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1002
1174
|
// Run setup steps
|
|
1003
1175
|
const packageJson = await setupNpmPackages(projectRoot);
|
|
1004
1176
|
setupVSCodeSettings(projectRoot, initials);
|