gaunt-sloth-assistant 0.1.5 → 0.3.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.
Files changed (145) hide show
  1. package/{.gsloth.preamble.review.md → .gsloth.guidelines.md} +0 -8
  2. package/.gsloth.review.md +7 -0
  3. package/.prettierrc.json +9 -0
  4. package/README.md +177 -158
  5. package/ROADMAP.md +1 -1
  6. package/dist/commands/askCommand.d.ts +6 -0
  7. package/dist/commands/askCommand.js +27 -0
  8. package/dist/commands/askCommand.js.map +1 -0
  9. package/dist/commands/initCommand.d.ts +6 -0
  10. package/dist/commands/initCommand.js +16 -0
  11. package/dist/commands/initCommand.js.map +1 -0
  12. package/dist/commands/reviewCommand.d.ts +3 -0
  13. package/dist/commands/reviewCommand.js +142 -0
  14. package/dist/commands/reviewCommand.js.map +1 -0
  15. package/dist/config.d.ts +84 -0
  16. package/dist/config.js +180 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/configs/anthropic.d.ts +4 -0
  19. package/{src → dist}/configs/anthropic.js +45 -48
  20. package/dist/configs/anthropic.js.map +1 -0
  21. package/dist/configs/fake.d.ts +3 -0
  22. package/{src → dist}/configs/fake.js +11 -14
  23. package/dist/configs/fake.js.map +1 -0
  24. package/dist/configs/groq.d.ts +4 -0
  25. package/{src → dist}/configs/groq.js +10 -13
  26. package/dist/configs/groq.js.map +1 -0
  27. package/dist/configs/types.d.ts +14 -0
  28. package/dist/configs/types.js +2 -0
  29. package/dist/configs/types.js.map +1 -0
  30. package/dist/configs/vertexai.d.ts +4 -0
  31. package/{src → dist}/configs/vertexai.js +44 -47
  32. package/dist/configs/vertexai.js.map +1 -0
  33. package/dist/consoleUtils.d.ts +6 -0
  34. package/{src → dist}/consoleUtils.js +10 -15
  35. package/dist/consoleUtils.js.map +1 -0
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +26 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/llmUtils.d.ts +4 -0
  40. package/dist/llmUtils.js +39 -0
  41. package/dist/llmUtils.js.map +1 -0
  42. package/dist/modules/questionAnsweringModule.d.ts +7 -0
  43. package/dist/modules/questionAnsweringModule.js +33 -0
  44. package/dist/modules/questionAnsweringModule.js.map +1 -0
  45. package/dist/modules/reviewModule.d.ts +1 -0
  46. package/dist/modules/reviewModule.js +29 -0
  47. package/dist/modules/reviewModule.js.map +1 -0
  48. package/dist/modules/types.d.ts +18 -0
  49. package/dist/modules/types.js +2 -0
  50. package/dist/modules/types.js.map +1 -0
  51. package/dist/prompt.d.ts +8 -0
  52. package/dist/prompt.js +45 -0
  53. package/dist/prompt.js.map +1 -0
  54. package/dist/providers/file.d.ts +8 -0
  55. package/dist/providers/file.js +20 -0
  56. package/dist/providers/file.js.map +1 -0
  57. package/dist/providers/ghPrDiffProvider.d.ts +8 -0
  58. package/dist/providers/ghPrDiffProvider.js +16 -0
  59. package/dist/providers/ghPrDiffProvider.js.map +1 -0
  60. package/dist/providers/jiraIssueLegacyAccessTokenProvider.d.ts +8 -0
  61. package/dist/providers/jiraIssueLegacyAccessTokenProvider.js +62 -0
  62. package/dist/providers/jiraIssueLegacyAccessTokenProvider.js.map +1 -0
  63. package/dist/providers/jiraIssueLegacyProvider.d.ts +8 -0
  64. package/dist/providers/jiraIssueLegacyProvider.js +74 -0
  65. package/dist/providers/jiraIssueLegacyProvider.js.map +1 -0
  66. package/dist/providers/jiraIssueProvider.d.ts +11 -0
  67. package/dist/providers/jiraIssueProvider.js +96 -0
  68. package/dist/providers/jiraIssueProvider.js.map +1 -0
  69. package/dist/providers/text.d.ts +8 -0
  70. package/dist/providers/text.js +10 -0
  71. package/dist/providers/text.js.map +1 -0
  72. package/dist/providers/types.d.ts +21 -0
  73. package/dist/providers/types.js +2 -0
  74. package/dist/providers/types.js.map +1 -0
  75. package/dist/systemUtils.d.ts +32 -0
  76. package/dist/systemUtils.js +70 -0
  77. package/dist/systemUtils.js.map +1 -0
  78. package/dist/utils.d.ts +49 -0
  79. package/dist/utils.js +192 -0
  80. package/dist/utils.js.map +1 -0
  81. package/docs/CONFIGURATION.md +99 -10
  82. package/docs/RELEASE-HOWTO.md +7 -1
  83. package/eslint.config.js +99 -21
  84. package/gth-ASK-2025-05-16T14-11-39.md +3 -0
  85. package/gth-ASK-2025-05-16T14-18-27.md +3 -0
  86. package/gth-ASK-2025-05-16T14-18-56.md +1 -0
  87. package/gth-ASK-2025-05-16T14-41-20.md +3 -0
  88. package/gth-ASK-2025-05-16T14-43-31.md +51 -0
  89. package/gth-ASK-2025-05-16T16-05-52.md +62 -0
  90. package/gth-DIFF-review-2025-05-16T16-07-53.md +56 -0
  91. package/gth-DIFF-review-2025-05-16T16-18-55.md +292 -0
  92. package/index.js +10 -27
  93. package/package.json +26 -15
  94. package/src/commands/askCommand.ts +35 -0
  95. package/src/commands/initCommand.ts +19 -0
  96. package/src/commands/reviewCommand.ts +223 -0
  97. package/src/config.ts +269 -0
  98. package/src/configs/anthropic.ts +57 -0
  99. package/src/configs/fake.ts +15 -0
  100. package/src/configs/groq.ts +54 -0
  101. package/src/configs/vertexai.ts +53 -0
  102. package/src/consoleUtils.ts +33 -0
  103. package/src/index.ts +30 -0
  104. package/src/llmUtils.ts +54 -0
  105. package/src/modules/questionAnsweringModule.ts +44 -0
  106. package/src/modules/reviewModule.ts +31 -0
  107. package/src/modules/types.ts +23 -0
  108. package/src/prompt.ts +54 -0
  109. package/src/providers/file.ts +24 -0
  110. package/src/providers/ghPrDiffProvider.ts +20 -0
  111. package/src/providers/jiraIssueLegacyProvider.ts +103 -0
  112. package/src/providers/jiraIssueProvider.ts +133 -0
  113. package/src/providers/text.ts +14 -0
  114. package/src/providers/types.ts +24 -0
  115. package/src/systemUtils.ts +90 -0
  116. package/src/utils.ts +232 -0
  117. package/tsconfig.json +24 -0
  118. package/vitest.config.ts +13 -0
  119. package/.eslint.config.mjs +0 -72
  120. package/.github/dependabot.yml +0 -11
  121. package/.github/workflows/ci.yml +0 -33
  122. package/spec/.gsloth.config.js +0 -22
  123. package/spec/.gsloth.config.json +0 -25
  124. package/spec/askCommand.spec.js +0 -92
  125. package/spec/config.spec.js +0 -421
  126. package/spec/initCommand.spec.js +0 -55
  127. package/spec/predefinedConfigs.spec.js +0 -100
  128. package/spec/questionAnsweringModule.spec.js +0 -137
  129. package/spec/reviewCommand.spec.js +0 -222
  130. package/spec/reviewModule.spec.js +0 -28
  131. package/spec/support/jasmine.mjs +0 -14
  132. package/src/commands/askCommand.js +0 -27
  133. package/src/commands/initCommand.js +0 -17
  134. package/src/commands/reviewCommand.js +0 -154
  135. package/src/config.js +0 -177
  136. package/src/modules/questionAnsweringModule.js +0 -82
  137. package/src/modules/reviewModule.js +0 -70
  138. package/src/prompt.js +0 -34
  139. package/src/providers/file.js +0 -19
  140. package/src/providers/ghPrDiffProvider.js +0 -11
  141. package/src/providers/jiraIssueLegacyAccessTokenProvider.js +0 -84
  142. package/src/providers/text.js +0 -6
  143. package/src/systemUtils.js +0 -32
  144. package/src/utils.js +0 -173
  145. /package/{.gsloth.preamble.internal.md → .gsloth.backstory.md} +0 -0
