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,95 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { SessionService } from '../services/sessionService.js';
|
|
3
|
-
import { FeatureService } from '../services/featureService.js';
|
|
4
|
-
import { TaskService } from '../services/taskService.js';
|
|
5
|
-
import { PlanService } from '../services/planService.js';
|
|
6
|
-
import { ContextService } from '../services/contextService.js';
|
|
7
|
-
import { detectContext } from '../utils/detection.js';
|
|
8
|
-
export function createSessionTools(projectRoot) {
|
|
9
|
-
const sessionService = new SessionService(projectRoot);
|
|
10
|
-
const featureService = new FeatureService(projectRoot);
|
|
11
|
-
const taskService = new TaskService(projectRoot);
|
|
12
|
-
const planService = new PlanService(projectRoot);
|
|
13
|
-
const contextService = new ContextService(projectRoot);
|
|
14
|
-
const getActiveFeature = () => {
|
|
15
|
-
const ctx = detectContext(projectRoot);
|
|
16
|
-
return ctx?.feature || null;
|
|
17
|
-
};
|
|
18
|
-
return {
|
|
19
|
-
hive_session_open: {
|
|
20
|
-
description: 'Open a Hive session for a feature or task. Returns full context needed to resume work.',
|
|
21
|
-
parameters: z.object({
|
|
22
|
-
feature: z.string().optional().describe('Feature name (defaults to active)'),
|
|
23
|
-
task: z.string().optional().describe('Task folder to focus on'),
|
|
24
|
-
}),
|
|
25
|
-
execute: async ({ feature, task }, toolContext) => {
|
|
26
|
-
const featureName = feature || getActiveFeature();
|
|
27
|
-
if (!featureName)
|
|
28
|
-
return { error: 'No feature specified and no active feature' };
|
|
29
|
-
const featureData = featureService.get(featureName);
|
|
30
|
-
if (!featureData)
|
|
31
|
-
return { error: `Feature '${featureName}' not found` };
|
|
32
|
-
const ctx = toolContext;
|
|
33
|
-
if (ctx?.sessionID) {
|
|
34
|
-
sessionService.track(featureName, ctx.sessionID, task);
|
|
35
|
-
}
|
|
36
|
-
const planResult = planService.read(featureName);
|
|
37
|
-
const tasks = taskService.list(featureName);
|
|
38
|
-
const contextCompiled = contextService.compile(featureName);
|
|
39
|
-
const sessions = sessionService.list(featureName);
|
|
40
|
-
const response = {
|
|
41
|
-
feature: {
|
|
42
|
-
name: featureData.name,
|
|
43
|
-
status: featureData.status,
|
|
44
|
-
ticket: featureData.ticket,
|
|
45
|
-
},
|
|
46
|
-
plan: planResult ? {
|
|
47
|
-
content: planResult.content,
|
|
48
|
-
commentCount: planResult.comments.length,
|
|
49
|
-
} : null,
|
|
50
|
-
tasks: tasks.map(t => ({
|
|
51
|
-
folder: t.folder,
|
|
52
|
-
name: t.name,
|
|
53
|
-
status: t.status,
|
|
54
|
-
origin: t.origin,
|
|
55
|
-
})),
|
|
56
|
-
context: contextCompiled || null,
|
|
57
|
-
sessions: sessions.map(s => ({
|
|
58
|
-
sessionId: s.sessionId,
|
|
59
|
-
taskFolder: s.taskFolder,
|
|
60
|
-
isMaster: s.sessionId === sessionService.getMaster(featureName),
|
|
61
|
-
})),
|
|
62
|
-
};
|
|
63
|
-
if (task) {
|
|
64
|
-
const taskInfo = taskService.get(featureName, task);
|
|
65
|
-
if (taskInfo) {
|
|
66
|
-
response.focusedTask = taskInfo;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return response;
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
hive_session_list: {
|
|
73
|
-
description: 'List all sessions for the active feature',
|
|
74
|
-
parameters: z.object({}),
|
|
75
|
-
execute: async () => {
|
|
76
|
-
const feature = getActiveFeature();
|
|
77
|
-
if (!feature)
|
|
78
|
-
return { error: 'No active feature' };
|
|
79
|
-
const sessions = sessionService.list(feature);
|
|
80
|
-
const master = sessionService.getMaster(feature);
|
|
81
|
-
return {
|
|
82
|
-
feature,
|
|
83
|
-
master,
|
|
84
|
-
sessions: sessions.map(s => ({
|
|
85
|
-
sessionId: s.sessionId,
|
|
86
|
-
taskFolder: s.taskFolder,
|
|
87
|
-
startedAt: s.startedAt,
|
|
88
|
-
lastActiveAt: s.lastActiveAt,
|
|
89
|
-
isMaster: s.sessionId === master,
|
|
90
|
-
})),
|
|
91
|
-
};
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export declare function createTaskTools(projectRoot: string): {
|
|
3
|
-
hive_tasks_sync: {
|
|
4
|
-
description: string;
|
|
5
|
-
parameters: z.ZodObject<{}, z.core.$strip>;
|
|
6
|
-
execute: () => Promise<{
|
|
7
|
-
error: string;
|
|
8
|
-
} | {
|
|
9
|
-
message: string;
|
|
10
|
-
created: string[];
|
|
11
|
-
removed: string[];
|
|
12
|
-
kept: string[];
|
|
13
|
-
manual: string[];
|
|
14
|
-
error?: undefined;
|
|
15
|
-
}>;
|
|
16
|
-
};
|
|
17
|
-
hive_task_create: {
|
|
18
|
-
description: string;
|
|
19
|
-
parameters: z.ZodObject<{
|
|
20
|
-
name: z.ZodString;
|
|
21
|
-
order: z.ZodOptional<z.ZodNumber>;
|
|
22
|
-
}, z.core.$strip>;
|
|
23
|
-
execute: ({ name, order }: {
|
|
24
|
-
name: string;
|
|
25
|
-
order?: number;
|
|
26
|
-
}) => Promise<{
|
|
27
|
-
error: string;
|
|
28
|
-
folder?: undefined;
|
|
29
|
-
origin?: undefined;
|
|
30
|
-
message?: undefined;
|
|
31
|
-
} | {
|
|
32
|
-
folder: string;
|
|
33
|
-
origin: string;
|
|
34
|
-
message: string;
|
|
35
|
-
error?: undefined;
|
|
36
|
-
}>;
|
|
37
|
-
};
|
|
38
|
-
hive_task_update: {
|
|
39
|
-
description: string;
|
|
40
|
-
parameters: z.ZodObject<{
|
|
41
|
-
task: z.ZodString;
|
|
42
|
-
status: z.ZodOptional<z.ZodEnum<{
|
|
43
|
-
pending: "pending";
|
|
44
|
-
in_progress: "in_progress";
|
|
45
|
-
done: "done";
|
|
46
|
-
cancelled: "cancelled";
|
|
47
|
-
}>>;
|
|
48
|
-
summary: z.ZodOptional<z.ZodString>;
|
|
49
|
-
}, z.core.$strip>;
|
|
50
|
-
execute: ({ task, status, summary }: {
|
|
51
|
-
task: string;
|
|
52
|
-
status?: "pending" | "in_progress" | "done" | "cancelled";
|
|
53
|
-
summary?: string;
|
|
54
|
-
}) => Promise<{
|
|
55
|
-
error: string;
|
|
56
|
-
updated?: undefined;
|
|
57
|
-
task?: undefined;
|
|
58
|
-
status?: undefined;
|
|
59
|
-
summary?: undefined;
|
|
60
|
-
} | {
|
|
61
|
-
updated: boolean;
|
|
62
|
-
task: string;
|
|
63
|
-
status: import("../types.js").TaskStatusType;
|
|
64
|
-
summary: string | undefined;
|
|
65
|
-
error?: undefined;
|
|
66
|
-
}>;
|
|
67
|
-
};
|
|
68
|
-
hive_task_list: {
|
|
69
|
-
description: string;
|
|
70
|
-
parameters: z.ZodObject<{}, z.core.$strip>;
|
|
71
|
-
execute: () => Promise<{
|
|
72
|
-
error: string;
|
|
73
|
-
tasks?: undefined;
|
|
74
|
-
} | {
|
|
75
|
-
tasks: import("../types.js").TaskInfo[];
|
|
76
|
-
error?: undefined;
|
|
77
|
-
}>;
|
|
78
|
-
};
|
|
79
|
-
};
|
package/dist/tools/taskTools.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { TaskService } from '../services/taskService.js';
|
|
3
|
-
import { FeatureService } from '../services/featureService.js';
|
|
4
|
-
import { detectContext } from '../utils/detection.js';
|
|
5
|
-
export function createTaskTools(projectRoot) {
|
|
6
|
-
const taskService = new TaskService(projectRoot);
|
|
7
|
-
const featureService = new FeatureService(projectRoot);
|
|
8
|
-
const getActiveFeature = () => {
|
|
9
|
-
const ctx = detectContext(projectRoot);
|
|
10
|
-
return ctx?.feature || null;
|
|
11
|
-
};
|
|
12
|
-
return {
|
|
13
|
-
hive_tasks_sync: {
|
|
14
|
-
description: 'Generate tasks from approved plan',
|
|
15
|
-
parameters: z.object({}),
|
|
16
|
-
execute: async () => {
|
|
17
|
-
const feature = getActiveFeature();
|
|
18
|
-
if (!feature) {
|
|
19
|
-
return { error: 'No active feature.' };
|
|
20
|
-
}
|
|
21
|
-
const featureData = featureService.get(feature);
|
|
22
|
-
if (!featureData) {
|
|
23
|
-
return { error: `Feature '${feature}' not found` };
|
|
24
|
-
}
|
|
25
|
-
if (featureData.status === 'planning') {
|
|
26
|
-
return { error: 'Plan must be approved before syncing tasks. Run hive_plan_approve first.' };
|
|
27
|
-
}
|
|
28
|
-
const result = taskService.sync(feature);
|
|
29
|
-
if (featureData.status === 'approved') {
|
|
30
|
-
featureService.updateStatus(feature, 'executing');
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
...result,
|
|
34
|
-
message: `Synced tasks: ${result.created.length} created, ${result.removed.length} removed, ${result.kept.length} kept, ${result.manual.length} manual`,
|
|
35
|
-
};
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
hive_task_create: {
|
|
39
|
-
description: 'Create manual task (not from plan)',
|
|
40
|
-
parameters: z.object({
|
|
41
|
-
name: z.string().describe('Task name (will be slugified)'),
|
|
42
|
-
order: z.number().optional().describe('Task order number (defaults to next available)'),
|
|
43
|
-
}),
|
|
44
|
-
execute: async ({ name, order }) => {
|
|
45
|
-
const feature = getActiveFeature();
|
|
46
|
-
if (!feature) {
|
|
47
|
-
return { error: 'No active feature.' };
|
|
48
|
-
}
|
|
49
|
-
const folder = taskService.create(feature, name, order);
|
|
50
|
-
return {
|
|
51
|
-
folder,
|
|
52
|
-
origin: 'manual',
|
|
53
|
-
message: `Created manual task '${folder}'. This task will not be affected by hive_tasks_sync.`,
|
|
54
|
-
};
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
hive_task_update: {
|
|
58
|
-
description: 'Update task status or summary',
|
|
59
|
-
parameters: z.object({
|
|
60
|
-
task: z.string().describe('Task folder name (e.g., "01-auth-service")'),
|
|
61
|
-
status: z.enum(['pending', 'in_progress', 'done', 'cancelled']).optional(),
|
|
62
|
-
summary: z.string().optional().describe('Summary of work done'),
|
|
63
|
-
}),
|
|
64
|
-
execute: async ({ task, status, summary }) => {
|
|
65
|
-
const feature = getActiveFeature();
|
|
66
|
-
if (!feature) {
|
|
67
|
-
return { error: 'No active feature.' };
|
|
68
|
-
}
|
|
69
|
-
const updated = taskService.update(feature, task, { status, summary });
|
|
70
|
-
return { updated: true, task, status: updated.status, summary: updated.summary };
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
hive_task_list: {
|
|
74
|
-
description: 'List all tasks for the active feature.',
|
|
75
|
-
parameters: z.object({}),
|
|
76
|
-
execute: async () => {
|
|
77
|
-
const feature = getActiveFeature();
|
|
78
|
-
if (!feature) {
|
|
79
|
-
return { error: 'No active feature.' };
|
|
80
|
-
}
|
|
81
|
-
const tasks = taskService.list(feature);
|
|
82
|
-
return { tasks };
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
}
|
package/dist/types.d.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
export type FeatureStatusType = 'planning' | 'approved' | 'executing' | 'completed';
|
|
2
|
-
export interface FeatureJson {
|
|
3
|
-
name: string;
|
|
4
|
-
status: FeatureStatusType;
|
|
5
|
-
ticket?: string;
|
|
6
|
-
sessionId?: string;
|
|
7
|
-
createdAt: string;
|
|
8
|
-
approvedAt?: string;
|
|
9
|
-
completedAt?: string;
|
|
10
|
-
}
|
|
11
|
-
export type TaskStatusType = 'pending' | 'in_progress' | 'done' | 'cancelled';
|
|
12
|
-
export type TaskOrigin = 'plan' | 'manual';
|
|
13
|
-
export interface TaskStatus {
|
|
14
|
-
status: TaskStatusType;
|
|
15
|
-
origin: TaskOrigin;
|
|
16
|
-
summary?: string;
|
|
17
|
-
startedAt?: string;
|
|
18
|
-
completedAt?: string;
|
|
19
|
-
baseCommit?: string;
|
|
20
|
-
}
|
|
21
|
-
export interface PlanComment {
|
|
22
|
-
id: string;
|
|
23
|
-
line: number;
|
|
24
|
-
body: string;
|
|
25
|
-
author: string;
|
|
26
|
-
timestamp: string;
|
|
27
|
-
}
|
|
28
|
-
export interface CommentsJson {
|
|
29
|
-
threads: PlanComment[];
|
|
30
|
-
}
|
|
31
|
-
export interface PlanReadResult {
|
|
32
|
-
content: string;
|
|
33
|
-
status: FeatureStatusType;
|
|
34
|
-
comments: PlanComment[];
|
|
35
|
-
}
|
|
36
|
-
export interface TasksSyncResult {
|
|
37
|
-
created: string[];
|
|
38
|
-
removed: string[];
|
|
39
|
-
kept: string[];
|
|
40
|
-
manual: string[];
|
|
41
|
-
}
|
|
42
|
-
export interface TaskInfo {
|
|
43
|
-
folder: string;
|
|
44
|
-
name: string;
|
|
45
|
-
status: TaskStatusType;
|
|
46
|
-
origin: TaskOrigin;
|
|
47
|
-
summary?: string;
|
|
48
|
-
}
|
|
49
|
-
export interface FeatureInfo {
|
|
50
|
-
name: string;
|
|
51
|
-
status: FeatureStatusType;
|
|
52
|
-
tasks: TaskInfo[];
|
|
53
|
-
hasPlan: boolean;
|
|
54
|
-
commentCount: number;
|
|
55
|
-
}
|
|
56
|
-
export interface ContextFile {
|
|
57
|
-
name: string;
|
|
58
|
-
content: string;
|
|
59
|
-
updatedAt: string;
|
|
60
|
-
}
|
|
61
|
-
export interface SessionInfo {
|
|
62
|
-
sessionId: string;
|
|
63
|
-
taskFolder?: string;
|
|
64
|
-
startedAt: string;
|
|
65
|
-
lastActiveAt: string;
|
|
66
|
-
messageCount?: number;
|
|
67
|
-
}
|
|
68
|
-
export interface SessionsJson {
|
|
69
|
-
master?: string;
|
|
70
|
-
sessions: SessionInfo[];
|
|
71
|
-
}
|
|
72
|
-
export interface TaskSpec {
|
|
73
|
-
taskFolder: string;
|
|
74
|
-
featureName: string;
|
|
75
|
-
planSection: string;
|
|
76
|
-
context: string;
|
|
77
|
-
priorTasks: Array<{
|
|
78
|
-
folder: string;
|
|
79
|
-
summary?: string;
|
|
80
|
-
}>;
|
|
81
|
-
}
|
|
82
|
-
export interface HiveConfig {
|
|
83
|
-
enableToolsFor: string[];
|
|
84
|
-
agents: {
|
|
85
|
-
worker: {
|
|
86
|
-
visible: boolean;
|
|
87
|
-
};
|
|
88
|
-
};
|
|
89
|
-
}
|
package/dist/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { FeatureJson } from '../types.js';
|
|
2
|
-
export interface DetectionResult {
|
|
3
|
-
projectRoot: string;
|
|
4
|
-
feature: string | null;
|
|
5
|
-
task: string | null;
|
|
6
|
-
isWorktree: boolean;
|
|
7
|
-
mainProjectRoot: string | null;
|
|
8
|
-
}
|
|
9
|
-
export declare function detectContext(cwd: string): DetectionResult;
|
|
10
|
-
export declare function listFeatures(projectRoot: string): string[];
|
|
11
|
-
export declare function getFeatureData(projectRoot: string, featureName: string): FeatureJson | null;
|
|
12
|
-
export declare function findProjectRoot(startDir: string): string | null;
|
package/dist/utils/detection.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import * as path from 'path';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
import { getFeaturesPath, getFeaturePath, readJson } from './paths.js';
|
|
4
|
-
export function detectContext(cwd) {
|
|
5
|
-
const result = {
|
|
6
|
-
projectRoot: cwd,
|
|
7
|
-
feature: null,
|
|
8
|
-
task: null,
|
|
9
|
-
isWorktree: false,
|
|
10
|
-
mainProjectRoot: null,
|
|
11
|
-
};
|
|
12
|
-
const worktreeMatch = cwd.match(/(.+)\/\.hive\/\.worktrees\/([^/]+)\/([^/]+)/);
|
|
13
|
-
if (worktreeMatch) {
|
|
14
|
-
result.mainProjectRoot = worktreeMatch[1];
|
|
15
|
-
result.feature = worktreeMatch[2];
|
|
16
|
-
result.task = worktreeMatch[3];
|
|
17
|
-
result.isWorktree = true;
|
|
18
|
-
result.projectRoot = worktreeMatch[1];
|
|
19
|
-
return result;
|
|
20
|
-
}
|
|
21
|
-
const gitPath = path.join(cwd, '.git');
|
|
22
|
-
if (fs.existsSync(gitPath)) {
|
|
23
|
-
const stat = fs.statSync(gitPath);
|
|
24
|
-
if (stat.isFile()) {
|
|
25
|
-
const gitContent = fs.readFileSync(gitPath, 'utf-8').trim();
|
|
26
|
-
const gitdirMatch = gitContent.match(/gitdir:\s*(.+)/);
|
|
27
|
-
if (gitdirMatch) {
|
|
28
|
-
const gitdir = gitdirMatch[1];
|
|
29
|
-
const worktreePathMatch = gitdir.match(/(.+)\/\.git\/worktrees\/(.+)/);
|
|
30
|
-
if (worktreePathMatch) {
|
|
31
|
-
const mainRepo = worktreePathMatch[1];
|
|
32
|
-
const cwdWorktreeMatch = cwd.match(/\.hive\/\.worktrees\/([^/]+)\/([^/]+)/);
|
|
33
|
-
if (cwdWorktreeMatch) {
|
|
34
|
-
result.mainProjectRoot = mainRepo;
|
|
35
|
-
result.feature = cwdWorktreeMatch[1];
|
|
36
|
-
result.task = cwdWorktreeMatch[2];
|
|
37
|
-
result.isWorktree = true;
|
|
38
|
-
result.projectRoot = mainRepo;
|
|
39
|
-
return result;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
export function listFeatures(projectRoot) {
|
|
48
|
-
const featuresPath = getFeaturesPath(projectRoot);
|
|
49
|
-
if (!fs.existsSync(featuresPath))
|
|
50
|
-
return [];
|
|
51
|
-
return fs.readdirSync(featuresPath, { withFileTypes: true })
|
|
52
|
-
.filter(d => d.isDirectory())
|
|
53
|
-
.map(d => d.name);
|
|
54
|
-
}
|
|
55
|
-
export function getFeatureData(projectRoot, featureName) {
|
|
56
|
-
const featurePath = getFeaturePath(projectRoot, featureName);
|
|
57
|
-
const featureJsonPath = path.join(featurePath, 'feature.json');
|
|
58
|
-
return readJson(featureJsonPath);
|
|
59
|
-
}
|
|
60
|
-
export function findProjectRoot(startDir) {
|
|
61
|
-
let current = startDir;
|
|
62
|
-
const root = path.parse(current).root;
|
|
63
|
-
while (current !== root) {
|
|
64
|
-
if (fs.existsSync(path.join(current, '.hive'))) {
|
|
65
|
-
return current;
|
|
66
|
-
}
|
|
67
|
-
if (fs.existsSync(path.join(current, '.git'))) {
|
|
68
|
-
return current;
|
|
69
|
-
}
|
|
70
|
-
current = path.dirname(current);
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}
|
package/dist/utils/paths.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export declare function getHivePath(projectRoot: string): string;
|
|
2
|
-
export declare function getFeaturesPath(projectRoot: string): string;
|
|
3
|
-
export declare function getFeaturePath(projectRoot: string, featureName: string): string;
|
|
4
|
-
export declare function getPlanPath(projectRoot: string, featureName: string): string;
|
|
5
|
-
export declare function getCommentsPath(projectRoot: string, featureName: string): string;
|
|
6
|
-
export declare function getFeatureJsonPath(projectRoot: string, featureName: string): string;
|
|
7
|
-
export declare function getContextPath(projectRoot: string, featureName: string): string;
|
|
8
|
-
export declare function getTasksPath(projectRoot: string, featureName: string): string;
|
|
9
|
-
export declare function getTaskPath(projectRoot: string, featureName: string, taskFolder: string): string;
|
|
10
|
-
export declare function getTaskStatusPath(projectRoot: string, featureName: string, taskFolder: string): string;
|
|
11
|
-
export declare function getTaskReportPath(projectRoot: string, featureName: string, taskFolder: string): string;
|
|
12
|
-
export declare function getTaskSpecPath(projectRoot: string, featureName: string, taskFolder: string): string;
|
|
13
|
-
export declare function ensureDir(dirPath: string): void;
|
|
14
|
-
export declare function fileExists(filePath: string): boolean;
|
|
15
|
-
export declare function readJson<T>(filePath: string): T | null;
|
|
16
|
-
export declare function writeJson<T>(filePath: string, data: T): void;
|
|
17
|
-
export declare function readText(filePath: string): string | null;
|
|
18
|
-
export declare function writeText(filePath: string, content: string): void;
|
package/dist/utils/paths.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import * as path from 'path';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
const HIVE_DIR = '.hive';
|
|
4
|
-
const FEATURES_DIR = 'features';
|
|
5
|
-
const TASKS_DIR = 'tasks';
|
|
6
|
-
const CONTEXT_DIR = 'context';
|
|
7
|
-
const PLAN_FILE = 'plan.md';
|
|
8
|
-
const COMMENTS_FILE = 'comments.json';
|
|
9
|
-
const FEATURE_FILE = 'feature.json';
|
|
10
|
-
const STATUS_FILE = 'status.json';
|
|
11
|
-
const REPORT_FILE = 'report.md';
|
|
12
|
-
export function getHivePath(projectRoot) {
|
|
13
|
-
return path.join(projectRoot, HIVE_DIR);
|
|
14
|
-
}
|
|
15
|
-
export function getFeaturesPath(projectRoot) {
|
|
16
|
-
return path.join(getHivePath(projectRoot), FEATURES_DIR);
|
|
17
|
-
}
|
|
18
|
-
export function getFeaturePath(projectRoot, featureName) {
|
|
19
|
-
return path.join(getFeaturesPath(projectRoot), featureName);
|
|
20
|
-
}
|
|
21
|
-
export function getPlanPath(projectRoot, featureName) {
|
|
22
|
-
return path.join(getFeaturePath(projectRoot, featureName), PLAN_FILE);
|
|
23
|
-
}
|
|
24
|
-
export function getCommentsPath(projectRoot, featureName) {
|
|
25
|
-
return path.join(getFeaturePath(projectRoot, featureName), COMMENTS_FILE);
|
|
26
|
-
}
|
|
27
|
-
export function getFeatureJsonPath(projectRoot, featureName) {
|
|
28
|
-
return path.join(getFeaturePath(projectRoot, featureName), FEATURE_FILE);
|
|
29
|
-
}
|
|
30
|
-
export function getContextPath(projectRoot, featureName) {
|
|
31
|
-
return path.join(getFeaturePath(projectRoot, featureName), CONTEXT_DIR);
|
|
32
|
-
}
|
|
33
|
-
export function getTasksPath(projectRoot, featureName) {
|
|
34
|
-
return path.join(getFeaturePath(projectRoot, featureName), TASKS_DIR);
|
|
35
|
-
}
|
|
36
|
-
export function getTaskPath(projectRoot, featureName, taskFolder) {
|
|
37
|
-
return path.join(getTasksPath(projectRoot, featureName), taskFolder);
|
|
38
|
-
}
|
|
39
|
-
export function getTaskStatusPath(projectRoot, featureName, taskFolder) {
|
|
40
|
-
return path.join(getTaskPath(projectRoot, featureName, taskFolder), STATUS_FILE);
|
|
41
|
-
}
|
|
42
|
-
export function getTaskReportPath(projectRoot, featureName, taskFolder) {
|
|
43
|
-
return path.join(getTaskPath(projectRoot, featureName, taskFolder), REPORT_FILE);
|
|
44
|
-
}
|
|
45
|
-
export function getTaskSpecPath(projectRoot, featureName, taskFolder) {
|
|
46
|
-
return path.join(getTaskPath(projectRoot, featureName, taskFolder), 'spec.md');
|
|
47
|
-
}
|
|
48
|
-
export function ensureDir(dirPath) {
|
|
49
|
-
if (!fs.existsSync(dirPath)) {
|
|
50
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
export function fileExists(filePath) {
|
|
54
|
-
return fs.existsSync(filePath);
|
|
55
|
-
}
|
|
56
|
-
export function readJson(filePath) {
|
|
57
|
-
if (!fs.existsSync(filePath))
|
|
58
|
-
return null;
|
|
59
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
60
|
-
return JSON.parse(content);
|
|
61
|
-
}
|
|
62
|
-
export function writeJson(filePath, data) {
|
|
63
|
-
ensureDir(path.dirname(filePath));
|
|
64
|
-
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
65
|
-
}
|
|
66
|
-
export function readText(filePath) {
|
|
67
|
-
if (!fs.existsSync(filePath))
|
|
68
|
-
return null;
|
|
69
|
-
return fs.readFileSync(filePath, 'utf-8');
|
|
70
|
-
}
|
|
71
|
-
export function writeText(filePath, content) {
|
|
72
|
-
ensureDir(path.dirname(filePath));
|
|
73
|
-
fs.writeFileSync(filePath, content);
|
|
74
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/utils/paths.test.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
import { getHivePath, getFeaturesPath, getFeaturePath, getPlanPath, getCommentsPath, getFeatureJsonPath, getContextPath, getTasksPath, getTaskPath, getTaskStatusPath, getTaskReportPath, ensureDir, fileExists, readJson, writeJson, readText, writeText, } from "./paths";
|
|
5
|
-
const TEST_ROOT = "/tmp/hive-test-paths";
|
|
6
|
-
describe("paths.ts", () => {
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
fs.rmSync(TEST_ROOT, { recursive: true, force: true });
|
|
9
|
-
fs.mkdirSync(TEST_ROOT, { recursive: true });
|
|
10
|
-
});
|
|
11
|
-
afterEach(() => {
|
|
12
|
-
fs.rmSync(TEST_ROOT, { recursive: true, force: true });
|
|
13
|
-
});
|
|
14
|
-
describe("path generators", () => {
|
|
15
|
-
it("getHivePath returns correct path", () => {
|
|
16
|
-
expect(getHivePath(TEST_ROOT)).toBe(path.join(TEST_ROOT, ".hive"));
|
|
17
|
-
});
|
|
18
|
-
it("getFeaturesPath returns correct path", () => {
|
|
19
|
-
expect(getFeaturesPath(TEST_ROOT)).toBe(path.join(TEST_ROOT, ".hive", "features"));
|
|
20
|
-
});
|
|
21
|
-
it("getFeaturePath returns correct path", () => {
|
|
22
|
-
expect(getFeaturePath(TEST_ROOT, "my-feature")).toBe(path.join(TEST_ROOT, ".hive", "features", "my-feature"));
|
|
23
|
-
});
|
|
24
|
-
it("getPlanPath returns correct path", () => {
|
|
25
|
-
expect(getPlanPath(TEST_ROOT, "my-feature")).toBe(path.join(TEST_ROOT, ".hive", "features", "my-feature", "plan.md"));
|
|
26
|
-
});
|
|
27
|
-
it("getCommentsPath returns correct path", () => {
|
|
28
|
-
expect(getCommentsPath(TEST_ROOT, "my-feature")).toBe(path.join(TEST_ROOT, ".hive", "features", "my-feature", "comments.json"));
|
|
29
|
-
});
|
|
30
|
-
it("getFeatureJsonPath returns correct path", () => {
|
|
31
|
-
expect(getFeatureJsonPath(TEST_ROOT, "my-feature")).toBe(path.join(TEST_ROOT, ".hive", "features", "my-feature", "feature.json"));
|
|
32
|
-
});
|
|
33
|
-
it("getContextPath returns correct path", () => {
|
|
34
|
-
expect(getContextPath(TEST_ROOT, "my-feature")).toBe(path.join(TEST_ROOT, ".hive", "features", "my-feature", "context"));
|
|
35
|
-
});
|
|
36
|
-
it("getTasksPath returns correct path", () => {
|
|
37
|
-
expect(getTasksPath(TEST_ROOT, "my-feature")).toBe(path.join(TEST_ROOT, ".hive", "features", "my-feature", "tasks"));
|
|
38
|
-
});
|
|
39
|
-
it("getTaskPath returns correct path", () => {
|
|
40
|
-
expect(getTaskPath(TEST_ROOT, "my-feature", "01-setup")).toBe(path.join(TEST_ROOT, ".hive", "features", "my-feature", "tasks", "01-setup"));
|
|
41
|
-
});
|
|
42
|
-
it("getTaskStatusPath returns correct path", () => {
|
|
43
|
-
expect(getTaskStatusPath(TEST_ROOT, "my-feature", "01-setup")).toBe(path.join(TEST_ROOT, ".hive", "features", "my-feature", "tasks", "01-setup", "status.json"));
|
|
44
|
-
});
|
|
45
|
-
it("getTaskReportPath returns correct path", () => {
|
|
46
|
-
expect(getTaskReportPath(TEST_ROOT, "my-feature", "01-setup")).toBe(path.join(TEST_ROOT, ".hive", "features", "my-feature", "tasks", "01-setup", "report.md"));
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
describe("file utilities", () => {
|
|
50
|
-
it("ensureDir creates directory recursively", () => {
|
|
51
|
-
const deepPath = path.join(TEST_ROOT, "a", "b", "c");
|
|
52
|
-
ensureDir(deepPath);
|
|
53
|
-
expect(fs.existsSync(deepPath)).toBe(true);
|
|
54
|
-
});
|
|
55
|
-
it("ensureDir is idempotent", () => {
|
|
56
|
-
const dirPath = path.join(TEST_ROOT, "existing");
|
|
57
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
58
|
-
expect(() => ensureDir(dirPath)).not.toThrow();
|
|
59
|
-
});
|
|
60
|
-
it("fileExists returns true for existing file", () => {
|
|
61
|
-
const filePath = path.join(TEST_ROOT, "exists.txt");
|
|
62
|
-
fs.writeFileSync(filePath, "content");
|
|
63
|
-
expect(fileExists(filePath)).toBe(true);
|
|
64
|
-
});
|
|
65
|
-
it("fileExists returns false for non-existing file", () => {
|
|
66
|
-
expect(fileExists(path.join(TEST_ROOT, "nope.txt"))).toBe(false);
|
|
67
|
-
});
|
|
68
|
-
it("writeJson and readJson roundtrip", () => {
|
|
69
|
-
const filePath = path.join(TEST_ROOT, "data.json");
|
|
70
|
-
const data = { name: "test", count: 42, nested: { value: true } };
|
|
71
|
-
writeJson(filePath, data);
|
|
72
|
-
const result = readJson(filePath);
|
|
73
|
-
expect(result).toEqual(data);
|
|
74
|
-
});
|
|
75
|
-
it("readJson returns null for non-existing file", () => {
|
|
76
|
-
expect(readJson(path.join(TEST_ROOT, "nope.json"))).toBeNull();
|
|
77
|
-
});
|
|
78
|
-
it("writeText and readText roundtrip", () => {
|
|
79
|
-
const filePath = path.join(TEST_ROOT, "text.md");
|
|
80
|
-
const content = "# Hello\n\nThis is content.";
|
|
81
|
-
writeText(filePath, content);
|
|
82
|
-
const result = readText(filePath);
|
|
83
|
-
expect(result).toBe(content);
|
|
84
|
-
});
|
|
85
|
-
it("readText returns null for non-existing file", () => {
|
|
86
|
-
expect(readText(path.join(TEST_ROOT, "nope.md"))).toBeNull();
|
|
87
|
-
});
|
|
88
|
-
it("writeJson creates parent directories", () => {
|
|
89
|
-
const filePath = path.join(TEST_ROOT, "deep", "nested", "data.json");
|
|
90
|
-
const expected = { test: true };
|
|
91
|
-
writeJson(filePath, expected);
|
|
92
|
-
expect(readJson(filePath)).toEqual(expected);
|
|
93
|
-
});
|
|
94
|
-
it("writeText creates parent directories", () => {
|
|
95
|
-
const filePath = path.join(TEST_ROOT, "deep", "nested", "file.txt");
|
|
96
|
-
writeText(filePath, "content");
|
|
97
|
-
expect(readText(filePath)).toBe("content");
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
});
|