@howaboua/opencode-roadmap-plugin 0.1.5 → 0.1.7
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 +85 -14
- package/dist/src/descriptions/createroadmap.txt +20 -16
- package/dist/src/descriptions/loader.js +8 -2
- package/dist/src/descriptions/readroadmap.txt +19 -2
- package/dist/src/descriptions/updateroadmap.txt +8 -6
- package/dist/src/errors/loader.js +8 -1
- package/dist/src/storage.d.ts +12 -0
- package/dist/src/storage.js +112 -21
- package/dist/src/tools/createroadmap.d.ts +4 -0
- package/dist/src/tools/createroadmap.js +98 -94
- package/dist/src/tools/readroadmap.d.ts +4 -0
- package/dist/src/tools/readroadmap.js +4 -0
- package/dist/src/tools/updateroadmap.d.ts +4 -0
- package/dist/src/tools/updateroadmap.js +88 -80
- package/dist/src/types.d.ts +16 -16
- package/dist/src/types.js +1 -1
- package/package.json +4 -2
- package/dist/descriptions/index.d.ts +0 -1
- package/dist/descriptions/index.js +0 -1
- package/dist/descriptions/loader.d.ts +0 -1
- package/dist/descriptions/loader.js +0 -17
- package/dist/errors/loader.d.ts +0 -2
- package/dist/errors/loader.js +0 -24
- package/dist/storage.d.ts +0 -25
- package/dist/storage.js +0 -214
- package/dist/tools/createroadmap.d.ts +0 -2
- package/dist/tools/createroadmap.js +0 -135
- package/dist/tools/readroadmap.d.ts +0 -2
- package/dist/tools/readroadmap.js +0 -90
- package/dist/tools/updateroadmap.d.ts +0 -2
- package/dist/tools/updateroadmap.js +0 -107
- package/dist/types.d.ts +0 -151
- package/dist/types.js +0 -18
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool for creating and appending to project roadmaps.
|
|
3
|
+
* Supports merge logic for adding new features/actions to existing roadmaps.
|
|
4
|
+
*/
|
|
1
5
|
import { tool } from "@opencode-ai/plugin";
|
|
2
6
|
import { FileStorage, RoadmapValidator } from "../storage.js";
|
|
3
7
|
import { loadDescription } from "../descriptions/index.js";
|
|
@@ -26,110 +30,110 @@ export async function createCreateRoadmapTool(directory) {
|
|
|
26
30
|
},
|
|
27
31
|
async execute(args) {
|
|
28
32
|
const storage = new FileStorage(directory);
|
|
29
|
-
let roadmap;
|
|
30
|
-
let isUpdate = false;
|
|
31
|
-
if (await storage.exists()) {
|
|
32
|
-
const existing = await storage.read();
|
|
33
|
-
if (!existing) {
|
|
34
|
-
throw new Error("Existing roadmap file is corrupted. Please fix manually.");
|
|
35
|
-
}
|
|
36
|
-
roadmap = existing;
|
|
37
|
-
isUpdate = true;
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
roadmap = { features: [] };
|
|
41
|
-
}
|
|
42
33
|
if (!args.features || args.features.length === 0) {
|
|
43
34
|
throw new Error('Roadmap must have at least one feature with at least one action. Example: {"features": [{"number": "1", "title": "Feature 1", "description": "Description", "actions": [{"number": "1.01", "description": "Action 1", "status": "pending"}]}]}');
|
|
44
35
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
36
|
+
return await storage.update(async (current) => {
|
|
37
|
+
const roadmap = current ?? { features: [] };
|
|
38
|
+
const isUpdate = current !== null;
|
|
39
|
+
const validationErrors = [];
|
|
40
|
+
// First pass: structural validation of input
|
|
41
|
+
for (const feature of args.features) {
|
|
42
|
+
if (!feature.actions || feature.actions.length === 0) {
|
|
43
|
+
throw new Error(`Feature "${feature.number}" must have at least one action. Each feature needs at least one action to be valid.`);
|
|
44
|
+
}
|
|
45
|
+
const titleError = RoadmapValidator.validateTitle(feature.title, "feature");
|
|
46
|
+
if (titleError)
|
|
47
|
+
validationErrors.push(titleError);
|
|
48
|
+
const descError = RoadmapValidator.validateDescription(feature.description, "feature");
|
|
49
|
+
if (descError)
|
|
50
|
+
validationErrors.push(descError);
|
|
51
|
+
for (const action of feature.actions) {
|
|
52
|
+
const actionTitleError = RoadmapValidator.validateTitle(action.description, "action");
|
|
53
|
+
if (actionTitleError)
|
|
54
|
+
validationErrors.push(actionTitleError);
|
|
55
|
+
}
|
|
50
56
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
for (const action of feature.actions) {
|
|
58
|
-
const actionTitleError = RoadmapValidator.validateTitle(action.description, "action");
|
|
59
|
-
if (actionTitleError)
|
|
60
|
-
validationErrors.push(actionTitleError);
|
|
57
|
+
// Validate sequence consistency of input (internal consistency)
|
|
58
|
+
const sequenceErrors = RoadmapValidator.validateFeatureSequence(args.features);
|
|
59
|
+
validationErrors.push(...sequenceErrors);
|
|
60
|
+
if (validationErrors.length > 0) {
|
|
61
|
+
const errorMessages = validationErrors.map((err) => err.message).join("\n");
|
|
62
|
+
throw new Error(`Validation errors:\n${errorMessages}\n\nPlease fix these issues and try again.`);
|
|
61
63
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (existingFeature.title !== inputFeature.title || existingFeature.description !== inputFeature.description) {
|
|
76
|
-
const msg = await getErrorMessage("immutable_feature", {
|
|
77
|
-
id: inputFeature.number,
|
|
78
|
-
oldTitle: existingFeature.title,
|
|
79
|
-
oldDesc: existingFeature.description,
|
|
80
|
-
newTitle: inputFeature.title,
|
|
81
|
-
newDesc: inputFeature.description
|
|
82
|
-
});
|
|
83
|
-
throw new Error(msg);
|
|
84
|
-
}
|
|
85
|
-
// Process Actions
|
|
86
|
-
for (const inputAction of inputFeature.actions) {
|
|
87
|
-
const existingAction = existingFeature.actions.find((a) => a.number === inputAction.number);
|
|
88
|
-
if (existingAction) {
|
|
89
|
-
// Action exists: skip (immutable)
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
// New Action: Append
|
|
94
|
-
existingFeature.actions.push({
|
|
95
|
-
number: inputAction.number,
|
|
96
|
-
description: inputAction.description,
|
|
97
|
-
status: inputAction.status,
|
|
64
|
+
// Merge Logic
|
|
65
|
+
for (const inputFeature of args.features) {
|
|
66
|
+
const existingFeature = roadmap.features.find((f) => f.number === inputFeature.number);
|
|
67
|
+
if (existingFeature) {
|
|
68
|
+
// Feature exists: Validate Immutability
|
|
69
|
+
if (existingFeature.title !== inputFeature.title ||
|
|
70
|
+
existingFeature.description !== inputFeature.description) {
|
|
71
|
+
const msg = await getErrorMessage("immutable_feature", {
|
|
72
|
+
id: inputFeature.number,
|
|
73
|
+
oldTitle: existingFeature.title,
|
|
74
|
+
oldDesc: existingFeature.description,
|
|
75
|
+
newTitle: inputFeature.title,
|
|
76
|
+
newDesc: inputFeature.description,
|
|
98
77
|
});
|
|
99
|
-
|
|
100
|
-
|
|
78
|
+
throw new Error(msg);
|
|
79
|
+
}
|
|
80
|
+
// Process Actions
|
|
81
|
+
for (const inputAction of inputFeature.actions) {
|
|
82
|
+
const existingAction = existingFeature.actions.find((a) => a.number === inputAction.number);
|
|
83
|
+
if (existingAction) {
|
|
84
|
+
// Action exists: skip (immutable)
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// New Action: Append
|
|
89
|
+
existingFeature.actions.push({
|
|
90
|
+
number: inputAction.number,
|
|
91
|
+
description: inputAction.description,
|
|
92
|
+
status: inputAction.status,
|
|
93
|
+
});
|
|
94
|
+
// Sort actions to ensure order
|
|
95
|
+
existingFeature.actions.sort((a, b) => parseFloat(a.number) - parseFloat(b.number));
|
|
96
|
+
}
|
|
101
97
|
}
|
|
102
98
|
}
|
|
99
|
+
else {
|
|
100
|
+
// New Feature: Append
|
|
101
|
+
roadmap.features.push({
|
|
102
|
+
number: inputFeature.number,
|
|
103
|
+
title: inputFeature.title,
|
|
104
|
+
description: inputFeature.description,
|
|
105
|
+
actions: inputFeature.actions.map((a) => ({
|
|
106
|
+
number: a.number,
|
|
107
|
+
description: a.description,
|
|
108
|
+
status: a.status,
|
|
109
|
+
})),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
103
112
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
number: a.number,
|
|
112
|
-
description: a.description,
|
|
113
|
-
status: a.status,
|
|
114
|
-
})),
|
|
115
|
-
});
|
|
113
|
+
// Final Sort of Features
|
|
114
|
+
roadmap.features.sort((a, b) => parseInt(a.number) - parseInt(b.number));
|
|
115
|
+
// Safety check: ensure no feature ended up with zero actions after merge
|
|
116
|
+
for (const feature of roadmap.features) {
|
|
117
|
+
if (feature.actions.length === 0) {
|
|
118
|
+
throw new Error(`Feature "${feature.number}" has no actions. This indicates a merge error.`);
|
|
119
|
+
}
|
|
116
120
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
121
|
+
// Final Validation of the Merged Roadmap
|
|
122
|
+
const finalErrors = RoadmapValidator.validateFeatureSequence(roadmap.features);
|
|
123
|
+
if (finalErrors.length > 0) {
|
|
124
|
+
throw new Error(`Resulting roadmap would be invalid:\n${finalErrors.map((e) => e.message).join("\n")}`);
|
|
125
|
+
}
|
|
126
|
+
const totalActions = roadmap.features.reduce((sum, feature) => sum + feature.actions.length, 0);
|
|
127
|
+
const action = isUpdate ? "Updated" : "Created";
|
|
128
|
+
const summary = `${action} roadmap with ${roadmap.features.length} features and ${totalActions} actions:\n` +
|
|
129
|
+
roadmap.features
|
|
130
|
+
.map((feature) => ` Feature ${feature.number}: ${feature.title} (${feature.actions.length} actions)`)
|
|
131
|
+
.join("\n");
|
|
132
|
+
return {
|
|
133
|
+
roadmap,
|
|
134
|
+
buildResult: () => summary,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
133
137
|
},
|
|
134
138
|
});
|
|
135
139
|
}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool for reading roadmap state, progress, and details.
|
|
3
|
+
* Supports filtering by feature or action number.
|
|
4
|
+
*/
|
|
1
5
|
import { type ToolDefinition } from "@opencode-ai/plugin";
|
|
2
6
|
export declare function createReadRoadmapTool(directory: string): Promise<ToolDefinition>;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool for reading roadmap state, progress, and details.
|
|
3
|
+
* Supports filtering by feature or action number.
|
|
4
|
+
*/
|
|
1
5
|
import { tool } from "@opencode-ai/plugin";
|
|
2
6
|
import { FileStorage, RoadmapValidator } from "../storage.js";
|
|
3
7
|
import { loadDescription } from "../descriptions/index.js";
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool for updating action status and descriptions in a roadmap.
|
|
3
|
+
* Enforces forward-only status progression and archives when complete.
|
|
4
|
+
*/
|
|
1
5
|
import { type ToolDefinition } from "@opencode-ai/plugin";
|
|
2
6
|
export declare function createUpdateRoadmapTool(directory: string): Promise<ToolDefinition>;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool for updating action status and descriptions in a roadmap.
|
|
3
|
+
* Enforces forward-only status progression and archives when complete.
|
|
4
|
+
*/
|
|
1
5
|
import { tool } from "@opencode-ai/plugin";
|
|
2
6
|
import { FileStorage, RoadmapValidator } from "../storage.js";
|
|
3
7
|
import { loadDescription } from "../descriptions/index.js";
|
|
@@ -12,99 +16,103 @@ export async function createUpdateRoadmapTool(directory) {
|
|
|
12
16
|
.optional()
|
|
13
17
|
.describe("New action description (full overwrite). If not provided, only status is updated."),
|
|
14
18
|
status: tool.schema
|
|
15
|
-
.enum(["pending", "in_progress", "completed"])
|
|
19
|
+
.enum(["pending", "in_progress", "completed", "cancelled"])
|
|
16
20
|
.optional()
|
|
17
|
-
.describe("New action status
|
|
21
|
+
.describe("New action status. Flexible transitions allowed except from cancelled."),
|
|
18
22
|
},
|
|
19
23
|
async execute(args) {
|
|
20
24
|
const storage = new FileStorage(directory);
|
|
21
|
-
if (!(await storage.exists())) {
|
|
22
|
-
throw new Error("Roadmap not found. Use CreateRoadmap to create one.");
|
|
23
|
-
}
|
|
24
|
-
const roadmap = await storage.read();
|
|
25
|
-
if (!roadmap) {
|
|
26
|
-
throw new Error("Roadmap file is corrupted. Please fix manually.");
|
|
27
|
-
}
|
|
28
25
|
const actionNumberError = RoadmapValidator.validateActionNumber(args.actionNumber);
|
|
29
26
|
if (actionNumberError) {
|
|
30
27
|
throw new Error(`${actionNumberError.message} Use ReadRoadmap to see valid action numbers.`);
|
|
31
28
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
for (const feature of roadmap.features) {
|
|
36
|
-
const action = feature.actions.find((a) => a.number === args.actionNumber);
|
|
37
|
-
if (action) {
|
|
38
|
-
targetAction = action;
|
|
39
|
-
targetFeature = feature;
|
|
40
|
-
actionFound = true;
|
|
41
|
-
break;
|
|
29
|
+
return await storage.update((roadmap) => {
|
|
30
|
+
if (!roadmap) {
|
|
31
|
+
throw new Error("Roadmap not found. Use CreateRoadmap to create one.");
|
|
42
32
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
const oldStatus = targetAction.status;
|
|
56
|
-
const oldDescription = targetAction.description;
|
|
57
|
-
// Validate description if provided
|
|
58
|
-
if (args.description !== undefined) {
|
|
59
|
-
const descError = RoadmapValidator.validateDescription(args.description, "action");
|
|
60
|
-
if (descError) {
|
|
61
|
-
throw new Error(`${descError.message}`);
|
|
33
|
+
let targetAction = null;
|
|
34
|
+
let targetFeature = null;
|
|
35
|
+
let actionFound = false;
|
|
36
|
+
for (const feature of roadmap.features) {
|
|
37
|
+
const action = feature.actions.find((a) => a.number === args.actionNumber);
|
|
38
|
+
if (action) {
|
|
39
|
+
targetAction = action;
|
|
40
|
+
targetFeature = feature;
|
|
41
|
+
actionFound = true;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
62
44
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// Validate and update status if provided
|
|
66
|
-
if (args.status !== undefined) {
|
|
67
|
-
const statusTransitionError = RoadmapValidator.validateStatusProgression(targetAction.status, args.status);
|
|
68
|
-
if (statusTransitionError) {
|
|
69
|
-
throw new Error(`${statusTransitionError.message} Current status: "${targetAction.status}", requested: "${args.status}"`);
|
|
45
|
+
if (!actionFound) {
|
|
46
|
+
throw new Error(`Action "${args.actionNumber}" not found. Use ReadRoadmap to see valid action numbers.`);
|
|
70
47
|
}
|
|
71
|
-
targetAction
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (action.status !== "completed") {
|
|
86
|
-
allCompleted = false;
|
|
87
|
-
break;
|
|
48
|
+
if (!targetAction || !targetFeature) {
|
|
49
|
+
throw new Error("Internal error: target action not found.");
|
|
50
|
+
}
|
|
51
|
+
// Validate that at least one field is being updated
|
|
52
|
+
if (args.description === undefined && args.status === undefined) {
|
|
53
|
+
throw new Error("No changes specified. Please provide description and/or status.");
|
|
54
|
+
}
|
|
55
|
+
const oldStatus = targetAction.status;
|
|
56
|
+
const oldDescription = targetAction.description;
|
|
57
|
+
// Validate description if provided
|
|
58
|
+
if (args.description !== undefined) {
|
|
59
|
+
const descError = RoadmapValidator.validateDescription(args.description, "action");
|
|
60
|
+
if (descError) {
|
|
61
|
+
throw new Error(`${descError.message}`);
|
|
88
62
|
}
|
|
63
|
+
targetAction.description = args.description;
|
|
89
64
|
}
|
|
90
|
-
if
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
65
|
+
// Validate and update status if provided
|
|
66
|
+
if (args.status !== undefined) {
|
|
67
|
+
const statusTransitionError = RoadmapValidator.validateStatusProgression(targetAction.status, args.status);
|
|
68
|
+
if (statusTransitionError) {
|
|
69
|
+
throw new Error(`${statusTransitionError.message} Current status: "${targetAction.status}", requested: "${args.status}"`);
|
|
70
|
+
}
|
|
71
|
+
targetAction.status = args.status;
|
|
72
|
+
}
|
|
73
|
+
const changes = [];
|
|
74
|
+
if (args.description !== undefined && oldDescription !== args.description) {
|
|
75
|
+
changes.push("description updated");
|
|
76
|
+
}
|
|
77
|
+
if (args.status !== undefined && oldStatus !== args.status) {
|
|
78
|
+
changes.push(`status: "${oldStatus}" → "${args.status}"`);
|
|
79
|
+
}
|
|
80
|
+
if (changes.length === 0) {
|
|
81
|
+
return {
|
|
82
|
+
roadmap,
|
|
83
|
+
buildResult: () => `Action ${args.actionNumber} unchanged. Provided values match current state.`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Check if all actions are completed
|
|
87
|
+
let allCompleted = true;
|
|
88
|
+
for (const feature of roadmap.features) {
|
|
89
|
+
for (const action of feature.actions) {
|
|
90
|
+
if (action.status !== "completed") {
|
|
91
|
+
allCompleted = false;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!allCompleted)
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
// Format feature context
|
|
99
|
+
const featureCompleted = targetFeature.actions.filter((a) => a.status === "completed").length;
|
|
100
|
+
const featureTotal = targetFeature.actions.length;
|
|
101
|
+
let featureContext = `\n\nFeature ${targetFeature.number}: ${targetFeature.title} (${featureCompleted}/${featureTotal} complete)\n`;
|
|
102
|
+
featureContext += `Description: ${targetFeature.description}\n`;
|
|
103
|
+
for (const action of targetFeature.actions) {
|
|
104
|
+
const statusIcon = action.status === "completed" ? "✓" : action.status === "in_progress" ? "→" : "○";
|
|
105
|
+
featureContext += `${action.number} ${statusIcon} ${action.description} [${action.status}]\n`;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
roadmap,
|
|
109
|
+
archive: allCompleted,
|
|
110
|
+
buildResult: (archiveName) => {
|
|
111
|
+
const archiveMsg = archiveName ? `\n\nAll actions completed! Roadmap archived to "${archiveName}".` : "";
|
|
112
|
+
return `Updated action ${args.actionNumber} in feature "${targetFeature.title}": ${changes.join(", ")}${featureContext}${archiveMsg}`;
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
});
|
|
108
116
|
},
|
|
109
117
|
});
|
|
110
118
|
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const ActionStatus: z.ZodEnum<["pending", "in_progress", "completed"]>;
|
|
2
|
+
export declare const ActionStatus: z.ZodEnum<["pending", "in_progress", "completed", "cancelled"]>;
|
|
3
3
|
export type ActionStatus = z.infer<typeof ActionStatus>;
|
|
4
4
|
export declare const Action: z.ZodObject<{
|
|
5
5
|
number: z.ZodString;
|
|
6
6
|
description: z.ZodString;
|
|
7
|
-
status: z.ZodEnum<["pending", "in_progress", "completed"]>;
|
|
7
|
+
status: z.ZodEnum<["pending", "in_progress", "completed", "cancelled"]>;
|
|
8
8
|
}, "strip", z.ZodTypeAny, {
|
|
9
9
|
number: string;
|
|
10
|
-
status: "pending" | "in_progress" | "completed";
|
|
10
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
11
11
|
description: string;
|
|
12
12
|
}, {
|
|
13
13
|
number: string;
|
|
14
|
-
status: "pending" | "in_progress" | "completed";
|
|
14
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
15
15
|
description: string;
|
|
16
16
|
}>;
|
|
17
17
|
export type Action = z.infer<typeof Action>;
|
|
@@ -22,14 +22,14 @@ export declare const Feature: z.ZodObject<{
|
|
|
22
22
|
actions: z.ZodArray<z.ZodObject<{
|
|
23
23
|
number: z.ZodString;
|
|
24
24
|
description: z.ZodString;
|
|
25
|
-
status: z.ZodEnum<["pending", "in_progress", "completed"]>;
|
|
25
|
+
status: z.ZodEnum<["pending", "in_progress", "completed", "cancelled"]>;
|
|
26
26
|
}, "strip", z.ZodTypeAny, {
|
|
27
27
|
number: string;
|
|
28
|
-
status: "pending" | "in_progress" | "completed";
|
|
28
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
29
29
|
description: string;
|
|
30
30
|
}, {
|
|
31
31
|
number: string;
|
|
32
|
-
status: "pending" | "in_progress" | "completed";
|
|
32
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
33
33
|
description: string;
|
|
34
34
|
}>, "many">;
|
|
35
35
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -38,7 +38,7 @@ export declare const Feature: z.ZodObject<{
|
|
|
38
38
|
title: string;
|
|
39
39
|
actions: {
|
|
40
40
|
number: string;
|
|
41
|
-
status: "pending" | "in_progress" | "completed";
|
|
41
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
42
42
|
description: string;
|
|
43
43
|
}[];
|
|
44
44
|
}, {
|
|
@@ -47,7 +47,7 @@ export declare const Feature: z.ZodObject<{
|
|
|
47
47
|
title: string;
|
|
48
48
|
actions: {
|
|
49
49
|
number: string;
|
|
50
|
-
status: "pending" | "in_progress" | "completed";
|
|
50
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
51
51
|
description: string;
|
|
52
52
|
}[];
|
|
53
53
|
}>;
|
|
@@ -60,14 +60,14 @@ export declare const Roadmap: z.ZodObject<{
|
|
|
60
60
|
actions: z.ZodArray<z.ZodObject<{
|
|
61
61
|
number: z.ZodString;
|
|
62
62
|
description: z.ZodString;
|
|
63
|
-
status: z.ZodEnum<["pending", "in_progress", "completed"]>;
|
|
63
|
+
status: z.ZodEnum<["pending", "in_progress", "completed", "cancelled"]>;
|
|
64
64
|
}, "strip", z.ZodTypeAny, {
|
|
65
65
|
number: string;
|
|
66
|
-
status: "pending" | "in_progress" | "completed";
|
|
66
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
67
67
|
description: string;
|
|
68
68
|
}, {
|
|
69
69
|
number: string;
|
|
70
|
-
status: "pending" | "in_progress" | "completed";
|
|
70
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
71
71
|
description: string;
|
|
72
72
|
}>, "many">;
|
|
73
73
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -76,7 +76,7 @@ export declare const Roadmap: z.ZodObject<{
|
|
|
76
76
|
title: string;
|
|
77
77
|
actions: {
|
|
78
78
|
number: string;
|
|
79
|
-
status: "pending" | "in_progress" | "completed";
|
|
79
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
80
80
|
description: string;
|
|
81
81
|
}[];
|
|
82
82
|
}, {
|
|
@@ -85,7 +85,7 @@ export declare const Roadmap: z.ZodObject<{
|
|
|
85
85
|
title: string;
|
|
86
86
|
actions: {
|
|
87
87
|
number: string;
|
|
88
|
-
status: "pending" | "in_progress" | "completed";
|
|
88
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
89
89
|
description: string;
|
|
90
90
|
}[];
|
|
91
91
|
}>, "many">;
|
|
@@ -96,7 +96,7 @@ export declare const Roadmap: z.ZodObject<{
|
|
|
96
96
|
title: string;
|
|
97
97
|
actions: {
|
|
98
98
|
number: string;
|
|
99
|
-
status: "pending" | "in_progress" | "completed";
|
|
99
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
100
100
|
description: string;
|
|
101
101
|
}[];
|
|
102
102
|
}[];
|
|
@@ -107,7 +107,7 @@ export declare const Roadmap: z.ZodObject<{
|
|
|
107
107
|
title: string;
|
|
108
108
|
actions: {
|
|
109
109
|
number: string;
|
|
110
|
-
status: "pending" | "in_progress" | "completed";
|
|
110
|
+
status: "pending" | "in_progress" | "completed" | "cancelled";
|
|
111
111
|
description: string;
|
|
112
112
|
}[];
|
|
113
113
|
}[];
|
package/dist/src/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@howaboua/opencode-roadmap-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Strategic roadmap planning and multi-agent coordination for OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"typescript": "^5.0.0"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"typescript": "^5.0.0",
|
|
36
35
|
"zod": "^3.22.0"
|
|
37
36
|
},
|
|
38
37
|
"scripts": {
|
|
@@ -41,6 +40,9 @@
|
|
|
41
40
|
"lint": "eslint . --ext .ts",
|
|
42
41
|
"prepublishOnly": "npm run build"
|
|
43
42
|
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
44
46
|
"opencode": {
|
|
45
47
|
"type": "plugin",
|
|
46
48
|
"tools": [
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { loadDescription } from "./loader";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { loadDescription } from "./loader.js";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function loadDescription(filename: string): Promise<string>;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
export async function loadDescription(filename) {
|
|
4
|
-
// In the compiled output, __dirname is .../dist/descriptions, but the assets are in .../src/descriptions.
|
|
5
|
-
// This path adjustment ensures the assets are found regardless of the build process.
|
|
6
|
-
const descriptionsDir = join(__dirname, "..", "..", "src", "descriptions");
|
|
7
|
-
const filePath = join(descriptionsDir, filename);
|
|
8
|
-
try {
|
|
9
|
-
return await fs.readFile(filePath, "utf-8");
|
|
10
|
-
}
|
|
11
|
-
catch (error) {
|
|
12
|
-
if (error.code === "ENOENT") {
|
|
13
|
-
throw new Error(`Description file not found: ${filename}. Looked in: ${filePath}. Please ensure asset files are correctly located.`);
|
|
14
|
-
}
|
|
15
|
-
throw error;
|
|
16
|
-
}
|
|
17
|
-
}
|
package/dist/errors/loader.d.ts
DELETED
package/dist/errors/loader.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
const ERROR_CACHE = {};
|
|
4
|
-
export async function loadErrorTemplate(filename) {
|
|
5
|
-
if (ERROR_CACHE[filename])
|
|
6
|
-
return ERROR_CACHE[filename];
|
|
7
|
-
const errorsDir = join(__dirname, "..", "..", "src", "errors");
|
|
8
|
-
const filePath = join(errorsDir, filename + ".txt");
|
|
9
|
-
try {
|
|
10
|
-
const content = await fs.readFile(filePath, "utf-8");
|
|
11
|
-
ERROR_CACHE[filename] = content.trim();
|
|
12
|
-
return ERROR_CACHE[filename];
|
|
13
|
-
}
|
|
14
|
-
catch (error) {
|
|
15
|
-
if (error.code === "ENOENT") {
|
|
16
|
-
throw new Error(`Error template not found: ${filename} at ${filePath}`);
|
|
17
|
-
}
|
|
18
|
-
throw error;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
export async function getErrorMessage(filename, params = {}) {
|
|
22
|
-
const template = await loadErrorTemplate(filename);
|
|
23
|
-
return template.replace(/\{(\w+)\}/g, (_, key) => params[key] || `{${key}}`);
|
|
24
|
-
}
|