package/src/config.js DELETED
@@ -1,177 +0,0 @@
1
- import path from "node:path/posix";
2
- import {v4 as uuidv4} from "uuid";
3
- import {displayDebug, displayError, displayInfo, displayWarning} from "./consoleUtils.js";
4
- import {importExternalFile, writeFileIfNotExistsWithMessages} from "./utils.js";
5
- import {existsSync, readFileSync} from "node:fs";
6
- import {exit, getCurrentDir} from "./systemUtils.js";
7
-
8
- export const USER_PROJECT_CONFIG_JS = '.gsloth.config.js';
9
- export const USER_PROJECT_CONFIG_JSON = '.gsloth.config.json';
10
- export const USER_PROJECT_CONFIG_MJS = '.gsloth.config.mjs';
11
- export const SLOTH_INTERNAL_PREAMBLE = '.gsloth.preamble.internal.md';
12
- export const USER_PROJECT_REVIEW_PREAMBLE = '.gsloth.preamble.review.md';
13
-
14
- export const availableDefaultConfigs = ['vertexai', 'anthropic', 'groq'];
15
-
16
- export const DEFAULT_CONFIG = {
17
- llm: undefined,
18
- contentProvider: "file",
19
- requirementsProvider: "file",
20
- commands: {
21
- pr: {
22
- contentProvider: "gh"
23
- }
24
- }
25
- };
26
-
27
- export const slothContext = {
28
- /**
29
- * Directory where the sloth is installed.
30
- * index.js should set up this.
31
- */
32
- installDir: null,
33
- /**
34
- * Directory where the sloth has been invoked. Usually user's project root.
35
- * index.js should set up this.
36
- */
37
- currentDir: null,
38
- config: DEFAULT_CONFIG,
39
- stdin: '',
40
- session: {configurable: {thread_id: uuidv4()}}
41
- };
42
-
43
- export async function initConfig() {
44
- const currentDir = getCurrentDir();
45
- const jsonConfigPath = path.join(currentDir, USER_PROJECT_CONFIG_JSON);
46
- const jsConfigPath = path.join(currentDir, USER_PROJECT_CONFIG_JS);
47
- const mjsConfigPath = path.join(currentDir, USER_PROJECT_CONFIG_MJS);
48
-
49
- // Try loading JSON config file first
50
- if (existsSync(jsonConfigPath)) {
51
- try {
52
- const jsonConfig = JSON.parse(readFileSync(jsonConfigPath, 'utf8'));
53
-
54
- // If the config has an LLM with a type, create the appropriate LLM instance
55
- if (jsonConfig.llm && jsonConfig.llm.type) {
56
- await tryJsonConfig(jsonConfig);
57
- } else {
58
- slothContext.config = {...slothContext.config, ...jsonConfig};
59
- }
60
- } catch (e) {
61
- displayDebug(e);
62
- displayError(`Failed to read config from ${USER_PROJECT_CONFIG_JSON}, will try other formats.`);
63
- // Continue to try other formats
64
- return tryJsConfig();
65
- }
66
- } else {
67
- // JSON config not found, try JS
68
- return tryJsConfig();
69
- }
70
-
71
- // Helper function to try loading JS config
72
- async function tryJsConfig() {
73
- if (existsSync(jsConfigPath)) {
74
- return importExternalFile(jsConfigPath)
75
- .then((i) => i.configure((module) => import(module)))
76
- .then((config) => {
77
- slothContext.config = {...slothContext.config, ...config};
78
- })
79
- .catch((e) => {
80
- displayDebug(e);
81
- displayError(`Failed to read config from ${USER_PROJECT_CONFIG_JS}, will try other formats.`);
82
- // Continue to try other formats
83
- return tryMjsConfig();
84
- });
85
- } else {
86
- // JS config not found, try MJS
87
- return tryMjsConfig();
88
- }
89
- }
90
-
91
- // Helper function to try loading MJS config
92
- async function tryMjsConfig() {
93
- if (existsSync(mjsConfigPath)) {
94
- return importExternalFile(mjsConfigPath)
95
- .then((i) => i.configure((module) => import(module)))
96
- .then((config) => {
97
- slothContext.config = {...slothContext.config, ...config};
98
- })
99
- .catch((e) => {
100
- displayDebug(e);
101
- displayError(`Failed to read config from ${USER_PROJECT_CONFIG_MJS}.`);
102
- displayError(`No valid configuration found. Please create a valid configuration file.`);
103
- exit();
104
- });
105
- } else {
106
- // No config files found
107
- displayError(
108
- 'No configuration file found. Please create one of: '
109
- + `${USER_PROJECT_CONFIG_JSON}, ${USER_PROJECT_CONFIG_JS}, or ${USER_PROJECT_CONFIG_MJS} `
110
- + 'in your project directory.'
111
- );
112
- exit();
113
- }
114
- }
115
- }
116
-
117
- // Process JSON LLM config by creating the appropriate LLM instance
118
- export async function tryJsonConfig(jsonConfig) {
119
- const llmConfig = jsonConfig.llm;
120
- const llmType = llmConfig.type.toLowerCase();
121
-
122
- // Check if the LLM type is in availableDefaultConfigs
123
- if (!availableDefaultConfigs.includes(llmType)) {
124
- displayError(`Unsupported LLM type: ${llmType}. Available types are: ${availableDefaultConfigs.join(', ')}`);
125
- slothContext.config = {...slothContext.config, ...jsonConfig};
126
- return;
127
- }
128
-
129
- try {
130
- // Import the appropriate config module based on the LLM type
131
- try {
132
- const configModule = await import(`./configs/${llmType}.js`);
133
- if (configModule.processJsonConfig) {
134
- jsonConfig.llm = await configModule.processJsonConfig(llmConfig);
135
- } else {
136
- displayWarning(`Config module for ${llmType} does not have processJsonConfig function.`);
137
- }
138
- } catch (importError) {
139
- displayDebug(importError);
140
- displayWarning(`Could not import config module for ${llmType}.`);
141
- }
142
- } catch (error) {
143
- displayDebug(error);
144
- displayError(`Error creating LLM instance for type ${llmType}.`);
145
- }
146
-
147
- slothContext.config = {...slothContext.config, ...jsonConfig};
148
- }
149
-
150
- export async function createProjectConfig(configType) {
151
- displayInfo(`Setting up your project\n`);
152
- writeProjectReviewPreamble();
153
- displayWarning(`Make sure you add as much detail as possible to your ${USER_PROJECT_REVIEW_PREAMBLE}.\n`);
154
-
155
- // Check if the config type is in availableDefaultConfigs
156
- if (!availableDefaultConfigs.includes(configType)) {
157
- displayError(`Unsupported config type: ${configType}. Available types are: ${availableDefaultConfigs.join(', ')}`);
158
- exit(1);
159
- return;
160
- }
161
-
162
- displayInfo(`Creating project config for ${configType}`);
163
- const vendorConfig = await import(`./configs/${configType}.js`);
164
- vendorConfig.init(USER_PROJECT_CONFIG_JSON, slothContext);
165
- }
166
-
167
- export function writeProjectReviewPreamble() {
168
- let reviewPreamblePath = path.join(slothContext.currentDir, USER_PROJECT_REVIEW_PREAMBLE);
169
- writeFileIfNotExistsWithMessages(
170
- reviewPreamblePath,
171
- 'You are doing generic code review.\n'
172
- + ' Important! Please remind user to prepare proper AI preamble in'
173
- + USER_PROJECT_REVIEW_PREAMBLE
174
- + ' for this project. Use decent amount of ⚠️ to highlight lack of config.'
175
- + ' Explicitly mention `' + USER_PROJECT_REVIEW_PREAMBLE + '`.'
176
- );
177
- }
@@ -1,82 +0,0 @@
1
- import {END, MemorySaver, MessagesAnnotation, START, StateGraph,} from "@langchain/langgraph";
2
- import {writeFileSync} from "node:fs";
3
- import * as path from "node:path";
4
- import {slothContext} from "../config.js";
5
- import {display, displayError, displaySuccess} from "../consoleUtils.js";
6
- import {extractLastMessageContent, fileSafeLocalDate, ProgressIndicator, toFileSafeString} from "../utils.js";
7
- import {getCurrentDir} from "../systemUtils.js";
8
-
9
- /**
10
- * Ask a question and get an answer from the LLM
11
- * @param {string} source - The source of the question (used for file naming)
12
- * @param {string} preamble - The preamble to send to the LLM
13
- * @param {string} content - The content of the question
14
- */
15
- export async function askQuestion(source, preamble, content) {
16
- const progressIndicator = new ProgressIndicator("Thinking.");
17
- const outputContent = await askQuestionInner(slothContext, () => progressIndicator.indicate(), preamble, content);
18
- const filePath = path.resolve(getCurrentDir(), toFileSafeString(source)+'-'+fileSafeLocalDate()+".md");
19
- display(`\nwriting ${filePath}`);
20
- // TODO highlight LLM output with something like Prism.JS
21
- display('\n' + outputContent);
22
- try {
23
- writeFileSync(filePath, outputContent);
24
- displaySuccess(`This report can be found in ${filePath}`);
25
- } catch (error) {
26
- displayError(`Failed to write answer to file: ${filePath}`);
27
- displayError(error.message);
28
- // TODO Consider if we want to exit or just log the error
29
- // exit(1);
30
- }
31
- }
32
-
33
- /**
34
- * Inner function to ask a question and get an answer from the LLM
35
- * @param {Object} context - The context object
36
- * @param {Function} indicateProgress - Function to indicate progress
37
- * @param {string} preamble - The preamble to send to the LLM
38
- * @param {string} content - The content of the question
39
- * @returns {string} The answer from the LLM
40
- */
41
- export async function askQuestionInner(context, indicateProgress, preamble, content) {
42
- // This node receives the current state (messages) and invokes the LLM
43
- const callModel = async (state) => {
44
- // state.messages will contain the list including the system preamble and user diff
45
- const response = await context.config.llm.invoke(state.messages);
46
- // MessagesAnnotation expects the node to return the new message(s) to be added to the state.
47
- // Wrap the response in an array if it's a single message object.
48
- return { messages: response };
49
- };
50
-
51
- // Define the graph structure with MessagesAnnotation state
52
- const workflow = new StateGraph(MessagesAnnotation)
53
- // Define the node and edge
54
- .addNode("model", callModel)
55
- .addEdge(START, "model") // Start at the 'model' node
56
- .addEdge("model", END); // End after the 'model' node completes
57
-
58
- // Set up memory (optional but good practice for potential future multi-turn interactions)
59
- const memory = new MemorySaver();
60
-
61
- // Compile the workflow into a runnable app
62
- const app = workflow.compile({ checkpointer: memory });
63
-
64
- // Construct the initial the messages including the preamble as a system message
65
- const messages = [
66
- {
67
- role: "system",
68
- content: preamble, // The preamble goes here
69
- },
70
- {
71
- role: "user",
72
- content, // The question goes here
73
- },
74
- ];
75
-
76
- indicateProgress();
77
- // TODO create proper progress indicator for async tasks.
78
- const progress = setInterval(() => indicateProgress(), 1000);
79
- const output = await app.invoke({messages}, context.session);
80
- clearInterval(progress);
81
- return extractLastMessageContent(output);
82
- }
@@ -1,70 +0,0 @@
1
- import {END, MemorySaver, MessagesAnnotation, START, StateGraph,} from "@langchain/langgraph";
2
- import {writeFileSync} from "node:fs";
3
- import path from "node:path";
4
- import {slothContext} from "../config.js";
5
- import {display, displayDebug, displayError, displaySuccess} from "../consoleUtils.js";
6
- import {extractLastMessageContent, fileSafeLocalDate, ProgressIndicator, toFileSafeString} from "../utils.js";
7
- import {getCurrentDir, stdout} from "../systemUtils.js";
8
-
9
- export async function review(source, preamble, diff) {
10
- const progressIndicator = new ProgressIndicator("Reviewing.");
11
- const outputContent = await reviewInner(slothContext, () => progressIndicator.indicate(), preamble, diff);
12
- const filePath = path.resolve(getCurrentDir(), toFileSafeString(source)+'-'+fileSafeLocalDate()+".md");
13
- stdout.write("\n");
14
- display(`writing ${filePath}`);
15
- stdout.write("\n");
16
- // TODO highlight LLM output with something like Prism.JS (maybe system emoj are enough ✅⚠️❌)
17
- display(outputContent);
18
- try {
19
- writeFileSync(filePath, outputContent);
20
- displaySuccess(`This report can be found in ${filePath}`);
21
- } catch (error) {
22
- displayDebug(error);
23
- displayError(`Failed to write review to file: ${filePath}`);
24
- // Consider if you want to exit or just log the error
25
- // exit(1);
26
- }
27
- }
28
-
29
- export async function reviewInner(context, indicateProgress, preamble, diff) {
30
- // This node receives the current state (messages) and invokes the LLM
31
- const callModel = async (state) => {
32
- // state.messages will contain the list including the system preamble and user diff
33
- const response = await context.config.llm.invoke(state.messages);
34
- // MessagesAnnotation expects the node to return the new message(s) to be added to the state.
35
- // Wrap the response in an array if it's a single message object.
36
- return { messages: response };
37
- };
38
-
39
- // Define the graph structure with MessagesAnnotation state
40
- const workflow = new StateGraph(MessagesAnnotation)
41
- // Define the node and edge
42
- .addNode("model", callModel)
43
- .addEdge(START, "model") // Start at the 'model' node
44
- .addEdge("model", END); // End after the 'model' node completes
45
-
46
- // Set up memory (optional but good practice for potential future multi-turn interactions)
47
- const memory = new MemorySaver(); // TODO extract to config
48
-
49
- // Compile the workflow into a runnable app
50
- const app = workflow.compile({ checkpointer: memory });
51
-
52
- // Construct the initial the messages including the preamble as a system message
53
- const messages = [
54
- {
55
- role: "system",
56
- content: preamble, // The preamble goes here
57
- },
58
- {
59
- role: "user",
60
- content: diff, // The code diff goes here
61
- },
62
- ];
63
-
64
- indicateProgress();
65
- // TODO create proper progress indicator for async tasks.
66
- const progress = setInterval(() => indicateProgress(), 1000);
67
- const output = await app.invoke({messages}, context.session);
68
- clearInterval(progress);
69
- return extractLastMessageContent(output);
70
- }
package/src/prompt.js DELETED
@@ -1,34 +0,0 @@
1
- import {resolve} from "node:path";
2
- import {SLOTH_INTERNAL_PREAMBLE, slothContext} from "./config.js";
3
- import {readFileSyncWithMessages, spawnCommand} from "./utils.js";
4
- import { displayError } from "./consoleUtils.js";
5
- import { exit } from "./systemUtils.js";
6
-
7
- export function readInternalPreamble() {
8
- const filePath = resolve(slothContext.installDir, SLOTH_INTERNAL_PREAMBLE);
9
- return readFileSyncWithMessages(filePath, "Error reading internal preamble file at:");
10
- }
11
-
12
- export function readPreamble(preambleFilename) {
13
- const filePath = resolve(slothContext.currentDir, preambleFilename);
14
- return readFileSyncWithMessages(
15
- filePath,
16
- "Error reading preamble file at:",
17
- "Consider running `gsloth init` to set up your project. Check `gsloth init --help` to see options."
18
- );
19
- }
20
-
21
- /**
22
- * This function expects https://cli.github.com/ to be installed and authenticated.
23
- * It does something like `gh pr diff 42`
24
- */
25
- export async function getPrDiff(pr) {
26
- // TODO makes sense to check if gh is available and authenticated
27
- try {
28
- return await spawnCommand('gh', ['pr', 'diff', pr], 'Loading PR diff...', 'Loaded PR diff.');
29
- } catch (e) {
30
- displayError(e.toString());
31
- displayError(`Failed to call "gh pr diff ${pr}", see message above for details.`);
32
- exit();
33
- }
34
- }
@@ -1,19 +0,0 @@
1
- import {resolve} from "node:path";
2
- import {slothContext} from "../config.js";
3
- import {display} from "../consoleUtils.js";
4
- import {readFileSyncWithMessages} from "../utils.js";
5
-
6
- /**
7
- * Reads the text file from current dir
8
- * @param _ config (unused in this provider)
9
- * @param fileName
10
- * @returns {string} file contents
11
- */
12
- export function get(_, fileName) {
13
- if (!fileName) {
14
- return null;
15
- }
16
- const filePath = resolve(slothContext.currentDir, fileName);
17
- display(`Reading file ${fileName}...`);
18
- return readFileSyncWithMessages(filePath);
19
- }
@@ -1,11 +0,0 @@
1
- import {spawnCommand} from "../utils.js";
2
- import {displayWarning} from "../consoleUtils.js";
3
-
4
- export async function get(_, pr) {
5
- // TODO makes sense to check if gh is available and authenticated
6
- if (!pr) {
7
- displayWarning("No PR provided, skipping PR diff fetching.");
8
- return "";
9
- }
10
- return spawnCommand('gh', ['pr', 'diff', pr], 'Loading PR diff...', 'Loaded PR diff.');
11
- }
@@ -1,84 +0,0 @@
1
- import {display, displayWarning} from "../consoleUtils.js";
2
- import { env } from "../systemUtils.js";
3
-
4
- export async function get(config, prId) {
5
- const issueData = await getJiraIssue(config, prId);
6
- return `## ${prId} Requirements - ${issueData.fields?.summary}\n\n${issueData.fields?.description}`;
7
- }
8
-
9
- /**
10
- * Fetches a Jira issue using the Atlassian REST API v2.
11
- *
12
- * @async
13
- * @param {object} config - Configuration object.
14
- * @param {string} config.username - Your Jira email address or username used for authentication.
15
- * @param {string} config.token - Your Jira API token (legacy access token).
16
- * @param {string} config.baseUrl - The base URL of your Jira instance API (e.g., 'https://your-domain.atlassian.net/rest/api/2/issue/').
17
- * @param {string} jiraKey - The Jira issue key (e.g., 'UI-1005').
18
- * @returns {Promise<object>} A promise that resolves with the Jira issue data as a JSON object.
19
- * @throws {Error} Throws an error if the fetch fails, authentication is wrong, the issue is not found, or the response status is not OK.
20
- */
21
- async function getJiraIssue(config, jiraKey) {
22
- const {username, baseUrl} = config;
23
- if (!jiraKey) {
24
- displayWarning("No jiraKey provided, skipping Jira issue fetching.");
25
- return "";
26
- }
27
- const token = env.JIRA_LEGACY_API_TOKEN ?? config?.token;
28
-
29
- if (!token) {
30
- throw new Error(
31
- 'Missing JIRA Legacy API token. ' +
32
- 'The legacy token can be defined as JIRA_LEGACY_API_TOKEN environment variable or as "token" in config.'
33
- );
34
- }
35
-
36
- // Validate essential inputs
37
- if (!username || !baseUrl) {
38
- throw new Error('Missing required parameters in config: username or baseUrl');
39
- }
40
-
41
- // Ensure baseUrl doesn't end with a slash to avoid double slashes in the URL
42
- const cleanBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
43
-
44
- // Construct the full API URL
45
- const apiUrl = `${cleanBaseUrl}/${jiraKey}`;
46
-
47
- // Encode credentials for Basic Authentication header
48
- const credentials = `${username}:${token}`;
49
- const encodedCredentials = Buffer.from(credentials).toString('base64');
50
- const authHeader = `Basic ${encodedCredentials}`;
51
-
52
- // Define request headers
53
- const headers = {
54
- 'Authorization': authHeader,
55
- 'Accept': 'application/json', // Tell the server we expect JSON back
56
- // 'Content-Type': 'application/json' // Usually not needed for GET requests
57
- };
58
-
59
- display(`Fetching Jira issue: ${apiUrl}`);
60
-
61
- const response = await fetch(apiUrl, {
62
- method: 'GET',
63
- headers: headers,
64
- });
65
-
66
- // Check if the response status code indicates success (e.g., 200 OK)
67
- if (!response.ok) {
68
- let errorBody = 'Could not read error body.';
69
- try {
70
- // Attempt to get more details from the response body for non-OK statuses
71
- errorBody = await response.text();
72
- // eslint-disable-next-line no-unused-vars
73
- } catch (e) {
74
- // Silent fail - we already have a generic error message
75
- }
76
- // Throw a detailed error including status, status text, URL, and body if available
77
- throw new Error(`HTTP error! Status: ${response.status} ${response.statusText}. URL: ${apiUrl}. Response Body: ${errorBody}`);
78
- }
79
-
80
- // Parse the JSON response body if the request was successful
81
- const issueData = await response.json();
82
- return issueData;
83
-
84
- }
@@ -1,6 +0,0 @@
1
- /**
2
- * Simple provider returning text as it is.
3
- */
4
- export async function get(_, text) {
5
- return text;
6
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * This file contains all system functions and objects that are globally available
3
- * but not imported directly, such as process.stdin, process.stdout, process.argv,
4
- * process.env, process.cwd(), process.exit(), etc.
5
- *
6
- * By centralizing these in one file, we improve testability and make it easier
7
- * to mock these dependencies in tests.
8
- */
9
-
10
- const innerState = {
11
- installDir: undefined
12
- };
13
-
14
- /* eslint-disable no-undef */
15
- // Process-related functions and objects
16
- export const getCurrentDir = () => process.cwd();
17
- export const getInstallDir = () => innerState.installDir;
18
- export const exit = (code) => process.exit(code);
19
- export const stdin = process.stdin;
20
- export const stdout = process.stdout;
21
- export const argv = process.argv;
22
- export const env = process.env;
23
-
24
- export const setInstallDir = (dir) => innerState.installDir = dir;
25
-
26
- // Console-related functions
27
- export const log = (message) => console.log(message);
28
- export const error = (message) => console.error(message);
29
- export const warn = (message) => console.warn(message);
30
- export const info = (message) => console.info(message);
31
- export const debug = (message) => console.debug(message);
32
- /* eslint-enable no-undef */