gaunt-sloth-assistant 0.1.2 → 0.1.4

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.
@@ -0,0 +1,33 @@
1
+ name: Tests and Lint
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ pull_request:
7
+ branches: [ "main" ]
8
+
9
+ jobs:
10
+ test-and-lint:
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ matrix:
15
+ node-version: [22.x]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Use Node.js ${{ matrix.node-version }}
21
+ uses: actions/setup-node@v4
22
+ with:
23
+ node-version: ${{ matrix.node-version }}
24
+ cache: 'npm'
25
+
26
+ - name: Install dependencies
27
+ run: npm ci
28
+
29
+ - name: Run ESLint
30
+ run: npm run lint
31
+
32
+ - name: Run Tests
33
+ run: npm test
package/README.md CHANGED
@@ -1,7 +1,33 @@
1
1
  # Gaunt Sloth Assistant
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)
2
+ [![Tests and Lint](https://github.com/andruhon/gaunt-sloth-assistant/actions/workflows/ci.yml/badge.svg?event=push)](https://github.com/andruhon/gaunt-sloth-assistant/actions/workflows/ci.yml)
3
+
4
+ Gaunt Sloth Assistant is a Simplistic **command line AI assistant**
5
+ for software developers,
6
+ who wish to reduce cognitive load and time spending on **code reviews** (and pull request diff reviews).
7
+
8
+ Based on [Langchain.js](https://github.com/langchain-ai/langchainjs)
9
+
10
+ ## What GSloth does:
11
+ - Reviews code;
12
+ - Suggests bug fixes;
13
+ - Explains provided code
14
+ - Reviews Diffs provided with pipe (|);
15
+ - You can ask GSloth to review your own code before committing.
16
+ - Reviews Pull Requests (PRs);
17
+ - Fetches descriptions (requirements) from Jira;
18
+ - Answers questions about provided code;
19
+ - Writes code;
20
+ - Saves all responses to the project directory;
21
+ - Anything else you need, when combined with other command line tools.
22
+
23
+ ### To make GSloth work, you need an **API key** from some AI provider, such as:
24
+ - Google Vertex AI;
25
+ - Anthropic;
26
+ - Groq.
27
+
28
+ ## Primary Functions:
29
+
30
+ ### Review PR (Pull Request)
5
31
  To review PR by PR number:
6
32
 
7
33
  First make sure the official [GitHub cli (gh)](https://cli.github.com/) is installed
@@ -19,13 +45,8 @@ Review providing markdown file with requirements and notes.
19
45
  ```shell
20
46
  gsloth pr 42 -f PROJ-1234.md
21
47
  ```
22
- Jira integration is in [ROADMAP](ROADMAP.md).
23
- Currently, the easiest ***meaningful*** way to add jira description is to
24
- open Jira XML with "Export XML" in jira and to copy `<description></description>` block.
25
- This block contains HTML and AI understands it easily
26
- (most importantly it understand nested lists like ul>li).
27
48
 
28
- ## JIRA Integration
49
+ ### JIRA Integration
29
50
 
30
51
  When JIRA integration is configured, the JIRA issue text can be included alongside the diff for review.
31
52
  The project review preamble can be modified to reject a pull request immediately
@@ -39,31 +60,26 @@ supplies description of JIRA issue with number PP-4242:
39
60
  gsloth pr 42 PP-4242
40
61
  ```
41
62
 
42
- Example configuration setting up JIRA integration using a legacy API token.
63
+ Example configuration setting up JIRA integration using a legacy API token for both `review` and `pr` commands.
43
64
  Make sure you use your actual company domain in `baseUrl` and your personal legacy `token`.
44
65
 
45
66
  A legacy token can be acquired from `Atlassian Account Settings -> Security -> Create and manage API tokens`.
46
67
 
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: 'user.name@company.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
- }
68
+ ```json
69
+ {
70
+ "llm": {"type": "vertexai", "model": "gemini-2.5-pro-exp-03-25"},
71
+ "requirementsProvider": "jira-legacy",
72
+ "requirementsProviderConfig": {
73
+ "jira-legacy": {
74
+ "username": "user@yourcompany.com",
75
+ "token": "YOUR_JIRA_LEGACY_TOKEN",
76
+ "baseUrl": "https://yourcompany.atlassian.net/rest/api/2/issue/"
62
77
  }
78
+ }
63
79
  }
64
80
  ```
65
81
 
66
- ## Review any Diff
82
+ ### Review any Diff
67
83
  ```shell
68
84
  git --no-pager diff origin/master...yourgitcommithash | gsloth review
69
85
  ```
@@ -74,7 +90,7 @@ Review current local changes:
74
90
  git --no-pager diff | gsloth review
75
91
  ```
76
92
 
77
- ## Question Answering
93
+ ### Question Answering
78
94
  ```shell
79
95
  gsloth ask "which types of primitives are available in JavaScript?"
80
96
  ```
@@ -83,19 +99,25 @@ gsloth ask "which types of primitives are available in JavaScript?"
83
99
  gsloth ask "Please have a look at this file" -f index.js
84
100
  ```
85
101
 
102
+ Multiple files may be provided as well
103
+
104
+ ```shell
105
+ gsloth ask "Please have a look at these files" -f index.js test.js
106
+ ```
107
+
86
108
  ## Installation
87
109
 
88
110
  Tested with Node 22 LTS.
89
111
 
90
- ## NPM
112
+ ### NPM
91
113
  ```shell
92
114
  npm install gaunt-sloth-assistant -g
93
115
  ```
94
116
 
95
117
  ## Configuration
96
118
 
97
- > Gaunt Sloth currently only functions from the directory which has `.gsloth.config.js` and `.gsloth.preamble.review.md`.
98
- > Global configuration to invoke gsloth anywhere is in [ROADMAP](../ROADMAP.md).
119
+ > Gaunt Sloth currently only functions from the directory which has a configuration file (`.gsloth.config.js`, `.gsloth.config.json`, or `.gsloth.config.mjs`) and `.gsloth.preamble.review.md`.
120
+ > Global configuration to invoke gsloth anywhere is in [ROADMAP](ROADMAP.md).
99
121
 
100
122
  Configuration can be created with `gsloth init [vendor]` command.
101
123
  Currently, vertexai, anthropic and groq can be configured with `gsloth init [vendor]`.
@@ -111,22 +133,26 @@ gcloud auth application-default login
111
133
  ```
112
134
 
113
135
  ### Anthropic
136
+
114
137
  ```shell
115
138
  cd ./your-project
116
139
  gsloth init anthropic
117
140
  ```
118
- Make sure you either define `ANTHROPIC_API_KEY` environment variable or edit `.gsloth.config.js` and set up your key.
141
+
142
+ Make sure you either define `ANTHROPIC_API_KEY` environment variable or edit your configuration file and set up your key.
119
143
 
120
144
  ### Groq
121
145
  ```shell
122
146
  cd ./your-project
123
147
  gsloth init groq
124
148
  ```
125
- Make sure you either define `GROQ_API_KEY` environment variable or edit `.gsloth.config.js` and set up your key.
149
+ Make sure you either define `GROQ_API_KEY` environment variable or edit your configuration file and set up your key.
150
+
151
+ ### Other AI providers
152
+ Any other AI provider supported by Langchain.js can be configured with js [Config](./docs/CONFIGURATION.md).
126
153
 
127
154
  ## Building from repo
128
155
  See [DEVELOPMENT.md](./docs/DEVELOPMENT.md)
129
156
 
130
157
  ## License
131
158
  License is [MIT](https://opensource.org/license/mit). See [LICENSE](LICENSE)
132
-
package/ROADMAP.md CHANGED
@@ -1,28 +1,28 @@
1
1
  # Gaunt Sloth Assistant roadmap
2
2
 
3
-
4
3
  ## 1.0.0
5
4
  Doing the following below and making it work stably should be sufficient to call it version 1.
6
5
 
7
- ### ⌛Add tests and gain reasonable coverage
8
- ### Configure eslint for code quality checks
9
- ### Automate release process
10
- ### Add project init command
11
- Add a command to init certain model in certain project, for example `gsloth init gemini`
12
- or `gsloth init` and select one of the provided options.
13
- -[x] VertexAI
14
- -[x] Anthropic
15
- -[x] Groq
16
- -[ ] Local LLm
17
- ### Allow global configuration
18
- ### Streamline and stabilize configuration
19
- ### Add JIRA legacy token integration plugin
20
- ### Teach assistant to identify important files and include their contents into prompt
21
- The idea is to ask smaller model like flash to find important files from diff then pick them up and include into prompt.
22
- ### Teach assistant to access provided public web links
23
- ### Consider adding an option to always include certain source code files into prompt
24
- ### Test with Groq
25
- ### Add general chat command
6
+ ### Checklist
7
+
8
+ - [x] Add tests and gain reasonable coverage
9
+ - [x] Configure eslint for code quality checks
10
+ - [ ] Automate release process
11
+ - [x] Add project init command
12
+ Add a command to init certain model in certain project, for example `gsloth init gemini`
13
+ or `gsloth init` and select one of the provided options.
14
+ -[x] VertexAI
15
+ -[x] Anthropic
16
+ -[x] Groq
17
+ -[ ] Local LLm
18
+ - [ ] Allow global configuration
19
+ - [ ] Streamline and stabilize configuration
20
+ - [ ] Add JIRA legacy token integration plugin
21
+ - [ ] Teach assistant to identify important files and include their contents into prompt
22
+ The idea is to ask smaller model like flash to find important files from diff then pick them up and include into prompt.
23
+ - [ ] Teach assistant to access provided public web links
24
+ - [ ] Consider adding an option to always include certain source code files into prompt
25
+ - [ ] Add general chat command
26
26
 
27
27
  ## Extra stuff for later
28
28
 
package/UX-RESEARCH.md CHANGED
@@ -34,7 +34,7 @@ Arguments:
34
34
  prNumber PR number to review
35
35
 
36
36
  Options:
37
- -f, --file <file> Input file. Context of this file will be added BEFORE the
37
+ -f, --file <file> Input file. Content of this file will be added BEFORE the
38
38
  diff
39
39
  -h, --help display help for command
40
40
  ```
@@ -5,10 +5,13 @@ Proper preamble is a paramount for good inference.
5
5
  Check [.gsloth.preamble.review.md](../.gsloth.preamble.review.md) for example.
6
6
 
7
7
  Your project should have the following files in order for gsloth to function:
8
- - `.gsloth.config.js`
8
+ - Configuration file (one of):
9
+ - `.gsloth.config.js` (JavaScript module)
10
+ - `.gsloth.config.json` (JSON file)
11
+ - `.gsloth.config.mjs` (JavaScript module with explicit module extension)
9
12
  - `.gsloth.preamble.review.md`
10
13
 
11
- > Gaunt Sloth currently only functions from the directory which has `.gsloth.config.js` and `.gsloth.preamble.review.md`.
14
+ > Gaunt Sloth currently only functions from the directory which has one of the configuration files and `.gsloth.preamble.review.md`.
12
15
  > Global configuration to invoke gsloth anywhere is in [ROADMAP](../ROADMAP.md).
13
16
 
14
17
  ## Config initialization
@@ -28,17 +31,56 @@ gcloud auth application-default login
28
31
  cd ./your-project
29
32
  gsloth init anthropic
30
33
  ```
31
- Make sure you either define `ANTHROPIC_API_KEY` environment variable or edit `.gsloth.config.js` and set up your key.
34
+ Make sure you either define `ANTHROPIC_API_KEY` environment variable or edit your configuration file and set up your key.
32
35
 
33
36
  ### Groq
34
37
  ```shell
35
38
  cd ./your-project
36
39
  gsloth init groq
37
40
  ```
38
- Make sure you either define `GROQ_API_KEY` environment variable or edit `.gsloth.config.js` and set up your key.
41
+ Make sure you either define `GROQ_API_KEY` environment variable or edit your configuration file and set up your key.
39
42
 
40
43
  ## Examples of configuration for different providers
41
44
 
45
+ ### JSON Configuration (.gsloth.config.json)
46
+
47
+ JSON configuration is simpler but less flexible than JavaScript configuration. It should directly contain the configuration object.
48
+
49
+ **Example of .gsloth.config.json for Anthropic**
50
+ ```json
51
+ {
52
+ "llm": {
53
+ "type": "anthropic",
54
+ "apiKey": "your-api-key-here",
55
+ "model": "claude-3-5-sonnet-20241022"
56
+ }
57
+ }
58
+ ```
59
+
60
+ **Example of .gsloth.config.json for VertexAI**
61
+ ```json
62
+ {
63
+ "llm": {
64
+ "type": "vertexai",
65
+ "model": "gemini-2.5-pro-exp-03-25",
66
+ "temperature": 0
67
+ }
68
+ }
69
+ ```
70
+
71
+ **Example of .gsloth.config.json for Groq**
72
+ ```json
73
+ {
74
+ "llm": {
75
+ "type": "groq",
76
+ "model": "deepseek-r1-distill-llama-70b",
77
+ "apiKey": "your-api-key-here"
78
+ }
79
+ }
80
+ ```
81
+
82
+ ### JavaScript Configuration (.gsloth.config.js or .gsloth.config.mjs)
83
+
42
84
  **Example of .gsloth.config.js for Anthropic**
43
85
  ```javascript
44
86
  export async function configure(importFunction, global) {
@@ -68,6 +110,7 @@ export async function configure(importFunction, global) {
68
110
  return {
69
111
  llm: new vertexAi.ChatVertexAI({
70
112
  model: "gemini-2.5-pro-exp-03-25", // Consider checking for latest recommended model versions
113
+ // API Key from AI Studio should also work
71
114
  temperature: 0,
72
115
  //// Other parameters might be relevant depending on Vertex AI API updates.
73
116
  //// The project is not in the interface, but it is in documentation and it seems to work.
@@ -94,7 +137,54 @@ export async function configure(importFunction, global) {
94
137
  }
95
138
  ```
96
139
 
97
- ## Using other providers
140
+ ## Using other AI providers
98
141
 
99
142
  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).
100
- See [Langchain documentation](https://js.langchain.com/docs/tutorials/llm_chain/) for more details.
143
+ See [Langchain documentation](https://js.langchain.com/docs/tutorials/llm_chain/) for more details.
144
+
145
+ ## Content providers
146
+
147
+ ### JIRA
148
+
149
+ Example configuration setting up JIRA integration using a legacy API token for both `review` and `pr` commands.
150
+ Make sure you use your actual company domain in `baseUrl` and your personal legacy `token`.
151
+
152
+ A legacy token can be acquired from `Atlassian Account Settings -> Security -> Create and manage API tokens`.
153
+
154
+ JSON:
155
+
156
+ ```json
157
+ {
158
+ "llm": {"type": "vertexai", "model": "gemini-2.5-pro-exp-03-25"},
159
+ "requirementsProvider": "jira-legacy",
160
+ "requirementsProviderConfig": {
161
+ "jira-legacy": {
162
+ "username": "username@yourcompany.com",
163
+ "token": "YOUR_JIRA_LEGACY_TOKEN",
164
+ "baseUrl": "https://yourcompany.atlassian.net/rest/api/2/issue/"
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ JavaScript:
171
+
172
+ ```javascript
173
+ export async function configure(importFunction, global) {
174
+ const vertexAi = await importFunction('@langchain/google-vertexai');
175
+ return {
176
+ llm: new vertexAi.ChatVertexAI({
177
+ model: "gemini-2.5-pro-exp-03-25"
178
+ }),
179
+ requirementsProvider: 'jira-legacy',
180
+ requirementsProviderConfig: {
181
+ 'jira-legacy': {
182
+ username: 'username@yourcompany.com', // Your Jira username/email
183
+ token: 'YOUR_JIRA_LEGACY_TOKEN', // Replace with your real Jira API token
184
+ baseUrl: 'https://yourcompany.atlassian.net/rest/api/2/issue/' // Your Jira instance base URL
185
+ }
186
+ }
187
+ }
188
+ }
189
+ ```
190
+
@@ -2,8 +2,20 @@
2
2
 
3
3
  ## GitHub (master)
4
4
 
5
+ Install dev version globally
6
+
5
7
  ```shell
6
8
  git clone https://github.com/andruhon/gaunt-sloth.git
7
9
  npm install
8
10
  npm install -g ./
9
11
  ```
12
+ ## Testing
13
+
14
+ Unit tests are implemented with [Jasmine](https://github.com/jasmine/jasmine)
15
+ and [testdouble.js](https://github.com/testdouble/testdouble.js).
16
+
17
+ Running unit tests:
18
+
19
+ ```shell
20
+ npm run test
21
+ ```
@@ -0,0 +1,38 @@
1
+ import js from "@eslint/js";
2
+ import { defineConfig } from "eslint/config";
3
+ import jasmine from "eslint-plugin-jasmine"
4
+ import globals from "globals";
5
+
6
+ const { setTimeout, setInterval, clearInterval, Buffer, fetch } = globals.node;
7
+
8
+ export default defineConfig([
9
+ {
10
+ files: ["src/**/*.js"],
11
+ plugins: {
12
+ js,
13
+ },
14
+ extends: ["js/recommended"],
15
+ rules: {
16
+ semi: "error",
17
+ "eol-last": "error"
18
+ },
19
+ languageOptions: {
20
+ globals: {
21
+ setTimeout,
22
+ setInterval,
23
+ clearInterval,
24
+ Buffer,
25
+ fetch
26
+ },
27
+ },
28
+ },
29
+ {
30
+ files: ["spec/**/*.js"],
31
+ plugins: { jasmine },
32
+ extends: ["jasmine/recommended"],
33
+ rules: {
34
+ semi: "error",
35
+ "eol-last": "error",
36
+ }
37
+ },
38
+ ]);
package/index.js CHANGED
@@ -7,11 +7,13 @@ import { initCommand } from "./src/commands/initCommand.js";
7
7
  import { askCommand } from "./src/commands/askCommand.js";
8
8
  import { slothContext } from "./src/config.js";
9
9
  import { getSlothVersion, readStdin } from "./src/utils.js";
10
+ import {getCurrentDir, getInstallDir, setInstallDir} from "./src/systemUtils.js";
10
11
 
11
12
  const program = new Command();
12
13
 
13
- slothContext.currentDir = process.cwd();
14
- slothContext.installDir = dirname(fileURLToPath(import.meta.url))
14
+ setInstallDir(dirname(fileURLToPath(import.meta.url)));
15
+ slothContext.currentDir = getCurrentDir();
16
+ slothContext.installDir = getInstallDir();
15
17
 
16
18
  program
17
19
  .name('gsloth')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gaunt-sloth-assistant",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "author": "Andrew Kondratev",
@@ -12,14 +12,14 @@
12
12
  "npm": ">=10.9.0"
13
13
  },
14
14
  "scripts": {
15
- "test": "jasmine"
15
+ "test": "jasmine",
16
+ "lint": "eslint"
16
17
  },
17
18
  "bin": {
18
19
  "gsloth": "index.js",
19
20
  "gth": "index.js"
20
21
  },
21
22
  "dependencies": {
22
- "@eslint/js": "^9.25.0",
23
23
  "@langchain/anthropic": "^0.3.18",
24
24
  "@langchain/core": "^0.3.45",
25
25
  "@langchain/google-vertexai": "^0.2.4",
@@ -31,6 +31,10 @@
31
31
  "uuid": "^11.1.0"
32
32
  },
33
33
  "devDependencies": {
34
+ "@eslint/js": "^9.25.1",
35
+ "eslint": "^9.25.1",
36
+ "eslint-plugin-jasmine": "^4.2.2",
37
+ "globals": "^16.0.0",
34
38
  "jasmine": "^5.6.0",
35
39
  "testdouble": "^3.20.2"
36
40
  }
@@ -18,5 +18,5 @@ export async function configure(importFunction, global) {
18
18
  test: 'example'
19
19
  }
20
20
  }
21
- }
21
+ };
22
22
  }
@@ -0,0 +1,25 @@
1
+ {
2
+ "llm": {
3
+ "type": "fake",
4
+ "responses": ["First LLM message", "Second LLM message"]
5
+ },
6
+ "requirementsProviderConfig": {
7
+ "jira-legacy": {
8
+ "username": "user.name@company.com",
9
+ "token": "YoUrToKeN",
10
+ "baseUrl": "https://company.atlassian.net/rest/api/2/issue/"
11
+ }
12
+ },
13
+ "requirementsProvider": "file",
14
+ "contentProvider": "somethingSpecial",
15
+ "contentProviderConfig": {
16
+ "somethingSpecial": {
17
+ "test": "example"
18
+ }
19
+ },
20
+ "commands": {
21
+ "pr": {
22
+ "requirementsProvider": "jira-legacy"
23
+ }
24
+ }
25
+ }
@@ -4,13 +4,36 @@ import * as td from 'testdouble';
4
4
  describe('askCommand', function (){
5
5
 
6
6
  beforeEach(async function() {
7
+ td.reset();
7
8
  this.askQuestion = td.function();
8
9
  this.prompt = await td.replaceEsm("../src/prompt.js");
9
10
  td.when(this.prompt.readInternalPreamble()).thenReturn("INTERNAL PREAMBLE");
10
11
  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");
12
+ await td.replaceEsm("../src/config.js", {
13
+ SLOTH_INTERNAL_PREAMBLE: '.gsloth.preamble.internal.md',
14
+ USER_PROJECT_REVIEW_PREAMBLE: '.gsloth.preamble.review.md',
15
+ slothContext: {
16
+ config: {},
17
+ currentDir: '/mock/current/dir'
18
+ },
19
+ initConfig: td.function()
20
+ });
21
+ const readFileFromCurrentDir = td.function();
22
+ const readMultipleFilesFromCurrentDir = td.function();
23
+ const extractLastMessageContent = td.function();
24
+ const toFileSafeString = td.function();
25
+ const fileSafeLocalDate = td.function();
26
+ this.utilsMock = {
27
+ readFileFromCurrentDir,
28
+ readMultipleFilesFromCurrentDir,
29
+ ProgressIndicator: td.constructor(),
30
+ extractLastMessageContent,
31
+ toFileSafeString,
32
+ fileSafeLocalDate
33
+ };
34
+ await td.replaceEsm("../src/utils.js", this.utilsMock);
35
+ td.when(this.utilsMock.readFileFromCurrentDir("test.file")).thenReturn("FILE CONTENT");
36
+ td.when(this.utilsMock.readMultipleFilesFromCurrentDir(["test.file"])).thenReturn("test.file:\n```\nFILE CONTENT\n```");
14
37
  td.when(this.questionAnsweringMock.askQuestion(
15
38
  'sloth-ASK',
16
39
  td.matchers.anything(),
@@ -31,7 +54,17 @@ describe('askCommand', function (){
31
54
  const program = new Command();
32
55
  await askCommand(program, {});
33
56
  await program.parseAsync(['na', 'na', 'ask', 'test message', '-f', 'test.file']);
34
- td.verify(this.askQuestion('sloth-ASK', "INTERNAL PREAMBLE", "test message\nFILE CONTENT"));
57
+ td.verify(this.askQuestion('sloth-ASK', "INTERNAL PREAMBLE", "test message\ntest.file:\n```\nFILE CONTENT\n```"));
58
+ });
59
+
60
+ it('Should call askQuestion with message and multiple file contents', async function() {
61
+ const { askCommand } = await import("../src/commands/askCommand.js");
62
+ const program = new Command();
63
+ await askCommand(program, {});
64
+ td.when(this.utilsMock.readMultipleFilesFromCurrentDir(["test.file", "test2.file"]))
65
+ .thenReturn("test.file:\n```\nFILE CONTENT\n```\n\ntest2.file:\n```\nFILE2 CONTENT\n```");
66
+ await program.parseAsync(['na', 'na', 'ask', 'test message', '-f', 'test.file', 'test2.file']);
67
+ td.verify(this.askQuestion('sloth-ASK', "INTERNAL PREAMBLE", "test message\ntest.file:\n```\nFILE CONTENT\n```\n\ntest2.file:\n```\nFILE2 CONTENT\n```"));
35
68
  });
36
69
 
37
70
  it('Should display help correctly', async function() {
@@ -47,6 +80,7 @@ describe('askCommand', function (){
47
80
  await askCommand(program, {});
48
81
 
49
82
  const commandUnderTest = program.commands.find(c => c.name() == 'ask');
83
+
50
84
  expect(commandUnderTest).toBeDefined();
51
85
  commandUnderTest.outputHelp();
52
86
 
@@ -55,4 +89,4 @@ describe('askCommand', function (){
55
89
  expect(testOutput.text).toContain('<message>');
56
90
  expect(testOutput.text).toContain('-f, --file');
57
91
  });
58
- });
92
+ });