opencode-lisa 0.2.1 → 0.3.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/README.md
CHANGED
|
@@ -26,10 +26,27 @@ The **Ralph Wiggum pattern** is a simple bash loop that keeps feeding prompts to
|
|
|
26
26
|
## Install
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npm install opencode-lisa
|
|
29
|
+
npm install -D opencode-lisa
|
|
30
|
+
npx opencode-lisa init
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
This will:
|
|
34
|
+
- Install command and skill files to `.opencode/`
|
|
35
|
+
- Add the plugin to your `opencode.json`
|
|
36
|
+
- Set up Lisa for use in your project
|
|
37
|
+
|
|
38
|
+
Requires [OpenCode](https://opencode.ai) 1.0+.
|
|
39
|
+
|
|
40
|
+
### Global Installation
|
|
41
|
+
|
|
42
|
+
To install Lisa globally for all projects:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install -g opencode-lisa
|
|
46
|
+
npx opencode-lisa init --global
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Add to your global `~/.config/opencode/opencode.json`:
|
|
33
50
|
|
|
34
51
|
```json
|
|
35
52
|
{
|
|
@@ -37,8 +54,6 @@ Add to your `opencode.json`:
|
|
|
37
54
|
}
|
|
38
55
|
```
|
|
39
56
|
|
|
40
|
-
Restart OpenCode. Requires [OpenCode](https://opencode.ai).
|
|
41
|
-
|
|
42
57
|
## Usage
|
|
43
58
|
|
|
44
59
|
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from 'fs';
|
|
4
|
+
import { join, dirname } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { createInterface } from 'readline';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
const ASSETS_DIR = join(__dirname, '..', 'assets');
|
|
11
|
+
|
|
12
|
+
// ANSI colors
|
|
13
|
+
const colors = {
|
|
14
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
15
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
16
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
17
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
18
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
async function prompt(question) {
|
|
22
|
+
const rl = createInterface({
|
|
23
|
+
input: process.stdin,
|
|
24
|
+
output: process.stdout,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
rl.question(question, (answer) => {
|
|
29
|
+
rl.close();
|
|
30
|
+
resolve(answer.toLowerCase().trim());
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function confirmOverwrite(filePath, force) {
|
|
36
|
+
if (!existsSync(filePath)) return true;
|
|
37
|
+
if (force) return true;
|
|
38
|
+
|
|
39
|
+
const answer = await prompt(
|
|
40
|
+
`${colors.yellow('?')} ${filePath} already exists. Overwrite? (y/N) `
|
|
41
|
+
);
|
|
42
|
+
return answer === 'y' || answer === 'yes';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function ensureDir(filePath) {
|
|
46
|
+
const dir = dirname(filePath);
|
|
47
|
+
if (!existsSync(dir)) {
|
|
48
|
+
mkdirSync(dir, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function copyAsset(srcRelative, destPath, force) {
|
|
53
|
+
return async () => {
|
|
54
|
+
const srcPath = join(ASSETS_DIR, srcRelative);
|
|
55
|
+
|
|
56
|
+
if (!existsSync(srcPath)) {
|
|
57
|
+
console.error(colors.red(`Error: Source file not found: ${srcPath}`));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const shouldWrite = await confirmOverwrite(destPath, force);
|
|
62
|
+
if (!shouldWrite) {
|
|
63
|
+
console.log(colors.dim(` Skipped: ${destPath}`));
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
ensureDir(destPath);
|
|
68
|
+
copyFileSync(srcPath, destPath);
|
|
69
|
+
console.log(colors.green(` Created: ${destPath}`));
|
|
70
|
+
return true;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function updateOpencodeJson(targetDir, force) {
|
|
75
|
+
return async () => {
|
|
76
|
+
const configPath = join(targetDir, 'opencode.json');
|
|
77
|
+
let config = {};
|
|
78
|
+
let existed = false;
|
|
79
|
+
|
|
80
|
+
if (existsSync(configPath)) {
|
|
81
|
+
existed = true;
|
|
82
|
+
try {
|
|
83
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
84
|
+
config = JSON.parse(content);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.error(colors.red(`Error parsing ${configPath}: ${e.message}`));
|
|
87
|
+
const answer = await prompt(
|
|
88
|
+
`${colors.yellow('?')} Create a new opencode.json? (y/N) `
|
|
89
|
+
);
|
|
90
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
91
|
+
console.log(colors.dim(` Skipped: opencode.json update`));
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
config = {};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Ensure plugin array exists
|
|
99
|
+
if (!config.plugin) {
|
|
100
|
+
config.plugin = [];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check if already has the plugin
|
|
104
|
+
if (config.plugin.includes('opencode-lisa')) {
|
|
105
|
+
console.log(colors.dim(` Already configured: opencode-lisa in plugins`));
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Add the plugin
|
|
110
|
+
config.plugin.push('opencode-lisa');
|
|
111
|
+
|
|
112
|
+
// Add schema if not present
|
|
113
|
+
if (!config.$schema) {
|
|
114
|
+
config.$schema = 'https://opencode.ai/config.json';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
ensureDir(configPath);
|
|
118
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
119
|
+
|
|
120
|
+
if (existed) {
|
|
121
|
+
console.log(colors.green(` Updated: opencode.json (added opencode-lisa to plugins)`));
|
|
122
|
+
} else {
|
|
123
|
+
console.log(colors.green(` Created: opencode.json`));
|
|
124
|
+
}
|
|
125
|
+
return true;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function init(targetDir, options) {
|
|
130
|
+
const { force, global: isGlobal } = options;
|
|
131
|
+
|
|
132
|
+
console.log('');
|
|
133
|
+
console.log(colors.cyan('Lisa - Intelligent Epic Workflow Plugin'));
|
|
134
|
+
console.log(colors.dim('Installing command and skill files...'));
|
|
135
|
+
console.log('');
|
|
136
|
+
|
|
137
|
+
const tasks = [
|
|
138
|
+
{
|
|
139
|
+
name: 'skill',
|
|
140
|
+
run: copyAsset(
|
|
141
|
+
'skills/lisa/SKILL.md',
|
|
142
|
+
join(targetDir, '.opencode', 'skills', 'lisa', 'SKILL.md'),
|
|
143
|
+
force
|
|
144
|
+
),
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'command',
|
|
148
|
+
run: copyAsset(
|
|
149
|
+
'commands/lisa.md',
|
|
150
|
+
join(targetDir, '.opencode', 'commands', 'lisa.md'),
|
|
151
|
+
force
|
|
152
|
+
),
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
// Only update opencode.json for local installs (not global)
|
|
157
|
+
if (!isGlobal) {
|
|
158
|
+
tasks.push({
|
|
159
|
+
name: 'config',
|
|
160
|
+
run: updateOpencodeJson(targetDir, force),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let anyCreated = false;
|
|
165
|
+
for (const task of tasks) {
|
|
166
|
+
const created = await task.run();
|
|
167
|
+
if (created) anyCreated = true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
console.log('');
|
|
171
|
+
|
|
172
|
+
if (anyCreated) {
|
|
173
|
+
console.log(colors.green('Done!'));
|
|
174
|
+
console.log('');
|
|
175
|
+
console.log('Lisa is now installed. You can use:');
|
|
176
|
+
console.log('');
|
|
177
|
+
console.log(` ${colors.cyan('/lisa')} - Run Lisa commands in OpenCode`);
|
|
178
|
+
console.log(` ${colors.cyan('/lisa help')} - Show available Lisa commands`);
|
|
179
|
+
console.log(` ${colors.cyan('/lisa list')} - List all your epics`);
|
|
180
|
+
console.log('');
|
|
181
|
+
console.log(colors.dim('Start by running /lisa <epic-name> to create your first epic!'));
|
|
182
|
+
} else {
|
|
183
|
+
console.log(colors.dim('Nothing to do - everything is already set up.'));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
console.log('');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function printHelp() {
|
|
190
|
+
console.log(`
|
|
191
|
+
${colors.cyan('Lisa - Intelligent Epic Workflow Plugin')}
|
|
192
|
+
|
|
193
|
+
Like Ralph Wiggum, but smarter. Lisa plans before she acts.
|
|
194
|
+
|
|
195
|
+
Usage:
|
|
196
|
+
npx opencode-lisa init [options]
|
|
197
|
+
|
|
198
|
+
Commands:
|
|
199
|
+
init Install the Lisa command and skill files
|
|
200
|
+
|
|
201
|
+
Options:
|
|
202
|
+
--force Overwrite existing files without asking
|
|
203
|
+
--global Install to ~/.config/opencode/ instead of current directory
|
|
204
|
+
--help, -h Show this help message
|
|
205
|
+
|
|
206
|
+
Examples:
|
|
207
|
+
npx opencode-lisa init
|
|
208
|
+
npx opencode-lisa init --force
|
|
209
|
+
npx opencode-lisa init --global
|
|
210
|
+
|
|
211
|
+
After installation, use /lisa in OpenCode to get started!
|
|
212
|
+
`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function main() {
|
|
216
|
+
const args = process.argv.slice(2);
|
|
217
|
+
|
|
218
|
+
// Parse flags
|
|
219
|
+
const force = args.includes('--force') || args.includes('-f');
|
|
220
|
+
const isGlobal = args.includes('--global') || args.includes('-g');
|
|
221
|
+
const help = args.includes('--help') || args.includes('-h');
|
|
222
|
+
|
|
223
|
+
// Get command (first non-flag argument)
|
|
224
|
+
const command = args.find((arg) => !arg.startsWith('-'));
|
|
225
|
+
|
|
226
|
+
if (help || (!command && args.length === 0)) {
|
|
227
|
+
printHelp();
|
|
228
|
+
process.exit(0);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (command !== 'init') {
|
|
232
|
+
console.error(colors.red(`Unknown command: ${command}`));
|
|
233
|
+
console.error(`Run ${colors.cyan('npx opencode-lisa --help')} for usage.`);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Determine target directory
|
|
238
|
+
let targetDir;
|
|
239
|
+
if (isGlobal) {
|
|
240
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
241
|
+
targetDir = join(home, '.config', 'opencode');
|
|
242
|
+
} else {
|
|
243
|
+
targetDir = process.cwd();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
await init(targetDir, { force, global: isGlobal });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
main().catch((err) => {
|
|
250
|
+
console.error(colors.red('Error:'), err.message);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
});
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-lisa",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Lisa - intelligent epic workflow plugin for OpenCode. Like Ralph Wiggum, but smarter.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"opencode-lisa": "./bin/cli.js"
|
|
9
|
+
},
|
|
7
10
|
"exports": {
|
|
8
11
|
".": {
|
|
9
12
|
"import": "./dist/index.js"
|
|
@@ -11,8 +14,8 @@
|
|
|
11
14
|
},
|
|
12
15
|
"files": [
|
|
13
16
|
"dist",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
17
|
+
"bin",
|
|
18
|
+
"assets"
|
|
16
19
|
],
|
|
17
20
|
"scripts": {
|
|
18
21
|
"build": "bun build src/index.ts --outdir dist --target bun --format esm",
|
|
@@ -39,11 +42,17 @@
|
|
|
39
42
|
"bugs": {
|
|
40
43
|
"url": "https://github.com/fractalswift/lisa-simpson/issues"
|
|
41
44
|
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
},
|
|
42
48
|
"peerDependencies": {
|
|
43
49
|
"@opencode-ai/plugin": ">=1.0.0"
|
|
44
50
|
},
|
|
45
51
|
"devDependencies": {
|
|
46
52
|
"@opencode-ai/plugin": "^1.1.25",
|
|
47
53
|
"typescript": "^5.0.0"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"opencode-lisa": "^0.2.1"
|
|
48
57
|
}
|
|
49
58
|
}
|
|
File without changes
|
|
File without changes
|