@juspay/yama 1.5.1 → 2.0.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/.mcp-config.example.json +26 -0
- package/CHANGELOG.md +40 -0
- package/README.md +311 -685
- package/dist/cli/v2.cli.d.ts +13 -0
- package/dist/cli/v2.cli.js +290 -0
- package/dist/index.d.ts +12 -13
- package/dist/index.js +18 -19
- package/dist/v2/config/ConfigLoader.d.ts +50 -0
- package/dist/v2/config/ConfigLoader.js +205 -0
- package/dist/v2/config/DefaultConfig.d.ts +9 -0
- package/dist/v2/config/DefaultConfig.js +191 -0
- package/dist/v2/core/MCPServerManager.d.ts +22 -0
- package/dist/v2/core/MCPServerManager.js +92 -0
- package/dist/v2/core/SessionManager.d.ts +72 -0
- package/dist/v2/core/SessionManager.js +200 -0
- package/dist/v2/core/YamaV2Orchestrator.d.ts +112 -0
- package/dist/v2/core/YamaV2Orchestrator.js +549 -0
- package/dist/v2/prompts/EnhancementSystemPrompt.d.ts +8 -0
- package/dist/v2/prompts/EnhancementSystemPrompt.js +216 -0
- package/dist/v2/prompts/PromptBuilder.d.ts +38 -0
- package/dist/v2/prompts/PromptBuilder.js +228 -0
- package/dist/v2/prompts/ReviewSystemPrompt.d.ts +8 -0
- package/dist/v2/prompts/ReviewSystemPrompt.js +270 -0
- package/dist/v2/types/config.types.d.ts +120 -0
- package/dist/v2/types/config.types.js +5 -0
- package/dist/v2/types/mcp.types.d.ts +191 -0
- package/dist/v2/types/mcp.types.js +6 -0
- package/dist/v2/types/v2.types.d.ts +182 -0
- package/dist/v2/types/v2.types.js +42 -0
- package/dist/v2/utils/ObservabilityConfig.d.ts +22 -0
- package/dist/v2/utils/ObservabilityConfig.js +48 -0
- package/package.json +11 -9
- package/yama.config.example.yaml +214 -193
- package/dist/cli/index.d.ts +0 -12
- package/dist/cli/index.js +0 -538
- package/dist/core/ContextGatherer.d.ts +0 -110
- package/dist/core/ContextGatherer.js +0 -470
- package/dist/core/Guardian.d.ts +0 -81
- package/dist/core/Guardian.js +0 -474
- package/dist/core/providers/BitbucketProvider.d.ts +0 -105
- package/dist/core/providers/BitbucketProvider.js +0 -489
- package/dist/features/CodeReviewer.d.ts +0 -173
- package/dist/features/CodeReviewer.js +0 -1707
- package/dist/features/DescriptionEnhancer.d.ts +0 -64
- package/dist/features/DescriptionEnhancer.js +0 -445
- package/dist/features/MultiInstanceProcessor.d.ts +0 -74
- package/dist/features/MultiInstanceProcessor.js +0 -360
- package/dist/types/index.d.ts +0 -624
- package/dist/types/index.js +0 -104
- package/dist/utils/Cache.d.ts +0 -103
- package/dist/utils/Cache.js +0 -444
- package/dist/utils/ConfigManager.d.ts +0 -88
- package/dist/utils/ConfigManager.js +0 -603
- package/dist/utils/ContentSimilarityService.d.ts +0 -74
- package/dist/utils/ContentSimilarityService.js +0 -215
- package/dist/utils/ExactDuplicateRemover.d.ts +0 -77
- package/dist/utils/ExactDuplicateRemover.js +0 -361
- package/dist/utils/Logger.d.ts +0 -31
- package/dist/utils/Logger.js +0 -214
- package/dist/utils/MemoryBankManager.d.ts +0 -73
- package/dist/utils/MemoryBankManager.js +0 -310
- package/dist/utils/ParallelProcessing.d.ts +0 -140
- package/dist/utils/ParallelProcessing.js +0 -333
- package/dist/utils/ProviderLimits.d.ts +0 -58
- package/dist/utils/ProviderLimits.js +0 -143
- package/dist/utils/RetryManager.d.ts +0 -78
- package/dist/utils/RetryManager.js +0 -205
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Yama V2 CLI - AI-Native Code Review Interface
|
|
4
|
+
* Command-line interface for autonomous AI-powered code review
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
declare const program: Command;
|
|
8
|
+
/**
|
|
9
|
+
* Setup V2 CLI
|
|
10
|
+
*/
|
|
11
|
+
export declare function setupV2CLI(): Command;
|
|
12
|
+
export default program;
|
|
13
|
+
//# sourceMappingURL=v2.cli.d.ts.map
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Yama V2 CLI - AI-Native Code Review Interface
|
|
4
|
+
* Command-line interface for autonomous AI-powered code review
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import dotenv from "dotenv";
|
|
8
|
+
import { createYamaV2 } from "../v2/core/YamaV2Orchestrator.js";
|
|
9
|
+
// Load environment variables
|
|
10
|
+
dotenv.config();
|
|
11
|
+
const program = new Command();
|
|
12
|
+
/**
|
|
13
|
+
* Setup V2 CLI
|
|
14
|
+
*/
|
|
15
|
+
export function setupV2CLI() {
|
|
16
|
+
program
|
|
17
|
+
.name("yama")
|
|
18
|
+
.description("Yama - AI-Native Autonomous Code Review")
|
|
19
|
+
.version("2.0.0");
|
|
20
|
+
// Global options
|
|
21
|
+
program
|
|
22
|
+
.option("-v, --verbose", "Enable verbose output")
|
|
23
|
+
.option("-c, --config <path>", "Path to configuration file")
|
|
24
|
+
.option("--dry-run", "Dry run mode - no actual changes")
|
|
25
|
+
.option("--no-banner", "Hide Yama banner");
|
|
26
|
+
// Review command
|
|
27
|
+
setupReviewCommand();
|
|
28
|
+
// Enhance description command
|
|
29
|
+
setupEnhanceCommand();
|
|
30
|
+
// Init command
|
|
31
|
+
setupInitCommand();
|
|
32
|
+
// Migrate config command
|
|
33
|
+
setupMigrateCommand();
|
|
34
|
+
return program;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Main review command
|
|
38
|
+
* Reviews code and enhances description in one session
|
|
39
|
+
*/
|
|
40
|
+
function setupReviewCommand() {
|
|
41
|
+
program
|
|
42
|
+
.command("review")
|
|
43
|
+
.description("Review code and enhance PR description (uses same AI session)")
|
|
44
|
+
.requiredOption("-w, --workspace <workspace>", "Bitbucket workspace")
|
|
45
|
+
.requiredOption("-r, --repository <repository>", "Repository name")
|
|
46
|
+
.option("-p, --pr <id>", "Pull request ID")
|
|
47
|
+
.option("-b, --branch <branch>", "Branch name (finds PR automatically)")
|
|
48
|
+
.option("--review-only", "Skip description enhancement, only review code")
|
|
49
|
+
.action(async (options) => {
|
|
50
|
+
try {
|
|
51
|
+
const globalOpts = program.opts();
|
|
52
|
+
// Validate required options
|
|
53
|
+
if (!options.pr && !options.branch) {
|
|
54
|
+
console.error("❌ Error: Either --pr or --branch must be specified");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
// Parse PR ID with validation
|
|
58
|
+
let pullRequestId;
|
|
59
|
+
if (options.pr) {
|
|
60
|
+
pullRequestId = parseInt(options.pr, 10);
|
|
61
|
+
if (isNaN(pullRequestId)) {
|
|
62
|
+
console.error(`❌ Error: Invalid PR ID "${options.pr}" (must be a number)`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const request = {
|
|
67
|
+
workspace: options.workspace,
|
|
68
|
+
repository: options.repository,
|
|
69
|
+
pullRequestId,
|
|
70
|
+
branch: options.branch,
|
|
71
|
+
dryRun: globalOpts.dryRun || false,
|
|
72
|
+
verbose: globalOpts.verbose || false,
|
|
73
|
+
configPath: globalOpts.config,
|
|
74
|
+
};
|
|
75
|
+
// Create orchestrator
|
|
76
|
+
const yama = createYamaV2();
|
|
77
|
+
// Initialize
|
|
78
|
+
await yama.initialize(request.configPath);
|
|
79
|
+
// Start review (with or without description enhancement)
|
|
80
|
+
console.log("🚀 Starting autonomous AI review...\n");
|
|
81
|
+
const result = options.reviewOnly
|
|
82
|
+
? await yama.startReview(request)
|
|
83
|
+
: await yama.startReviewAndEnhance(request);
|
|
84
|
+
// Show results
|
|
85
|
+
console.log("\n📊 Review Results:");
|
|
86
|
+
console.log(` Decision: ${result.decision}`);
|
|
87
|
+
console.log(` Files Reviewed: ${result.statistics.filesReviewed}`);
|
|
88
|
+
console.log(` Total Comments: ${result.totalComments || result.statistics.totalComments || 0}`);
|
|
89
|
+
if (result.descriptionEnhanced !== undefined) {
|
|
90
|
+
console.log(` Description Enhanced: ${result.descriptionEnhanced ? "✅ Yes" : "⏭️ Skipped"}`);
|
|
91
|
+
}
|
|
92
|
+
console.log(` Duration: ${Math.round(result.duration / 1000)}s`);
|
|
93
|
+
console.log(` Token Usage: ${result.tokenUsage.total.toLocaleString()} tokens`);
|
|
94
|
+
if (globalOpts.verbose) {
|
|
95
|
+
console.log("\n📄 Full Results:");
|
|
96
|
+
console.log(JSON.stringify(result, null, 2));
|
|
97
|
+
}
|
|
98
|
+
// Exit with appropriate code
|
|
99
|
+
if (result.decision === "BLOCKED") {
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.error("\n❌ Review failed:", error.message);
|
|
108
|
+
if (error.stack && program.opts().verbose) {
|
|
109
|
+
console.error("\nStack trace:");
|
|
110
|
+
console.error(error.stack);
|
|
111
|
+
}
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Enhance description command
|
|
118
|
+
*/
|
|
119
|
+
function setupEnhanceCommand() {
|
|
120
|
+
program
|
|
121
|
+
.command("enhance")
|
|
122
|
+
.description("Enhance PR description using AI (without full review)")
|
|
123
|
+
.requiredOption("-w, --workspace <workspace>", "Bitbucket workspace")
|
|
124
|
+
.requiredOption("-r, --repository <repository>", "Repository name")
|
|
125
|
+
.option("-p, --pr <id>", "Pull request ID")
|
|
126
|
+
.option("-b, --branch <branch>", "Branch name")
|
|
127
|
+
.action(async (options) => {
|
|
128
|
+
try {
|
|
129
|
+
const globalOpts = program.opts();
|
|
130
|
+
if (!options.pr && !options.branch) {
|
|
131
|
+
console.error("❌ Error: Either --pr or --branch must be specified");
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
// Parse PR ID with validation
|
|
135
|
+
let pullRequestId;
|
|
136
|
+
if (options.pr) {
|
|
137
|
+
pullRequestId = parseInt(options.pr, 10);
|
|
138
|
+
if (isNaN(pullRequestId)) {
|
|
139
|
+
console.error(`❌ Error: Invalid PR ID "${options.pr}" (must be a number)`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const request = {
|
|
144
|
+
workspace: options.workspace,
|
|
145
|
+
repository: options.repository,
|
|
146
|
+
pullRequestId,
|
|
147
|
+
branch: options.branch,
|
|
148
|
+
dryRun: globalOpts.dryRun || false,
|
|
149
|
+
verbose: globalOpts.verbose || false,
|
|
150
|
+
configPath: globalOpts.config,
|
|
151
|
+
};
|
|
152
|
+
const yama = createYamaV2();
|
|
153
|
+
await yama.initialize(request.configPath);
|
|
154
|
+
const result = await yama.enhanceDescription(request);
|
|
155
|
+
console.log("\n✅ Description enhanced successfully");
|
|
156
|
+
console.log(JSON.stringify(result, null, 2));
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.error("\n❌ Enhancement failed:", error.message);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Initialize configuration command
|
|
167
|
+
*/
|
|
168
|
+
function setupInitCommand() {
|
|
169
|
+
program
|
|
170
|
+
.command("init")
|
|
171
|
+
.description("Initialize Yama configuration")
|
|
172
|
+
.option("--interactive", "Interactive configuration setup")
|
|
173
|
+
.action(async (options) => {
|
|
174
|
+
try {
|
|
175
|
+
console.log("\n⚔️ Yama Configuration Setup\n");
|
|
176
|
+
if (options.interactive) {
|
|
177
|
+
console.log("Interactive setup not yet implemented.");
|
|
178
|
+
console.log("Please copy yama.config.example.yaml to yama.config.yaml");
|
|
179
|
+
console.log("and edit it manually.\n");
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
console.log("Creating default configuration file...\n");
|
|
183
|
+
const fs = await import("fs/promises");
|
|
184
|
+
const path = await import("path");
|
|
185
|
+
// Check if config already exists
|
|
186
|
+
if (await fs
|
|
187
|
+
.access("yama.config.yaml")
|
|
188
|
+
.then(() => true)
|
|
189
|
+
.catch(() => false)) {
|
|
190
|
+
console.log("❌ yama.config.yaml already exists");
|
|
191
|
+
console.log(" Remove it first or use a different location\n");
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
// Copy example config
|
|
195
|
+
const examplePath = path.join(process.cwd(), "yama.config.example.yaml");
|
|
196
|
+
const targetPath = path.join(process.cwd(), "yama.config.yaml");
|
|
197
|
+
if (await fs
|
|
198
|
+
.access(examplePath)
|
|
199
|
+
.then(() => true)
|
|
200
|
+
.catch(() => false)) {
|
|
201
|
+
await fs.copyFile(examplePath, targetPath);
|
|
202
|
+
console.log("✅ Created yama.config.yaml from example");
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
console.log("⚠️ Example config not found, creating minimal config...");
|
|
206
|
+
await fs.writeFile(targetPath, `version: 2
|
|
207
|
+
configType: "yama-v2"
|
|
208
|
+
|
|
209
|
+
ai:
|
|
210
|
+
provider: "auto"
|
|
211
|
+
model: "gemini-2.5-pro"
|
|
212
|
+
|
|
213
|
+
mcpServers:
|
|
214
|
+
jira:
|
|
215
|
+
enabled: false
|
|
216
|
+
|
|
217
|
+
review:
|
|
218
|
+
enabled: true
|
|
219
|
+
|
|
220
|
+
descriptionEnhancement:
|
|
221
|
+
enabled: true
|
|
222
|
+
`);
|
|
223
|
+
console.log("✅ Created minimal yama.config.yaml");
|
|
224
|
+
}
|
|
225
|
+
console.log("\n📝 Next steps:");
|
|
226
|
+
console.log(" 1. Edit yama.config.yaml with your settings");
|
|
227
|
+
console.log(" 2. Set environment variables (BITBUCKET_*, JIRA_*)");
|
|
228
|
+
console.log(" 3. Run: yama review --help\n");
|
|
229
|
+
}
|
|
230
|
+
process.exit(0);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
console.error("\n❌ Initialization failed:", error.message);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Migrate V1 config to V2 format
|
|
240
|
+
*/
|
|
241
|
+
function setupMigrateCommand() {
|
|
242
|
+
program
|
|
243
|
+
.command("migrate-config")
|
|
244
|
+
.description("Migrate V1 configuration to V2 format")
|
|
245
|
+
.option("-i, --input <file>", "Input V1 config file", "yama.v1.config.yaml")
|
|
246
|
+
.option("-o, --output <file>", "Output V2 config file", "yama.config.yaml")
|
|
247
|
+
.option("--force", "Overwrite existing output file")
|
|
248
|
+
.action(async (options) => {
|
|
249
|
+
try {
|
|
250
|
+
const globalOpts = program.opts();
|
|
251
|
+
console.log("\n🔄 Yama V1 → V2 Configuration Migration\n");
|
|
252
|
+
// Use child_process to run the migration script
|
|
253
|
+
const { spawn } = await import("child_process");
|
|
254
|
+
const path = await import("path");
|
|
255
|
+
const { fileURLToPath } = await import("url");
|
|
256
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
257
|
+
const __dirname = path.dirname(__filename);
|
|
258
|
+
const scriptPath = path.resolve(__dirname, "../../scripts/migrate-config.cjs");
|
|
259
|
+
const args = ["--input", options.input, "--output", options.output];
|
|
260
|
+
if (options.force) {
|
|
261
|
+
args.push("--force");
|
|
262
|
+
}
|
|
263
|
+
if (globalOpts.dryRun) {
|
|
264
|
+
args.push("--dry-run");
|
|
265
|
+
}
|
|
266
|
+
const child = spawn("node", [scriptPath, ...args], {
|
|
267
|
+
stdio: "inherit",
|
|
268
|
+
cwd: process.cwd(),
|
|
269
|
+
});
|
|
270
|
+
child.on("close", (code) => {
|
|
271
|
+
process.exit(code || 0);
|
|
272
|
+
});
|
|
273
|
+
child.on("error", (err) => {
|
|
274
|
+
console.error("❌ Failed to run migration script:", err.message);
|
|
275
|
+
process.exit(1);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
console.error("\n❌ Migration failed:", error.message);
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
// Only run if this is the main module
|
|
285
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
286
|
+
const cli = setupV2CLI();
|
|
287
|
+
cli.parse(process.argv);
|
|
288
|
+
}
|
|
289
|
+
export default program;
|
|
290
|
+
//# sourceMappingURL=v2.cli.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Yama -
|
|
3
|
-
*
|
|
2
|
+
* Yama - AI-Native Code Review
|
|
3
|
+
* Main export file
|
|
4
4
|
*/
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
13
|
-
export
|
|
14
|
-
export
|
|
15
|
-
export { main as cli } from "./cli/index.js";
|
|
5
|
+
export { YamaV2Orchestrator, createYamaV2, } from "./v2/core/YamaV2Orchestrator.js";
|
|
6
|
+
export { ConfigLoader } from "./v2/config/ConfigLoader.js";
|
|
7
|
+
export { MCPServerManager } from "./v2/core/MCPServerManager.js";
|
|
8
|
+
export { SessionManager } from "./v2/core/SessionManager.js";
|
|
9
|
+
export { PromptBuilder } from "./v2/prompts/PromptBuilder.js";
|
|
10
|
+
export type { ReviewRequest, ReviewResult, ReviewUpdate, ReviewSession, ReviewStatistics, IssuesBySeverity, TokenUsage, } from "./v2/types/v2.types.js";
|
|
11
|
+
export type { YamaV2Config, AIConfig, MCPServersConfig, ReviewConfig, DescriptionEnhancementConfig, } from "./v2/types/config.types.js";
|
|
12
|
+
export type { GetPullRequestResponse, GetPullRequestDiffResponse, GetIssueResponse, SearchCodeResponse, } from "./v2/types/mcp.types.js";
|
|
13
|
+
export declare const VERSION = "2.0.0";
|
|
14
|
+
export { createYamaV2 as default } from "./v2/core/YamaV2Orchestrator.js";
|
|
16
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Yama -
|
|
3
|
-
*
|
|
2
|
+
* Yama - AI-Native Code Review
|
|
3
|
+
* Main export file
|
|
4
4
|
*/
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export {
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
export {
|
|
21
|
-
// Note: Use named import { Guardian } from '@juspay/yama' instead
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Core Exports
|
|
7
|
+
// ============================================================================
|
|
8
|
+
export { YamaV2Orchestrator, createYamaV2, } from "./v2/core/YamaV2Orchestrator.js";
|
|
9
|
+
export { ConfigLoader } from "./v2/config/ConfigLoader.js";
|
|
10
|
+
export { MCPServerManager } from "./v2/core/MCPServerManager.js";
|
|
11
|
+
export { SessionManager } from "./v2/core/SessionManager.js";
|
|
12
|
+
export { PromptBuilder } from "./v2/prompts/PromptBuilder.js";
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Version Information
|
|
15
|
+
// ============================================================================
|
|
16
|
+
export const VERSION = "2.0.0";
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Default Export
|
|
19
|
+
// ============================================================================
|
|
20
|
+
export { createYamaV2 as default } from "./v2/core/YamaV2Orchestrator.js";
|
|
22
21
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Layer Configuration Loader for Yama V2
|
|
3
|
+
* Loads and merges configuration from multiple sources
|
|
4
|
+
*/
|
|
5
|
+
import { YamaV2Config } from "../types/config.types.js";
|
|
6
|
+
export declare class ConfigLoader {
|
|
7
|
+
private config;
|
|
8
|
+
private configPath;
|
|
9
|
+
/**
|
|
10
|
+
* Load configuration from file with multi-layer support
|
|
11
|
+
*/
|
|
12
|
+
loadConfig(configPath?: string): Promise<YamaV2Config>;
|
|
13
|
+
/**
|
|
14
|
+
* Get current loaded configuration
|
|
15
|
+
*/
|
|
16
|
+
getConfig(): YamaV2Config;
|
|
17
|
+
/**
|
|
18
|
+
* Validate configuration completeness and correctness
|
|
19
|
+
*/
|
|
20
|
+
validate(): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve configuration file path
|
|
23
|
+
*/
|
|
24
|
+
private resolveConfigPath;
|
|
25
|
+
/**
|
|
26
|
+
* Load configuration from YAML file
|
|
27
|
+
*/
|
|
28
|
+
private loadConfigFile;
|
|
29
|
+
/**
|
|
30
|
+
* Deep merge two configuration objects
|
|
31
|
+
*/
|
|
32
|
+
private mergeConfigs;
|
|
33
|
+
/**
|
|
34
|
+
* Deep merge utility
|
|
35
|
+
*/
|
|
36
|
+
private deepMerge;
|
|
37
|
+
/**
|
|
38
|
+
* Check if value is an object
|
|
39
|
+
*/
|
|
40
|
+
private isObject;
|
|
41
|
+
/**
|
|
42
|
+
* Apply environment variable overrides
|
|
43
|
+
*/
|
|
44
|
+
private applyEnvironmentOverrides;
|
|
45
|
+
/**
|
|
46
|
+
* Basic configuration validation
|
|
47
|
+
*/
|
|
48
|
+
private validateConfig;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=ConfigLoader.d.ts.map
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Layer Configuration Loader for Yama V2
|
|
3
|
+
* Loads and merges configuration from multiple sources
|
|
4
|
+
*/
|
|
5
|
+
import { readFile } from "fs/promises";
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
import { parse as parseYAML } from "yaml";
|
|
8
|
+
import { resolve } from "path";
|
|
9
|
+
import { ConfigurationError } from "../types/v2.types.js";
|
|
10
|
+
import { DefaultConfig } from "./DefaultConfig.js";
|
|
11
|
+
export class ConfigLoader {
|
|
12
|
+
config = null;
|
|
13
|
+
configPath = null;
|
|
14
|
+
/**
|
|
15
|
+
* Load configuration from file with multi-layer support
|
|
16
|
+
*/
|
|
17
|
+
async loadConfig(configPath) {
|
|
18
|
+
console.log("📋 Loading Yama V2 configuration...");
|
|
19
|
+
// Layer 1: Start with default config
|
|
20
|
+
let config = DefaultConfig.get();
|
|
21
|
+
// Layer 2: Load from file if provided or search for default locations
|
|
22
|
+
const filePath = await this.resolveConfigPath(configPath);
|
|
23
|
+
if (filePath) {
|
|
24
|
+
console.log(` Reading config from: ${filePath}`);
|
|
25
|
+
const fileConfig = await this.loadConfigFile(filePath);
|
|
26
|
+
config = this.mergeConfigs(config, fileConfig);
|
|
27
|
+
this.configPath = filePath;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(" Using default configuration (no config file found)");
|
|
31
|
+
}
|
|
32
|
+
// Layer 3: Apply environment variable overrides
|
|
33
|
+
config = this.applyEnvironmentOverrides(config);
|
|
34
|
+
// Validate configuration
|
|
35
|
+
this.validateConfig(config);
|
|
36
|
+
this.config = config;
|
|
37
|
+
console.log("✅ Configuration loaded successfully\n");
|
|
38
|
+
return config;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get current loaded configuration
|
|
42
|
+
*/
|
|
43
|
+
getConfig() {
|
|
44
|
+
if (!this.config) {
|
|
45
|
+
throw new ConfigurationError("Configuration not loaded. Call loadConfig() first.");
|
|
46
|
+
}
|
|
47
|
+
return this.config;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validate configuration completeness and correctness
|
|
51
|
+
*/
|
|
52
|
+
async validate() {
|
|
53
|
+
if (!this.config) {
|
|
54
|
+
throw new ConfigurationError("No configuration to validate");
|
|
55
|
+
}
|
|
56
|
+
const errors = [];
|
|
57
|
+
// Validate AI config
|
|
58
|
+
if (!this.config.ai.provider) {
|
|
59
|
+
errors.push("AI provider not configured");
|
|
60
|
+
}
|
|
61
|
+
if (!this.config.ai.model) {
|
|
62
|
+
errors.push("AI model not configured");
|
|
63
|
+
}
|
|
64
|
+
// Check environment variables for Bitbucket (always required)
|
|
65
|
+
if (!process.env.BITBUCKET_USERNAME) {
|
|
66
|
+
errors.push("BITBUCKET_USERNAME environment variable not set");
|
|
67
|
+
}
|
|
68
|
+
if (!process.env.BITBUCKET_TOKEN) {
|
|
69
|
+
errors.push("BITBUCKET_TOKEN environment variable not set");
|
|
70
|
+
}
|
|
71
|
+
if (!process.env.BITBUCKET_BASE_URL) {
|
|
72
|
+
errors.push("BITBUCKET_BASE_URL environment variable not set");
|
|
73
|
+
}
|
|
74
|
+
if (this.config.mcpServers.jira.enabled) {
|
|
75
|
+
if (!process.env.JIRA_EMAIL) {
|
|
76
|
+
errors.push("JIRA_EMAIL environment variable not set");
|
|
77
|
+
}
|
|
78
|
+
if (!process.env.JIRA_API_TOKEN) {
|
|
79
|
+
errors.push("JIRA_API_TOKEN environment variable not set");
|
|
80
|
+
}
|
|
81
|
+
if (!process.env.JIRA_BASE_URL) {
|
|
82
|
+
errors.push("JIRA_BASE_URL environment variable not set");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (errors.length > 0) {
|
|
86
|
+
throw new ConfigurationError(`Configuration validation failed:\n${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve configuration file path
|
|
91
|
+
*/
|
|
92
|
+
async resolveConfigPath(configPath) {
|
|
93
|
+
// If explicit path provided, use it
|
|
94
|
+
if (configPath) {
|
|
95
|
+
const resolvedPath = resolve(configPath);
|
|
96
|
+
if (!existsSync(resolvedPath)) {
|
|
97
|
+
throw new ConfigurationError(`Configuration file not found: ${resolvedPath}`);
|
|
98
|
+
}
|
|
99
|
+
return resolvedPath;
|
|
100
|
+
}
|
|
101
|
+
// Search for default config files
|
|
102
|
+
const defaultPaths = [
|
|
103
|
+
"yama.config.yaml",
|
|
104
|
+
"config/yama.config.yaml",
|
|
105
|
+
".yama/config.yaml",
|
|
106
|
+
];
|
|
107
|
+
for (const path of defaultPaths) {
|
|
108
|
+
const resolvedPath = resolve(path);
|
|
109
|
+
if (existsSync(resolvedPath)) {
|
|
110
|
+
return resolvedPath;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Load configuration from YAML file
|
|
117
|
+
*/
|
|
118
|
+
async loadConfigFile(filePath) {
|
|
119
|
+
try {
|
|
120
|
+
const content = await readFile(filePath, "utf-8");
|
|
121
|
+
const parsed = parseYAML(content);
|
|
122
|
+
return parsed;
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
throw new ConfigurationError(`Failed to load config file: ${error.message}`, { filePath });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Deep merge two configuration objects
|
|
130
|
+
*/
|
|
131
|
+
mergeConfigs(base, override) {
|
|
132
|
+
return this.deepMerge(base, override);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Deep merge utility
|
|
136
|
+
*/
|
|
137
|
+
deepMerge(target, source) {
|
|
138
|
+
const output = { ...target };
|
|
139
|
+
if (this.isObject(target) && this.isObject(source)) {
|
|
140
|
+
Object.keys(source).forEach((key) => {
|
|
141
|
+
if (this.isObject(source[key])) {
|
|
142
|
+
if (!(key in target)) {
|
|
143
|
+
Object.assign(output, { [key]: source[key] });
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
output[key] = this.deepMerge(target[key], source[key]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
Object.assign(output, { [key]: source[key] });
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return output;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Check if value is an object
|
|
158
|
+
*/
|
|
159
|
+
isObject(item) {
|
|
160
|
+
return item && typeof item === "object" && !Array.isArray(item);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Apply environment variable overrides
|
|
164
|
+
*/
|
|
165
|
+
applyEnvironmentOverrides(config) {
|
|
166
|
+
// Override AI provider if env var set
|
|
167
|
+
if (process.env.AI_PROVIDER) {
|
|
168
|
+
config.ai.provider = process.env.AI_PROVIDER;
|
|
169
|
+
}
|
|
170
|
+
// Override AI model if env var set
|
|
171
|
+
if (process.env.AI_MODEL) {
|
|
172
|
+
config.ai.model = process.env.AI_MODEL;
|
|
173
|
+
}
|
|
174
|
+
// Override temperature if env var set
|
|
175
|
+
if (process.env.AI_TEMPERATURE) {
|
|
176
|
+
config.ai.temperature = parseFloat(process.env.AI_TEMPERATURE);
|
|
177
|
+
}
|
|
178
|
+
// Override max tokens if env var set
|
|
179
|
+
if (process.env.AI_MAX_TOKENS) {
|
|
180
|
+
config.ai.maxTokens = parseInt(process.env.AI_MAX_TOKENS, 10);
|
|
181
|
+
}
|
|
182
|
+
return config;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Basic configuration validation
|
|
186
|
+
*/
|
|
187
|
+
validateConfig(config) {
|
|
188
|
+
if (!config.version) {
|
|
189
|
+
throw new ConfigurationError("Configuration version not specified");
|
|
190
|
+
}
|
|
191
|
+
if (config.version !== 2) {
|
|
192
|
+
throw new ConfigurationError(`Invalid configuration version: ${config.version}. Expected version 2.`);
|
|
193
|
+
}
|
|
194
|
+
if (!config.ai) {
|
|
195
|
+
throw new ConfigurationError("AI configuration missing");
|
|
196
|
+
}
|
|
197
|
+
if (!config.mcpServers) {
|
|
198
|
+
throw new ConfigurationError("MCP servers configuration missing");
|
|
199
|
+
}
|
|
200
|
+
if (!config.review) {
|
|
201
|
+
throw new ConfigurationError("Review configuration missing");
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=ConfigLoader.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Configuration for Yama V2
|
|
3
|
+
* Provides sensible defaults when no config file is present
|
|
4
|
+
*/
|
|
5
|
+
import { YamaV2Config } from "../types/config.types.js";
|
|
6
|
+
export declare class DefaultConfig {
|
|
7
|
+
static get(): YamaV2Config;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=DefaultConfig.d.ts.map
|