@0xarcano/open-lisa 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arcano
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # OpenCode Lisa
2
+
3
+ Native OpenCode port of [Lisa](https://github.com/blencorp/lisa): an interactive specification interview workflow that turns a feature idea into a Markdown PRD, Ralph-compatible JSON, and a progress file.
4
+
5
+ ## Install
6
+
7
+ Add the plugin to `opencode.json`:
8
+
9
+ ```json
10
+ {
11
+ "$schema": "https://opencode.ai/config.json",
12
+ "plugin": ["@0xarcano/open-lisa"]
13
+ }
14
+ ```
15
+
16
+ Install the command templates into a project:
17
+
18
+ ```sh
19
+ npx --package @0xarcano/open-lisa opencode-lisa-install
20
+ ```
21
+
22
+ This copies command files into `.opencode/commands/`.
23
+
24
+ ## Commands
25
+
26
+ ```text
27
+ /lisa-plan "user authentication"
28
+ /lisa-plan "payments" --context docs/prd.md --output-dir specs --max-questions 15
29
+ /lisa-plan "dashboard" --first-principles
30
+ /lisa-resume
31
+ /lisa-cleanup
32
+ /lisa-help
33
+ ```
34
+
35
+ ## Runtime Files
36
+
37
+ During an interview, state is stored under `.opencode/lisa/`:
38
+
39
+ - `.opencode/lisa/state/{slug}.json`
40
+ - `.opencode/lisa/draft/{slug}.md`
41
+
42
+ When finalized, Lisa writes:
43
+
44
+ - `{output-dir}/{slug}.md`
45
+ - `{output-dir}/{slug}.json`
46
+ - `{output-dir}/{slug}-progress.txt`
47
+
48
+ The default output directory is `docs/specs`.
49
+
50
+ ## Workflow
51
+
52
+ `/lisa-plan` initializes state and starts an interview. The agent asks focused questions, updates the draft after every few answers, and stops only when the user asks to finalize. Finalization writes the spec files and does not implement the feature.
53
+
54
+ `--first-principles` adds an initial phase that challenges whether the feature should be built and what the smallest useful solution might be.
55
+
56
+ ## Development
57
+
58
+ ```sh
59
+ npm install
60
+ npm run build
61
+ npm test
62
+ ```
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { copyFile, mkdir } from "node:fs/promises"
3
+ import { dirname, join } from "node:path"
4
+ import { fileURLToPath } from "node:url"
5
+
6
+ const root = dirname(dirname(fileURLToPath(import.meta.url)))
7
+ const source = join(root, "commands")
8
+ const target = join(process.cwd(), ".opencode", "commands")
9
+ const files = ["lisa-plan.md", "lisa-resume.md", "lisa-cleanup.md", "lisa-help.md"]
10
+
11
+ await mkdir(target, { recursive: true })
12
+
13
+ for (const file of files) {
14
+ await copyFile(join(source, file), join(target, file))
15
+ }
16
+
17
+ console.log(`Installed ${files.length} Lisa command(s) to ${target}`)
@@ -0,0 +1,7 @@
1
+ ---
2
+ description: Remove all in-progress Lisa interview state files
3
+ ---
4
+
5
+ Call `lisa_cleanup`.
6
+
7
+ Report how many in-progress Lisa interviews were removed. Make clear that finalized specs were not deleted.
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Show Lisa workflow help
3
+ ---
4
+
5
+ Show concise help for OpenCode Lisa.
6
+
7
+ Include:
8
+
9
+ - `/lisa-plan "feature name"` starts a specification interview.
10
+ - Options: `--context <file>`, `--output-dir <dir>`, `--max-questions <n>`, `--first-principles`.
11
+ - `/lisa-resume` resumes an interrupted interview.
12
+ - `/lisa-cleanup` removes in-progress state only.
13
+ - Finalized outputs are Markdown PRD, Ralph-compatible JSON, and progress file.
14
+ - Default output directory is `docs/specs`.
@@ -0,0 +1,29 @@
1
+ ---
2
+ description: Start a Lisa specification interview for a feature
3
+ ---
4
+
5
+ Initialize a Lisa interview by calling `lisa_init` with these raw arguments:
6
+
7
+ ```text
8
+ $ARGUMENTS
9
+ ```
10
+
11
+ Then conduct a specification interview for the initialized feature.
12
+
13
+ Rules:
14
+
15
+ - Ask one focused question at a time.
16
+ - Prefer non-obvious questions about scope, constraints, data, UX, edge cases, risks, and tradeoffs.
17
+ - If `firstPrinciples` is true, first ask 3-5 questions that challenge whether this is the right problem and smallest useful solution.
18
+ - Continue until the user says done, finalize, finished, complete, wrap up, or equivalent.
19
+ - Respect `maxQuestions`; when the limit is reached, summarize what is known and ask whether to finalize or continue.
20
+ - Update the draft every 2-3 answers by calling `lisa_update_draft`.
21
+ - Use context files listed in state when relevant.
22
+
23
+ Finalization:
24
+
25
+ - Do not implement the feature.
26
+ - Do not edit unrelated files.
27
+ - Create a complete Markdown PRD and Ralph-compatible user story list.
28
+ - Call `lisa_finalize` with the slug, description, markdown, and user stories.
29
+ - After finalization, output exactly `SPEC COMPLETE` and stop.
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: Resume an in-progress Lisa specification interview
3
+ ---
4
+
5
+ Call `lisa_list` to find in-progress Lisa interviews.
6
+
7
+ If none exist, say that no in-progress Lisa interviews were found.
8
+
9
+ If exactly one exists, call `lisa_read` with its slug and continue the interview from the draft.
10
+
11
+ If multiple exist, list their feature names, slugs, and updated timestamps, then ask the user which one to resume. After the user chooses, call `lisa_read` with that slug.
12
+
13
+ Continue with the same rules as `/lisa-plan`: ask one focused question at a time, update the draft with `lisa_update_draft`, and finalize only when the user asks to finish. On finalization, call `lisa_finalize`, output exactly `SPEC COMPLETE`, and stop.
@@ -0,0 +1,5 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ export * from "./lisa.js";
3
+ declare const LisaPlugin: Plugin;
4
+ export { LisaPlugin, LisaPlugin as server };
5
+ export default LisaPlugin;
package/dist/index.js ADDED
@@ -0,0 +1,91 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { cleanup, finalizeInterview, initializeInterview, listInterviews, readInterview, updateDraft, } from "./lisa.js";
3
+ export * from "./lisa.js";
4
+ const LisaPlugin = async () => {
5
+ return {
6
+ tool: {
7
+ lisa_init: tool({
8
+ description: "Initialize a Lisa specification interview from command arguments.",
9
+ args: {
10
+ arguments: tool.schema.string().describe("Raw command arguments from /lisa-plan."),
11
+ },
12
+ async execute(args, context) {
13
+ const state = await initializeInterview(context.directory, args.arguments);
14
+ return JSON.stringify(state, null, 2);
15
+ },
16
+ }),
17
+ lisa_list: tool({
18
+ description: "List in-progress Lisa interviews.",
19
+ args: {},
20
+ async execute(_args, context) {
21
+ const interviews = await listInterviews(context.directory);
22
+ return JSON.stringify(interviews, null, 2);
23
+ },
24
+ }),
25
+ lisa_read: tool({
26
+ description: "Read a Lisa interview state and draft. If slug is omitted, read the most recently updated interview.",
27
+ args: {
28
+ slug: tool.schema.string().optional().describe("Feature slug to resume."),
29
+ },
30
+ async execute(args, context) {
31
+ const interview = await readInterview(context.directory, args.slug);
32
+ return JSON.stringify(interview, null, 2);
33
+ },
34
+ }),
35
+ lisa_update_draft: tool({
36
+ description: "Replace the Lisa draft and update question count.",
37
+ args: {
38
+ slug: tool.schema.string(),
39
+ draft: tool.schema.string(),
40
+ questionCount: tool.schema.number().optional(),
41
+ },
42
+ async execute(args, context) {
43
+ const state = await updateDraft(context.directory, args.slug, args.draft, args.questionCount);
44
+ return JSON.stringify(state, null, 2);
45
+ },
46
+ }),
47
+ lisa_finalize: tool({
48
+ description: "Finalize a Lisa interview by writing Markdown, JSON, and progress output files.",
49
+ args: {
50
+ slug: tool.schema.string(),
51
+ description: tool.schema.string(),
52
+ markdown: tool.schema.string(),
53
+ userStories: tool.schema.array(tool.schema.object({
54
+ id: tool.schema.string(),
55
+ category: tool.schema.enum(["setup", "core", "integration", "polish"]),
56
+ title: tool.schema.string(),
57
+ description: tool.schema.string(),
58
+ acceptanceCriteria: tool.schema.array(tool.schema.string()),
59
+ passes: tool.schema.boolean().default(false),
60
+ notes: tool.schema.string().default(""),
61
+ })),
62
+ },
63
+ async execute(args, context) {
64
+ const stories = args.userStories.map((story) => ({
65
+ ...story,
66
+ category: story.category,
67
+ passes: false,
68
+ notes: story.notes ?? "",
69
+ }));
70
+ const output = await finalizeInterview(context.directory, args.slug, {
71
+ description: args.description,
72
+ markdown: args.markdown,
73
+ userStories: stories,
74
+ });
75
+ return JSON.stringify(output, null, 2);
76
+ },
77
+ }),
78
+ lisa_cleanup: tool({
79
+ description: "Remove all in-progress Lisa interview state files. Does not delete finalized specs.",
80
+ args: {},
81
+ async execute(_args, context) {
82
+ const removed = await cleanup(context.directory);
83
+ return `Removed ${removed} in-progress Lisa interview(s).`;
84
+ },
85
+ }),
86
+ },
87
+ };
88
+ };
89
+ export { LisaPlugin, LisaPlugin as server };
90
+ export default LisaPlugin;
91
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/lisa.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ export type LisaCategory = "setup" | "core" | "integration" | "polish";
2
+ export interface LisaOptions {
3
+ feature: string;
4
+ slug: string;
5
+ context: string[];
6
+ outputDir: string;
7
+ maxQuestions: number;
8
+ firstPrinciples: boolean;
9
+ }
10
+ export interface LisaState extends LisaOptions {
11
+ createdAt: string;
12
+ updatedAt: string;
13
+ questionCount: number;
14
+ status: "in-progress" | "finalized";
15
+ draftPath: string;
16
+ statePath: string;
17
+ markdownPath: string;
18
+ jsonPath: string;
19
+ progressPath: string;
20
+ }
21
+ export interface UserStory {
22
+ id: string;
23
+ category: LisaCategory;
24
+ title: string;
25
+ description: string;
26
+ acceptanceCriteria: string[];
27
+ passes: boolean;
28
+ notes: string;
29
+ }
30
+ export interface FinalSpec {
31
+ description: string;
32
+ markdown: string;
33
+ userStories: UserStory[];
34
+ }
35
+ export interface RalphSpec {
36
+ project: string;
37
+ branchName: string;
38
+ description: string;
39
+ userStories: UserStory[];
40
+ }
41
+ export declare function slugify(input: string): string;
42
+ export declare function parseLisaArgs(input: string): LisaOptions;
43
+ export declare function initializeInterview(cwd: string, input: string): Promise<LisaState>;
44
+ export declare function listInterviews(cwd: string): Promise<LisaState[]>;
45
+ export declare function readInterview(cwd: string, slug?: string): Promise<{
46
+ state: LisaState;
47
+ draft: string;
48
+ }>;
49
+ export declare function updateDraft(cwd: string, slug: string, draft: string, questionCount?: number): Promise<LisaState>;
50
+ export declare function finalizeInterview(cwd: string, slug: string, spec: FinalSpec): Promise<RalphSpec>;
51
+ export declare function cleanup(cwd: string): Promise<number>;
52
+ export declare function createRalphSpec(slug: string, description: string, userStories: UserStory[]): RalphSpec;
53
+ export declare function createProgressFile(userStories: UserStory[]): string;
package/dist/lisa.js ADDED
@@ -0,0 +1,234 @@
1
+ import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ const DEFAULT_OUTPUT_DIR = "docs/specs";
4
+ const STATE_ROOT = ".opencode/lisa";
5
+ export function slugify(input) {
6
+ const slug = input
7
+ .trim()
8
+ .toLowerCase()
9
+ .replace(/['"]/g, "")
10
+ .replace(/[^a-z0-9]+/g, "-")
11
+ .replace(/^-+|-+$/g, "")
12
+ .slice(0, 80)
13
+ .replace(/-+$/g, "");
14
+ return slug || "feature";
15
+ }
16
+ export function parseLisaArgs(input) {
17
+ const tokens = tokenize(input);
18
+ const featureParts = [];
19
+ const context = [];
20
+ let outputDir = DEFAULT_OUTPUT_DIR;
21
+ let maxQuestions = 0;
22
+ let firstPrinciples = false;
23
+ for (let index = 0; index < tokens.length; index += 1) {
24
+ const token = tokens[index];
25
+ if (token === "--context" || token === "-c") {
26
+ const value = tokens[index + 1];
27
+ if (!value)
28
+ throw new Error(`${token} requires a file path`);
29
+ context.push(value);
30
+ index += 1;
31
+ continue;
32
+ }
33
+ if (token === "--output-dir") {
34
+ const value = tokens[index + 1];
35
+ if (!value)
36
+ throw new Error("--output-dir requires a directory");
37
+ outputDir = value;
38
+ index += 1;
39
+ continue;
40
+ }
41
+ if (token === "--max-questions") {
42
+ const value = tokens[index + 1];
43
+ if (!value)
44
+ throw new Error("--max-questions requires a number");
45
+ maxQuestions = Number.parseInt(value, 10);
46
+ if (!Number.isInteger(maxQuestions) || maxQuestions < 0) {
47
+ throw new Error("--max-questions must be a non-negative integer");
48
+ }
49
+ index += 1;
50
+ continue;
51
+ }
52
+ if (token === "--first-principles" || token === "-f") {
53
+ firstPrinciples = true;
54
+ continue;
55
+ }
56
+ if (token === "--help" || token === "-h") {
57
+ throw new Error("Help requested");
58
+ }
59
+ if (token.startsWith("-"))
60
+ throw new Error(`Unknown option: ${token}`);
61
+ featureParts.push(token);
62
+ }
63
+ const feature = featureParts.join(" ").trim();
64
+ if (!feature)
65
+ throw new Error("Feature name is required");
66
+ return {
67
+ feature,
68
+ slug: slugify(feature),
69
+ context,
70
+ outputDir,
71
+ maxQuestions,
72
+ firstPrinciples,
73
+ };
74
+ }
75
+ export async function initializeInterview(cwd, input) {
76
+ const options = parseLisaArgs(input);
77
+ const now = new Date().toISOString();
78
+ const paths = resolveInterviewPaths(cwd, options.slug, options.outputDir);
79
+ const state = {
80
+ ...options,
81
+ createdAt: now,
82
+ updatedAt: now,
83
+ questionCount: 0,
84
+ status: "in-progress",
85
+ ...paths,
86
+ };
87
+ await mkdir(path.dirname(paths.statePath), { recursive: true });
88
+ await mkdir(path.dirname(paths.draftPath), { recursive: true });
89
+ await writeState(state);
90
+ await writeFile(paths.draftPath, [
91
+ `# ${options.feature}`,
92
+ "",
93
+ "## Current Understanding",
94
+ "",
95
+ "Interview initialized. Update this draft as the specification becomes clearer.",
96
+ "",
97
+ ].join("\n"), "utf8");
98
+ return state;
99
+ }
100
+ export async function listInterviews(cwd) {
101
+ const dir = path.join(cwd, STATE_ROOT, "state");
102
+ let files;
103
+ try {
104
+ files = await readdir(dir);
105
+ }
106
+ catch (error) {
107
+ if (isNotFound(error))
108
+ return [];
109
+ throw error;
110
+ }
111
+ const states = await Promise.all(files
112
+ .filter((file) => file.endsWith(".json"))
113
+ .map(async (file) => readState(path.join(dir, file))));
114
+ return states
115
+ .filter((state) => state.status === "in-progress")
116
+ .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
117
+ }
118
+ export async function readInterview(cwd, slug) {
119
+ const state = slug ? await readState(resolveInterviewPaths(cwd, slug, DEFAULT_OUTPUT_DIR).statePath) : await latestState(cwd);
120
+ const draft = await readFile(state.draftPath, "utf8");
121
+ return { state, draft };
122
+ }
123
+ export async function updateDraft(cwd, slug, draft, questionCount) {
124
+ const statePath = resolveInterviewPaths(cwd, slug, DEFAULT_OUTPUT_DIR).statePath;
125
+ const state = await readState(statePath);
126
+ const updated = {
127
+ ...state,
128
+ updatedAt: new Date().toISOString(),
129
+ questionCount: questionCount ?? state.questionCount,
130
+ };
131
+ await writeFile(state.draftPath, draft, "utf8");
132
+ await writeState(updated);
133
+ return updated;
134
+ }
135
+ export async function finalizeInterview(cwd, slug, spec) {
136
+ const statePath = resolveInterviewPaths(cwd, slug, DEFAULT_OUTPUT_DIR).statePath;
137
+ const state = await readState(statePath);
138
+ const output = createRalphSpec(state.slug, spec.description, spec.userStories);
139
+ await mkdir(path.dirname(state.markdownPath), { recursive: true });
140
+ await writeFile(state.markdownPath, spec.markdown, "utf8");
141
+ await writeFile(state.jsonPath, `${JSON.stringify(output, null, 2)}\n`, "utf8");
142
+ await writeFile(state.progressPath, createProgressFile(spec.userStories), "utf8");
143
+ await rm(state.statePath, { force: true });
144
+ return output;
145
+ }
146
+ export async function cleanup(cwd) {
147
+ const dir = path.join(cwd, STATE_ROOT);
148
+ const interviews = await listInterviews(cwd);
149
+ await rm(dir, { recursive: true, force: true });
150
+ return interviews.length;
151
+ }
152
+ export function createRalphSpec(slug, description, userStories) {
153
+ return {
154
+ project: slug,
155
+ branchName: `ralph/${slug}`,
156
+ description,
157
+ userStories: userStories.map((story, index) => ({
158
+ id: story.id || `US-${String(index + 1).padStart(3, "0")}`,
159
+ category: story.category,
160
+ title: story.title,
161
+ description: story.description,
162
+ acceptanceCriteria: story.acceptanceCriteria,
163
+ passes: false,
164
+ notes: story.notes ?? "",
165
+ })),
166
+ };
167
+ }
168
+ export function createProgressFile(userStories) {
169
+ const lines = userStories.map((story, index) => {
170
+ const id = story.id || `US-${String(index + 1).padStart(3, "0")}`;
171
+ return `[PENDING] ${id} - ${story.title}`;
172
+ });
173
+ return `${lines.join("\n")}${lines.length ? "\n" : ""}`;
174
+ }
175
+ function resolveInterviewPaths(cwd, slug, outputDir) {
176
+ const absoluteOutputDir = path.resolve(cwd, outputDir);
177
+ return {
178
+ statePath: path.join(cwd, STATE_ROOT, "state", `${slug}.json`),
179
+ draftPath: path.join(cwd, STATE_ROOT, "draft", `${slug}.md`),
180
+ markdownPath: path.join(absoluteOutputDir, `${slug}.md`),
181
+ jsonPath: path.join(absoluteOutputDir, `${slug}.json`),
182
+ progressPath: path.join(absoluteOutputDir, `${slug}-progress.txt`),
183
+ };
184
+ }
185
+ async function latestState(cwd) {
186
+ const interviews = await listInterviews(cwd);
187
+ if (interviews.length === 0)
188
+ throw new Error("No in-progress Lisa interviews found");
189
+ return interviews[0];
190
+ }
191
+ async function readState(filePath) {
192
+ return JSON.parse(await readFile(filePath, "utf8"));
193
+ }
194
+ async function writeState(state) {
195
+ await writeFile(state.statePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
196
+ }
197
+ function tokenize(input) {
198
+ const tokens = [];
199
+ let current = "";
200
+ let quote;
201
+ for (let index = 0; index < input.length; index += 1) {
202
+ const char = input[index];
203
+ if (quote) {
204
+ if (char === quote) {
205
+ quote = undefined;
206
+ }
207
+ else {
208
+ current += char;
209
+ }
210
+ continue;
211
+ }
212
+ if (char === "'" || char === "\"") {
213
+ quote = char;
214
+ continue;
215
+ }
216
+ if (/\s/.test(char)) {
217
+ if (current) {
218
+ tokens.push(current);
219
+ current = "";
220
+ }
221
+ continue;
222
+ }
223
+ current += char;
224
+ }
225
+ if (quote)
226
+ throw new Error("Unclosed quote in arguments");
227
+ if (current)
228
+ tokens.push(current);
229
+ return tokens;
230
+ }
231
+ function isNotFound(error) {
232
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
233
+ }
234
+ //# sourceMappingURL=lisa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lisa.js","sourceRoot":"","sources":["../src/lisa.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@0xarcano/open-lisa",
3
+ "version": "0.1.0",
4
+ "description": "Native OpenCode port of Lisa's interactive specification interview workflow.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./commands/*": "./commands/*"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "commands",
18
+ "bin",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "bin": {
23
+ "opencode-lisa-install": "bin/install-commands.js"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc -p tsconfig.json",
27
+ "test": "node --test test/*.test.mjs",
28
+ "prepublishOnly": "npm run build && npm test"
29
+ },
30
+ "keywords": [
31
+ "opencode",
32
+ "plugin",
33
+ "lisa",
34
+ "specification",
35
+ "planning"
36
+ ],
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "@opencode-ai/plugin": "^1.14.48"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^22.15.0",
43
+ "typescript": "^5.8.0"
44
+ }
45
+ }