opencode-hive 0.6.0 → 0.8.1
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/dist/index.js +19268 -479
- package/package.json +4 -3
- package/dist/e2e/opencode-runtime-smoke.test.d.ts +0 -1
- package/dist/e2e/opencode-runtime-smoke.test.js +0 -243
- package/dist/e2e/plugin-smoke.test.d.ts +0 -1
- package/dist/e2e/plugin-smoke.test.js +0 -127
- package/dist/services/contextService.d.ts +0 -15
- package/dist/services/contextService.js +0 -59
- package/dist/services/featureService.d.ts +0 -14
- package/dist/services/featureService.js +0 -107
- package/dist/services/featureService.test.d.ts +0 -1
- package/dist/services/featureService.test.js +0 -127
- package/dist/services/index.d.ts +0 -5
- package/dist/services/index.js +0 -4
- package/dist/services/planService.d.ts +0 -11
- package/dist/services/planService.js +0 -59
- package/dist/services/planService.test.d.ts +0 -1
- package/dist/services/planService.test.js +0 -115
- package/dist/services/sessionService.d.ts +0 -31
- package/dist/services/sessionService.js +0 -125
- package/dist/services/taskService.d.ts +0 -17
- package/dist/services/taskService.js +0 -230
- package/dist/services/taskService.test.d.ts +0 -1
- package/dist/services/taskService.test.js +0 -159
- package/dist/services/worktreeService.d.ts +0 -66
- package/dist/services/worktreeService.js +0 -498
- package/dist/services/worktreeService.test.d.ts +0 -1
- package/dist/services/worktreeService.test.js +0 -185
- package/dist/tools/contextTools.d.ts +0 -93
- package/dist/tools/contextTools.js +0 -83
- package/dist/tools/execTools.d.ts +0 -66
- package/dist/tools/execTools.js +0 -125
- package/dist/tools/featureTools.d.ts +0 -60
- package/dist/tools/featureTools.js +0 -73
- package/dist/tools/planTools.d.ts +0 -47
- package/dist/tools/planTools.js +0 -65
- package/dist/tools/sessionTools.d.ts +0 -35
- package/dist/tools/sessionTools.js +0 -95
- package/dist/tools/taskTools.d.ts +0 -79
- package/dist/tools/taskTools.js +0 -86
- package/dist/types.d.ts +0 -89
- package/dist/types.js +0 -1
- package/dist/utils/detection.d.ts +0 -12
- package/dist/utils/detection.js +0 -73
- package/dist/utils/paths.d.ts +0 -18
- package/dist/utils/paths.js +0 -74
- package/dist/utils/paths.test.d.ts +0 -1
- package/dist/utils/paths.test.js +0 -100
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import { getTasksPath, getTaskPath, getTaskStatusPath, getTaskReportPath, getTaskSpecPath, getPlanPath, ensureDir, readJson, writeJson, readText, writeText, fileExists, } from '../utils/paths.js';
|
|
3
|
-
export class TaskService {
|
|
4
|
-
projectRoot;
|
|
5
|
-
constructor(projectRoot) {
|
|
6
|
-
this.projectRoot = projectRoot;
|
|
7
|
-
}
|
|
8
|
-
sync(featureName) {
|
|
9
|
-
const planPath = getPlanPath(this.projectRoot, featureName);
|
|
10
|
-
const planContent = readText(planPath);
|
|
11
|
-
if (!planContent) {
|
|
12
|
-
throw new Error(`No plan.md found for feature '${featureName}'`);
|
|
13
|
-
}
|
|
14
|
-
const planTasks = this.parseTasksFromPlan(planContent);
|
|
15
|
-
const existingTasks = this.list(featureName);
|
|
16
|
-
const result = {
|
|
17
|
-
created: [],
|
|
18
|
-
removed: [],
|
|
19
|
-
kept: [],
|
|
20
|
-
manual: [],
|
|
21
|
-
};
|
|
22
|
-
const existingByName = new Map(existingTasks.map(t => [t.folder, t]));
|
|
23
|
-
for (const existing of existingTasks) {
|
|
24
|
-
if (existing.origin === 'manual') {
|
|
25
|
-
result.manual.push(existing.folder);
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
28
|
-
if (existing.status === 'done' || existing.status === 'in_progress') {
|
|
29
|
-
result.kept.push(existing.folder);
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (existing.status === 'cancelled') {
|
|
33
|
-
this.deleteTask(featureName, existing.folder);
|
|
34
|
-
result.removed.push(existing.folder);
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
const stillInPlan = planTasks.some(p => p.folder === existing.folder);
|
|
38
|
-
if (!stillInPlan) {
|
|
39
|
-
this.deleteTask(featureName, existing.folder);
|
|
40
|
-
result.removed.push(existing.folder);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
result.kept.push(existing.folder);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
for (const planTask of planTasks) {
|
|
47
|
-
if (!existingByName.has(planTask.folder)) {
|
|
48
|
-
this.createFromPlan(featureName, planTask, planTasks);
|
|
49
|
-
result.created.push(planTask.folder);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return result;
|
|
53
|
-
}
|
|
54
|
-
create(featureName, name, order) {
|
|
55
|
-
const tasksPath = getTasksPath(this.projectRoot, featureName);
|
|
56
|
-
const existingFolders = this.listFolders(featureName);
|
|
57
|
-
const nextOrder = order ?? this.getNextOrder(existingFolders);
|
|
58
|
-
const folder = `${String(nextOrder).padStart(2, '0')}-${name}`;
|
|
59
|
-
const taskPath = getTaskPath(this.projectRoot, featureName, folder);
|
|
60
|
-
ensureDir(taskPath);
|
|
61
|
-
const status = {
|
|
62
|
-
status: 'pending',
|
|
63
|
-
origin: 'manual',
|
|
64
|
-
};
|
|
65
|
-
writeJson(getTaskStatusPath(this.projectRoot, featureName, folder), status);
|
|
66
|
-
return folder;
|
|
67
|
-
}
|
|
68
|
-
createFromPlan(featureName, task, allTasks) {
|
|
69
|
-
const taskPath = getTaskPath(this.projectRoot, featureName, task.folder);
|
|
70
|
-
ensureDir(taskPath);
|
|
71
|
-
const status = {
|
|
72
|
-
status: 'pending',
|
|
73
|
-
origin: 'plan',
|
|
74
|
-
};
|
|
75
|
-
writeJson(getTaskStatusPath(this.projectRoot, featureName, task.folder), status);
|
|
76
|
-
// Write enhanced spec.md with full context
|
|
77
|
-
const specLines = [
|
|
78
|
-
`# Task ${task.order}: ${task.name}`,
|
|
79
|
-
'',
|
|
80
|
-
`**Feature:** ${featureName}`,
|
|
81
|
-
`**Folder:** ${task.folder}`,
|
|
82
|
-
`**Status:** pending`,
|
|
83
|
-
'',
|
|
84
|
-
'---',
|
|
85
|
-
'',
|
|
86
|
-
'## Description',
|
|
87
|
-
'',
|
|
88
|
-
task.description || '_No description provided in plan_',
|
|
89
|
-
'',
|
|
90
|
-
];
|
|
91
|
-
// Add prior tasks section if not first task
|
|
92
|
-
if (task.order > 1) {
|
|
93
|
-
const priorTasks = allTasks.filter(t => t.order < task.order);
|
|
94
|
-
if (priorTasks.length > 0) {
|
|
95
|
-
specLines.push('---', '', '## Prior Tasks', '');
|
|
96
|
-
for (const prior of priorTasks) {
|
|
97
|
-
specLines.push(`- **${prior.order}. ${prior.name}** (${prior.folder})`);
|
|
98
|
-
}
|
|
99
|
-
specLines.push('');
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
// Add next tasks section if not last task
|
|
103
|
-
const nextTasks = allTasks.filter(t => t.order > task.order);
|
|
104
|
-
if (nextTasks.length > 0) {
|
|
105
|
-
specLines.push('---', '', '## Upcoming Tasks', '');
|
|
106
|
-
for (const next of nextTasks) {
|
|
107
|
-
specLines.push(`- **${next.order}. ${next.name}** (${next.folder})`);
|
|
108
|
-
}
|
|
109
|
-
specLines.push('');
|
|
110
|
-
}
|
|
111
|
-
writeText(getTaskSpecPath(this.projectRoot, featureName, task.folder), specLines.join('\n'));
|
|
112
|
-
}
|
|
113
|
-
writeSpec(featureName, taskFolder, content) {
|
|
114
|
-
const specPath = getTaskSpecPath(this.projectRoot, featureName, taskFolder);
|
|
115
|
-
writeText(specPath, content);
|
|
116
|
-
return specPath;
|
|
117
|
-
}
|
|
118
|
-
update(featureName, taskFolder, updates) {
|
|
119
|
-
const statusPath = getTaskStatusPath(this.projectRoot, featureName, taskFolder);
|
|
120
|
-
const current = readJson(statusPath);
|
|
121
|
-
if (!current) {
|
|
122
|
-
throw new Error(`Task '${taskFolder}' not found`);
|
|
123
|
-
}
|
|
124
|
-
const updated = {
|
|
125
|
-
...current,
|
|
126
|
-
...updates,
|
|
127
|
-
};
|
|
128
|
-
if (updates.status === 'in_progress' && !current.startedAt) {
|
|
129
|
-
updated.startedAt = new Date().toISOString();
|
|
130
|
-
}
|
|
131
|
-
if (updates.status === 'done' && !current.completedAt) {
|
|
132
|
-
updated.completedAt = new Date().toISOString();
|
|
133
|
-
}
|
|
134
|
-
writeJson(statusPath, updated);
|
|
135
|
-
return updated;
|
|
136
|
-
}
|
|
137
|
-
get(featureName, taskFolder) {
|
|
138
|
-
const statusPath = getTaskStatusPath(this.projectRoot, featureName, taskFolder);
|
|
139
|
-
const status = readJson(statusPath);
|
|
140
|
-
if (!status)
|
|
141
|
-
return null;
|
|
142
|
-
return {
|
|
143
|
-
folder: taskFolder,
|
|
144
|
-
name: taskFolder.replace(/^\d+-/, ''),
|
|
145
|
-
status: status.status,
|
|
146
|
-
origin: status.origin,
|
|
147
|
-
summary: status.summary,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
list(featureName) {
|
|
151
|
-
const folders = this.listFolders(featureName);
|
|
152
|
-
return folders
|
|
153
|
-
.map(folder => this.get(featureName, folder))
|
|
154
|
-
.filter((t) => t !== null);
|
|
155
|
-
}
|
|
156
|
-
writeReport(featureName, taskFolder, report) {
|
|
157
|
-
const reportPath = getTaskReportPath(this.projectRoot, featureName, taskFolder);
|
|
158
|
-
writeText(reportPath, report);
|
|
159
|
-
return reportPath;
|
|
160
|
-
}
|
|
161
|
-
listFolders(featureName) {
|
|
162
|
-
const tasksPath = getTasksPath(this.projectRoot, featureName);
|
|
163
|
-
if (!fileExists(tasksPath))
|
|
164
|
-
return [];
|
|
165
|
-
return fs.readdirSync(tasksPath, { withFileTypes: true })
|
|
166
|
-
.filter(d => d.isDirectory())
|
|
167
|
-
.map(d => d.name)
|
|
168
|
-
.sort();
|
|
169
|
-
}
|
|
170
|
-
deleteTask(featureName, taskFolder) {
|
|
171
|
-
const taskPath = getTaskPath(this.projectRoot, featureName, taskFolder);
|
|
172
|
-
if (fileExists(taskPath)) {
|
|
173
|
-
fs.rmSync(taskPath, { recursive: true });
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
getNextOrder(existingFolders) {
|
|
177
|
-
if (existingFolders.length === 0)
|
|
178
|
-
return 1;
|
|
179
|
-
const orders = existingFolders
|
|
180
|
-
.map(f => parseInt(f.split('-')[0], 10))
|
|
181
|
-
.filter(n => !isNaN(n));
|
|
182
|
-
return Math.max(...orders, 0) + 1;
|
|
183
|
-
}
|
|
184
|
-
parseTasksFromPlan(content) {
|
|
185
|
-
const tasks = [];
|
|
186
|
-
const lines = content.split('\n');
|
|
187
|
-
let currentTask = null;
|
|
188
|
-
let descriptionLines = [];
|
|
189
|
-
for (const line of lines) {
|
|
190
|
-
// Check for task header: ### N. Task Name
|
|
191
|
-
const taskMatch = line.match(/^###\s+(\d+)\.\s+(.+)$/);
|
|
192
|
-
if (taskMatch) {
|
|
193
|
-
// Save previous task if exists
|
|
194
|
-
if (currentTask) {
|
|
195
|
-
currentTask.description = descriptionLines.join('\n').trim();
|
|
196
|
-
tasks.push(currentTask);
|
|
197
|
-
}
|
|
198
|
-
const order = parseInt(taskMatch[1], 10);
|
|
199
|
-
const rawName = taskMatch[2].trim();
|
|
200
|
-
const folderName = rawName.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
201
|
-
const folder = `${String(order).padStart(2, '0')}-${folderName}`;
|
|
202
|
-
currentTask = {
|
|
203
|
-
folder,
|
|
204
|
-
order,
|
|
205
|
-
name: rawName,
|
|
206
|
-
description: '',
|
|
207
|
-
};
|
|
208
|
-
descriptionLines = [];
|
|
209
|
-
}
|
|
210
|
-
else if (currentTask) {
|
|
211
|
-
// Check for end of task section (next ## header or ### without number)
|
|
212
|
-
if (line.match(/^##\s+/) || line.match(/^###\s+[^0-9]/)) {
|
|
213
|
-
currentTask.description = descriptionLines.join('\n').trim();
|
|
214
|
-
tasks.push(currentTask);
|
|
215
|
-
currentTask = null;
|
|
216
|
-
descriptionLines = [];
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
descriptionLines.push(line);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
// Don't forget the last task
|
|
224
|
-
if (currentTask) {
|
|
225
|
-
currentTask.description = descriptionLines.join('\n').trim();
|
|
226
|
-
tasks.push(currentTask);
|
|
227
|
-
}
|
|
228
|
-
return tasks;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import { TaskService } from "./taskService";
|
|
4
|
-
import { FeatureService } from "./featureService";
|
|
5
|
-
import { PlanService } from "./planService";
|
|
6
|
-
import { getTaskPath, getTaskStatusPath, getTaskReportPath } from "../utils/paths";
|
|
7
|
-
const TEST_ROOT = "/tmp/hive-test-task";
|
|
8
|
-
describe("TaskService", () => {
|
|
9
|
-
let taskService;
|
|
10
|
-
let featureService;
|
|
11
|
-
let planService;
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
fs.rmSync(TEST_ROOT, { recursive: true, force: true });
|
|
14
|
-
fs.mkdirSync(TEST_ROOT, { recursive: true });
|
|
15
|
-
featureService = new FeatureService(TEST_ROOT);
|
|
16
|
-
planService = new PlanService(TEST_ROOT);
|
|
17
|
-
taskService = new TaskService(TEST_ROOT);
|
|
18
|
-
featureService.create("test-feature");
|
|
19
|
-
});
|
|
20
|
-
afterEach(() => {
|
|
21
|
-
fs.rmSync(TEST_ROOT, { recursive: true, force: true });
|
|
22
|
-
});
|
|
23
|
-
describe("sync", () => {
|
|
24
|
-
it("throws when no plan exists", () => {
|
|
25
|
-
featureService.create("no-plan");
|
|
26
|
-
expect(() => taskService.sync("no-plan")).toThrow();
|
|
27
|
-
});
|
|
28
|
-
it("creates tasks from plan", () => {
|
|
29
|
-
planService.write("test-feature", `# Plan
|
|
30
|
-
|
|
31
|
-
## Tasks
|
|
32
|
-
|
|
33
|
-
### 1. Setup Database
|
|
34
|
-
Description
|
|
35
|
-
|
|
36
|
-
### 2. Create API
|
|
37
|
-
Description
|
|
38
|
-
`);
|
|
39
|
-
const result = taskService.sync("test-feature");
|
|
40
|
-
expect(result.created).toContain("01-setup-database");
|
|
41
|
-
expect(result.created).toContain("02-create-api");
|
|
42
|
-
expect(result.created.length).toBe(2);
|
|
43
|
-
});
|
|
44
|
-
it("keeps done tasks even if removed from plan", () => {
|
|
45
|
-
planService.write("test-feature", `# Plan\n\n## Tasks\n\n### 1. First Task\nDesc`);
|
|
46
|
-
taskService.sync("test-feature");
|
|
47
|
-
taskService.update("test-feature", "01-first-task", { status: "done" });
|
|
48
|
-
planService.write("test-feature", `# Plan\n\n## Tasks\n\n### 1. Different Task\nDesc`);
|
|
49
|
-
const result = taskService.sync("test-feature");
|
|
50
|
-
expect(result.kept).toContain("01-first-task");
|
|
51
|
-
});
|
|
52
|
-
it("removes cancelled tasks", () => {
|
|
53
|
-
planService.write("test-feature", `# Plan\n\n## Tasks\n\n### 1. Task One\nDesc`);
|
|
54
|
-
taskService.sync("test-feature");
|
|
55
|
-
taskService.update("test-feature", "01-task-one", { status: "cancelled" });
|
|
56
|
-
const result = taskService.sync("test-feature");
|
|
57
|
-
expect(result.removed).toContain("01-task-one");
|
|
58
|
-
});
|
|
59
|
-
it("preserves manual tasks", () => {
|
|
60
|
-
planService.write("test-feature", `# Plan\n\n## Tasks\n\n### 1. Plan Task\nDesc`);
|
|
61
|
-
taskService.sync("test-feature");
|
|
62
|
-
taskService.create("test-feature", "manual-task");
|
|
63
|
-
const result = taskService.sync("test-feature");
|
|
64
|
-
expect(result.manual).toContain("02-manual-task");
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
describe("create", () => {
|
|
68
|
-
it("creates a manual task", () => {
|
|
69
|
-
const folder = taskService.create("test-feature", "my-task");
|
|
70
|
-
expect(folder).toBe("01-my-task");
|
|
71
|
-
expect(fs.existsSync(getTaskPath(TEST_ROOT, "test-feature", folder))).toBe(true);
|
|
72
|
-
});
|
|
73
|
-
it("auto-increments order", () => {
|
|
74
|
-
taskService.create("test-feature", "first");
|
|
75
|
-
taskService.create("test-feature", "second");
|
|
76
|
-
const third = taskService.create("test-feature", "third");
|
|
77
|
-
expect(third).toBe("03-third");
|
|
78
|
-
});
|
|
79
|
-
it("respects explicit order", () => {
|
|
80
|
-
const folder = taskService.create("test-feature", "specific", 10);
|
|
81
|
-
expect(folder).toBe("10-specific");
|
|
82
|
-
});
|
|
83
|
-
it("creates task with pending status and manual origin", () => {
|
|
84
|
-
const folder = taskService.create("test-feature", "test");
|
|
85
|
-
const task = taskService.get("test-feature", folder);
|
|
86
|
-
expect(task?.status).toBe("pending");
|
|
87
|
-
expect(task?.origin).toBe("manual");
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
describe("update", () => {
|
|
91
|
-
it("updates task status", () => {
|
|
92
|
-
const folder = taskService.create("test-feature", "task");
|
|
93
|
-
taskService.update("test-feature", folder, { status: "in_progress" });
|
|
94
|
-
const task = taskService.get("test-feature", folder);
|
|
95
|
-
expect(task?.status).toBe("in_progress");
|
|
96
|
-
});
|
|
97
|
-
it("sets startedAt when status becomes in_progress", () => {
|
|
98
|
-
const folder = taskService.create("test-feature", "task");
|
|
99
|
-
taskService.update("test-feature", folder, { status: "in_progress" });
|
|
100
|
-
const statusPath = getTaskStatusPath(TEST_ROOT, "test-feature", folder);
|
|
101
|
-
const status = JSON.parse(fs.readFileSync(statusPath, "utf-8"));
|
|
102
|
-
expect(status.startedAt).toBeDefined();
|
|
103
|
-
});
|
|
104
|
-
it("sets completedAt when status becomes done", () => {
|
|
105
|
-
const folder = taskService.create("test-feature", "task");
|
|
106
|
-
taskService.update("test-feature", folder, { status: "done" });
|
|
107
|
-
const statusPath = getTaskStatusPath(TEST_ROOT, "test-feature", folder);
|
|
108
|
-
const status = JSON.parse(fs.readFileSync(statusPath, "utf-8"));
|
|
109
|
-
expect(status.completedAt).toBeDefined();
|
|
110
|
-
});
|
|
111
|
-
it("updates summary", () => {
|
|
112
|
-
const folder = taskService.create("test-feature", "task");
|
|
113
|
-
taskService.update("test-feature", folder, { summary: "Completed setup" });
|
|
114
|
-
const task = taskService.get("test-feature", folder);
|
|
115
|
-
expect(task?.summary).toBe("Completed setup");
|
|
116
|
-
});
|
|
117
|
-
it("throws for non-existing task", () => {
|
|
118
|
-
expect(() => taskService.update("test-feature", "nope", { status: "done" })).toThrow();
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
describe("get", () => {
|
|
122
|
-
it("returns task info", () => {
|
|
123
|
-
const folder = taskService.create("test-feature", "my-task");
|
|
124
|
-
const task = taskService.get("test-feature", folder);
|
|
125
|
-
expect(task).not.toBeNull();
|
|
126
|
-
expect(task.folder).toBe("01-my-task");
|
|
127
|
-
expect(task.name).toBe("my-task");
|
|
128
|
-
expect(task.status).toBe("pending");
|
|
129
|
-
expect(task.origin).toBe("manual");
|
|
130
|
-
});
|
|
131
|
-
it("returns null for non-existing task", () => {
|
|
132
|
-
expect(taskService.get("test-feature", "nope")).toBeNull();
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
describe("list", () => {
|
|
136
|
-
it("returns empty array when no tasks", () => {
|
|
137
|
-
expect(taskService.list("test-feature")).toEqual([]);
|
|
138
|
-
});
|
|
139
|
-
it("returns all tasks sorted", () => {
|
|
140
|
-
taskService.create("test-feature", "third", 3);
|
|
141
|
-
taskService.create("test-feature", "first", 1);
|
|
142
|
-
taskService.create("test-feature", "second", 2);
|
|
143
|
-
const tasks = taskService.list("test-feature");
|
|
144
|
-
expect(tasks.length).toBe(3);
|
|
145
|
-
expect(tasks[0].folder).toBe("01-first");
|
|
146
|
-
expect(tasks[1].folder).toBe("02-second");
|
|
147
|
-
expect(tasks[2].folder).toBe("03-third");
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
describe("writeReport", () => {
|
|
151
|
-
it("writes report file", () => {
|
|
152
|
-
const folder = taskService.create("test-feature", "task");
|
|
153
|
-
const report = "## Summary\n\nCompleted the task successfully.";
|
|
154
|
-
taskService.writeReport("test-feature", folder, report);
|
|
155
|
-
const reportPath = getTaskReportPath(TEST_ROOT, "test-feature", folder);
|
|
156
|
-
expect(fs.readFileSync(reportPath, "utf-8")).toBe(report);
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
export interface WorktreeInfo {
|
|
2
|
-
path: string;
|
|
3
|
-
branch: string;
|
|
4
|
-
commit: string;
|
|
5
|
-
feature: string;
|
|
6
|
-
step: string;
|
|
7
|
-
}
|
|
8
|
-
export interface DiffResult {
|
|
9
|
-
hasDiff: boolean;
|
|
10
|
-
diffContent: string;
|
|
11
|
-
filesChanged: string[];
|
|
12
|
-
insertions: number;
|
|
13
|
-
deletions: number;
|
|
14
|
-
}
|
|
15
|
-
export interface ApplyResult {
|
|
16
|
-
success: boolean;
|
|
17
|
-
error?: string;
|
|
18
|
-
filesAffected: string[];
|
|
19
|
-
}
|
|
20
|
-
export interface CommitResult {
|
|
21
|
-
committed: boolean;
|
|
22
|
-
sha: string;
|
|
23
|
-
message?: string;
|
|
24
|
-
}
|
|
25
|
-
export interface MergeResult {
|
|
26
|
-
success: boolean;
|
|
27
|
-
merged: boolean;
|
|
28
|
-
sha?: string;
|
|
29
|
-
filesChanged?: string[];
|
|
30
|
-
conflicts?: string[];
|
|
31
|
-
error?: string;
|
|
32
|
-
}
|
|
33
|
-
export interface WorktreeConfig {
|
|
34
|
-
baseDir: string;
|
|
35
|
-
hiveDir: string;
|
|
36
|
-
}
|
|
37
|
-
export declare class WorktreeService {
|
|
38
|
-
private config;
|
|
39
|
-
constructor(config: WorktreeConfig);
|
|
40
|
-
private getGit;
|
|
41
|
-
private getWorktreesDir;
|
|
42
|
-
private getWorktreePath;
|
|
43
|
-
private getStepStatusPath;
|
|
44
|
-
private getBranchName;
|
|
45
|
-
create(feature: string, step: string, baseBranch?: string): Promise<WorktreeInfo>;
|
|
46
|
-
get(feature: string, step: string): Promise<WorktreeInfo | null>;
|
|
47
|
-
getDiff(feature: string, step: string, baseCommit?: string): Promise<DiffResult>;
|
|
48
|
-
exportPatch(feature: string, step: string, baseBranch?: string): Promise<string>;
|
|
49
|
-
applyDiff(feature: string, step: string, baseBranch?: string): Promise<ApplyResult>;
|
|
50
|
-
revertDiff(feature: string, step: string, baseBranch?: string): Promise<ApplyResult>;
|
|
51
|
-
private parseFilesFromDiff;
|
|
52
|
-
revertFromSavedDiff(diffPath: string): Promise<ApplyResult>;
|
|
53
|
-
remove(feature: string, step: string, deleteBranch?: boolean): Promise<void>;
|
|
54
|
-
list(feature?: string): Promise<WorktreeInfo[]>;
|
|
55
|
-
cleanup(feature?: string): Promise<{
|
|
56
|
-
removed: string[];
|
|
57
|
-
pruned: boolean;
|
|
58
|
-
}>;
|
|
59
|
-
checkConflicts(feature: string, step: string, baseBranch?: string): Promise<string[]>;
|
|
60
|
-
checkConflictsFromSavedDiff(diffPath: string, reverse?: boolean): Promise<string[]>;
|
|
61
|
-
commitChanges(feature: string, step: string, message?: string): Promise<CommitResult>;
|
|
62
|
-
merge(feature: string, step: string, strategy?: 'merge' | 'squash' | 'rebase'): Promise<MergeResult>;
|
|
63
|
-
hasUncommittedChanges(feature: string, step: string): Promise<boolean>;
|
|
64
|
-
private parseConflictsFromError;
|
|
65
|
-
}
|
|
66
|
-
export declare function createWorktreeService(projectDir: string): WorktreeService;
|