haitask 0.1.6 → 0.3.3
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/.env.example +27 -24
- package/README.md +119 -100
- package/package.json +47 -44
- package/src/ai/deepseek.js +54 -54
- package/src/ai/groq.js +56 -56
- package/src/ai/index.js +34 -34
- package/src/ai/openai.js +52 -52
- package/src/ai/utils.js +57 -52
- package/src/backend/index.js +61 -34
- package/src/commands/check.js +31 -0
- package/src/commands/init.js +207 -166
- package/src/commands/run.js +70 -57
- package/src/config/constants.js +8 -8
- package/src/config/env-loader.js +37 -37
- package/src/config/init.js +75 -72
- package/src/config/load.js +56 -56
- package/src/core/pipeline.js +113 -86
- package/src/git/commit.js +44 -29
- package/src/index.js +40 -32
- package/src/jira/client.js +266 -228
- package/src/linear/client.js +150 -0
- package/src/trello/client.js +108 -71
- package/src/utils/http-hints.js +34 -0
- package/src/utils/idempotency.js +46 -0
- package/src/utils/index.js +4 -4
- package/src/utils/issue-key.js +46 -0
- package/src/utils/retry.js +50 -0
- package/src/utils/urls.js +14 -14
package/.env.example
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
|
-
# HAITASK — copy to .env and fill in
|
|
2
|
-
# Set only the key for the provider you use in .haitaskrc (ai.provider)
|
|
3
|
-
|
|
4
|
-
# Groq (FREE, fast): https://console.groq.com/keys
|
|
5
|
-
GROQ_API_KEY=your-groq-api-key
|
|
6
|
-
|
|
7
|
-
# Deepseek (FREE): https://platform.deepseek.com/
|
|
8
|
-
DEEPSEEK_API_KEY=your-deepseek-api-key
|
|
9
|
-
|
|
10
|
-
# OpenAI (paid): https://platform.openai.com/api-keys
|
|
11
|
-
OPENAI_API_KEY=your-openai-api-key
|
|
12
|
-
|
|
13
|
-
# Jira (required for creating issues)
|
|
14
|
-
JIRA_BASE_URL=https://your-domain.atlassian.net
|
|
15
|
-
JIRA_EMAIL=your@email.com
|
|
16
|
-
JIRA_API_TOKEN=your-jira-api-token
|
|
17
|
-
# Optional: assign new issues to you. Jira Cloud account ID: Profile → Account ID, or admin.atlassian.com → Directory → Users. If value contains ":", use quotes: JIRA_ACCOUNT_ID="id:uuid"
|
|
18
|
-
JIRA_ACCOUNT_ID=your-account-id
|
|
19
|
-
|
|
20
|
-
# Trello (when target is trello in .haitaskrc). Get key + token: https://trello.com/app-key
|
|
21
|
-
TRELLO_API_KEY=your-trello-api-key
|
|
22
|
-
TRELLO_TOKEN=your-trello-token
|
|
23
|
-
# Optional: assign new cards to you (Trello member ID)
|
|
24
|
-
TRELLO_MEMBER_ID=your-member-id
|
|
1
|
+
# HAITASK — copy to .env and fill in
|
|
2
|
+
# Set only the key for the provider you use in .haitaskrc (ai.provider)
|
|
3
|
+
|
|
4
|
+
# Groq (FREE, fast): https://console.groq.com/keys
|
|
5
|
+
GROQ_API_KEY=your-groq-api-key
|
|
6
|
+
|
|
7
|
+
# Deepseek (FREE): https://platform.deepseek.com/
|
|
8
|
+
DEEPSEEK_API_KEY=your-deepseek-api-key
|
|
9
|
+
|
|
10
|
+
# OpenAI (paid): https://platform.openai.com/api-keys
|
|
11
|
+
OPENAI_API_KEY=your-openai-api-key
|
|
12
|
+
|
|
13
|
+
# Jira (required for creating issues)
|
|
14
|
+
JIRA_BASE_URL=https://your-domain.atlassian.net
|
|
15
|
+
JIRA_EMAIL=your@email.com
|
|
16
|
+
JIRA_API_TOKEN=your-jira-api-token
|
|
17
|
+
# Optional: assign new issues to you. Jira Cloud account ID: Profile → Account ID, or admin.atlassian.com → Directory → Users. If value contains ":", use quotes: JIRA_ACCOUNT_ID="id:uuid"
|
|
18
|
+
JIRA_ACCOUNT_ID=your-account-id
|
|
19
|
+
|
|
20
|
+
# Trello (when target is trello in .haitaskrc). Get key + token: https://trello.com/app-key
|
|
21
|
+
TRELLO_API_KEY=your-trello-api-key
|
|
22
|
+
TRELLO_TOKEN=your-trello-token
|
|
23
|
+
# Optional: assign new cards to you (Trello member ID)
|
|
24
|
+
TRELLO_MEMBER_ID=your-member-id
|
|
25
|
+
|
|
26
|
+
# Linear (when target is linear in .haitaskrc). Get key: https://linear.app/settings/api
|
|
27
|
+
LINEAR_API_KEY=your-linear-api-key
|
package/README.md
CHANGED
|
@@ -1,100 +1,119 @@
|
|
|
1
|
-
# HAITASK
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
|
62
|
-
|
|
63
|
-
| `haitask
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
**
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
1
|
+
# HAITASK
|
|
2
|
+
|
|
3
|
+
> Turn a Git commit into a task in Jira, Trello, or Linear — one command.
|
|
4
|
+
|
|
5
|
+
HAITASK reads your latest commit message and branch, uses AI to shape a clear title and description, and creates the issue or card in the tool you use. No framework lock-in: works in any Git repo.
|
|
6
|
+
|
|
7
|
+
**Requires:** Node.js 18+
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g haitask
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Run without installing:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx haitask
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
**1. Configure (once)**
|
|
28
|
+
In your project root:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
haitask init
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Pick a target (1 = Jira, 2 = Trello, 3 = Linear) and answer the prompts. You get a `.haitaskrc` and an optional `.env` template.
|
|
35
|
+
**Quick mode:** `haitask init --quick` — fewer questions, sensible defaults.
|
|
36
|
+
|
|
37
|
+
**2. Add API keys**
|
|
38
|
+
In the generated `.env`, set the keys for your target and AI provider:
|
|
39
|
+
|
|
40
|
+
| Target | Keys |
|
|
41
|
+
|---------|------|
|
|
42
|
+
| **Jira** | `JIRA_BASE_URL`, `JIRA_EMAIL`, `JIRA_API_TOKEN` — [Atlassian API tokens](https://id.atlassian.com/manage-profile/security/api-tokens) |
|
|
43
|
+
| **Trello** | `TRELLO_API_KEY`, `TRELLO_TOKEN` — [trello.com/app-key](https://trello.com/app-key) |
|
|
44
|
+
| **Linear** | `LINEAR_API_KEY` — [linear.app/settings/api](https://linear.app/settings/api) |
|
|
45
|
+
|
|
46
|
+
For AI, set one of: `GROQ_API_KEY`, `DEEPSEEK_API_KEY`, or `OPENAI_API_KEY` (default is Groq).
|
|
47
|
+
|
|
48
|
+
**3. Create a task**
|
|
49
|
+
After you commit:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
haitask run
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
To try without creating a task: `haitask run --dry`.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Commands
|
|
60
|
+
|
|
61
|
+
| Command | Description |
|
|
62
|
+
|---------|-------------|
|
|
63
|
+
| `haitask init` | Interactive setup: target, AI, rules → writes `.haitaskrc` and optional `.env` |
|
|
64
|
+
| `haitask init --quick` | Minimal prompts: target + required fields only; defaults for AI, branches, prefixes |
|
|
65
|
+
| `haitask check` | Validate `.haitaskrc` + required env keys without running the pipeline |
|
|
66
|
+
| `haitask run` | Creates a task from the latest commit (Jira / Trello / Linear) |
|
|
67
|
+
| `haitask run --dry` | Same flow, but does not create a task |
|
|
68
|
+
| `haitask run --commits N` | Combine the last N commits into one task (e.g. `--commits 3`) |
|
|
69
|
+
| `haitask run --type <type>` | (Jira only) Override issue type for this run (Task, Bug, Story) |
|
|
70
|
+
| `haitask run --status <status>` | (Jira only) Override status after create (Done, "To Do", etc.) |
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
- **`.haitaskrc`** — `target` (jira / trello / linear), target-specific options, `ai` (provider, model), `rules` (allowedBranches, commitPrefixes).
|
|
77
|
+
- **`.env`** — API keys only. Load order: project `.env` then `~/.haitask/.env`.
|
|
78
|
+
|
|
79
|
+
**Security:** Keys live only in `.env` (do not commit it). They are sent only to the services you use: your target (Jira/Trello/Linear) and your chosen AI provider.
|
|
80
|
+
|
|
81
|
+
**Jira:** `jira.baseUrl`, `jira.projectKey`, `jira.issueType`, `jira.transitionToStatus`. Optional assignee: `JIRA_ACCOUNT_ID` in `.env`.
|
|
82
|
+
|
|
83
|
+
**Trello:** `trello.listId` (required — the list where new cards go). To get the list ID: open the board → **⋯** on the list header → “Copy list link” → the 24-character ID is in the URL. From the haitask repo you can run `node scripts/get-trello-list.js` (with Trello keys in `.env`) to print the first list ID.
|
|
84
|
+
|
|
85
|
+
**Linear:** `linear.teamId` (required). In Linear: Team → Settings → copy Team ID.
|
|
86
|
+
|
|
87
|
+
**AI:** `groq` (free), `deepseek` (free), `openai` (paid). Set the matching key in `.env`.
|
|
88
|
+
|
|
89
|
+
**Rules:** If `allowedBranches` or `commitPrefixes` are set, the current branch and commit message are checked. If the commit message contains an issue key (e.g. `PROJ-123`, `ENG-42`), haitask adds a comment to that issue instead of creating a new task; set `rules.linkToExistingIssue: false` in `.haitaskrc` to always create new tasks.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Usage
|
|
94
|
+
|
|
95
|
+
- **Global:** `npm install -g haitask` → run `haitask init` once per repo, then `haitask run` after commits.
|
|
96
|
+
- **Per project:** `npm install haitask --save-dev` → `npx haitask run`.
|
|
97
|
+
- **CI / scripts:** Run `npx haitask run` from the repo root; ensure `.haitaskrc` and env vars are available.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Roadmap
|
|
102
|
+
|
|
103
|
+
- ✅ **Batch** — `haitask run --commits N`
|
|
104
|
+
- ✅ **Link to existing issue** — commit message with issue key (e.g. PROJ-123) → comment on that issue
|
|
105
|
+
- ✅ **Linear** — target `linear`, `linear.teamId`, `LINEAR_API_KEY`
|
|
106
|
+
- 🔜 More targets (same adapter pattern)
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Troubleshooting
|
|
111
|
+
|
|
112
|
+
**Jira — assignee not set**
|
|
113
|
+
The user in `JIRA_ACCOUNT_ID` must be an assignable user in that project. In Jira: Project → Space settings → People, or assign manually.
|
|
114
|
+
|
|
115
|
+
**Trello — list ID**
|
|
116
|
+
You need the 24-character hex list ID. Open the board → **⋯** on the list → “Copy list link” → use the ID from the URL as `trello.listId`. Or run `node scripts/get-trello-list.js` from the haitask repo (Trello keys in `.env`).
|
|
117
|
+
|
|
118
|
+
**Linear — team ID or API key**
|
|
119
|
+
Copy Team ID from Linear → Team → Settings. Create an API key at [linear.app/settings/api](https://linear.app/settings/api) and set `LINEAR_API_KEY` in `.env`.
|
package/package.json
CHANGED
|
@@ -1,44 +1,47 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "haitask",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "HAITASK — AI-powered task creation from Git commits. Creates issues in Jira or
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "src/index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"haitask": "src/index.js"
|
|
9
|
-
},
|
|
10
|
-
"engines": {
|
|
11
|
-
"node": ">=18"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"src",
|
|
15
|
-
"README.md",
|
|
16
|
-
".env.example"
|
|
17
|
-
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"start": "node src/index.js",
|
|
20
|
-
"init": "node src/index.js init",
|
|
21
|
-
"run": "node src/index.js run"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "haitask",
|
|
3
|
+
"version": "0.3.3",
|
|
4
|
+
"description": "HAITASK — AI-powered task creation from Git commits. Creates issues in Jira, Trello, or Linear from your latest commit message and branch.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"haitask": "src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"README.md",
|
|
16
|
+
".env.example"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"start": "node src/index.js",
|
|
20
|
+
"init": "node src/index.js init",
|
|
21
|
+
"run": "node src/index.js run",
|
|
22
|
+
"test": "node --test test/*.test.js"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"cli",
|
|
26
|
+
"jira",
|
|
27
|
+
"trello",
|
|
28
|
+
"linear",
|
|
29
|
+
"git",
|
|
30
|
+
"ai",
|
|
31
|
+
"automation",
|
|
32
|
+
"commit",
|
|
33
|
+
"task"
|
|
34
|
+
],
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/HidayetHidayetov/haitask.git"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/HidayetHidayetov/haitask#readme",
|
|
41
|
+
"bugs": "https://github.com/HidayetHidayetov/haitask/issues",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"commander": "^12.0.0",
|
|
44
|
+
"dotenv": "^16.4.5",
|
|
45
|
+
"execa": "^9.5.2"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/ai/deepseek.js
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deepseek Chat provider (free tier, JSON mode supported).
|
|
3
|
-
* API: https://api.deepseek.com/v1/chat/completions
|
|
4
|
-
* Docs: https://platform.deepseek.com/api-docs/
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { buildPrompt, parseTaskPayload } from './utils.js';
|
|
8
|
-
|
|
9
|
-
const DEEPSEEK_API_URL = 'https://api.deepseek.com/v1/chat/completions';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Call Deepseek and return task payload for Jira.
|
|
13
|
-
* @param {{ message: string, branch: string, repoName: string }} commitData
|
|
14
|
-
* @param {{ ai: { model?: string } }} config
|
|
15
|
-
* @returns {Promise<{ title: string, description: string, labels: string[] }>}
|
|
16
|
-
*/
|
|
17
|
-
export async function generateDeepseek(commitData, config) {
|
|
18
|
-
const apiKey = process.env.DEEPSEEK_API_KEY;
|
|
19
|
-
if (!apiKey?.trim()) {
|
|
20
|
-
throw new Error('DEEPSEEK_API_KEY is not set. Add it to .env. Get free key at https://platform.deepseek.com/');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const model = config?.ai?.model || 'deepseek-chat';
|
|
24
|
-
const { system, user } = buildPrompt(commitData);
|
|
25
|
-
|
|
26
|
-
const response = await fetch(DEEPSEEK_API_URL, {
|
|
27
|
-
method: 'POST',
|
|
28
|
-
headers: {
|
|
29
|
-
'Content-Type': 'application/json',
|
|
30
|
-
Authorization: `Bearer ${apiKey}`,
|
|
31
|
-
},
|
|
32
|
-
body: JSON.stringify({
|
|
33
|
-
model,
|
|
34
|
-
messages: [
|
|
35
|
-
{ role: 'system', content: system },
|
|
36
|
-
{ role: 'user', content: user },
|
|
37
|
-
],
|
|
38
|
-
response_format: { type: 'json_object' },
|
|
39
|
-
}),
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!response.ok) {
|
|
43
|
-
const body = await response.text();
|
|
44
|
-
throw new Error(`Deepseek API error ${response.status}: ${body || response.statusText}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const data = await response.json();
|
|
48
|
-
const content = data?.choices?.[0]?.message?.content;
|
|
49
|
-
if (typeof content !== 'string') {
|
|
50
|
-
throw new Error('Deepseek response missing choices[0].message.content');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return parseTaskPayload(content.trim());
|
|
54
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Deepseek Chat provider (free tier, JSON mode supported).
|
|
3
|
+
* API: https://api.deepseek.com/v1/chat/completions
|
|
4
|
+
* Docs: https://platform.deepseek.com/api-docs/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { buildPrompt, parseTaskPayload } from './utils.js';
|
|
8
|
+
|
|
9
|
+
const DEEPSEEK_API_URL = 'https://api.deepseek.com/v1/chat/completions';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Call Deepseek and return task payload for Jira.
|
|
13
|
+
* @param {{ message: string, branch: string, repoName: string }} commitData
|
|
14
|
+
* @param {{ ai: { model?: string } }} config
|
|
15
|
+
* @returns {Promise<{ title: string, description: string, labels: string[] }>}
|
|
16
|
+
*/
|
|
17
|
+
export async function generateDeepseek(commitData, config) {
|
|
18
|
+
const apiKey = process.env.DEEPSEEK_API_KEY;
|
|
19
|
+
if (!apiKey?.trim()) {
|
|
20
|
+
throw new Error('DEEPSEEK_API_KEY is not set. Add it to .env. Get free key at https://platform.deepseek.com/');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const model = config?.ai?.model || 'deepseek-chat';
|
|
24
|
+
const { system, user } = buildPrompt(commitData);
|
|
25
|
+
|
|
26
|
+
const response = await fetch(DEEPSEEK_API_URL, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
Authorization: `Bearer ${apiKey}`,
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
model,
|
|
34
|
+
messages: [
|
|
35
|
+
{ role: 'system', content: system },
|
|
36
|
+
{ role: 'user', content: user },
|
|
37
|
+
],
|
|
38
|
+
response_format: { type: 'json_object' },
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const body = await response.text();
|
|
44
|
+
throw new Error(`Deepseek API error ${response.status}: ${body || response.statusText}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const data = await response.json();
|
|
48
|
+
const content = data?.choices?.[0]?.message?.content;
|
|
49
|
+
if (typeof content !== 'string') {
|
|
50
|
+
throw new Error('Deepseek response missing choices[0].message.content');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return parseTaskPayload(content.trim());
|
|
54
|
+
}
|
package/src/ai/groq.js
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Groq provider (free tier, fast inference).
|
|
3
|
-
* API: https://api.groq.com/openai/v1/chat/completions (OpenAI-compatible)
|
|
4
|
-
* Docs: https://console.groq.com/docs
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { buildPrompt, parseTaskPayload } from './utils.js';
|
|
8
|
-
|
|
9
|
-
const GROQ_API_URL = 'https://api.groq.com/openai/v1/chat/completions';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Call Groq and return task payload for Jira.
|
|
13
|
-
* @param {{ message: string, branch: string, repoName: string }} commitData
|
|
14
|
-
* @param {{ ai: { model?: string } }} config
|
|
15
|
-
* @returns {Promise<{ title: string, description: string, labels: string[] }>}
|
|
16
|
-
*/
|
|
17
|
-
export async function generateGroq(commitData, config) {
|
|
18
|
-
const apiKey = process.env.GROQ_API_KEY;
|
|
19
|
-
if (!apiKey?.trim()) {
|
|
20
|
-
throw new Error(
|
|
21
|
-
'GROQ_API_KEY is not set. Add it to .env. Get free key at https://console.groq.com/keys'
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const model = config?.ai?.model || 'llama-3.1-8b-instant';
|
|
26
|
-
const { system, user } = buildPrompt(commitData);
|
|
27
|
-
|
|
28
|
-
const response = await fetch(GROQ_API_URL, {
|
|
29
|
-
method: 'POST',
|
|
30
|
-
headers: {
|
|
31
|
-
'Content-Type': 'application/json',
|
|
32
|
-
Authorization: `Bearer ${apiKey}`,
|
|
33
|
-
},
|
|
34
|
-
body: JSON.stringify({
|
|
35
|
-
model,
|
|
36
|
-
messages: [
|
|
37
|
-
{ role: 'system', content: system },
|
|
38
|
-
{ role: 'user', content: user },
|
|
39
|
-
],
|
|
40
|
-
response_format: { type: 'json_object' },
|
|
41
|
-
}),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
if (!response.ok) {
|
|
45
|
-
const body = await response.text();
|
|
46
|
-
throw new Error(`Groq API error ${response.status}: ${body || response.statusText}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const data = await response.json();
|
|
50
|
-
const content = data?.choices?.[0]?.message?.content;
|
|
51
|
-
if (typeof content !== 'string') {
|
|
52
|
-
throw new Error('Groq response missing choices[0].message.content');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return parseTaskPayload(content.trim());
|
|
56
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Groq provider (free tier, fast inference).
|
|
3
|
+
* API: https://api.groq.com/openai/v1/chat/completions (OpenAI-compatible)
|
|
4
|
+
* Docs: https://console.groq.com/docs
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { buildPrompt, parseTaskPayload } from './utils.js';
|
|
8
|
+
|
|
9
|
+
const GROQ_API_URL = 'https://api.groq.com/openai/v1/chat/completions';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Call Groq and return task payload for Jira.
|
|
13
|
+
* @param {{ message: string, branch: string, repoName: string }} commitData
|
|
14
|
+
* @param {{ ai: { model?: string } }} config
|
|
15
|
+
* @returns {Promise<{ title: string, description: string, labels: string[] }>}
|
|
16
|
+
*/
|
|
17
|
+
export async function generateGroq(commitData, config) {
|
|
18
|
+
const apiKey = process.env.GROQ_API_KEY;
|
|
19
|
+
if (!apiKey?.trim()) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
'GROQ_API_KEY is not set. Add it to .env. Get free key at https://console.groq.com/keys'
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const model = config?.ai?.model || 'llama-3.1-8b-instant';
|
|
26
|
+
const { system, user } = buildPrompt(commitData);
|
|
27
|
+
|
|
28
|
+
const response = await fetch(GROQ_API_URL, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
Authorization: `Bearer ${apiKey}`,
|
|
33
|
+
},
|
|
34
|
+
body: JSON.stringify({
|
|
35
|
+
model,
|
|
36
|
+
messages: [
|
|
37
|
+
{ role: 'system', content: system },
|
|
38
|
+
{ role: 'user', content: user },
|
|
39
|
+
],
|
|
40
|
+
response_format: { type: 'json_object' },
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
const body = await response.text();
|
|
46
|
+
throw new Error(`Groq API error ${response.status}: ${body || response.statusText}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const data = await response.json();
|
|
50
|
+
const content = data?.choices?.[0]?.message?.content;
|
|
51
|
+
if (typeof content !== 'string') {
|
|
52
|
+
throw new Error('Groq response missing choices[0].message.content');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return parseTaskPayload(content.trim());
|
|
56
|
+
}
|