gaunt-sloth-assistant 0.0.7 → 0.1.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/DEVELOPMENT.md +9 -0
- package/README.md +76 -16
- package/RELEASE-HOWTO.md +14 -0
- package/ROADMAP.md +2 -2
- package/UX-RESEARCH.md +78 -0
- package/index.js +12 -74
- package/package.json +14 -9
- package/spec/.gsloth.config.js +22 -0
- package/spec/askCommand.spec.js +58 -0
- package/spec/initCommand.spec.js +54 -0
- package/spec/questionAnsweringModule.spec.js +137 -0
- package/spec/reviewCommand.spec.js +144 -0
- package/spec/reviewModule.spec.js +22 -0
- package/spec/support/jasmine.mjs +14 -0
- package/src/commands/askCommand.js +26 -0
- package/src/commands/initCommand.js +16 -0
- package/src/commands/reviewCommand.js +147 -0
- package/src/config.js +15 -10
- package/src/configs/anthropic.js +1 -1
- package/src/configs/groq.js +25 -0
- package/src/consoleUtils.js +2 -0
- package/src/{questionAnswering.js → modules/questionAnsweringModule.js} +87 -68
- package/src/{codeReview.js → modules/reviewModule.js} +28 -26
- package/src/prompt.js +9 -1
- package/src/providers/ghPrDiffProvider.js +11 -0
- package/src/providers/jiraIssueLegacyAccessTokenProvider.js +81 -0
- package/src/providers/text.js +6 -0
- package/src/utils.js +65 -22
package/DEVELOPMENT.md
ADDED
package/README.md
CHANGED
@@ -1,13 +1,19 @@
|
|
1
1
|
# Gaunt Sloth Assistant
|
2
|
-
Simplistic assistant helping to do code reviews from command line based on [Langchain.js](https://github.com/langchain-ai/langchainjs)
|
2
|
+
Simplistic AI assistant helping to do **code reviews from command line** based on [Langchain.js](https://github.com/langchain-ai/langchainjs)
|
3
|
+
|
4
|
+
## Review PR (Pull Request)
|
5
|
+
To review PR by PR number:
|
6
|
+
|
7
|
+
First make sure the official [GitHub cli (gh)](https://cli.github.com/) is installed
|
8
|
+
and authenticated to have access to your project.
|
9
|
+
|
10
|
+
Open terminal (command line) in your project directory.
|
11
|
+
|
12
|
+
Type command: `gsloth pr [desired pull request number]`, for example:
|
3
13
|
|
4
|
-
## Review PR
|
5
|
-
Review PR by PR number:
|
6
14
|
```shell
|
7
15
|
gsloth pr 42
|
8
16
|
```
|
9
|
-
Official [GitHub cli (gh)](https://cli.github.com/) should be installed
|
10
|
-
and authenticated to have access to your project.
|
11
17
|
|
12
18
|
Review providing markdown file with requirements and notes.
|
13
19
|
```shell
|
@@ -19,6 +25,44 @@ open Jira XML with "Export XML" in jira and to copy `<description></description>
|
|
19
25
|
This block contains HTML and AI understands it easily
|
20
26
|
(most importantly it understand nested lists like ul>li).
|
21
27
|
|
28
|
+
## JIRA Integration
|
29
|
+
|
30
|
+
When JIRA integration is configured, the JIRA issue text can be included alongside the diff for review.
|
31
|
+
The project review preamble can be modified to reject a pull request immediately
|
32
|
+
if it appears to implement something different from what was requested in the requirements.
|
33
|
+
|
34
|
+
The command syntax is generally `gsloth pr <prId> [requirementsId]`,
|
35
|
+
for example, the snippet below does review of PR 42 and
|
36
|
+
supplies description of JIRA issue with number PP-4242:
|
37
|
+
|
38
|
+
```shell
|
39
|
+
gsloth pr 42 PP-4242
|
40
|
+
```
|
41
|
+
|
42
|
+
Example configuration setting up JIRA integration using a legacy API token.
|
43
|
+
Make sure you use your actual company domain in `baseUrl` and your personal legacy `token`.
|
44
|
+
|
45
|
+
A legacy token can be acquired from `Atlassian Account Settings -> Security -> Create and manage API tokens`.
|
46
|
+
|
47
|
+
```javascript
|
48
|
+
export async function configure(importFunction, global) {
|
49
|
+
const vertexAi = await importFunction('@langchain/google-vertexai');
|
50
|
+
return {
|
51
|
+
llm: new vertexAi.ChatVertexAI({
|
52
|
+
model: "gemini-2.5-pro-exp-03-25"
|
53
|
+
}),
|
54
|
+
requirementsProvider: 'jira-legacy',
|
55
|
+
requirementsProviderConfig: {
|
56
|
+
'jira-legacy': {
|
57
|
+
username: 'andrei.kondratev@unimarket.com', // Your Jira username/email
|
58
|
+
token: 'YOURSECRETTOKEN', // Replace with your real Jira API token
|
59
|
+
baseUrl: 'https://yourcompany.atlassian.net/rest/api/2/issue/' // Your Jira instance base URL
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
```
|
65
|
+
|
22
66
|
## Review any Diff
|
23
67
|
```shell
|
24
68
|
git --no-pager diff origin/master...yourgitcommithash | gsloth review
|
@@ -48,14 +92,6 @@ Tested with Node 22 LTS.
|
|
48
92
|
npm install gaunt-sloth-assistant -g
|
49
93
|
```
|
50
94
|
|
51
|
-
## GitHub (master)
|
52
|
-
|
53
|
-
```shell
|
54
|
-
git clone https://github.com/andruhon/gaunt-sloth.git
|
55
|
-
npm install
|
56
|
-
npm install -g ./
|
57
|
-
```
|
58
|
-
|
59
95
|
## Configuration
|
60
96
|
Go to your project directory and init sloth with vendor of your choice.
|
61
97
|
|
@@ -72,11 +108,18 @@ gcloud auth application-default login
|
|
72
108
|
cd ./your-project
|
73
109
|
gsloth init anthropic
|
74
110
|
```
|
75
|
-
Make sure you edit `.gsloth.config.js` and set up your key.
|
111
|
+
Make sure you either define `ANTHROPIC_API_KEY` environment variable or edit `.gsloth.config.js` and set up your key.
|
112
|
+
|
113
|
+
### Groq
|
114
|
+
```shell
|
115
|
+
cd ./your-project
|
116
|
+
gsloth init groq
|
117
|
+
```
|
118
|
+
Make sure you either define `GROQ_API_KEY` environment variable or edit `.gsloth.config.js` and set up your key.
|
76
119
|
|
77
120
|
### Further configuration
|
78
121
|
|
79
|
-
Currently
|
122
|
+
Currently vertexai, anthropic and groq can be configured with `gsloth init`.
|
80
123
|
|
81
124
|
Populate `.gsloth.preamble.review.md` with your project details and quality requirements.
|
82
125
|
Proper preamble is a paramount for good inference.
|
@@ -98,7 +141,7 @@ export async function configure(importFunction, global) {
|
|
98
141
|
const anthropic = await importFunction('@langchain/anthropic');
|
99
142
|
return {
|
100
143
|
llm: new anthropic.ChatAnthropic({
|
101
|
-
apiKey:
|
144
|
+
apiKey: process.env.ANTHROPIC_API_KEY, // Default value, but you can provide the key in many different ways, even as literal
|
102
145
|
model: "claude-3-5-sonnet-20241022"
|
103
146
|
})
|
104
147
|
};
|
@@ -127,6 +170,23 @@ export async function configure(importFunction, global) {
|
|
127
170
|
}
|
128
171
|
```
|
129
172
|
|
173
|
+
**Example of .gsloth.config.js for Groq**
|
174
|
+
VertexAI usually needs `gcloud auth application-default login`
|
175
|
+
(or both `gcloud auth login` and `gcloud auth application-default login`) and does not need any separate API keys.
|
176
|
+
```javascript
|
177
|
+
export async function configure(importFunction, global) {
|
178
|
+
// this is going to be imported from sloth dependencies,
|
179
|
+
// but can potentially be pulled from global node modules or from this project
|
180
|
+
const groq = await importFunction('@langchain/groq');
|
181
|
+
return {
|
182
|
+
llm: new groq.ChatGroq({
|
183
|
+
model: "deepseek-r1-distill-llama-70b", // Check other models available
|
184
|
+
apiKey: process.env.GROQ_API_KEY, // Default value, but you can provide the key in many different ways, even as literal
|
185
|
+
})
|
186
|
+
};
|
187
|
+
}
|
188
|
+
```
|
189
|
+
|
130
190
|
The configure function should simply return instance of langchain [chat model](https://v03.api.js.langchain.com/classes/_langchain_core.language_models_chat_models.BaseChatModel.html).
|
131
191
|
See [Langchain documentation](https://js.langchain.com/docs/tutorials/llm_chain/) for more details.
|
132
192
|
|
package/RELEASE-HOWTO.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
Make sure `npm config set git-tag-version true`
|
2
2
|
|
3
|
+
For patch, e.g., from 0.0.8 to 0.0.9
|
4
|
+
```shell
|
5
|
+
npm version patch
|
6
|
+
git push
|
7
|
+
git push --tags
|
8
|
+
```
|
9
|
+
|
10
|
+
For minor, e.g., from 0.0.8 to 0.1.0
|
3
11
|
```shell
|
4
12
|
npm version patch
|
5
13
|
git push
|
@@ -9,4 +17,10 @@ git push --tags
|
|
9
17
|
Note the release version and do
|
10
18
|
```shell
|
11
19
|
gh release create --generate-notes
|
20
|
+
```
|
21
|
+
|
22
|
+
Publish to NPM
|
23
|
+
```shell
|
24
|
+
npm login
|
25
|
+
npm publish
|
12
26
|
```
|
package/ROADMAP.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
## 1.0.0
|
5
5
|
Doing the following below and making it work stably should be sufficient to call it version 1.
|
6
6
|
|
7
|
-
### Add tests and gain reasonable coverage
|
7
|
+
### ⌛Add tests and gain reasonable coverage
|
8
8
|
### Configure eslint for code quality checks
|
9
9
|
### Automate release process
|
10
10
|
### Add project init command
|
@@ -12,7 +12,7 @@ Add a command to init certain model in certain project, for example `gsloth init
|
|
12
12
|
or `gsloth init` and select one of the provided options.
|
13
13
|
-[x] VertexAI
|
14
14
|
-[x] Anthropic
|
15
|
-
-[
|
15
|
+
-[x] Groq
|
16
16
|
-[ ] Local LLm
|
17
17
|
### Allow global configuration
|
18
18
|
### Streamline and stabilize configuration
|
package/UX-RESEARCH.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# UX Research
|
2
|
+
|
3
|
+
## Currently available commands:
|
4
|
+
```
|
5
|
+
gsloth --help
|
6
|
+
Usage: gsloth [options] [command]
|
7
|
+
|
8
|
+
Gaunt Sloth Assistant reviewing your PRs
|
9
|
+
|
10
|
+
Options:
|
11
|
+
-V, --version output the version number
|
12
|
+
-h, --help display help for command
|
13
|
+
|
14
|
+
Commands:
|
15
|
+
init <type> Initialize the Gaunt Sloth Assistant in your project.
|
16
|
+
This will write necessary config files.
|
17
|
+
pr [options] <prNumber> Review a PR in current git directory (assuming that
|
18
|
+
GH cli is installed and authenticated for current
|
19
|
+
project
|
20
|
+
review [options] Review provided diff or other content
|
21
|
+
ask [options] <message> Ask a question
|
22
|
+
help [command] display help for command
|
23
|
+
```
|
24
|
+
|
25
|
+
pr (we decided to rename it to r as shortcut for review with pull request provider)
|
26
|
+
```
|
27
|
+
gsloth pr --help
|
28
|
+
Usage: gsloth pr [options] <prNumber>
|
29
|
+
|
30
|
+
Review a PR in current git directory (assuming that GH cli is installed and
|
31
|
+
authenticated for current project
|
32
|
+
|
33
|
+
Arguments:
|
34
|
+
prNumber PR number to review
|
35
|
+
|
36
|
+
Options:
|
37
|
+
-f, --file <file> Input file. Context of this file will be added BEFORE the
|
38
|
+
diff
|
39
|
+
-h, --help display help for command
|
40
|
+
```
|
41
|
+
|
42
|
+
ask
|
43
|
+
```
|
44
|
+
gsloth ask --help
|
45
|
+
Usage: gsloth ask [options] <message>
|
46
|
+
|
47
|
+
Ask a question
|
48
|
+
|
49
|
+
Arguments:
|
50
|
+
message A message
|
51
|
+
|
52
|
+
Options:
|
53
|
+
-f, --file <file> Input file. Context of this file will be added BEFORE the
|
54
|
+
diff
|
55
|
+
-h, --help display help for command
|
56
|
+
```
|
57
|
+
|
58
|
+
review (lacks documentaion, it also accepts pipe with stdin)
|
59
|
+
```
|
60
|
+
gsloth review --help
|
61
|
+
Usage: gsloth review [options]
|
62
|
+
|
63
|
+
Review provided diff or other content
|
64
|
+
|
65
|
+
Options:
|
66
|
+
-f, --file <file> Input file. Context of this file will be added BEFORE the
|
67
|
+
diff
|
68
|
+
-h, --help display help for command
|
69
|
+
```
|
70
|
+
|
71
|
+
## Future functions
|
72
|
+
|
73
|
+
- JIRA. We need to privide a jira number as a criteria, this should somehow go through separate provider and using config from .gsloth.config.
|
74
|
+
- External links (simple public links)
|
75
|
+
- Editing files
|
76
|
+
- Improve experience with specs or criteria (or requirements?)
|
77
|
+
- Should we allow to mention that the data is diff or plain code? Maybe we can somehow deduct it or ask smaller model to guess?
|
78
|
+
- Slot editing local files
|
package/index.js
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
|
-
import {
|
3
|
-
import {dirname} from 'node:path';
|
4
|
-
import {
|
5
|
-
import {
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
} from "./src/config.js";
|
11
|
-
import {fileURLToPath} from "url";
|
12
|
-
import {getSlothVersion, readFileFromCurrentDir, readStdin} from "./src/utils.js";
|
13
|
-
import {getPrDiff, readInternalPreamble, readPreamble} from "./src/prompt.js";
|
2
|
+
import { Command } from 'commander';
|
3
|
+
import { dirname } from 'node:path';
|
4
|
+
import { fileURLToPath } from "url";
|
5
|
+
import { reviewCommand } from "./src/commands/reviewCommand.js";
|
6
|
+
import { initCommand } from "./src/commands/initCommand.js";
|
7
|
+
import { askCommand } from "./src/commands/askCommand.js";
|
8
|
+
import { slothContext } from "./src/config.js";
|
9
|
+
import { getSlothVersion, readStdin } from "./src/utils.js";
|
14
10
|
|
15
11
|
const program = new Command();
|
16
12
|
|
@@ -22,70 +18,12 @@ program
|
|
22
18
|
.description('Gaunt Sloth Assistant reviewing your PRs')
|
23
19
|
.version(getSlothVersion());
|
24
20
|
|
25
|
-
program
|
26
|
-
.description('Initialize the Gaunt Sloth Assistant in your project. This will write necessary config files.')
|
27
|
-
.addArgument(new Argument('<type>', 'Config type').choices(availableDefaultConfigs))
|
28
|
-
.action(async (config) => {
|
29
|
-
await createProjectConfig(config);
|
30
|
-
});
|
21
|
+
initCommand(program, slothContext);
|
31
22
|
|
32
|
-
program
|
33
|
-
.description('Review a PR in current git directory (assuming that GH cli is installed and authenticated for current project')
|
34
|
-
.argument('<prNumber>', 'PR number to review')
|
35
|
-
.option('-f, --file <file>', 'Input file. Context of this file will be added BEFORE the diff')
|
36
|
-
// TODO add option consuming extra message as argument
|
37
|
-
.action(async (pr, options) => {
|
38
|
-
if (slothContext.stdin) {
|
39
|
-
displayError('`gsloth pr` does not expect stdin, use `gsloth review` instead');
|
40
|
-
return;
|
41
|
-
}
|
42
|
-
displayInfo('Starting review of PR', pr);
|
43
|
-
const diff = await getPrDiff(pr);
|
44
|
-
const preamble = [readInternalPreamble(), readPreamble(USER_PROJECT_REVIEW_PREAMBLE)];
|
45
|
-
const content = [diff];
|
46
|
-
if (options.file) {
|
47
|
-
content.push(readFileFromCurrentDir(options.file));
|
48
|
-
}
|
49
|
-
const { review } = await import('./src/codeReview.js');
|
50
|
-
await review('sloth-PR-review-'+pr, preamble.join("\n"), content.join("\n"));
|
51
|
-
});
|
23
|
+
reviewCommand(program, slothContext)
|
52
24
|
|
53
|
-
program
|
54
|
-
.description('Review provided diff or other content')
|
55
|
-
.option('-f, --file <file>', 'Input file. Context of this file will be added BEFORE the diff')
|
56
|
-
// TODO add option consuming extra message as argument
|
57
|
-
.action(async (options) => {
|
58
|
-
if (!slothContext.stdin || options.file) {
|
59
|
-
displayError('gsloth review expects stdin with github diff stdin or a file');
|
60
|
-
return
|
61
|
-
}
|
62
|
-
const preamble = [readInternalPreamble(), readPreamble(USER_PROJECT_REVIEW_PREAMBLE)];
|
63
|
-
const content = [];
|
64
|
-
if (slothContext.stdin) {
|
65
|
-
content.push(slothContext.stdin);
|
66
|
-
}
|
67
|
-
if (options.file) {
|
68
|
-
content.push(readFileFromCurrentDir(options.file));
|
69
|
-
}
|
70
|
-
const { review } = await import('./src/codeReview.js');
|
71
|
-
await review('sloth-DIFF-review', preamble.join("\n"), content.join("\n"));
|
72
|
-
});
|
73
|
-
|
74
|
-
program.command('ask')
|
75
|
-
.description('Ask a question')
|
76
|
-
.argument('<message>', 'A message')
|
77
|
-
.option('-f, --file <file>', 'Input file. Context of this file will be added BEFORE the diff')
|
78
|
-
// TODO add option consuming extra message as argument
|
79
|
-
.action(async (message, options) => {
|
80
|
-
const preamble = [readInternalPreamble()];
|
81
|
-
const content = [message];
|
82
|
-
if (options.file) {
|
83
|
-
content.push(readFileFromCurrentDir(options.file));
|
84
|
-
}
|
85
|
-
const { askQuestion } = await import('./src/questionAnswering.js');
|
86
|
-
await askQuestion('sloth-ASK', preamble.join("\n"), content.join("\n"));
|
87
|
-
});
|
25
|
+
askCommand(program, slothContext);
|
88
26
|
|
89
27
|
// TODO add general interactive chat command
|
90
28
|
|
91
|
-
readStdin(program);
|
29
|
+
await readStdin(program);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "gaunt-sloth-assistant",
|
3
|
-
"version": "0.0
|
3
|
+
"version": "0.1.0",
|
4
4
|
"description": "",
|
5
5
|
"license": "MIT",
|
6
6
|
"author": "Andrew Kondratev",
|
@@ -12,21 +12,26 @@
|
|
12
12
|
"npm": ">=10.9.0"
|
13
13
|
},
|
14
14
|
"scripts": {
|
15
|
-
"test": "
|
16
|
-
"test-run": "node --trace-deprecation index.js ask \"status check\""
|
15
|
+
"test": "jasmine"
|
17
16
|
},
|
18
17
|
"bin": {
|
19
|
-
"gsloth": "index.js"
|
18
|
+
"gsloth": "index.js",
|
19
|
+
"gth": "index.js"
|
20
20
|
},
|
21
21
|
"dependencies": {
|
22
|
-
"@eslint/js": "^9.
|
23
|
-
"@langchain/anthropic": "^0.3.
|
24
|
-
"@langchain/core": "^0.3.
|
25
|
-
"@langchain/google-vertexai": "^0.2.
|
26
|
-
"@langchain/
|
22
|
+
"@eslint/js": "^9.25.0",
|
23
|
+
"@langchain/anthropic": "^0.3.18",
|
24
|
+
"@langchain/core": "^0.3.45",
|
25
|
+
"@langchain/google-vertexai": "^0.2.4",
|
26
|
+
"@langchain/groq": "^0.2.2",
|
27
|
+
"@langchain/langgraph": "^0.2.65",
|
27
28
|
"@types/node": "^22.14.1",
|
28
29
|
"chalk": "^5.4.1",
|
29
30
|
"commander": "^13.1.0",
|
30
31
|
"uuid": "^11.1.0"
|
32
|
+
},
|
33
|
+
"devDependencies": {
|
34
|
+
"jasmine": "^5.6.0",
|
35
|
+
"testdouble": "^3.20.2"
|
31
36
|
}
|
32
37
|
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
export async function configure(importFunction, global) {
|
2
|
+
const test = await importFunction('@langchain/core/utils/testing');
|
3
|
+
return {
|
4
|
+
llm: new test.FakeListChatModel({
|
5
|
+
responses: ["First LLM message", "Second LLM message"],
|
6
|
+
}),
|
7
|
+
requirementsProviderConfig: {
|
8
|
+
'jira-legacy': {
|
9
|
+
username: 'user.name@company.com', // Your Jira username/email
|
10
|
+
token: 'YoUrToKeN', // Replace with your real Jira API token
|
11
|
+
baseUrl: 'https://company.atlassian.net/rest/api/2/issue/' // Your Jira instance base URL
|
12
|
+
}
|
13
|
+
},
|
14
|
+
requirementsProvider: "jira-legacy",
|
15
|
+
contentProvider: "somethingSpecial",
|
16
|
+
contentProviderConfig: {
|
17
|
+
somethingSpecial: {
|
18
|
+
test: 'example'
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import {Command} from 'commander';
|
2
|
+
import * as td from 'testdouble';
|
3
|
+
|
4
|
+
describe('askCommand', function (){
|
5
|
+
|
6
|
+
beforeEach(async function() {
|
7
|
+
this.askQuestion = td.function();
|
8
|
+
this.prompt = await td.replaceEsm("../src/prompt.js");
|
9
|
+
td.when(this.prompt.readInternalPreamble()).thenReturn("INTERNAL PREAMBLE");
|
10
|
+
this.questionAnsweringMock = await td.replaceEsm("../src/modules/questionAnsweringModule.js");
|
11
|
+
await td.replaceEsm("../src/config.js");
|
12
|
+
this.utils = await td.replaceEsm("../src/utils.js");
|
13
|
+
td.when(this.utils.readFileFromCurrentDir("test.file")).thenReturn("FILE CONTENT");
|
14
|
+
td.when(this.questionAnsweringMock.askQuestion(
|
15
|
+
'sloth-ASK',
|
16
|
+
td.matchers.anything(),
|
17
|
+
td.matchers.anything())
|
18
|
+
).thenDo(this.askQuestion);
|
19
|
+
});
|
20
|
+
|
21
|
+
it('Should call askQuestion with message', async function() {
|
22
|
+
const { askCommand } = await import("../src/commands/askCommand.js");
|
23
|
+
const program = new Command();
|
24
|
+
await askCommand(program, {});
|
25
|
+
await program.parseAsync(['na', 'na', 'ask', 'test message']);
|
26
|
+
td.verify(this.askQuestion('sloth-ASK', "INTERNAL PREAMBLE", "test message"));
|
27
|
+
});
|
28
|
+
|
29
|
+
it('Should call askQuestion with message and file content', async function() {
|
30
|
+
const { askCommand } = await import("../src/commands/askCommand.js");
|
31
|
+
const program = new Command();
|
32
|
+
await askCommand(program, {});
|
33
|
+
await program.parseAsync(['na', 'na', 'ask', 'test message', '-f', 'test.file']);
|
34
|
+
td.verify(this.askQuestion('sloth-ASK', "INTERNAL PREAMBLE", "test message\nFILE CONTENT"));
|
35
|
+
});
|
36
|
+
|
37
|
+
it('Should display help correctly', async function() {
|
38
|
+
const { askCommand } = await import("../src/commands/askCommand.js");
|
39
|
+
const program = new Command();
|
40
|
+
const testOutput = { text: '' };
|
41
|
+
|
42
|
+
program.configureOutput({
|
43
|
+
writeOut: (str) => testOutput.text += str,
|
44
|
+
writeErr: (str) => testOutput.text += str
|
45
|
+
});
|
46
|
+
|
47
|
+
await askCommand(program, {});
|
48
|
+
|
49
|
+
const commandUnderTest = program.commands.find(c => c.name() == 'ask');
|
50
|
+
expect(commandUnderTest).toBeDefined();
|
51
|
+
commandUnderTest.outputHelp();
|
52
|
+
|
53
|
+
// Verify help content
|
54
|
+
expect(testOutput.text).toContain('Ask a question');
|
55
|
+
expect(testOutput.text).toContain('<message>');
|
56
|
+
expect(testOutput.text).toContain('-f, --file');
|
57
|
+
});
|
58
|
+
});
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import {Command} from 'commander';
|
2
|
+
import * as td from 'testdouble';
|
3
|
+
|
4
|
+
describe('initCommand', function (){
|
5
|
+
|
6
|
+
beforeEach(async function() {
|
7
|
+
// Create a mock for createProjectConfig
|
8
|
+
this.createProjectConfig = td.function();
|
9
|
+
|
10
|
+
// Replace the config module
|
11
|
+
await td.replaceEsm("../src/config.js", {
|
12
|
+
createProjectConfig: this.createProjectConfig,
|
13
|
+
availableDefaultConfigs: ['vertexai', 'anthropic', 'groq'],
|
14
|
+
SLOTH_INTERNAL_PREAMBLE: '.gsloth.preamble.internal.md',
|
15
|
+
USER_PROJECT_REVIEW_PREAMBLE: '.gsloth.preamble.review.md',
|
16
|
+
USER_PROJECT_CONFIG_FILE: '.gsloth.config.js',
|
17
|
+
slothContext: {
|
18
|
+
installDir: '/mock/install/dir',
|
19
|
+
currentDir: '/mock/current/dir',
|
20
|
+
config: {},
|
21
|
+
session: {}
|
22
|
+
}
|
23
|
+
});
|
24
|
+
});
|
25
|
+
|
26
|
+
it('Should call createProjectConfig with the provided config type', async function() {
|
27
|
+
const { initCommand } = await import("../src/commands/initCommand.js");
|
28
|
+
const program = new Command();
|
29
|
+
await initCommand(program, {});
|
30
|
+
await program.parseAsync(['na', 'na', 'init', 'vertexai']);
|
31
|
+
td.verify(this.createProjectConfig('vertexai'));
|
32
|
+
});
|
33
|
+
|
34
|
+
it('Should display available config types in help', async function() {
|
35
|
+
const { initCommand } = await import("../src/commands/initCommand.js");
|
36
|
+
const program = new Command();
|
37
|
+
const testOutput = { text: '' };
|
38
|
+
|
39
|
+
program.configureOutput({
|
40
|
+
writeOut: (str) => testOutput.text += str,
|
41
|
+
writeErr: (str) => testOutput.text += str
|
42
|
+
});
|
43
|
+
|
44
|
+
await initCommand(program, {});
|
45
|
+
|
46
|
+
const commandUnderTest = program.commands.find(c => c.name() == 'init');
|
47
|
+
expect(commandUnderTest).toBeDefined();
|
48
|
+
commandUnderTest.outputHelp();
|
49
|
+
|
50
|
+
// Verify available config types are displayed
|
51
|
+
expect(testOutput.text).toContain('<type>');
|
52
|
+
expect(testOutput.text).toContain('(choices: "vertexai", "anthropic", "groq")');
|
53
|
+
});
|
54
|
+
});
|