opencode-hive 0.2.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/README.md +91 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +307 -0
- package/dist/services/featureService.d.ts +16 -0
- package/dist/services/featureService.js +117 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.js +4 -0
- package/dist/services/planService.d.ts +11 -0
- package/dist/services/planService.js +59 -0
- package/dist/services/taskService.d.ts +16 -0
- package/dist/services/taskService.js +155 -0
- package/dist/services/worktreeService.d.ts +49 -0
- package/dist/services/worktreeService.js +331 -0
- package/dist/tools/execTools.d.ts +72 -0
- package/dist/tools/execTools.js +123 -0
- package/dist/tools/featureTools.d.ts +104 -0
- package/dist/tools/featureTools.js +113 -0
- package/dist/tools/planTools.d.ts +57 -0
- package/dist/tools/planTools.js +65 -0
- package/dist/tools/taskTools.d.ts +91 -0
- package/dist/tools/taskTools.js +87 -0
- package/dist/types.d.ts +54 -0
- package/dist/types.js +1 -0
- package/dist/utils/paths.d.ts +18 -0
- package/dist/utils/paths.js +75 -0
- package/package.json +47 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { TaskService } from '../services/taskService.js';
|
|
4
|
+
import { FeatureService } from '../services/featureService.js';
|
|
5
|
+
import { WorktreeService } from '../services/worktreeService';
|
|
6
|
+
export function createExecTools(projectRoot) {
|
|
7
|
+
const taskService = new TaskService(projectRoot);
|
|
8
|
+
const featureService = new FeatureService(projectRoot);
|
|
9
|
+
const worktreeService = new WorktreeService({
|
|
10
|
+
baseDir: projectRoot,
|
|
11
|
+
hiveDir: path.join(projectRoot, '.hive'),
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
hive_exec_start: {
|
|
15
|
+
description: 'Create a git worktree and begin work on a task. Sets task status to in_progress.',
|
|
16
|
+
parameters: z.object({
|
|
17
|
+
task: z.string().describe('Task folder name (e.g., "01-auth-service")'),
|
|
18
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
19
|
+
}),
|
|
20
|
+
execute: async ({ task, featureName }) => {
|
|
21
|
+
const feature = featureName || featureService.getActive();
|
|
22
|
+
if (!feature) {
|
|
23
|
+
return { error: 'No active feature.' };
|
|
24
|
+
}
|
|
25
|
+
const featureData = featureService.get(feature);
|
|
26
|
+
if (!featureData || featureData.status === 'planning') {
|
|
27
|
+
return { error: 'Feature must be approved before starting execution.' };
|
|
28
|
+
}
|
|
29
|
+
const taskInfo = taskService.get(feature, task);
|
|
30
|
+
if (!taskInfo) {
|
|
31
|
+
return { error: `Task '${task}' not found` };
|
|
32
|
+
}
|
|
33
|
+
if (taskInfo.status === 'done') {
|
|
34
|
+
return { error: `Task '${task}' is already completed` };
|
|
35
|
+
}
|
|
36
|
+
if (taskInfo.status === 'in_progress') {
|
|
37
|
+
const existing = await worktreeService.get(feature, task);
|
|
38
|
+
if (existing) {
|
|
39
|
+
return {
|
|
40
|
+
worktreePath: existing.path,
|
|
41
|
+
branch: existing.branch,
|
|
42
|
+
message: `Task already in progress. Worktree at ${existing.path}`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const worktree = await worktreeService.create(feature, task);
|
|
47
|
+
taskService.update(feature, task, { status: 'in_progress' });
|
|
48
|
+
return {
|
|
49
|
+
worktreePath: worktree.path,
|
|
50
|
+
branch: worktree.branch,
|
|
51
|
+
message: `Worktree created at ${worktree.path}. Work on branch ${worktree.branch}.`,
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
hive_exec_complete: {
|
|
56
|
+
description: 'Complete a task: commit changes, apply to main branch, write report, cleanup worktree.',
|
|
57
|
+
parameters: z.object({
|
|
58
|
+
task: z.string().describe('Task folder name'),
|
|
59
|
+
summary: z.string().describe('Summary of what was done'),
|
|
60
|
+
report: z.string().optional().describe('Detailed report (markdown). If not provided, summary is used.'),
|
|
61
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
62
|
+
}),
|
|
63
|
+
execute: async ({ task, summary, report, featureName }) => {
|
|
64
|
+
const feature = featureName || featureService.getActive();
|
|
65
|
+
if (!feature) {
|
|
66
|
+
return { error: 'No active feature.' };
|
|
67
|
+
}
|
|
68
|
+
const taskInfo = taskService.get(feature, task);
|
|
69
|
+
if (!taskInfo) {
|
|
70
|
+
return { error: `Task '${task}' not found` };
|
|
71
|
+
}
|
|
72
|
+
if (taskInfo.status !== 'in_progress') {
|
|
73
|
+
return { error: `Task '${task}' is not in progress (status: ${taskInfo.status})` };
|
|
74
|
+
}
|
|
75
|
+
const worktree = await worktreeService.get(feature, task);
|
|
76
|
+
if (!worktree) {
|
|
77
|
+
return { error: `No worktree found for task '${task}'` };
|
|
78
|
+
}
|
|
79
|
+
const diff = await worktreeService.getDiff(feature, task);
|
|
80
|
+
if (diff) {
|
|
81
|
+
await worktreeService.applyDiff(feature, task);
|
|
82
|
+
}
|
|
83
|
+
const reportContent = report || `# ${task}\n\n## Summary\n\n${summary}\n`;
|
|
84
|
+
taskService.writeReport(feature, task, reportContent);
|
|
85
|
+
taskService.update(feature, task, { status: 'done', summary });
|
|
86
|
+
await worktreeService.remove(feature, task);
|
|
87
|
+
return {
|
|
88
|
+
completed: true,
|
|
89
|
+
task,
|
|
90
|
+
summary,
|
|
91
|
+
message: `Task '${task}' completed. Changes applied to main branch.`,
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
hive_exec_abort: {
|
|
96
|
+
description: 'Abort work on a task: discard changes, cleanup worktree, reset status to pending.',
|
|
97
|
+
parameters: z.object({
|
|
98
|
+
task: z.string().describe('Task folder name'),
|
|
99
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
100
|
+
}),
|
|
101
|
+
execute: async ({ task, featureName }) => {
|
|
102
|
+
const feature = featureName || featureService.getActive();
|
|
103
|
+
if (!feature) {
|
|
104
|
+
return { error: 'No active feature.' };
|
|
105
|
+
}
|
|
106
|
+
const taskInfo = taskService.get(feature, task);
|
|
107
|
+
if (!taskInfo) {
|
|
108
|
+
return { error: `Task '${task}' not found` };
|
|
109
|
+
}
|
|
110
|
+
if (taskInfo.status !== 'in_progress') {
|
|
111
|
+
return { error: `Task '${task}' is not in progress` };
|
|
112
|
+
}
|
|
113
|
+
await worktreeService.remove(feature, task);
|
|
114
|
+
taskService.update(feature, task, { status: 'pending' });
|
|
115
|
+
return {
|
|
116
|
+
aborted: true,
|
|
117
|
+
task,
|
|
118
|
+
message: `Task '${task}' aborted. Worktree removed, status reset to pending.`,
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { TaskInfo } from '../types.js';
|
|
3
|
+
export declare function createFeatureTools(projectRoot: string): {
|
|
4
|
+
hive_feature_create: {
|
|
5
|
+
description: string;
|
|
6
|
+
parameters: z.ZodObject<{
|
|
7
|
+
name: z.ZodString;
|
|
8
|
+
ticket: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
execute: ({ name, ticket }: {
|
|
11
|
+
name: string;
|
|
12
|
+
ticket?: string;
|
|
13
|
+
}) => Promise<{
|
|
14
|
+
name: string;
|
|
15
|
+
status: import("../types.js").FeatureStatusType;
|
|
16
|
+
path: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
20
|
+
hive_feature_list: {
|
|
21
|
+
description: string;
|
|
22
|
+
parameters: z.ZodObject<{}, z.core.$strip>;
|
|
23
|
+
execute: () => Promise<{
|
|
24
|
+
features: {
|
|
25
|
+
name: string;
|
|
26
|
+
status: string;
|
|
27
|
+
taskCount: number;
|
|
28
|
+
isActive: boolean;
|
|
29
|
+
}[];
|
|
30
|
+
active: string | null;
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
hive_feature_switch: {
|
|
34
|
+
description: string;
|
|
35
|
+
parameters: z.ZodObject<{
|
|
36
|
+
name: z.ZodString;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
execute: ({ name }: {
|
|
39
|
+
name: string;
|
|
40
|
+
}) => Promise<{
|
|
41
|
+
switched: boolean;
|
|
42
|
+
name: string;
|
|
43
|
+
status: import("../types.js").FeatureStatusType | undefined;
|
|
44
|
+
message: string;
|
|
45
|
+
}>;
|
|
46
|
+
};
|
|
47
|
+
hive_feature_complete: {
|
|
48
|
+
description: string;
|
|
49
|
+
parameters: z.ZodObject<{
|
|
50
|
+
featureName: z.ZodOptional<z.ZodString>;
|
|
51
|
+
}, z.core.$strip>;
|
|
52
|
+
execute: ({ featureName }: {
|
|
53
|
+
featureName?: string;
|
|
54
|
+
}) => Promise<{
|
|
55
|
+
error: string;
|
|
56
|
+
pendingTasks?: undefined;
|
|
57
|
+
completed?: undefined;
|
|
58
|
+
name?: undefined;
|
|
59
|
+
message?: undefined;
|
|
60
|
+
} | {
|
|
61
|
+
error: string;
|
|
62
|
+
pendingTasks: string[];
|
|
63
|
+
completed?: undefined;
|
|
64
|
+
name?: undefined;
|
|
65
|
+
message?: undefined;
|
|
66
|
+
} | {
|
|
67
|
+
completed: boolean;
|
|
68
|
+
name: string;
|
|
69
|
+
message: string;
|
|
70
|
+
error?: undefined;
|
|
71
|
+
pendingTasks?: undefined;
|
|
72
|
+
}>;
|
|
73
|
+
};
|
|
74
|
+
hive_status: {
|
|
75
|
+
description: string;
|
|
76
|
+
parameters: z.ZodObject<{
|
|
77
|
+
featureName: z.ZodOptional<z.ZodString>;
|
|
78
|
+
}, z.core.$strip>;
|
|
79
|
+
execute: ({ featureName }: {
|
|
80
|
+
featureName?: string;
|
|
81
|
+
}) => Promise<{
|
|
82
|
+
error: string;
|
|
83
|
+
feature?: undefined;
|
|
84
|
+
status?: undefined;
|
|
85
|
+
hasPlan?: undefined;
|
|
86
|
+
commentCount?: undefined;
|
|
87
|
+
tasks?: undefined;
|
|
88
|
+
taskSummary?: undefined;
|
|
89
|
+
} | {
|
|
90
|
+
feature: string;
|
|
91
|
+
status: import("../types.js").FeatureStatusType;
|
|
92
|
+
hasPlan: boolean;
|
|
93
|
+
commentCount: number;
|
|
94
|
+
tasks: TaskInfo[];
|
|
95
|
+
taskSummary: {
|
|
96
|
+
pending: number;
|
|
97
|
+
in_progress: number;
|
|
98
|
+
done: number;
|
|
99
|
+
cancelled: number;
|
|
100
|
+
};
|
|
101
|
+
error?: undefined;
|
|
102
|
+
}>;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { FeatureService } from '../services/featureService.js';
|
|
3
|
+
export function createFeatureTools(projectRoot) {
|
|
4
|
+
const featureService = new FeatureService(projectRoot);
|
|
5
|
+
return {
|
|
6
|
+
hive_feature_create: {
|
|
7
|
+
description: 'Create a new feature and set it as active.',
|
|
8
|
+
parameters: z.object({
|
|
9
|
+
name: z.string().describe('Feature name (will be used as folder name)'),
|
|
10
|
+
ticket: z.string().optional().describe('Ticket/issue reference'),
|
|
11
|
+
}),
|
|
12
|
+
execute: async ({ name, ticket }) => {
|
|
13
|
+
const feature = featureService.create(name, ticket);
|
|
14
|
+
return {
|
|
15
|
+
name: feature.name,
|
|
16
|
+
status: feature.status,
|
|
17
|
+
path: `.hive/features/${name}`,
|
|
18
|
+
message: `Feature '${name}' created and set as active. Write a plan with hive_plan_write.`,
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
hive_feature_list: {
|
|
23
|
+
description: 'List all features.',
|
|
24
|
+
parameters: z.object({}),
|
|
25
|
+
execute: async () => {
|
|
26
|
+
const features = featureService.list();
|
|
27
|
+
const active = featureService.getActive();
|
|
28
|
+
const details = features.map(name => {
|
|
29
|
+
const info = featureService.getInfo(name);
|
|
30
|
+
return {
|
|
31
|
+
name,
|
|
32
|
+
status: info?.status || 'unknown',
|
|
33
|
+
taskCount: info?.tasks.length || 0,
|
|
34
|
+
isActive: name === active,
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
return { features: details, active };
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
hive_feature_switch: {
|
|
41
|
+
description: 'Switch to a different feature.',
|
|
42
|
+
parameters: z.object({
|
|
43
|
+
name: z.string().describe('Feature name to switch to'),
|
|
44
|
+
}),
|
|
45
|
+
execute: async ({ name }) => {
|
|
46
|
+
featureService.setActive(name);
|
|
47
|
+
const info = featureService.getInfo(name);
|
|
48
|
+
return {
|
|
49
|
+
switched: true,
|
|
50
|
+
name,
|
|
51
|
+
status: info?.status,
|
|
52
|
+
message: `Switched to feature '${name}'`,
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
hive_feature_complete: {
|
|
57
|
+
description: 'Mark a feature as completed. This is irreversible.',
|
|
58
|
+
parameters: z.object({
|
|
59
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
60
|
+
}),
|
|
61
|
+
execute: async ({ featureName }) => {
|
|
62
|
+
const feature = featureName || featureService.getActive();
|
|
63
|
+
if (!feature) {
|
|
64
|
+
return { error: 'No active feature.' };
|
|
65
|
+
}
|
|
66
|
+
const info = featureService.getInfo(feature);
|
|
67
|
+
const pendingTasks = info?.tasks.filter((t) => t.status === 'pending' || t.status === 'in_progress') || [];
|
|
68
|
+
if (pendingTasks.length > 0) {
|
|
69
|
+
return {
|
|
70
|
+
error: `Cannot complete: ${pendingTasks.length} task(s) still pending or in progress`,
|
|
71
|
+
pendingTasks: pendingTasks.map((t) => t.folder),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
featureService.complete(feature);
|
|
75
|
+
return {
|
|
76
|
+
completed: true,
|
|
77
|
+
name: feature,
|
|
78
|
+
message: `Feature '${feature}' marked as completed.`,
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
hive_status: {
|
|
83
|
+
description: 'Get overview of the active feature: status, plan, tasks.',
|
|
84
|
+
parameters: z.object({
|
|
85
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
86
|
+
}),
|
|
87
|
+
execute: async ({ featureName }) => {
|
|
88
|
+
const feature = featureName || featureService.getActive();
|
|
89
|
+
if (!feature) {
|
|
90
|
+
return { error: 'No active feature. Create one with hive_feature_create.' };
|
|
91
|
+
}
|
|
92
|
+
const info = featureService.getInfo(feature);
|
|
93
|
+
if (!info) {
|
|
94
|
+
return { error: `Feature '${feature}' not found` };
|
|
95
|
+
}
|
|
96
|
+
const tasksByStatus = {
|
|
97
|
+
pending: info.tasks.filter((t) => t.status === 'pending').length,
|
|
98
|
+
in_progress: info.tasks.filter((t) => t.status === 'in_progress').length,
|
|
99
|
+
done: info.tasks.filter((t) => t.status === 'done').length,
|
|
100
|
+
cancelled: info.tasks.filter((t) => t.status === 'cancelled').length,
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
feature: info.name,
|
|
104
|
+
status: info.status,
|
|
105
|
+
hasPlan: info.hasPlan,
|
|
106
|
+
commentCount: info.commentCount,
|
|
107
|
+
tasks: info.tasks,
|
|
108
|
+
taskSummary: tasksByStatus,
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare function createPlanTools(projectRoot: string): {
|
|
3
|
+
hive_plan_write: {
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: z.ZodObject<{
|
|
6
|
+
content: z.ZodString;
|
|
7
|
+
featureName: z.ZodOptional<z.ZodString>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
execute: ({ content, featureName }: {
|
|
10
|
+
content: string;
|
|
11
|
+
featureName?: string;
|
|
12
|
+
}) => Promise<{
|
|
13
|
+
error: string;
|
|
14
|
+
path?: undefined;
|
|
15
|
+
message?: undefined;
|
|
16
|
+
} | {
|
|
17
|
+
path: string;
|
|
18
|
+
message: string;
|
|
19
|
+
error?: undefined;
|
|
20
|
+
}>;
|
|
21
|
+
};
|
|
22
|
+
hive_plan_read: {
|
|
23
|
+
description: string;
|
|
24
|
+
parameters: z.ZodObject<{
|
|
25
|
+
featureName: z.ZodOptional<z.ZodString>;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
execute: ({ featureName }: {
|
|
28
|
+
featureName?: string;
|
|
29
|
+
}) => Promise<import("../types.js").PlanReadResult | {
|
|
30
|
+
error: string;
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
hive_plan_approve: {
|
|
34
|
+
description: string;
|
|
35
|
+
parameters: z.ZodObject<{
|
|
36
|
+
featureName: z.ZodOptional<z.ZodString>;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
execute: ({ featureName }: {
|
|
39
|
+
featureName?: string;
|
|
40
|
+
}) => Promise<{
|
|
41
|
+
error: string;
|
|
42
|
+
comments?: undefined;
|
|
43
|
+
approved?: undefined;
|
|
44
|
+
message?: undefined;
|
|
45
|
+
} | {
|
|
46
|
+
error: string;
|
|
47
|
+
comments: import("../types.js").PlanComment[];
|
|
48
|
+
approved?: undefined;
|
|
49
|
+
message?: undefined;
|
|
50
|
+
} | {
|
|
51
|
+
approved: boolean;
|
|
52
|
+
message: string;
|
|
53
|
+
error?: undefined;
|
|
54
|
+
comments?: undefined;
|
|
55
|
+
}>;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PlanService } from '../services/planService.js';
|
|
3
|
+
import { FeatureService } from '../services/featureService.js';
|
|
4
|
+
export function createPlanTools(projectRoot) {
|
|
5
|
+
const planService = new PlanService(projectRoot);
|
|
6
|
+
const featureService = new FeatureService(projectRoot);
|
|
7
|
+
return {
|
|
8
|
+
hive_plan_write: {
|
|
9
|
+
description: 'Create or update the plan.md for the active feature. Clears any existing comments (new plan = fresh review).',
|
|
10
|
+
parameters: z.object({
|
|
11
|
+
content: z.string().describe('The markdown content for the plan'),
|
|
12
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
13
|
+
}),
|
|
14
|
+
execute: async ({ content, featureName }) => {
|
|
15
|
+
const feature = featureName || featureService.getActive();
|
|
16
|
+
if (!feature) {
|
|
17
|
+
return { error: 'No active feature. Create one with hive_feature_create first.' };
|
|
18
|
+
}
|
|
19
|
+
const path = planService.write(feature, content);
|
|
20
|
+
return { path, message: `Plan written to ${path}. Comments cleared for fresh review.` };
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
hive_plan_read: {
|
|
24
|
+
description: 'Read the plan.md and any user comments for the active feature.',
|
|
25
|
+
parameters: z.object({
|
|
26
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
27
|
+
}),
|
|
28
|
+
execute: async ({ featureName }) => {
|
|
29
|
+
const feature = featureName || featureService.getActive();
|
|
30
|
+
if (!feature) {
|
|
31
|
+
return { error: 'No active feature.' };
|
|
32
|
+
}
|
|
33
|
+
const result = planService.read(feature);
|
|
34
|
+
if (!result) {
|
|
35
|
+
return { error: `No plan.md found for feature '${feature}'` };
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
hive_plan_approve: {
|
|
41
|
+
description: 'Approve the plan for execution. After approval, run hive_tasks_sync to generate tasks.',
|
|
42
|
+
parameters: z.object({
|
|
43
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
44
|
+
}),
|
|
45
|
+
execute: async ({ featureName }) => {
|
|
46
|
+
const feature = featureName || featureService.getActive();
|
|
47
|
+
if (!feature) {
|
|
48
|
+
return { error: 'No active feature.' };
|
|
49
|
+
}
|
|
50
|
+
const comments = planService.getComments(feature);
|
|
51
|
+
if (comments.length > 0) {
|
|
52
|
+
return {
|
|
53
|
+
error: `Cannot approve: ${comments.length} unresolved comment(s). Address comments and rewrite plan first.`,
|
|
54
|
+
comments,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
planService.approve(feature);
|
|
58
|
+
return {
|
|
59
|
+
approved: true,
|
|
60
|
+
message: 'Plan approved. Run hive_tasks_sync to generate tasks from the plan.',
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare function createTaskTools(projectRoot: string): {
|
|
3
|
+
hive_tasks_sync: {
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: z.ZodObject<{
|
|
6
|
+
featureName: z.ZodOptional<z.ZodString>;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
execute: ({ featureName }: {
|
|
9
|
+
featureName?: string;
|
|
10
|
+
}) => Promise<{
|
|
11
|
+
error: string;
|
|
12
|
+
} | {
|
|
13
|
+
message: string;
|
|
14
|
+
created: string[];
|
|
15
|
+
removed: string[];
|
|
16
|
+
kept: string[];
|
|
17
|
+
manual: string[];
|
|
18
|
+
error?: undefined;
|
|
19
|
+
}>;
|
|
20
|
+
};
|
|
21
|
+
hive_task_create: {
|
|
22
|
+
description: string;
|
|
23
|
+
parameters: z.ZodObject<{
|
|
24
|
+
name: z.ZodString;
|
|
25
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
26
|
+
featureName: z.ZodOptional<z.ZodString>;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
execute: ({ name, order, featureName }: {
|
|
29
|
+
name: string;
|
|
30
|
+
order?: number;
|
|
31
|
+
featureName?: string;
|
|
32
|
+
}) => Promise<{
|
|
33
|
+
error: string;
|
|
34
|
+
folder?: undefined;
|
|
35
|
+
origin?: undefined;
|
|
36
|
+
message?: undefined;
|
|
37
|
+
} | {
|
|
38
|
+
folder: string;
|
|
39
|
+
origin: string;
|
|
40
|
+
message: string;
|
|
41
|
+
error?: undefined;
|
|
42
|
+
}>;
|
|
43
|
+
};
|
|
44
|
+
hive_task_update: {
|
|
45
|
+
description: string;
|
|
46
|
+
parameters: z.ZodObject<{
|
|
47
|
+
task: z.ZodString;
|
|
48
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
49
|
+
pending: "pending";
|
|
50
|
+
in_progress: "in_progress";
|
|
51
|
+
done: "done";
|
|
52
|
+
cancelled: "cancelled";
|
|
53
|
+
}>>;
|
|
54
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
55
|
+
featureName: z.ZodOptional<z.ZodString>;
|
|
56
|
+
}, z.core.$strip>;
|
|
57
|
+
execute: ({ task, status, summary, featureName }: {
|
|
58
|
+
task: string;
|
|
59
|
+
status?: "pending" | "in_progress" | "done" | "cancelled";
|
|
60
|
+
summary?: string;
|
|
61
|
+
featureName?: string;
|
|
62
|
+
}) => Promise<{
|
|
63
|
+
error: string;
|
|
64
|
+
updated?: undefined;
|
|
65
|
+
task?: undefined;
|
|
66
|
+
status?: undefined;
|
|
67
|
+
summary?: undefined;
|
|
68
|
+
} | {
|
|
69
|
+
updated: boolean;
|
|
70
|
+
task: string;
|
|
71
|
+
status: import("../types.js").TaskStatusType;
|
|
72
|
+
summary: string | undefined;
|
|
73
|
+
error?: undefined;
|
|
74
|
+
}>;
|
|
75
|
+
};
|
|
76
|
+
hive_task_list: {
|
|
77
|
+
description: string;
|
|
78
|
+
parameters: z.ZodObject<{
|
|
79
|
+
featureName: z.ZodOptional<z.ZodString>;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
execute: ({ featureName }: {
|
|
82
|
+
featureName?: string;
|
|
83
|
+
}) => Promise<{
|
|
84
|
+
error: string;
|
|
85
|
+
tasks?: undefined;
|
|
86
|
+
} | {
|
|
87
|
+
tasks: import("../types.js").TaskInfo[];
|
|
88
|
+
error?: undefined;
|
|
89
|
+
}>;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { TaskService } from '../services/taskService.js';
|
|
3
|
+
import { FeatureService } from '../services/featureService.js';
|
|
4
|
+
export function createTaskTools(projectRoot) {
|
|
5
|
+
const taskService = new TaskService(projectRoot);
|
|
6
|
+
const featureService = new FeatureService(projectRoot);
|
|
7
|
+
return {
|
|
8
|
+
hive_tasks_sync: {
|
|
9
|
+
description: 'Parse the approved plan.md and sync task folders. Creates new tasks, removes obsolete pending tasks, keeps completed/in-progress tasks.',
|
|
10
|
+
parameters: z.object({
|
|
11
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
12
|
+
}),
|
|
13
|
+
execute: async ({ featureName }) => {
|
|
14
|
+
const feature = featureName || featureService.getActive();
|
|
15
|
+
if (!feature) {
|
|
16
|
+
return { error: 'No active feature.' };
|
|
17
|
+
}
|
|
18
|
+
const featureData = featureService.get(feature);
|
|
19
|
+
if (!featureData) {
|
|
20
|
+
return { error: `Feature '${feature}' not found` };
|
|
21
|
+
}
|
|
22
|
+
if (featureData.status === 'planning') {
|
|
23
|
+
return { error: 'Plan must be approved before syncing tasks. Run hive_plan_approve first.' };
|
|
24
|
+
}
|
|
25
|
+
const result = taskService.sync(feature);
|
|
26
|
+
if (featureData.status === 'approved') {
|
|
27
|
+
featureService.updateStatus(feature, 'executing');
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
...result,
|
|
31
|
+
message: `Synced tasks: ${result.created.length} created, ${result.removed.length} removed, ${result.kept.length} kept, ${result.manual.length} manual`,
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
hive_task_create: {
|
|
36
|
+
description: 'Create a manual task (not from plan). Use for hotfixes or ad-hoc work during execution.',
|
|
37
|
+
parameters: z.object({
|
|
38
|
+
name: z.string().describe('Task name (will be slugified)'),
|
|
39
|
+
order: z.number().optional().describe('Task order number (defaults to next available)'),
|
|
40
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
41
|
+
}),
|
|
42
|
+
execute: async ({ name, order, featureName }) => {
|
|
43
|
+
const feature = featureName || featureService.getActive();
|
|
44
|
+
if (!feature) {
|
|
45
|
+
return { error: 'No active feature.' };
|
|
46
|
+
}
|
|
47
|
+
const folder = taskService.create(feature, name, order);
|
|
48
|
+
return {
|
|
49
|
+
folder,
|
|
50
|
+
origin: 'manual',
|
|
51
|
+
message: `Created manual task '${folder}'. This task will not be affected by hive_tasks_sync.`,
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
hive_task_update: {
|
|
56
|
+
description: 'Update a task status or summary.',
|
|
57
|
+
parameters: z.object({
|
|
58
|
+
task: z.string().describe('Task folder name (e.g., "01-auth-service")'),
|
|
59
|
+
status: z.enum(['pending', 'in_progress', 'done', 'cancelled']).optional(),
|
|
60
|
+
summary: z.string().optional().describe('Summary of work done'),
|
|
61
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
62
|
+
}),
|
|
63
|
+
execute: async ({ task, status, summary, featureName }) => {
|
|
64
|
+
const feature = featureName || featureService.getActive();
|
|
65
|
+
if (!feature) {
|
|
66
|
+
return { error: 'No active feature.' };
|
|
67
|
+
}
|
|
68
|
+
const updated = taskService.update(feature, task, { status, summary });
|
|
69
|
+
return { updated: true, task, status: updated.status, summary: updated.summary };
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
hive_task_list: {
|
|
73
|
+
description: 'List all tasks for the active feature.',
|
|
74
|
+
parameters: z.object({
|
|
75
|
+
featureName: z.string().optional().describe('Feature name (defaults to active feature)'),
|
|
76
|
+
}),
|
|
77
|
+
execute: async ({ featureName }) => {
|
|
78
|
+
const feature = featureName || featureService.getActive();
|
|
79
|
+
if (!feature) {
|
|
80
|
+
return { error: 'No active feature.' };
|
|
81
|
+
}
|
|
82
|
+
const tasks = taskService.list(feature);
|
|
83
|
+
return { tasks };
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|