gaunt-sloth-assistant 0.5.0 → 0.5.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/.claude/settings.local.json +15 -0
- package/.gsloth.backstory.md +0 -0
- package/.gsloth.guidelines.md +0 -0
- package/.gsloth.review.md +0 -0
- package/.gsloth.system.md +10 -0
- package/.prettierrc.json +0 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +0 -0
- package/README.md +9 -0
- package/ROADMAP.md +0 -0
- package/assets/gaunt-sloth-logo.png +0 -0
- package/assets/release-notes/v0_4_0.md +0 -0
- package/assets/release-notes/v0_5_0.md +0 -0
- package/assets/release-notes/v0_5_1.md +47 -0
- package/dist/commands/askCommand.d.ts +0 -0
- package/dist/commands/askCommand.js +9 -5
- package/dist/commands/askCommand.js.map +1 -1
- package/dist/commands/commandUtils.d.ts +25 -0
- package/dist/commands/commandUtils.js +48 -0
- package/dist/commands/commandUtils.js.map +1 -0
- package/dist/commands/initCommand.d.ts +0 -0
- package/dist/commands/initCommand.js +0 -0
- package/dist/commands/initCommand.js.map +0 -0
- package/dist/commands/prCommand.d.ts +2 -0
- package/dist/commands/prCommand.js +52 -0
- package/dist/commands/prCommand.js.map +1 -0
- package/dist/commands/reviewCommand.d.ts +1 -2
- package/dist/commands/reviewCommand.js +17 -98
- package/dist/commands/reviewCommand.js.map +1 -1
- package/dist/config.d.ts +18 -36
- package/dist/config.js +104 -84
- package/dist/config.js.map +1 -1
- package/dist/configs/anthropic.d.ts +0 -0
- package/dist/configs/anthropic.js +0 -0
- package/dist/configs/anthropic.js.map +0 -0
- package/dist/configs/fake.d.ts +0 -0
- package/dist/configs/fake.js +0 -0
- package/dist/configs/fake.js.map +0 -0
- package/dist/configs/groq.d.ts +0 -0
- package/dist/configs/groq.js +0 -0
- package/dist/configs/groq.js.map +0 -0
- package/dist/configs/vertexai.d.ts +0 -0
- package/dist/configs/vertexai.js +0 -0
- package/dist/configs/vertexai.js.map +0 -0
- package/dist/consoleUtils.d.ts +0 -0
- package/dist/consoleUtils.js +0 -0
- package/dist/consoleUtils.js.map +0 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -0
- package/dist/filePathUtils.d.ts +0 -0
- package/dist/filePathUtils.js +0 -0
- package/dist/filePathUtils.js.map +0 -0
- package/dist/index.d.ts +0 -0
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/llmUtils.d.ts +1 -1
- package/dist/llmUtils.js +85 -22
- package/dist/llmUtils.js.map +1 -1
- package/dist/modules/questionAnsweringModule.d.ts +2 -1
- package/dist/modules/questionAnsweringModule.js +4 -7
- package/dist/modules/questionAnsweringModule.js.map +1 -1
- package/dist/modules/reviewModule.d.ts +2 -1
- package/dist/modules/reviewModule.js +4 -7
- package/dist/modules/reviewModule.js.map +1 -1
- package/dist/modules/types.d.ts +0 -0
- package/dist/modules/types.js +0 -0
- package/dist/modules/types.js.map +0 -0
- package/dist/prompt.d.ts +1 -0
- package/dist/prompt.js +4 -1
- package/dist/prompt.js.map +1 -1
- package/dist/providers/file.d.ts +0 -0
- package/dist/providers/file.js +0 -0
- package/dist/providers/file.js.map +0 -0
- package/dist/providers/ghIssueProvider.d.ts +0 -0
- package/dist/providers/ghIssueProvider.js +3 -1
- package/dist/providers/ghIssueProvider.js.map +1 -1
- package/dist/providers/ghPrDiffProvider.d.ts +0 -0
- package/dist/providers/ghPrDiffProvider.js +3 -1
- package/dist/providers/ghPrDiffProvider.js.map +1 -1
- package/dist/providers/jiraIssueLegacyProvider.d.ts +0 -0
- package/dist/providers/jiraIssueLegacyProvider.js +0 -0
- package/dist/providers/jiraIssueLegacyProvider.js.map +0 -0
- package/dist/providers/jiraIssueProvider.d.ts +0 -0
- package/dist/providers/jiraIssueProvider.js +0 -0
- package/dist/providers/jiraIssueProvider.js.map +0 -0
- package/dist/providers/text.d.ts +0 -0
- package/dist/providers/text.js +0 -0
- package/dist/providers/text.js.map +0 -0
- package/dist/providers/types.d.ts +0 -0
- package/dist/providers/types.js +0 -0
- package/dist/providers/types.js.map +0 -0
- package/dist/systemUtils.d.ts +0 -0
- package/dist/systemUtils.js +0 -0
- package/dist/systemUtils.js.map +0 -0
- package/dist/utils.d.ts +0 -0
- package/dist/utils.js +0 -0
- package/dist/utils.js.map +0 -0
- package/docs/CONFIGURATION.md +0 -0
- package/docs/DEVELOPMENT.md +0 -0
- package/docs/RELEASE-HOWTO.md +0 -0
- package/eslint.config.js +0 -0
- package/maintenance/doc-maintenance.md +0 -0
- package/package.json +10 -8
- package/src/commands/askCommand.ts +9 -5
- package/src/commands/commandUtils.ts +77 -0
- package/src/commands/initCommand.ts +0 -0
- package/src/commands/prCommand.ts +93 -0
- package/src/commands/reviewCommand.ts +33 -155
- package/src/config.ts +121 -119
- package/src/configs/anthropic.ts +0 -0
- package/src/configs/fake.ts +0 -0
- package/src/configs/groq.ts +0 -0
- package/src/configs/vertexai.ts +0 -0
- package/src/consoleUtils.ts +0 -0
- package/src/constants.ts +7 -0
- package/src/filePathUtils.ts +0 -0
- package/src/index.ts +4 -2
- package/src/llmUtils.ts +100 -23
- package/src/modules/questionAnsweringModule.ts +6 -12
- package/src/modules/reviewModule.ts +11 -7
- package/src/modules/types.ts +0 -0
- package/src/prompt.ts +5 -1
- package/src/providers/file.ts +0 -0
- package/src/providers/ghIssueProvider.ts +3 -1
- package/src/providers/ghPrDiffProvider.ts +3 -1
- package/src/providers/jiraIssueLegacyProvider.ts +0 -0
- package/src/providers/jiraIssueProvider.ts +0 -0
- package/src/providers/text.ts +0 -0
- package/src/providers/types.ts +0 -0
- package/src/systemUtils.ts +0 -0
- package/src/utils.ts +0 -0
- package/tsconfig.json +0 -0
- package/vitest-it.config.ts +0 -0
- package/vitest.config.ts +0 -0
@@ -1,36 +1,20 @@
|
|
1
1
|
import { Command, Option } from 'commander';
|
2
|
-
import
|
3
|
-
|
4
|
-
|
2
|
+
import {
|
3
|
+
readBackstory,
|
4
|
+
readGuidelines,
|
5
|
+
readReviewInstructions,
|
6
|
+
readSystemPrompt,
|
7
|
+
} from '#src/prompt.js';
|
5
8
|
import { readMultipleFilesFromCurrentDir } from '#src/utils.js';
|
6
|
-
import { displayError } from '#src/consoleUtils.js';
|
7
9
|
import { getStringFromStdin } from '#src/systemUtils.js';
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
github: 'ghIssueProvider.js',
|
17
|
-
text: 'text.js',
|
18
|
-
file: 'file.js',
|
19
|
-
} as const;
|
20
|
-
|
21
|
-
type RequirementsProviderType = keyof typeof REQUIREMENTS_PROVIDERS;
|
22
|
-
|
23
|
-
/**
|
24
|
-
* Content providers. Expected to be in `.providers/` dir.
|
25
|
-
* Aliases are mapped to actual providers in this file
|
26
|
-
*/
|
27
|
-
const CONTENT_PROVIDERS = {
|
28
|
-
github: 'ghPrDiffProvider.js',
|
29
|
-
text: 'text.js',
|
30
|
-
file: 'file.js',
|
31
|
-
} as const;
|
32
|
-
|
33
|
-
type ContentProviderType = keyof typeof CONTENT_PROVIDERS;
|
10
|
+
import {
|
11
|
+
REQUIREMENTS_PROVIDERS,
|
12
|
+
CONTENT_PROVIDERS,
|
13
|
+
type RequirementsProviderType,
|
14
|
+
type ContentProviderType,
|
15
|
+
getRequirementsFromProvider,
|
16
|
+
getContentFromProvider,
|
17
|
+
} from './commandUtils.js';
|
34
18
|
|
35
19
|
interface ReviewCommandOptions {
|
36
20
|
file?: string[];
|
@@ -40,12 +24,7 @@ interface ReviewCommandOptions {
|
|
40
24
|
message?: string;
|
41
25
|
}
|
42
26
|
|
43
|
-
|
44
|
-
file?: string[];
|
45
|
-
requirementsProvider?: RequirementsProviderType;
|
46
|
-
}
|
47
|
-
|
48
|
-
export function reviewCommand(program: Command, context: SlothContext): void {
|
27
|
+
export function reviewCommand(program: Command): void {
|
49
28
|
program
|
50
29
|
.command('review')
|
51
30
|
.description('Review provided diff or other content')
|
@@ -75,32 +54,38 @@ export function reviewCommand(program: Command, context: SlothContext): void {
|
|
75
54
|
.option('-m, --message <message>', 'Extra message to provide just before the content')
|
76
55
|
.action(async (contentId: string | undefined, options: ReviewCommandOptions) => {
|
77
56
|
const { initConfig } = await import('#src/config.js');
|
78
|
-
await initConfig();
|
57
|
+
const config = await initConfig(); // Initialize and get config
|
58
|
+
const systemPrompt = readSystemPrompt();
|
79
59
|
const systemMessage = [
|
80
60
|
readBackstory(),
|
81
|
-
readGuidelines(
|
82
|
-
readReviewInstructions(
|
61
|
+
readGuidelines(config.projectGuidelines),
|
62
|
+
readReviewInstructions(config.projectReviewInstructions),
|
83
63
|
];
|
64
|
+
if (systemPrompt) {
|
65
|
+
systemMessage.push(systemPrompt);
|
66
|
+
}
|
84
67
|
const content: string[] = [];
|
85
68
|
const requirementsId = options.requirements;
|
86
69
|
const requirementsProvider =
|
87
70
|
options.requirementsProvider ??
|
88
|
-
(
|
89
|
-
|
90
|
-
| undefined) ??
|
91
|
-
(context.config?.requirementsProvider as RequirementsProviderType | undefined);
|
71
|
+
(config?.commands?.review?.requirementsProvider as RequirementsProviderType | undefined) ??
|
72
|
+
(config?.requirementsProvider as RequirementsProviderType | undefined);
|
92
73
|
const contentProvider =
|
93
74
|
options.contentProvider ??
|
94
|
-
(
|
95
|
-
(
|
75
|
+
(config?.commands?.review?.contentProvider as ContentProviderType | undefined) ??
|
76
|
+
(config?.contentProvider as ContentProviderType | undefined);
|
96
77
|
|
97
78
|
// TODO consider calling these in parallel
|
98
|
-
const requirements = await getRequirementsFromProvider(
|
79
|
+
const requirements = await getRequirementsFromProvider(
|
80
|
+
requirementsProvider,
|
81
|
+
requirementsId,
|
82
|
+
config
|
83
|
+
);
|
99
84
|
if (requirements) {
|
100
85
|
content.push(requirements);
|
101
86
|
}
|
102
87
|
|
103
|
-
const providedContent = await getContentFromProvider(contentProvider, contentId);
|
88
|
+
const providedContent = await getContentFromProvider(contentProvider, contentId, config);
|
104
89
|
if (providedContent) {
|
105
90
|
content.push(providedContent);
|
106
91
|
}
|
@@ -116,113 +101,6 @@ export function reviewCommand(program: Command, context: SlothContext): void {
|
|
116
101
|
content.push(options.message);
|
117
102
|
}
|
118
103
|
const { review } = await import('#src/modules/reviewModule.js');
|
119
|
-
await review('REVIEW', systemMessage.join('\n'), content.join('\n'));
|
120
|
-
});
|
121
|
-
|
122
|
-
program
|
123
|
-
.command('pr')
|
124
|
-
.description(
|
125
|
-
'Review provided Pull Request in current directory. ' +
|
126
|
-
'This command is similar to `review`, but default content provider is `github`. ' +
|
127
|
-
'(assuming that GitHub CLI is installed and authenticated for current project'
|
128
|
-
)
|
129
|
-
.argument('<prId>', 'Pull request ID to review.')
|
130
|
-
.argument(
|
131
|
-
'[requirementsId]',
|
132
|
-
'Optional requirements ID argument to retrieve requirements with requirements provider'
|
133
|
-
)
|
134
|
-
.addOption(
|
135
|
-
new Option(
|
136
|
-
'-p, --requirements-provider <requirementsProvider>',
|
137
|
-
'Requirements provider for this review.'
|
138
|
-
).choices(Object.keys(REQUIREMENTS_PROVIDERS))
|
139
|
-
)
|
140
|
-
.option(
|
141
|
-
'-f, --file [files...]',
|
142
|
-
'Input files. Content of these files will be added BEFORE the diff, but after requirements'
|
143
|
-
)
|
144
|
-
.action(async (prId: string, requirementsId: string | undefined, options: PrCommandOptions) => {
|
145
|
-
const { initConfig } = await import('#src/config.js');
|
146
|
-
await initConfig();
|
147
|
-
|
148
|
-
const systemMessage = [
|
149
|
-
readBackstory(),
|
150
|
-
readGuidelines(slothContext.config.projectGuidelines),
|
151
|
-
readReviewInstructions(slothContext.config.projectReviewInstructions),
|
152
|
-
];
|
153
|
-
const content: string[] = [];
|
154
|
-
const requirementsProvider =
|
155
|
-
options.requirementsProvider ??
|
156
|
-
(context.config?.commands?.pr?.requirementsProvider as
|
157
|
-
| RequirementsProviderType
|
158
|
-
| undefined) ??
|
159
|
-
(context.config?.requirementsProvider as RequirementsProviderType | undefined);
|
160
|
-
|
161
|
-
// Handle requirements
|
162
|
-
const requirements = await getRequirementsFromProvider(requirementsProvider, requirementsId);
|
163
|
-
if (requirements) {
|
164
|
-
content.push(requirements);
|
165
|
-
}
|
166
|
-
|
167
|
-
if (options.file) {
|
168
|
-
content.push(readMultipleFilesFromCurrentDir(options.file));
|
169
|
-
}
|
170
|
-
|
171
|
-
// Get PR diff using the 'github' provider
|
172
|
-
const providerPath = `#src/providers/${CONTENT_PROVIDERS['github']}`;
|
173
|
-
const { get } = await import(providerPath);
|
174
|
-
content.push(await get(null, prId));
|
175
|
-
|
176
|
-
const { review } = await import('#src/modules/reviewModule.js');
|
177
|
-
// TODO consider including requirements id
|
178
|
-
// TODO sanitize prId
|
179
|
-
await review(`PR-${prId}`, systemMessage.join('\n'), content.join('\n'));
|
104
|
+
await review('REVIEW', systemMessage.join('\n'), content.join('\n'), config);
|
180
105
|
});
|
181
|
-
|
182
|
-
async function getRequirementsFromProvider(
|
183
|
-
requirementsProvider: RequirementsProviderType | undefined,
|
184
|
-
requirementsId: string | undefined
|
185
|
-
): Promise<string> {
|
186
|
-
return getFromProvider(
|
187
|
-
requirementsProvider,
|
188
|
-
requirementsId,
|
189
|
-
(context.config?.requirementsProviderConfig ?? {})[requirementsProvider as string],
|
190
|
-
REQUIREMENTS_PROVIDERS
|
191
|
-
);
|
192
|
-
}
|
193
|
-
|
194
|
-
async function getContentFromProvider(
|
195
|
-
contentProvider: ContentProviderType | undefined,
|
196
|
-
contentId: string | undefined
|
197
|
-
): Promise<string> {
|
198
|
-
return getFromProvider(
|
199
|
-
contentProvider,
|
200
|
-
contentId,
|
201
|
-
(context.config?.contentProviderConfig ?? {})[contentProvider as string],
|
202
|
-
CONTENT_PROVIDERS
|
203
|
-
);
|
204
|
-
}
|
205
|
-
|
206
|
-
async function getFromProvider(
|
207
|
-
provider: RequirementsProviderType | ContentProviderType | undefined,
|
208
|
-
id: string | undefined,
|
209
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
210
|
-
config: any,
|
211
|
-
legitPredefinedProviders: typeof REQUIREMENTS_PROVIDERS | typeof CONTENT_PROVIDERS
|
212
|
-
): Promise<string> {
|
213
|
-
if (typeof provider === 'string') {
|
214
|
-
// Use one of the predefined providers
|
215
|
-
if (legitPredefinedProviders[provider as keyof typeof legitPredefinedProviders]) {
|
216
|
-
const providerPath = `#src/providers/${legitPredefinedProviders[provider as keyof typeof legitPredefinedProviders]}`;
|
217
|
-
const { get } = await import(providerPath);
|
218
|
-
return await get(config, id);
|
219
|
-
} else {
|
220
|
-
displayError(`Unknown provider: ${provider}. Continuing without it.`);
|
221
|
-
}
|
222
|
-
} else if (typeof provider === 'function') {
|
223
|
-
// Type assertion to handle function call
|
224
|
-
return await (provider as (id: string | undefined) => Promise<string>)(id);
|
225
|
-
}
|
226
|
-
return '';
|
227
|
-
}
|
228
106
|
}
|
package/src/config.ts
CHANGED
@@ -5,6 +5,13 @@ import { error, exit } from '#src/systemUtils.js';
|
|
5
5
|
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
6
6
|
import { getGslothConfigReadPath, getGslothConfigWritePath } from '#src/filePathUtils.js';
|
7
7
|
import type { Connection } from '@langchain/mcp-adapters';
|
8
|
+
import {
|
9
|
+
USER_PROJECT_CONFIG_JS,
|
10
|
+
USER_PROJECT_CONFIG_JSON,
|
11
|
+
USER_PROJECT_CONFIG_MJS,
|
12
|
+
PROJECT_GUIDELINES,
|
13
|
+
PROJECT_REVIEW_INSTRUCTIONS,
|
14
|
+
} from '#src/constants.js';
|
8
15
|
|
9
16
|
export interface SlothConfig extends BaseSlothConfig {
|
10
17
|
llm: BaseChatModel; // FIXME this is still bad keeping instance in config is probably not best choice
|
@@ -13,16 +20,7 @@ export interface SlothConfig extends BaseSlothConfig {
|
|
13
20
|
projectGuidelines: string;
|
14
21
|
projectReviewInstructions: string;
|
15
22
|
streamOutput: boolean;
|
16
|
-
|
17
|
-
pr: {
|
18
|
-
contentProvider: string;
|
19
|
-
requirementsProvider?: string;
|
20
|
-
};
|
21
|
-
review?: {
|
22
|
-
requirementsProvider?: string;
|
23
|
-
contentProvider?: string;
|
24
|
-
};
|
25
|
-
};
|
23
|
+
filesystem: string[] | 'all' | 'none';
|
26
24
|
}
|
27
25
|
|
28
26
|
/**
|
@@ -42,14 +40,20 @@ interface BaseSlothConfig {
|
|
42
40
|
projectGuidelines?: string;
|
43
41
|
projectReviewInstructions?: string;
|
44
42
|
streamOutput?: boolean;
|
43
|
+
filesystem?: string[] | 'all' | 'none';
|
45
44
|
commands?: {
|
46
|
-
pr
|
47
|
-
contentProvider
|
45
|
+
pr?: {
|
46
|
+
contentProvider?: string;
|
48
47
|
requirementsProvider?: string;
|
48
|
+
filesystem?: string[] | 'all' | 'none';
|
49
49
|
};
|
50
50
|
review?: {
|
51
51
|
requirementsProvider?: string;
|
52
52
|
contentProvider?: string;
|
53
|
+
filesystem?: string[] | 'all' | 'none';
|
54
|
+
};
|
55
|
+
ask?: {
|
56
|
+
filesystem?: string[] | 'all' | 'none';
|
53
57
|
};
|
54
58
|
};
|
55
59
|
requirementsProviderConfig?: Record<string, unknown>;
|
@@ -57,26 +61,11 @@ interface BaseSlothConfig {
|
|
57
61
|
mcpServers?: Record<string, Connection>;
|
58
62
|
}
|
59
63
|
|
60
|
-
/**
|
61
|
-
* @deprecated
|
62
|
-
* this object has blurred responsibility lines and bad name.
|
63
|
-
*/
|
64
|
-
export interface SlothContext {
|
65
|
-
config: SlothConfig;
|
66
|
-
}
|
67
|
-
|
68
64
|
export interface LLMConfig extends Record<string, unknown> {
|
69
65
|
type: string;
|
70
66
|
model: string;
|
71
67
|
}
|
72
68
|
|
73
|
-
export const USER_PROJECT_CONFIG_JS = '.gsloth.config.js';
|
74
|
-
export const USER_PROJECT_CONFIG_JSON = '.gsloth.config.json';
|
75
|
-
export const USER_PROJECT_CONFIG_MJS = '.gsloth.config.mjs';
|
76
|
-
export const GSLOTH_BACKSTORY = '.gsloth.backstory.md';
|
77
|
-
export const PROJECT_GUIDELINES = '.gsloth.guidelines.md';
|
78
|
-
export const PROJECT_REVIEW_INSTRUCTIONS = '.gsloth.review.md';
|
79
|
-
|
80
69
|
export const availableDefaultConfigs = ['vertexai', 'anthropic', 'groq'] as const;
|
81
70
|
export type ConfigType = (typeof availableDefaultConfigs)[number];
|
82
71
|
|
@@ -87,6 +76,15 @@ export const DEFAULT_CONFIG: Partial<SlothConfig> = {
|
|
87
76
|
projectGuidelines: PROJECT_GUIDELINES,
|
88
77
|
projectReviewInstructions: PROJECT_REVIEW_INSTRUCTIONS,
|
89
78
|
streamOutput: true,
|
79
|
+
filesystem: [
|
80
|
+
'read_file',
|
81
|
+
'read_multiple_files',
|
82
|
+
'list_directory',
|
83
|
+
'directory_tree',
|
84
|
+
'search_files',
|
85
|
+
'get_file_info',
|
86
|
+
'list_allowed_directories',
|
87
|
+
],
|
90
88
|
commands: {
|
91
89
|
pr: {
|
92
90
|
contentProvider: 'github', // gh pr diff NN
|
@@ -96,30 +94,26 @@ export const DEFAULT_CONFIG: Partial<SlothConfig> = {
|
|
96
94
|
};
|
97
95
|
|
98
96
|
/**
|
99
|
-
*
|
100
|
-
*
|
101
|
-
* TODO this should be reworked to something more robust
|
97
|
+
* Initialize configuration by loading from available config files
|
98
|
+
* @returns The loaded SlothConfig
|
102
99
|
*/
|
103
|
-
export
|
104
|
-
config: DEFAULT_CONFIG,
|
105
|
-
} as Partial<SlothContext> as SlothContext;
|
106
|
-
|
107
|
-
export async function initConfig(): Promise<void> {
|
100
|
+
export async function initConfig(): Promise<SlothConfig> {
|
108
101
|
const jsonConfigPath = getGslothConfigReadPath(USER_PROJECT_CONFIG_JSON);
|
109
|
-
const jsConfigPath = getGslothConfigReadPath(USER_PROJECT_CONFIG_JS);
|
110
|
-
const mjsConfigPath = getGslothConfigReadPath(USER_PROJECT_CONFIG_MJS);
|
111
102
|
|
112
|
-
// Try loading JSON config file first
|
103
|
+
// Try loading the JSON config file first
|
113
104
|
if (existsSync(jsonConfigPath)) {
|
114
105
|
try {
|
115
106
|
// TODO makes sense to employ ZOD to validate config
|
116
107
|
const jsonConfig = JSON.parse(readFileSync(jsonConfigPath, 'utf8')) as RawSlothConfig;
|
117
108
|
// If the config has an LLM with a type, create the appropriate LLM instance
|
118
109
|
if (jsonConfig.llm && typeof jsonConfig.llm === 'object' && 'type' in jsonConfig.llm) {
|
119
|
-
await tryJsonConfig(jsonConfig);
|
110
|
+
return await tryJsonConfig(jsonConfig);
|
120
111
|
} else {
|
121
112
|
error(`${jsonConfigPath} is not in valid format. Should at least define llm.type`);
|
122
113
|
exit(1);
|
114
|
+
// noinspection ExceptionCaughtLocallyJS
|
115
|
+
// This throw is unreachable due to exit(1) above, but satisfies TS type analysis and prevents tests from exiting
|
116
|
+
throw new Error('Unexpected error occurred.');
|
123
117
|
}
|
124
118
|
} catch (e) {
|
125
119
|
displayDebug(e instanceof Error ? e : String(e));
|
@@ -133,113 +127,113 @@ export async function initConfig(): Promise<void> {
|
|
133
127
|
// JSON config not found, try JS
|
134
128
|
return tryJsConfig();
|
135
129
|
}
|
130
|
+
}
|
136
131
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
displayError(
|
150
|
-
`Failed to read config from ${USER_PROJECT_CONFIG_JS}, will try other formats.`
|
151
|
-
);
|
152
|
-
// Continue to try other formats
|
153
|
-
return tryMjsConfig();
|
154
|
-
});
|
155
|
-
} else {
|
156
|
-
// JS config not found, try MJS
|
132
|
+
// Helper function to try loading JS config
|
133
|
+
async function tryJsConfig(): Promise<SlothConfig> {
|
134
|
+
const jsConfigPath = getGslothConfigReadPath(USER_PROJECT_CONFIG_JS);
|
135
|
+
if (existsSync(jsConfigPath)) {
|
136
|
+
try {
|
137
|
+
const i = await importExternalFile(jsConfigPath);
|
138
|
+
const customConfig = await i.configure(jsConfigPath);
|
139
|
+
return mergeConfig(customConfig) as SlothConfig;
|
140
|
+
} catch (e) {
|
141
|
+
displayDebug(e instanceof Error ? e : String(e));
|
142
|
+
displayError(`Failed to read config from ${USER_PROJECT_CONFIG_JS}, will try other formats.`);
|
143
|
+
// Continue to try other formats
|
157
144
|
return tryMjsConfig();
|
158
145
|
}
|
159
|
-
}
|
160
|
-
|
161
|
-
|
162
|
-
async function tryMjsConfig(): Promise<void> {
|
163
|
-
if (existsSync(mjsConfigPath)) {
|
164
|
-
return importExternalFile(mjsConfigPath)
|
165
|
-
.then((i: { configure: (module: string) => Promise<Partial<SlothConfig>> }) =>
|
166
|
-
i.configure(mjsConfigPath)
|
167
|
-
)
|
168
|
-
.then((config) => {
|
169
|
-
slothContext.config = { ...slothContext.config, ...config };
|
170
|
-
})
|
171
|
-
.catch((e) => {
|
172
|
-
displayDebug(e instanceof Error ? e : String(e));
|
173
|
-
displayError(`Failed to read config from ${USER_PROJECT_CONFIG_MJS}.`);
|
174
|
-
displayError(`No valid configuration found. Please create a valid configuration file.`);
|
175
|
-
exit();
|
176
|
-
});
|
177
|
-
} else {
|
178
|
-
// No config files found
|
179
|
-
displayError(
|
180
|
-
'No configuration file found. Please create one of: ' +
|
181
|
-
`${USER_PROJECT_CONFIG_JSON}, ${USER_PROJECT_CONFIG_JS}, or ${USER_PROJECT_CONFIG_MJS} ` +
|
182
|
-
'in your project directory.'
|
183
|
-
);
|
184
|
-
exit();
|
185
|
-
}
|
146
|
+
} else {
|
147
|
+
// JS config not found, try MJS
|
148
|
+
return tryMjsConfig();
|
186
149
|
}
|
187
150
|
}
|
188
151
|
|
189
|
-
//
|
190
|
-
|
191
|
-
const
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
152
|
+
// Helper function to try loading MJS config
|
153
|
+
async function tryMjsConfig(): Promise<SlothConfig> {
|
154
|
+
const mjsConfigPath = getGslothConfigReadPath(USER_PROJECT_CONFIG_MJS);
|
155
|
+
if (existsSync(mjsConfigPath)) {
|
156
|
+
try {
|
157
|
+
const i = await importExternalFile(mjsConfigPath);
|
158
|
+
const customConfig = await i.configure(mjsConfigPath);
|
159
|
+
return mergeConfig(customConfig) as SlothConfig;
|
160
|
+
} catch (e) {
|
161
|
+
displayDebug(e instanceof Error ? e : String(e));
|
162
|
+
displayError(`Failed to read config from ${USER_PROJECT_CONFIG_MJS}.`);
|
163
|
+
displayError(`No valid configuration found. Please create a valid configuration file.`);
|
164
|
+
exit(1);
|
165
|
+
}
|
166
|
+
} else {
|
167
|
+
// No config files found
|
196
168
|
displayError(
|
197
|
-
|
169
|
+
'No configuration file found. Please create one of: ' +
|
170
|
+
`${USER_PROJECT_CONFIG_JSON}, ${USER_PROJECT_CONFIG_JS}, or ${USER_PROJECT_CONFIG_MJS} ` +
|
171
|
+
'in your project directory.'
|
198
172
|
);
|
199
173
|
exit(1);
|
200
|
-
return;
|
201
174
|
}
|
175
|
+
// This throw is unreachable due to exit(1) above, but satisfies TS type analysis and prevents tests from exiting
|
176
|
+
throw new Error('Unexpected error occurred.');
|
177
|
+
}
|
202
178
|
|
179
|
+
/**
|
180
|
+
* Process JSON LLM config by creating the appropriate LLM instance
|
181
|
+
* @param jsonConfig - The parsed JSON config
|
182
|
+
* @returns Promise<SlothConfig>
|
183
|
+
*/
|
184
|
+
export async function tryJsonConfig(jsonConfig: RawSlothConfig): Promise<SlothConfig> {
|
203
185
|
try {
|
204
|
-
|
205
|
-
|
186
|
+
if (jsonConfig.llm && typeof jsonConfig.llm === 'object') {
|
187
|
+
// Get the type of LLM (e.g., 'vertexai', 'anthropic') - this should exist
|
188
|
+
const llmType = (jsonConfig.llm as LLMConfig).type;
|
189
|
+
if (!llmType) {
|
190
|
+
displayError('LLM type not specified in config.');
|
191
|
+
exit(1);
|
192
|
+
}
|
193
|
+
|
194
|
+
// Get the configuration for the specific LLM type
|
195
|
+
const llmConfig = jsonConfig.llm;
|
196
|
+
// Import the appropriate config module
|
206
197
|
const configModule = await import(`./configs/${llmType}.js`);
|
207
198
|
if (configModule.processJsonConfig) {
|
208
199
|
const llm = (await configModule.processJsonConfig(llmConfig)) as BaseChatModel;
|
209
|
-
|
200
|
+
return mergeRawConfig(jsonConfig, llm);
|
210
201
|
} else {
|
211
202
|
displayWarning(`Config module for ${llmType} does not have processJsonConfig function.`);
|
212
203
|
exit(1);
|
213
204
|
}
|
214
|
-
}
|
215
|
-
|
216
|
-
displayWarning(`Could not import config module for ${llmType}.`);
|
205
|
+
} else {
|
206
|
+
displayError('No LLM configuration found in config.');
|
217
207
|
exit(1);
|
218
208
|
}
|
219
|
-
} catch (
|
220
|
-
|
221
|
-
|
209
|
+
} catch (e) {
|
210
|
+
if (e instanceof Error && e.message.includes('Cannot find module')) {
|
211
|
+
displayError(`LLM type '${(jsonConfig.llm as LLMConfig).type}' not supported.`);
|
212
|
+
} else {
|
213
|
+
displayError(`Error processing LLM config: ${e instanceof Error ? e.message : String(e)}`);
|
214
|
+
}
|
222
215
|
exit(1);
|
223
216
|
}
|
217
|
+
// This throw is unreachable due to exit(1) above, but satisfies TS type analysis and prevents tests from exiting
|
218
|
+
throw new Error('Unexpected error occurred.');
|
224
219
|
}
|
225
220
|
|
226
221
|
export async function createProjectConfig(configType: string): Promise<void> {
|
227
|
-
|
228
|
-
writeProjectReviewPreamble();
|
229
|
-
displayWarning(`Make sure you add as much detail as possible to your ${PROJECT_GUIDELINES}.\n`);
|
230
|
-
|
231
|
-
// Check if the config type is in availableDefaultConfigs
|
222
|
+
// Check if the config type is valid
|
232
223
|
if (!availableDefaultConfigs.includes(configType as ConfigType)) {
|
233
224
|
displayError(
|
234
|
-
`
|
225
|
+
`Unknown config type: ${configType}. Available options: ${availableDefaultConfigs.join(', ')}`
|
235
226
|
);
|
236
227
|
exit(1);
|
237
|
-
return;
|
238
228
|
}
|
239
229
|
|
230
|
+
displayInfo(`Setting up your project\n`);
|
231
|
+
writeProjectReviewPreamble();
|
232
|
+
displayWarning(`Make sure you add as much detail as possible to your ${PROJECT_GUIDELINES}.\n`);
|
233
|
+
|
240
234
|
displayInfo(`Creating project config for ${configType}`);
|
241
235
|
const vendorConfig = await import(`./configs/${configType}.js`);
|
242
|
-
vendorConfig.init(getGslothConfigWritePath(USER_PROJECT_CONFIG_JSON)
|
236
|
+
vendorConfig.init(getGslothConfigWritePath(USER_PROJECT_CONFIG_JSON));
|
243
237
|
}
|
244
238
|
|
245
239
|
export function writeProjectReviewPreamble(): void {
|
@@ -287,12 +281,20 @@ Important! You are likely to be dealing with git diff below, please don't confus
|
|
287
281
|
}
|
288
282
|
|
289
283
|
/**
|
290
|
-
*
|
291
|
-
|
284
|
+
* Merge config with default config
|
285
|
+
*/
|
286
|
+
function mergeConfig(partialConfig: Partial<SlothConfig>): SlothConfig {
|
287
|
+
const config = partialConfig as SlothConfig;
|
288
|
+
return {
|
289
|
+
...DEFAULT_CONFIG,
|
290
|
+
...config,
|
291
|
+
commands: { ...DEFAULT_CONFIG.commands, ...(config?.commands ?? {}) },
|
292
|
+
};
|
293
|
+
}
|
294
|
+
|
295
|
+
/**
|
296
|
+
* Merge raw with default config
|
292
297
|
*/
|
293
|
-
|
294
|
-
|
295
|
-
delete (slothContext as unknown as Record<string, unknown>)[key];
|
296
|
-
});
|
297
|
-
slothContext.config = DEFAULT_CONFIG as SlothConfig;
|
298
|
+
function mergeRawConfig(config: RawSlothConfig, llm: BaseChatModel): SlothConfig {
|
299
|
+
return mergeConfig({ ...config, llm });
|
298
300
|
}
|
package/src/configs/anthropic.ts
CHANGED
File without changes
|
package/src/configs/fake.ts
CHANGED
File without changes
|
package/src/configs/groq.ts
CHANGED
File without changes
|
package/src/configs/vertexai.ts
CHANGED
File without changes
|
package/src/consoleUtils.ts
CHANGED
File without changes
|
package/src/constants.ts
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
export const USER_PROJECT_CONFIG_JS = '.gsloth.config.js';
|
2
|
+
export const USER_PROJECT_CONFIG_JSON = '.gsloth.config.json';
|
3
|
+
export const USER_PROJECT_CONFIG_MJS = '.gsloth.config.mjs';
|
4
|
+
export const GSLOTH_BACKSTORY = '.gsloth.backstory.md';
|
5
|
+
export const PROJECT_GUIDELINES = '.gsloth.guidelines.md';
|
6
|
+
export const PROJECT_REVIEW_INSTRUCTIONS = '.gsloth.review.md';
|
7
|
+
export const GSLOTH_SYSTEM_PROMPT = '.gsloth.system.md';
|
package/src/filePathUtils.ts
CHANGED
File without changes
|
package/src/index.ts
CHANGED
@@ -2,7 +2,7 @@ import { Command } from 'commander';
|
|
2
2
|
import { askCommand } from '#src/commands/askCommand.js';
|
3
3
|
import { initCommand } from '#src/commands/initCommand.js';
|
4
4
|
import { reviewCommand } from '#src/commands/reviewCommand.js';
|
5
|
-
import {
|
5
|
+
import { prCommand } from '#src/commands/prCommand.js';
|
6
6
|
import { getSlothVersion } from '#src/utils.js';
|
7
7
|
import { argv, readStdin } from '#src/systemUtils.js';
|
8
8
|
import { setVerbose } from '#src/llmUtils.js';
|
@@ -22,8 +22,10 @@ if (program.getOptionValue('verbose')) {
|
|
22
22
|
setVerbose(true);
|
23
23
|
}
|
24
24
|
|
25
|
+
// Initialize all commands - they will handle their own config loading
|
25
26
|
initCommand(program);
|
26
|
-
reviewCommand(program
|
27
|
+
reviewCommand(program);
|
28
|
+
prCommand(program);
|
27
29
|
askCommand(program);
|
28
30
|
// TODO add general interactive chat command
|
29
31
|
|