@qelos/aidev 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/.claude/settings.local.json +9 -0
- package/.cursor/rules/aidev.mdc +57 -0
- package/.env.aidev.example +17 -0
- package/CLAUDE.md +32 -0
- package/CONTRIBUTING.md +78 -0
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/dist/ai/base.d.ts.map +1 -0
- package/dist/ai/base.js +3 -0
- package/dist/ai/claude.d.ts.map +1 -0
- package/dist/ai/claude.js +33 -0
- package/dist/ai/cursor.d.ts.map +1 -0
- package/dist/ai/cursor.js +33 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +74 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +48 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +255 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +257 -0
- package/dist/commands/schedule.d.ts.map +1 -0
- package/dist/commands/schedule.js +191 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +89 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +108 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +88 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +69 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +3 -0
- package/dist/providers/clickup.d.ts.map +1 -0
- package/dist/providers/clickup.js +67 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +19 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/package.json +28 -0
- package/src/ai/base.ts +11 -0
- package/src/ai/claude.ts +35 -0
- package/src/ai/cursor.ts +35 -0
- package/src/ai/index.ts +15 -0
- package/src/cli.ts +83 -0
- package/src/commands/help.ts +43 -0
- package/src/commands/init.ts +296 -0
- package/src/commands/run.ts +283 -0
- package/src/commands/schedule.ts +179 -0
- package/src/config.ts +59 -0
- package/src/git.ts +109 -0
- package/src/logger.ts +53 -0
- package/src/platform.ts +33 -0
- package/src/providers/base.ts +8 -0
- package/src/providers/clickup.ts +107 -0
- package/src/providers/index.ts +20 -0
- package/src/types.ts +33 -0
- package/tsconfig.json +19 -0
package/src/platform.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const isWindows = process.platform === 'win32';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Finds the full path of a binary by searching PATH (and PATHEXT on Windows).
|
|
8
|
+
* Returns the resolved path, or null if not found.
|
|
9
|
+
* Uses only Node.js fs — no `which` / `where` subprocess.
|
|
10
|
+
*/
|
|
11
|
+
export function findBin(name: string): string | null {
|
|
12
|
+
const dirs = (process.env.PATH ?? '').split(path.delimiter).filter(Boolean);
|
|
13
|
+
const exts = isWindows
|
|
14
|
+
? (process.env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').map((e) => e.toLowerCase())
|
|
15
|
+
: [''];
|
|
16
|
+
|
|
17
|
+
for (const dir of dirs) {
|
|
18
|
+
for (const ext of exts) {
|
|
19
|
+
const full = path.join(dir, name + ext);
|
|
20
|
+
try {
|
|
21
|
+
fs.accessSync(full, fs.constants.F_OK);
|
|
22
|
+
return full;
|
|
23
|
+
} catch {
|
|
24
|
+
// not here — keep searching
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function commandExists(name: string): boolean {
|
|
32
|
+
return findBin(name) !== null;
|
|
33
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Task, Comment } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface TaskProvider {
|
|
4
|
+
fetchTasks(): Promise<Task[]>;
|
|
5
|
+
postComment(taskId: string, text: string): Promise<void>;
|
|
6
|
+
getComments(taskId: string): Promise<Comment[]>;
|
|
7
|
+
updateStatus(taskId: string, status: string): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Task, Comment, Config } from '../types';
|
|
2
|
+
import { TaskProvider } from './base';
|
|
3
|
+
import { logger } from '../logger';
|
|
4
|
+
|
|
5
|
+
export class ClickUpProvider implements TaskProvider {
|
|
6
|
+
private apiKey: string;
|
|
7
|
+
private teamId: string;
|
|
8
|
+
private tag: string;
|
|
9
|
+
private assigneeTag: string;
|
|
10
|
+
|
|
11
|
+
constructor(config: Config) {
|
|
12
|
+
this.apiKey = config.clickupApiKey;
|
|
13
|
+
this.teamId = config.clickupTeamId;
|
|
14
|
+
this.tag = config.clickupTag;
|
|
15
|
+
this.assigneeTag = config.assigneeTag;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private async request<T>(path: string, options: RequestInit = {}): Promise<T> {
|
|
19
|
+
const url = `https://api.clickup.com/api/v2${path}`;
|
|
20
|
+
const res = await fetch(url, {
|
|
21
|
+
...options,
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: this.apiKey,
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
...(options.headers || {}),
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
const body = await res.text();
|
|
31
|
+
throw new Error(`ClickUp API error ${res.status}: ${body}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return res.json() as Promise<T>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async fetchTasks(): Promise<Task[]> {
|
|
38
|
+
logger.debug(`Fetching tasks with tag "${this.tag}" from team ${this.teamId}`);
|
|
39
|
+
|
|
40
|
+
interface RawTask {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
status: { status: string };
|
|
45
|
+
url: string;
|
|
46
|
+
tags: Array<{ name: string }>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface TasksResponse {
|
|
50
|
+
tasks: RawTask[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const data = await this.request<TasksResponse>(
|
|
54
|
+
`/team/${this.teamId}/task?tags[]=${encodeURIComponent(this.tag)}&subtasks=true&include_closed=false`
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return data.tasks.map((t) => ({
|
|
58
|
+
id: t.id,
|
|
59
|
+
name: t.name,
|
|
60
|
+
description: t.description || '',
|
|
61
|
+
status: t.status.status.toLowerCase(),
|
|
62
|
+
url: t.url,
|
|
63
|
+
tags: t.tags.map((tag) => tag.name),
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async postComment(taskId: string, text: string): Promise<void> {
|
|
68
|
+
logger.debug(`Posting comment to task ${taskId}`);
|
|
69
|
+
await this.request(`/task/${taskId}/comment`, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
body: JSON.stringify({ comment_text: text }),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async getComments(taskId: string): Promise<Comment[]> {
|
|
76
|
+
logger.debug(`Fetching comments for task ${taskId}`);
|
|
77
|
+
|
|
78
|
+
interface RawComment {
|
|
79
|
+
id: string;
|
|
80
|
+
comment_text: string;
|
|
81
|
+
user: { username: string; id: number };
|
|
82
|
+
date: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface CommentsResponse {
|
|
86
|
+
comments: RawComment[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const data = await this.request<CommentsResponse>(`/task/${taskId}/comment`);
|
|
90
|
+
|
|
91
|
+
return data.comments.map((c) => ({
|
|
92
|
+
id: c.id,
|
|
93
|
+
text: c.comment_text,
|
|
94
|
+
author: c.user.username,
|
|
95
|
+
authorId: String(c.user.id),
|
|
96
|
+
date: parseInt(c.date, 10),
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async updateStatus(taskId: string, status: string): Promise<void> {
|
|
101
|
+
logger.debug(`Updating task ${taskId} status to "${status}"`);
|
|
102
|
+
await this.request(`/task/${taskId}`, {
|
|
103
|
+
method: 'PUT',
|
|
104
|
+
body: JSON.stringify({ status }),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Config } from '../types';
|
|
2
|
+
import { TaskProvider } from './base';
|
|
3
|
+
import { ClickUpProvider } from './clickup';
|
|
4
|
+
|
|
5
|
+
export function createProvider(config: Config): TaskProvider {
|
|
6
|
+
switch (config.provider.toLowerCase()) {
|
|
7
|
+
case 'clickup':
|
|
8
|
+
return new ClickUpProvider(config);
|
|
9
|
+
case 'jira':
|
|
10
|
+
throw new Error('Jira provider is not yet implemented. Contributions welcome!');
|
|
11
|
+
case 'notion':
|
|
12
|
+
throw new Error('Notion provider is not yet implemented. Contributions welcome!');
|
|
13
|
+
case 'trello':
|
|
14
|
+
throw new Error('Trello provider is not yet implemented. Contributions welcome!');
|
|
15
|
+
default:
|
|
16
|
+
throw new Error(`Unknown provider: ${config.provider}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { TaskProvider };
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface Task {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
status: string;
|
|
6
|
+
url: string;
|
|
7
|
+
tags: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Comment {
|
|
11
|
+
id: string;
|
|
12
|
+
text: string;
|
|
13
|
+
author: string;
|
|
14
|
+
authorId: string;
|
|
15
|
+
date: number; // epoch ms
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type AgentName = 'claude' | 'cursor';
|
|
19
|
+
|
|
20
|
+
export interface Config {
|
|
21
|
+
provider: string;
|
|
22
|
+
clickupApiKey: string;
|
|
23
|
+
clickupTeamId: string;
|
|
24
|
+
clickupTag: string;
|
|
25
|
+
clickupPendingStatus: string;
|
|
26
|
+
clickupInReviewStatus: string;
|
|
27
|
+
assigneeTag: string;
|
|
28
|
+
gitRemote: string;
|
|
29
|
+
githubBaseBranch: string;
|
|
30
|
+
githubRepo: string;
|
|
31
|
+
agents: AgentName[];
|
|
32
|
+
devNotesMode: 'smart' | 'always';
|
|
33
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist"]
|
|
19
|
+
}
|