haitask 0.1.0 → 0.1.2
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/README.md +7 -10
- package/package.json +2 -2
- package/src/commands/init.js +86 -14
- package/src/config/env-loader.js +37 -0
- package/src/config/init.js +2 -5
- package/src/index.js +3 -2
package/README.md
CHANGED
|
@@ -28,19 +28,16 @@ git clone https://github.com/HidayetHidayetov/haitask.git && cd haitask && npm i
|
|
|
28
28
|
|
|
29
29
|
## Quick start
|
|
30
30
|
|
|
31
|
-
1. **
|
|
32
|
-
Create a `.env` (e.g. in your home dir or a shared config folder) with:
|
|
33
|
-
- One AI provider key: `GROQ_API_KEY`, `DEEPSEEK_API_KEY`, or `OPENAI_API_KEY`
|
|
34
|
-
- Jira: `JIRA_BASE_URL`, `JIRA_EMAIL`, `JIRA_API_TOKEN`
|
|
35
|
-
Copy from `.env.example` in the repo.
|
|
36
|
-
|
|
37
|
-
2. **Per project (one-time)**
|
|
31
|
+
1. **Per project (one-time)**
|
|
38
32
|
In the Git repo where you want to create Jira issues:
|
|
39
33
|
```bash
|
|
40
34
|
cd /path/to/your/repo
|
|
41
35
|
haitask init
|
|
42
36
|
```
|
|
43
|
-
|
|
37
|
+
**Interactive setup:** you’ll be asked for Jira base URL, project key, issue type, AI provider (groq / deepseek / openai), allowed branches, and commit prefixes. A `.haitaskrc` file is created from your answers. You’ll then choose where to store API keys: **this project** (`.env` in the repo) or **global** (`~/.haitask/.env`, shared across projects). A template `.env` is created in the chosen place — add your own keys there (never commit real keys).
|
|
38
|
+
|
|
39
|
+
2. **Add your API keys**
|
|
40
|
+
Edit the `.env` that was created (project or `~/.haitask/.env`): set one AI key (`GROQ_API_KEY`, `DEEPSEEK_API_KEY`, or `OPENAI_API_KEY`) and Jira keys (`JIRA_BASE_URL`, `JIRA_EMAIL`, `JIRA_API_TOKEN`). Optional: `JIRA_ACCOUNT_ID` to auto-assign issues.
|
|
44
41
|
|
|
45
42
|
3. **Create a Jira issue from the latest commit**
|
|
46
43
|
After committing:
|
|
@@ -56,7 +53,7 @@ git clone https://github.com/HidayetHidayetov/haitask.git && cd haitask && npm i
|
|
|
56
53
|
|
|
57
54
|
| Command | Description |
|
|
58
55
|
|--------|-------------|
|
|
59
|
-
| `haitask init` |
|
|
56
|
+
| `haitask init` | Interactive setup: prompts for Jira/AI/rules → writes `.haitaskrc`, optional `.env` (project or ~/.haitask/.env). |
|
|
60
57
|
| `haitask run` | Run pipeline: Git → AI → Jira (create issue). |
|
|
61
58
|
| `haitask run --dry` | Same as above but skips the Jira API call. |
|
|
62
59
|
|
|
@@ -65,7 +62,7 @@ git clone https://github.com/HidayetHidayetov/haitask.git && cd haitask && npm i
|
|
|
65
62
|
## Configuration
|
|
66
63
|
|
|
67
64
|
- **`.haitaskrc`** (project root): Jira `baseUrl`, `projectKey`, `issueType`; AI `provider` and `model`; `rules.allowedBranches` and `rules.commitPrefixes`. Single source of truth for behaviour.
|
|
68
|
-
- **`.env`**: API keys only.
|
|
65
|
+
- **`.env`**: API keys only. Loaded in order: **project** `.env` (current directory), then **global** `~/.haitask/.env`. So you can use one global `.env` for all projects or override per repo.
|
|
69
66
|
|
|
70
67
|
**AI providers** (set `ai.provider` in `.haitaskrc`): `groq` (default, free), `deepseek` (free), `openai` (paid). Set the corresponding key in `.env`.
|
|
71
68
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "haitask",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Hidayet AI Task — Generate Jira tasks from Git commits using AI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -41,4 +41,4 @@
|
|
|
41
41
|
"dotenv": "^16.4.5",
|
|
42
42
|
"execa": "^9.5.2"
|
|
43
43
|
}
|
|
44
|
-
}
|
|
44
|
+
}
|
package/src/commands/init.js
CHANGED
|
@@ -1,40 +1,112 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* haitask init —
|
|
3
|
-
* Thin handler: delegates to config layer, handles CLI output only.
|
|
2
|
+
* haitask init — Interactive setup: prompts → .haitaskrc, optional .env placement.
|
|
4
3
|
*/
|
|
5
4
|
|
|
5
|
+
import { createInterface } from 'readline';
|
|
6
|
+
import { writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
7
|
+
import { resolve } from 'path';
|
|
6
8
|
import { createDefaultConfigFile, validateEnv } from '../config/init.js';
|
|
7
9
|
import { loadConfig } from '../config/load.js';
|
|
8
10
|
|
|
11
|
+
const DEFAULT_MODELS = { groq: 'llama-3.1-8b-instant', deepseek: 'deepseek-chat', openai: 'gpt-4o-mini' };
|
|
12
|
+
|
|
13
|
+
function question(rl, prompt, defaultVal = '') {
|
|
14
|
+
const def = defaultVal ? ` (${defaultVal})` : '';
|
|
15
|
+
return new Promise((resolve) => rl.question(prompt + def + ': ', (ans) => resolve((ans || defaultVal).trim())));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getEnvExampleContent() {
|
|
19
|
+
return `# HAITASK — fill in your own API keys (never commit real keys)
|
|
20
|
+
# AI: set the key for the provider in .haitaskrc (ai.provider)
|
|
21
|
+
GROQ_API_KEY=
|
|
22
|
+
DEEPSEEK_API_KEY=
|
|
23
|
+
OPENAI_API_KEY=
|
|
24
|
+
|
|
25
|
+
# Jira (required)
|
|
26
|
+
JIRA_BASE_URL=
|
|
27
|
+
JIRA_EMAIL=
|
|
28
|
+
JIRA_API_TOKEN=
|
|
29
|
+
JIRA_ACCOUNT_ID=
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
32
|
+
|
|
9
33
|
export async function runInit() {
|
|
10
|
-
const
|
|
34
|
+
const cwd = process.cwd();
|
|
35
|
+
const rcPath = resolve(cwd, '.haitaskrc');
|
|
11
36
|
|
|
12
|
-
if (
|
|
37
|
+
if (existsSync(rcPath)) {
|
|
13
38
|
console.warn('haitask init: .haitaskrc already exists. Not overwriting.');
|
|
14
39
|
process.exitCode = 1;
|
|
15
40
|
return;
|
|
16
41
|
}
|
|
17
42
|
|
|
18
|
-
|
|
43
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
console.log('haitask init — answer the questions (Enter = use default).\n');
|
|
47
|
+
|
|
48
|
+
const jiraBaseUrl = await question(rl, 'Jira base URL', 'https://your-domain.atlassian.net');
|
|
49
|
+
const jiraProjectKey = await question(rl, 'Jira project key', 'PROJ');
|
|
50
|
+
const jiraIssueType = await question(rl, 'Jira issue type', 'Task');
|
|
51
|
+
const aiProvider = await question(rl, 'AI provider (groq | deepseek | openai)', 'groq');
|
|
52
|
+
const allowedBranchesStr = await question(rl, 'Allowed branches (comma-separated)', 'main,develop,master');
|
|
53
|
+
const commitPrefixesStr = await question(rl, 'Commit prefixes (comma-separated)', 'feat,fix,chore');
|
|
54
|
+
|
|
55
|
+
const allowedBranches = allowedBranchesStr.split(',').map((s) => s.trim()).filter(Boolean);
|
|
56
|
+
const commitPrefixes = commitPrefixesStr.split(',').map((s) => s.trim()).filter(Boolean);
|
|
57
|
+
const model = DEFAULT_MODELS[aiProvider.toLowerCase()] || DEFAULT_MODELS.groq;
|
|
58
|
+
|
|
59
|
+
const config = {
|
|
60
|
+
jira: { baseUrl: jiraBaseUrl, projectKey: jiraProjectKey, issueType: jiraIssueType },
|
|
61
|
+
ai: { provider: aiProvider.toLowerCase(), model },
|
|
62
|
+
rules: { allowedBranches, commitPrefixes },
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
writeFileSync(rcPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
66
|
+
console.log('\nCreated .haitaskrc');
|
|
67
|
+
|
|
68
|
+
const where = await question(rl, 'Where to store API keys? (1 = this project .env, 2 = global ~/.haitask/.env)', '1');
|
|
69
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
70
|
+
|
|
71
|
+
if (where.trim() === '2' && home) {
|
|
72
|
+
const globalDir = resolve(home, '.haitask');
|
|
73
|
+
const globalEnv = resolve(globalDir, '.env');
|
|
74
|
+
if (!existsSync(globalDir)) mkdirSync(globalDir, { recursive: true });
|
|
75
|
+
if (!existsSync(globalEnv)) {
|
|
76
|
+
writeFileSync(globalEnv, getEnvExampleContent(), 'utf-8');
|
|
77
|
+
console.log('Created ~/.haitask/.env — add your API keys there (used by all projects).');
|
|
78
|
+
} else {
|
|
79
|
+
console.log('~/.haitask/.env already exists.');
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
const projectEnv = resolve(cwd, '.env');
|
|
83
|
+
if (!existsSync(projectEnv)) {
|
|
84
|
+
writeFileSync(projectEnv, getEnvExampleContent(), 'utf-8');
|
|
85
|
+
console.log('Created .env in this project — add your API keys there.');
|
|
86
|
+
} else {
|
|
87
|
+
console.log('.env already exists in this project.');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} finally {
|
|
91
|
+
rl.close();
|
|
92
|
+
}
|
|
19
93
|
|
|
20
|
-
// Load config to check AI provider key
|
|
21
94
|
let config;
|
|
22
95
|
try {
|
|
23
96
|
config = loadConfig();
|
|
24
|
-
} catch
|
|
25
|
-
// Config just created, might not be readable yet, skip AI key check
|
|
97
|
+
} catch {
|
|
26
98
|
config = null;
|
|
27
99
|
}
|
|
28
100
|
|
|
29
|
-
const { valid, missing } = validateEnv(
|
|
101
|
+
const { valid, missing } = validateEnv(cwd, config);
|
|
30
102
|
if (!valid) {
|
|
31
|
-
console.warn('
|
|
32
|
-
|
|
33
|
-
if (provider === 'groq') console.log('
|
|
34
|
-
if (provider === 'deepseek') console.log('
|
|
103
|
+
console.warn('\nAdd these to your .env before "haitask run":', missing.join(', '));
|
|
104
|
+
console.log('Env is read from: project .env, then ~/.haitask/.env');
|
|
105
|
+
if (config?.ai?.provider === 'groq') console.log('Groq key: https://console.groq.com/keys');
|
|
106
|
+
if (config?.ai?.provider === 'deepseek') console.log('Deepseek key: https://platform.deepseek.com/');
|
|
35
107
|
process.exitCode = 1;
|
|
36
108
|
return;
|
|
37
109
|
}
|
|
38
110
|
|
|
39
|
-
console.log('
|
|
111
|
+
console.log('\nEnvironment looks good. Run "haitask run" after your next commit.');
|
|
40
112
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load .env: try cwd first, then ~/.haitask/.env (Option C — hybrid).
|
|
3
|
+
* So users can have one global .env for all projects or override per repo.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from 'fs';
|
|
7
|
+
import { resolve } from 'path';
|
|
8
|
+
import { config as loadDotenv } from 'dotenv';
|
|
9
|
+
|
|
10
|
+
const CWD = process.cwd();
|
|
11
|
+
const HOME = process.env.HOME || process.env.USERPROFILE || '';
|
|
12
|
+
|
|
13
|
+
function loadEnv(path) {
|
|
14
|
+
if (path && existsSync(path)) {
|
|
15
|
+
loadDotenv({ path });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Load environment: first .env in current directory, then ~/.haitask/.env.
|
|
21
|
+
* Call once at CLI entry (e.g. index.js).
|
|
22
|
+
*/
|
|
23
|
+
export function loadEnvFiles() {
|
|
24
|
+
loadEnv(resolve(CWD, '.env'));
|
|
25
|
+
if (HOME) {
|
|
26
|
+
loadEnv(resolve(HOME, '.haitask', '.env'));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Paths we check for .env (for messaging).
|
|
32
|
+
*/
|
|
33
|
+
export function getEnvPaths() {
|
|
34
|
+
const paths = [resolve(CWD, '.env')];
|
|
35
|
+
if (HOME) paths.push(resolve(HOME, '.haitask', '.env'));
|
|
36
|
+
return paths;
|
|
37
|
+
}
|
package/src/config/init.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import { writeFileSync, existsSync } from 'fs';
|
|
7
7
|
import { resolve } from 'path';
|
|
8
|
-
import { config as loadEnv } from 'dotenv';
|
|
9
8
|
|
|
10
9
|
const DEFAULT_RC = {
|
|
11
10
|
jira: {
|
|
@@ -41,15 +40,13 @@ export function createDefaultConfigFile(dir = process.cwd()) {
|
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
/**
|
|
44
|
-
*
|
|
43
|
+
* Check required env keys (env is already loaded by loadEnvFiles at CLI entry).
|
|
45
44
|
* If config is provided, also validates AI provider key.
|
|
46
|
-
* @param {string} [dir] -
|
|
45
|
+
* @param {string} [dir] - Unused; kept for API compatibility
|
|
47
46
|
* @param {object} [config] - Optional config to check AI provider key
|
|
48
47
|
* @returns {{ valid: boolean, missing: string[] }}
|
|
49
48
|
*/
|
|
50
49
|
export function validateEnv(dir = process.cwd(), config = null) {
|
|
51
|
-
const envPath = resolve(dir, '.env');
|
|
52
|
-
loadEnv({ path: envPath });
|
|
53
50
|
const missing = [...REQUIRED_ENV_KEYS];
|
|
54
51
|
|
|
55
52
|
// Check AI provider key if config provided
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import '
|
|
3
|
+
import { loadEnvFiles } from './config/env-loader.js';
|
|
4
|
+
loadEnvFiles();
|
|
4
5
|
import { program } from 'commander';
|
|
5
6
|
import { runInit } from './commands/init.js';
|
|
6
7
|
import { runRun } from './commands/run.js';
|
|
@@ -8,7 +9,7 @@ import { runRun } from './commands/run.js';
|
|
|
8
9
|
program
|
|
9
10
|
.name('haitask')
|
|
10
11
|
.description('HAITASK — Generate Jira tasks from Git commits using AI')
|
|
11
|
-
.version('0.1.
|
|
12
|
+
.version('0.1.1');
|
|
12
13
|
|
|
13
14
|
program
|
|
14
15
|
.command('init')
|