@howaboua/opencode-roadmap-plugin 0.1.4 → 0.1.5
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/src/descriptions/loader.js +3 -2
- package/dist/src/errors/loader.js +3 -2
- package/dist/src/index.js +1 -1
- package/dist/src/storage.d.ts +12 -12
- package/dist/src/storage.js +29 -34
- package/dist/src/tools/createroadmap.js +5 -5
- package/dist/src/tools/readroadmap.js +4 -5
- package/dist/src/tools/updateroadmap.js +11 -8
- package/dist/src/types.d.ts +5 -5
- package/package.json +8 -4
|
@@ -7,9 +7,10 @@ export async function loadDescription(filename) {
|
|
|
7
7
|
return await fs.readFile(filePath, "utf-8");
|
|
8
8
|
}
|
|
9
9
|
catch (error) {
|
|
10
|
-
|
|
10
|
+
const err = error;
|
|
11
|
+
if (err?.code === "ENOENT") {
|
|
11
12
|
throw new Error(`Description file not found: ${filename}. Looked in: ${filePath}. Please ensure asset files are correctly located.`);
|
|
12
13
|
}
|
|
13
|
-
throw error;
|
|
14
|
+
throw err ?? new Error("Unknown error while loading description");
|
|
14
15
|
}
|
|
15
16
|
}
|
|
@@ -11,10 +11,11 @@ export async function loadErrorTemplate(filename) {
|
|
|
11
11
|
return ERROR_CACHE[filename];
|
|
12
12
|
}
|
|
13
13
|
catch (error) {
|
|
14
|
-
|
|
14
|
+
const err = error;
|
|
15
|
+
if (err?.code === "ENOENT") {
|
|
15
16
|
throw new Error(`Error template not found: ${filename} at ${filePath}`);
|
|
16
17
|
}
|
|
17
|
-
throw error;
|
|
18
|
+
throw err ?? new Error("Unknown error loading error template");
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
export async function getErrorMessage(filename, params = {}) {
|
package/dist/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createCreateRoadmapTool } from "./tools/createroadmap.js";
|
|
2
2
|
import { createUpdateRoadmapTool } from "./tools/updateroadmap.js";
|
|
3
3
|
import { createReadRoadmapTool } from "./tools/readroadmap.js";
|
|
4
|
-
export const RoadmapPlugin = async ({
|
|
4
|
+
export const RoadmapPlugin = async ({ directory }) => {
|
|
5
5
|
return {
|
|
6
6
|
tool: {
|
|
7
7
|
createroadmap: await createCreateRoadmapTool(directory),
|
package/dist/src/storage.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Roadmap, RoadmapStorage, ValidationError } from "./types.js";
|
|
2
2
|
export declare class FileStorage implements RoadmapStorage {
|
|
3
|
-
private directory;
|
|
3
|
+
private readonly directory;
|
|
4
4
|
constructor(directory: string);
|
|
5
5
|
exists(): Promise<boolean>;
|
|
6
6
|
read(): Promise<Roadmap | null>;
|
|
@@ -8,18 +8,18 @@ export declare class FileStorage implements RoadmapStorage {
|
|
|
8
8
|
archive(): Promise<string>;
|
|
9
9
|
}
|
|
10
10
|
export declare class RoadmapValidator {
|
|
11
|
-
static validateFeatureNumber(number: string):
|
|
12
|
-
static validateActionNumber(number: string):
|
|
13
|
-
static validateActionSequence(actions:
|
|
11
|
+
static validateFeatureNumber(number: string): ValidationError | null;
|
|
12
|
+
static validateActionNumber(number: string): ValidationError | null;
|
|
13
|
+
static validateActionSequence(actions: {
|
|
14
14
|
number: string;
|
|
15
|
-
}
|
|
16
|
-
static validateFeatureSequence(features:
|
|
15
|
+
}[], globalSeenNumbers?: Set<string>, featureNumber?: string): ValidationError[];
|
|
16
|
+
static validateFeatureSequence(features: {
|
|
17
17
|
number: string;
|
|
18
|
-
actions:
|
|
18
|
+
actions: {
|
|
19
19
|
number: string;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
static validateTitle(title: string, fieldType: "feature" | "action"):
|
|
23
|
-
static validateDescription(description: string, fieldType: "feature" | "action"):
|
|
24
|
-
static validateStatusProgression(currentStatus: string, newStatus: string):
|
|
20
|
+
}[];
|
|
21
|
+
}[]): ValidationError[];
|
|
22
|
+
static validateTitle(title: string, fieldType: "feature" | "action"): ValidationError | null;
|
|
23
|
+
static validateDescription(description: string, fieldType: "feature" | "action"): ValidationError | null;
|
|
24
|
+
static validateStatusProgression(currentStatus: string, newStatus: string): ValidationError | null;
|
|
25
25
|
}
|
package/dist/src/storage.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
|
-
import {
|
|
3
|
+
import { Roadmap as RoadmapSchema } from "./types.js";
|
|
4
4
|
const ROADMAP_FILE = "roadmap.json";
|
|
5
5
|
export class FileStorage {
|
|
6
6
|
directory;
|
|
@@ -24,13 +24,8 @@ export class FileStorage {
|
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
26
|
const parsed = JSON.parse(data);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
if (!Array.isArray(parsed.features)) {
|
|
31
|
-
throw new Error("Invalid roadmap format: missing or invalid features array");
|
|
32
|
-
}
|
|
33
|
-
return parsed;
|
|
27
|
+
const validated = RoadmapSchema.parse(parsed);
|
|
28
|
+
return validated;
|
|
34
29
|
}
|
|
35
30
|
catch (error) {
|
|
36
31
|
if (error instanceof SyntaxError) {
|
|
@@ -39,7 +34,10 @@ export class FileStorage {
|
|
|
39
34
|
if (error instanceof Error && error.message.includes("ENOENT")) {
|
|
40
35
|
return null;
|
|
41
36
|
}
|
|
42
|
-
|
|
37
|
+
if (error instanceof Error) {
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
throw new Error("Unknown error while reading roadmap");
|
|
43
41
|
}
|
|
44
42
|
}
|
|
45
43
|
async write(roadmap) {
|
|
@@ -57,7 +55,10 @@ export class FileStorage {
|
|
|
57
55
|
catch {
|
|
58
56
|
// Ignore cleanup errors
|
|
59
57
|
}
|
|
60
|
-
|
|
58
|
+
if (error instanceof Error) {
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
throw new Error("Unknown error while writing roadmap");
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
async archive() {
|
|
@@ -70,42 +71,41 @@ export class FileStorage {
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
export class RoadmapValidator {
|
|
73
|
-
static
|
|
74
|
+
static validateFeatureNumber(number) {
|
|
74
75
|
if (!number || typeof number !== "string") {
|
|
75
76
|
return {
|
|
76
77
|
code: "INVALID_FEATURE_NUMBER",
|
|
77
|
-
message:
|
|
78
|
+
message: "Invalid feature ID: must be a string.",
|
|
78
79
|
};
|
|
79
80
|
}
|
|
80
81
|
if (!/^\d+$/.test(number)) {
|
|
81
82
|
return {
|
|
82
83
|
code: "INVALID_FEATURE_NUMBER_FORMAT",
|
|
83
|
-
message:
|
|
84
|
+
message: "Invalid feature ID format: must be a simple number.",
|
|
84
85
|
};
|
|
85
86
|
}
|
|
86
87
|
return null;
|
|
87
88
|
}
|
|
88
|
-
static
|
|
89
|
+
static validateActionNumber(number) {
|
|
89
90
|
if (!number || typeof number !== "string") {
|
|
90
91
|
return {
|
|
91
92
|
code: "INVALID_ACTION_NUMBER",
|
|
92
|
-
message:
|
|
93
|
+
message: "Invalid action ID: must be a string.",
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
96
|
if (!/^\d+\.\d{2}$/.test(number)) {
|
|
96
97
|
return {
|
|
97
98
|
code: "INVALID_ACTION_NUMBER_FORMAT",
|
|
98
|
-
message:
|
|
99
|
+
message: "Invalid action ID format: must be X.YY (e.g., 1.01).",
|
|
99
100
|
};
|
|
100
101
|
}
|
|
101
102
|
return null;
|
|
102
103
|
}
|
|
103
|
-
static
|
|
104
|
+
static validateActionSequence(actions, globalSeenNumbers, featureNumber) {
|
|
104
105
|
const errors = [];
|
|
105
106
|
const seenNumbers = new Set();
|
|
106
|
-
for (
|
|
107
|
-
const
|
|
108
|
-
const numberError = await this.validateActionNumber(action.number);
|
|
107
|
+
for (const action of actions) {
|
|
108
|
+
const numberError = this.validateActionNumber(action.number);
|
|
109
109
|
if (numberError) {
|
|
110
110
|
errors.push(numberError);
|
|
111
111
|
continue;
|
|
@@ -116,7 +116,7 @@ export class RoadmapValidator {
|
|
|
116
116
|
if (actionFeaturePrefix !== featureNumber) {
|
|
117
117
|
errors.push({
|
|
118
118
|
code: "ACTION_FEATURE_MISMATCH",
|
|
119
|
-
message:
|
|
119
|
+
message: `Action "${action.number}" does not belong to feature "${featureNumber}".`,
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
}
|
|
@@ -139,13 +139,12 @@ export class RoadmapValidator {
|
|
|
139
139
|
}
|
|
140
140
|
return errors;
|
|
141
141
|
}
|
|
142
|
-
static
|
|
142
|
+
static validateFeatureSequence(features) {
|
|
143
143
|
const errors = [];
|
|
144
144
|
const seenNumbers = new Set();
|
|
145
145
|
const seenActionNumbers = new Set();
|
|
146
|
-
for (
|
|
147
|
-
const
|
|
148
|
-
const numberError = await this.validateFeatureNumber(feature.number);
|
|
146
|
+
for (const feature of features) {
|
|
147
|
+
const numberError = this.validateFeatureNumber(feature.number);
|
|
149
148
|
if (numberError) {
|
|
150
149
|
errors.push(numberError);
|
|
151
150
|
continue;
|
|
@@ -157,12 +156,12 @@ export class RoadmapValidator {
|
|
|
157
156
|
});
|
|
158
157
|
}
|
|
159
158
|
seenNumbers.add(feature.number);
|
|
160
|
-
const actionErrors =
|
|
159
|
+
const actionErrors = this.validateActionSequence(feature.actions, seenActionNumbers, feature.number);
|
|
161
160
|
errors.push(...actionErrors);
|
|
162
161
|
}
|
|
163
162
|
return errors;
|
|
164
163
|
}
|
|
165
|
-
static
|
|
164
|
+
static validateTitle(title, fieldType) {
|
|
166
165
|
if (!title || typeof title !== "string") {
|
|
167
166
|
return {
|
|
168
167
|
code: "INVALID_TITLE",
|
|
@@ -177,7 +176,7 @@ export class RoadmapValidator {
|
|
|
177
176
|
}
|
|
178
177
|
return null;
|
|
179
178
|
}
|
|
180
|
-
static
|
|
179
|
+
static validateDescription(description, fieldType) {
|
|
181
180
|
if (!description || typeof description !== "string") {
|
|
182
181
|
return {
|
|
183
182
|
code: "INVALID_DESCRIPTION",
|
|
@@ -192,7 +191,7 @@ export class RoadmapValidator {
|
|
|
192
191
|
}
|
|
193
192
|
return null;
|
|
194
193
|
}
|
|
195
|
-
static
|
|
194
|
+
static validateStatusProgression(currentStatus, newStatus) {
|
|
196
195
|
const statusFlow = {
|
|
197
196
|
pending: ["in_progress", "completed"],
|
|
198
197
|
in_progress: ["completed"],
|
|
@@ -202,11 +201,7 @@ export class RoadmapValidator {
|
|
|
202
201
|
if (!allowedTransitions.includes(newStatus)) {
|
|
203
202
|
return {
|
|
204
203
|
code: "INVALID_STATUS_TRANSITION",
|
|
205
|
-
message:
|
|
206
|
-
from: currentStatus,
|
|
207
|
-
to: newStatus,
|
|
208
|
-
allowed: allowedTransitions.length > 0 ? allowedTransitions.join(", ") : "None (terminal state)"
|
|
209
|
-
}),
|
|
204
|
+
message: `Invalid transition from "${currentStatus}" to "${newStatus}". Allowed: ${allowedTransitions.length > 0 ? allowedTransitions.join(", ") : "None (terminal state)"}`,
|
|
210
205
|
};
|
|
211
206
|
}
|
|
212
207
|
return null;
|
|
@@ -48,20 +48,20 @@ export async function createCreateRoadmapTool(directory) {
|
|
|
48
48
|
if (!feature.actions || feature.actions.length === 0) {
|
|
49
49
|
throw new Error(`Feature "${feature.number}" must have at least one action. Each feature needs at least one action to be valid.`);
|
|
50
50
|
}
|
|
51
|
-
const titleError =
|
|
51
|
+
const titleError = RoadmapValidator.validateTitle(feature.title, "feature");
|
|
52
52
|
if (titleError)
|
|
53
53
|
validationErrors.push(titleError);
|
|
54
|
-
const descError =
|
|
54
|
+
const descError = RoadmapValidator.validateDescription(feature.description, "feature");
|
|
55
55
|
if (descError)
|
|
56
56
|
validationErrors.push(descError);
|
|
57
57
|
for (const action of feature.actions) {
|
|
58
|
-
const actionTitleError =
|
|
58
|
+
const actionTitleError = RoadmapValidator.validateTitle(action.description, "action");
|
|
59
59
|
if (actionTitleError)
|
|
60
60
|
validationErrors.push(actionTitleError);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
// Validate sequence consistency of input (internal consistency)
|
|
64
|
-
const sequenceErrors =
|
|
64
|
+
const sequenceErrors = RoadmapValidator.validateFeatureSequence(args.features);
|
|
65
65
|
validationErrors.push(...sequenceErrors);
|
|
66
66
|
if (validationErrors.length > 0) {
|
|
67
67
|
const errorMessages = validationErrors.map((err) => err.message).join("\n");
|
|
@@ -118,7 +118,7 @@ export async function createCreateRoadmapTool(directory) {
|
|
|
118
118
|
// Final Sort of Features
|
|
119
119
|
roadmap.features.sort((a, b) => parseInt(a.number) - parseInt(b.number));
|
|
120
120
|
// Final Validation of the Merged Roadmap
|
|
121
|
-
const finalErrors =
|
|
121
|
+
const finalErrors = RoadmapValidator.validateFeatureSequence(roadmap.features);
|
|
122
122
|
if (finalErrors.length > 0) {
|
|
123
123
|
throw new Error(`Resulting roadmap would be invalid:\n${finalErrors.map(e => e.message).join("\n")}`);
|
|
124
124
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin";
|
|
2
2
|
import { FileStorage, RoadmapValidator } from "../storage.js";
|
|
3
3
|
import { loadDescription } from "../descriptions/index.js";
|
|
4
|
-
import { getErrorMessage } from "../errors/loader.js";
|
|
5
4
|
export async function createReadRoadmapTool(directory) {
|
|
6
5
|
const description = await loadDescription("readroadmap.txt");
|
|
7
6
|
return tool({
|
|
@@ -19,17 +18,17 @@ export async function createReadRoadmapTool(directory) {
|
|
|
19
18
|
async execute(args) {
|
|
20
19
|
const storage = new FileStorage(directory);
|
|
21
20
|
if (!(await storage.exists())) {
|
|
22
|
-
throw new Error(
|
|
21
|
+
throw new Error("Roadmap not found. Use CreateRoadmap to create one.");
|
|
23
22
|
}
|
|
24
23
|
const roadmap = await storage.read();
|
|
25
24
|
if (!roadmap) {
|
|
26
|
-
throw new Error(
|
|
25
|
+
throw new Error("Roadmap file is corrupted. Please fix manually.");
|
|
27
26
|
}
|
|
28
27
|
if (args.actionNumber && args.featureNumber) {
|
|
29
28
|
throw new Error("Cannot specify both actionNumber and featureNumber. Use one or the other, or neither for full roadmap.");
|
|
30
29
|
}
|
|
31
30
|
if (args.actionNumber) {
|
|
32
|
-
const actionNumberError =
|
|
31
|
+
const actionNumberError = RoadmapValidator.validateActionNumber(args.actionNumber);
|
|
33
32
|
if (actionNumberError) {
|
|
34
33
|
throw new Error(`${actionNumberError.message} Use ReadRoadmap to see valid action numbers.`);
|
|
35
34
|
}
|
|
@@ -46,7 +45,7 @@ export async function createReadRoadmapTool(directory) {
|
|
|
46
45
|
throw new Error(`Action "${args.actionNumber}" not found. Use ReadRoadmap with no arguments to see all available actions.`);
|
|
47
46
|
}
|
|
48
47
|
if (args.featureNumber) {
|
|
49
|
-
const featureNumberError =
|
|
48
|
+
const featureNumberError = RoadmapValidator.validateFeatureNumber(args.featureNumber);
|
|
50
49
|
if (featureNumberError) {
|
|
51
50
|
throw new Error(`${featureNumberError.message} Use ReadRoadmap to see valid feature numbers.`);
|
|
52
51
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin";
|
|
2
2
|
import { FileStorage, RoadmapValidator } from "../storage.js";
|
|
3
3
|
import { loadDescription } from "../descriptions/index.js";
|
|
4
|
-
import { getErrorMessage } from "../errors/loader.js";
|
|
5
4
|
export async function createUpdateRoadmapTool(directory) {
|
|
6
5
|
const description = await loadDescription("updateroadmap.txt");
|
|
7
6
|
return tool({
|
|
@@ -20,13 +19,13 @@ export async function createUpdateRoadmapTool(directory) {
|
|
|
20
19
|
async execute(args) {
|
|
21
20
|
const storage = new FileStorage(directory);
|
|
22
21
|
if (!(await storage.exists())) {
|
|
23
|
-
throw new Error(
|
|
22
|
+
throw new Error("Roadmap not found. Use CreateRoadmap to create one.");
|
|
24
23
|
}
|
|
25
24
|
const roadmap = await storage.read();
|
|
26
25
|
if (!roadmap) {
|
|
27
|
-
throw new Error(
|
|
26
|
+
throw new Error("Roadmap file is corrupted. Please fix manually.");
|
|
28
27
|
}
|
|
29
|
-
const actionNumberError =
|
|
28
|
+
const actionNumberError = RoadmapValidator.validateActionNumber(args.actionNumber);
|
|
30
29
|
if (actionNumberError) {
|
|
31
30
|
throw new Error(`${actionNumberError.message} Use ReadRoadmap to see valid action numbers.`);
|
|
32
31
|
}
|
|
@@ -43,17 +42,21 @@ export async function createUpdateRoadmapTool(directory) {
|
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
44
|
if (!actionFound) {
|
|
46
|
-
throw new Error(
|
|
45
|
+
throw new Error(`Action "${args.actionNumber}" not found. Use ReadRoadmap to see valid action numbers.`);
|
|
46
|
+
}
|
|
47
|
+
// TypeScript: we know targetAction and targetFeature are not null here
|
|
48
|
+
if (!targetAction || !targetFeature) {
|
|
49
|
+
throw new Error("Internal error: target action not found.");
|
|
47
50
|
}
|
|
48
51
|
// Validate that at least one field is being updated
|
|
49
52
|
if (args.description === undefined && args.status === undefined) {
|
|
50
|
-
throw new Error(
|
|
53
|
+
throw new Error("No changes specified. Please provide description and/or status.");
|
|
51
54
|
}
|
|
52
55
|
const oldStatus = targetAction.status;
|
|
53
56
|
const oldDescription = targetAction.description;
|
|
54
57
|
// Validate description if provided
|
|
55
58
|
if (args.description !== undefined) {
|
|
56
|
-
const descError =
|
|
59
|
+
const descError = RoadmapValidator.validateDescription(args.description, "action");
|
|
57
60
|
if (descError) {
|
|
58
61
|
throw new Error(`${descError.message}`);
|
|
59
62
|
}
|
|
@@ -61,7 +64,7 @@ export async function createUpdateRoadmapTool(directory) {
|
|
|
61
64
|
}
|
|
62
65
|
// Validate and update status if provided
|
|
63
66
|
if (args.status !== undefined) {
|
|
64
|
-
const statusTransitionError =
|
|
67
|
+
const statusTransitionError = RoadmapValidator.validateStatusProgression(targetAction.status, args.status);
|
|
65
68
|
if (statusTransitionError) {
|
|
66
69
|
throw new Error(`${statusTransitionError.message} Current status: "${targetAction.status}", requested: "${args.status}"`);
|
|
67
70
|
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -129,21 +129,21 @@ export interface ValidationResult {
|
|
|
129
129
|
errors: ValidationError[];
|
|
130
130
|
}
|
|
131
131
|
export interface CreateRoadmapInput {
|
|
132
|
-
features:
|
|
132
|
+
features: {
|
|
133
133
|
number: string;
|
|
134
134
|
title: string;
|
|
135
135
|
description: string;
|
|
136
|
-
actions:
|
|
136
|
+
actions: {
|
|
137
137
|
number: string;
|
|
138
138
|
description: string;
|
|
139
139
|
status: "pending";
|
|
140
|
-
}
|
|
141
|
-
}
|
|
140
|
+
}[];
|
|
141
|
+
}[];
|
|
142
142
|
}
|
|
143
143
|
export interface UpdateRoadmapInput {
|
|
144
144
|
actionNumber: string;
|
|
145
145
|
description?: string;
|
|
146
|
-
status
|
|
146
|
+
status?: ActionStatus;
|
|
147
147
|
}
|
|
148
148
|
export interface ReadRoadmapInput {
|
|
149
149
|
actionNumber?: string;
|
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.5",
|
|
4
4
|
"description": "Strategic roadmap planning and multi-agent coordination for OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,16 +25,20 @@
|
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@opencode-ai/plugin": "^1.0.0",
|
|
27
27
|
"@types/node": "^20.0.0",
|
|
28
|
+
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
|
29
|
+
"@typescript-eslint/parser": "^8.48.1",
|
|
30
|
+
"eslint": "^9.39.1",
|
|
31
|
+
"eslint-config-prettier": "^10.1.8",
|
|
28
32
|
"typescript": "^5.0.0"
|
|
29
33
|
},
|
|
30
34
|
"dependencies": {
|
|
31
|
-
"
|
|
32
|
-
"
|
|
35
|
+
"typescript": "^5.0.0",
|
|
36
|
+
"zod": "^3.22.0"
|
|
33
37
|
},
|
|
34
|
-
|
|
35
38
|
"scripts": {
|
|
36
39
|
"build": "tsc -p tsconfig.json && npm run copy-assets",
|
|
37
40
|
"copy-assets": "mkdir -p dist/src/descriptions dist/src/errors && cp src/descriptions/*.txt dist/src/descriptions/ && cp src/errors/*.txt dist/src/errors/",
|
|
41
|
+
"lint": "eslint . --ext .ts",
|
|
38
42
|
"prepublishOnly": "npm run build"
|
|
39
43
|
},
|
|
40
44
|
"opencode": {
|