@codyswann/lisa 2.16.5 → 2.16.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/.agents/plugins/marketplace.json +80 -0
- package/README.md +28 -0
- package/dist/codex/agent-transformer.js +42 -17
- package/dist/codex/agent-transformer.js.map +1 -1
- package/dist/codex/agents-md-installer.d.ts +1 -1
- package/dist/codex/agents-md-installer.d.ts.map +1 -1
- package/dist/codex/agents-md-installer.js +7 -0
- package/dist/codex/agents-md-installer.js.map +1 -1
- package/dist/codex/command-skill-transformer.d.ts +14 -0
- package/dist/codex/command-skill-transformer.d.ts.map +1 -0
- package/dist/codex/command-skill-transformer.js +106 -0
- package/dist/codex/command-skill-transformer.js.map +1 -0
- package/dist/codex/hooks-installer.d.ts.map +1 -1
- package/dist/codex/hooks-installer.js +24 -0
- package/dist/codex/hooks-installer.js.map +1 -1
- package/dist/codex/plugin-marketplace-installer.d.ts +23 -0
- package/dist/codex/plugin-marketplace-installer.d.ts.map +1 -0
- package/dist/codex/plugin-marketplace-installer.js +131 -0
- package/dist/codex/plugin-marketplace-installer.js.map +1 -0
- package/dist/codex/settings-installer.d.ts +2 -0
- package/dist/codex/settings-installer.d.ts.map +1 -1
- package/dist/codex/settings-installer.js +120 -2
- package/dist/codex/settings-installer.js.map +1 -1
- package/dist/codex/skills-installer.d.ts +1 -12
- package/dist/codex/skills-installer.d.ts.map +1 -1
- package/dist/codex/skills-installer.js +5 -55
- package/dist/codex/skills-installer.js.map +1 -1
- package/dist/core/lisa.d.ts +6 -6
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +9 -7
- package/dist/core/lisa.js.map +1 -1
- package/package.json +2 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +32 -0
- package/plugins/lisa/hooks/hooks.json +104 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +27 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +31 -0
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +31 -0
- package/plugins/lisa-nestjs/hooks/hooks.json +15 -0
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +31 -0
- package/plugins/lisa-rails/hooks/hooks.json +19 -0
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +28 -0
- package/plugins/lisa-typescript/hooks/hooks.json +23 -0
- package/scripts/build-plugins.sh +2 -0
- package/scripts/generate-codex-plugin-artifacts.mjs +294 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate Codex plugin artifacts from the built Claude plugin directories.
|
|
4
|
+
*
|
|
5
|
+
* Claude remains Lisa's production path; this script makes the Codex side
|
|
6
|
+
* durable by deriving .codex-plugin metadata and compatible hook manifests
|
|
7
|
+
* every time plugins are rebuilt.
|
|
8
|
+
*/
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
|
|
12
|
+
const [pluginDirArg, versionArg] = process.argv.slice(2);
|
|
13
|
+
if (!pluginDirArg || !versionArg) {
|
|
14
|
+
console.error(
|
|
15
|
+
"Usage: generate-codex-plugin-artifacts.mjs <plugin-dir> <version>"
|
|
16
|
+
);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const pluginDir = path.resolve(pluginDirArg);
|
|
21
|
+
const claudeManifestPath = path.join(
|
|
22
|
+
pluginDir,
|
|
23
|
+
".claude-plugin",
|
|
24
|
+
"plugin.json"
|
|
25
|
+
);
|
|
26
|
+
if (!fs.existsSync(claudeManifestPath)) {
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const claudeManifest = JSON.parse(fs.readFileSync(claudeManifestPath, "utf8"));
|
|
31
|
+
const pluginName = claudeManifest.name;
|
|
32
|
+
const UNSUPPORTED_CODEX_HOOK_SCRIPTS = new Set([
|
|
33
|
+
"hooks/enforce-team-first.sh",
|
|
34
|
+
"hooks/inject-flow-context.sh",
|
|
35
|
+
"hooks/inject-rules.sh",
|
|
36
|
+
]);
|
|
37
|
+
const codexHooks = convertHooks(pluginName, claudeManifest.hooks ?? {});
|
|
38
|
+
|
|
39
|
+
writeCodexManifest(pluginName, versionArg, codexHooks);
|
|
40
|
+
writeCodexHooks(codexHooks);
|
|
41
|
+
|
|
42
|
+
function writeCodexManifest(pluginName, version, hooksFile) {
|
|
43
|
+
const metadata = metadataFor(pluginName);
|
|
44
|
+
const manifest = {
|
|
45
|
+
name: pluginName,
|
|
46
|
+
version,
|
|
47
|
+
description: metadata.description ?? claudeManifest.description,
|
|
48
|
+
author: claudeManifest.author ?? { name: "Cody Swann" },
|
|
49
|
+
keywords: metadata.keywords,
|
|
50
|
+
...componentPointers(hooksFile),
|
|
51
|
+
interface: {
|
|
52
|
+
displayName: metadata.displayName,
|
|
53
|
+
shortDescription: metadata.shortDescription,
|
|
54
|
+
longDescription: metadata.longDescription,
|
|
55
|
+
developerName: "Cody Swann",
|
|
56
|
+
category: metadata.category,
|
|
57
|
+
capabilities: metadata.capabilities,
|
|
58
|
+
defaultPrompt: metadata.defaultPrompt,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const manifestDir = path.join(pluginDir, ".codex-plugin");
|
|
63
|
+
fs.mkdirSync(manifestDir, { recursive: true });
|
|
64
|
+
fs.writeFileSync(
|
|
65
|
+
path.join(manifestDir, "plugin.json"),
|
|
66
|
+
`${JSON.stringify(manifest, null, 2)}\n`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function componentPointers(hooksFile) {
|
|
71
|
+
return {
|
|
72
|
+
...(fs.existsSync(path.join(pluginDir, "skills"))
|
|
73
|
+
? { skills: "./skills/" }
|
|
74
|
+
: {}),
|
|
75
|
+
...(fs.existsSync(path.join(pluginDir, ".mcp.json"))
|
|
76
|
+
? { mcpServers: "./.mcp.json" }
|
|
77
|
+
: {}),
|
|
78
|
+
...(hooksFile ? { hooks: "./hooks/hooks.json" } : {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function writeCodexHooks(hooksFile) {
|
|
83
|
+
const hooksDir = path.join(pluginDir, "hooks");
|
|
84
|
+
const hooksPath = path.join(hooksDir, "hooks.json");
|
|
85
|
+
if (!hooksFile) {
|
|
86
|
+
if (fs.existsSync(hooksPath)) {
|
|
87
|
+
fs.rmSync(hooksPath);
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
92
|
+
fs.writeFileSync(hooksPath, `${JSON.stringify(hooksFile, null, 2)}\n`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function convertHooks(pluginName, claudeHooks) {
|
|
96
|
+
const supportedEvents = new Set([
|
|
97
|
+
"UserPromptSubmit",
|
|
98
|
+
"PostToolUse",
|
|
99
|
+
"PreToolUse",
|
|
100
|
+
"Stop",
|
|
101
|
+
"SessionStart",
|
|
102
|
+
]);
|
|
103
|
+
const entries = Object.entries(claudeHooks)
|
|
104
|
+
.filter(([event]) => supportedEvents.has(event))
|
|
105
|
+
.map(([event, groups]) => [event, convertHookGroups(pluginName, groups)])
|
|
106
|
+
.filter(([, groups]) => groups.length > 0);
|
|
107
|
+
return entries.length > 0
|
|
108
|
+
? { hooks: Object.fromEntries(entries) }
|
|
109
|
+
: undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function convertHookGroups(pluginName, groups) {
|
|
113
|
+
return groups
|
|
114
|
+
.map(group => ({
|
|
115
|
+
...(group.matcher !== undefined
|
|
116
|
+
? { matcher: normalizeMatcher(group.matcher) }
|
|
117
|
+
: {}),
|
|
118
|
+
hooks: (group.hooks ?? [])
|
|
119
|
+
.map(hook => convertHookHandler(pluginName, hook))
|
|
120
|
+
.filter(Boolean),
|
|
121
|
+
}))
|
|
122
|
+
.filter(group => group.hooks.length > 0);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function normalizeMatcher(matcher) {
|
|
126
|
+
const normalized = String(matcher).replaceAll("Write|Edit", "Edit|Write");
|
|
127
|
+
return normalized.includes("apply_patch")
|
|
128
|
+
? normalized
|
|
129
|
+
: normalized.replaceAll("Edit|Write", "Edit|Write|apply_patch");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function convertHookHandler(pluginName, hook) {
|
|
133
|
+
if (hook.type !== "command" || typeof hook.command !== "string") {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
const command = convertHookCommand(pluginName, hook.command);
|
|
137
|
+
if (command === undefined) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
type: "command",
|
|
142
|
+
command,
|
|
143
|
+
...(hook.timeout !== undefined ? { timeout: hook.timeout } : {}),
|
|
144
|
+
...(hook.statusMessage !== undefined
|
|
145
|
+
? { statusMessage: hook.statusMessage }
|
|
146
|
+
: {}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function convertHookCommand(pluginName, command) {
|
|
151
|
+
const pluginScript = command.match(
|
|
152
|
+
/\$\{CLAUDE_PLUGIN_ROOT\}\/(hooks\/[^ "';]+)/
|
|
153
|
+
);
|
|
154
|
+
if (!pluginScript) {
|
|
155
|
+
return normalizeInlineCommand(command);
|
|
156
|
+
}
|
|
157
|
+
if (UNSUPPORTED_CODEX_HOOK_SCRIPTS.has(pluginScript[1])) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
return buildPluginScriptRunner(pluginName, pluginScript[1]);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function normalizeInlineCommand(command) {
|
|
164
|
+
const entireMatch = command.match(
|
|
165
|
+
/^command -v entire >\/dev\/null 2>&1 && entire hooks claude-code ([a-z-]+) \|\| true$/
|
|
166
|
+
);
|
|
167
|
+
if (!entireMatch) {
|
|
168
|
+
return command;
|
|
169
|
+
}
|
|
170
|
+
return `if command -v entire >/dev/null 2>&1; then entire hooks claude-code ${entireMatch[1]}; fi`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function buildPluginScriptRunner(pluginName, scriptPath) {
|
|
174
|
+
const script = JSON.stringify(scriptPath);
|
|
175
|
+
const plugin = JSON.stringify(pluginName);
|
|
176
|
+
return [
|
|
177
|
+
"bash -lc '",
|
|
178
|
+
`plugin=${shellQuote(plugin)}; script=${shellQuote(script)}; `,
|
|
179
|
+
"repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); ",
|
|
180
|
+
'for root in "${CODEX_PLUGIN_ROOT:-}" "${CLAUDE_PLUGIN_ROOT:-}" "$repo/plugins/$plugin" "$HOME/.codex/plugins/cache/lisa/$plugin/local"; do ',
|
|
181
|
+
'[ -n "$root" ] || continue; ',
|
|
182
|
+
'if [ -x "$root/$script" ]; then CLAUDE_PLUGIN_ROOT="$root" CODEX_PLUGIN_ROOT="$root" exec "$root/$script"; fi; ',
|
|
183
|
+
"done; ",
|
|
184
|
+
'found=$(find "$HOME/.codex/plugins/cache" -path "*/$plugin/*/$script" -type f -exec ls -t {} + 2>/dev/null | head -n 1); ',
|
|
185
|
+
'[ -n "$found" ] || exit 0; ',
|
|
186
|
+
"root=${found%/$script}; ",
|
|
187
|
+
'CLAUDE_PLUGIN_ROOT="$root" CODEX_PLUGIN_ROOT="$root" exec "$found"',
|
|
188
|
+
"'",
|
|
189
|
+
].join("");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function shellQuote(jsonStringLiteral) {
|
|
193
|
+
return jsonStringLiteral.replaceAll("'", "'\\''");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function metadataFor(pluginName) {
|
|
197
|
+
const map = {
|
|
198
|
+
lisa: {
|
|
199
|
+
displayName: "Lisa",
|
|
200
|
+
description:
|
|
201
|
+
"Universal governance: agents, skills, commands, hooks, and rules for all projects.",
|
|
202
|
+
shortDescription: "Universal project governance workflows",
|
|
203
|
+
longDescription:
|
|
204
|
+
"Reusable Lisa skills and lifecycle checks for planning, implementation, review, verification, and tracker workflows.",
|
|
205
|
+
category: "Productivity",
|
|
206
|
+
capabilities: ["Interactive", "Write"],
|
|
207
|
+
keywords: ["governance", "skills", "hooks", "workflow"],
|
|
208
|
+
defaultPrompt: [
|
|
209
|
+
"Plan this implementation with Lisa",
|
|
210
|
+
"Review this change with Lisa",
|
|
211
|
+
"Verify this project with Lisa",
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
"lisa-typescript": {
|
|
215
|
+
displayName: "Lisa TypeScript",
|
|
216
|
+
description:
|
|
217
|
+
"TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
|
|
218
|
+
shortDescription: "TypeScript lifecycle checks",
|
|
219
|
+
longDescription:
|
|
220
|
+
"TypeScript-focused Lisa hooks for formatting, linting, and ast-grep scanning around file edits.",
|
|
221
|
+
category: "Productivity",
|
|
222
|
+
capabilities: ["Write"],
|
|
223
|
+
keywords: ["typescript", "linting", "formatting", "hooks"],
|
|
224
|
+
defaultPrompt: ["Check this TypeScript change"],
|
|
225
|
+
},
|
|
226
|
+
"lisa-expo": {
|
|
227
|
+
displayName: "Lisa Expo",
|
|
228
|
+
description:
|
|
229
|
+
"Expo and React Native-specific skills, agents, rules, and MCP servers.",
|
|
230
|
+
shortDescription: "Expo and React Native workflows",
|
|
231
|
+
longDescription:
|
|
232
|
+
"Lisa skills and MCP configuration for Expo and React Native development, testing, operations, and security review.",
|
|
233
|
+
category: "Coding",
|
|
234
|
+
capabilities: ["Interactive", "Write"],
|
|
235
|
+
keywords: ["expo", "react-native", "mobile", "mcp"],
|
|
236
|
+
defaultPrompt: [
|
|
237
|
+
"Use Lisa Expo to review this screen",
|
|
238
|
+
"Debug this Expo app workflow",
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
"lisa-nestjs": {
|
|
242
|
+
displayName: "Lisa NestJS",
|
|
243
|
+
description:
|
|
244
|
+
"NestJS-specific skills and migration write-protection hooks.",
|
|
245
|
+
shortDescription: "NestJS workflow guidance",
|
|
246
|
+
longDescription:
|
|
247
|
+
"Lisa skills and lifecycle checks for NestJS GraphQL, TypeORM, and migration-safe development.",
|
|
248
|
+
category: "Coding",
|
|
249
|
+
capabilities: ["Interactive", "Write"],
|
|
250
|
+
keywords: ["nestjs", "graphql", "typeorm", "hooks"],
|
|
251
|
+
defaultPrompt: [
|
|
252
|
+
"Review this NestJS resolver",
|
|
253
|
+
"Check this TypeORM change",
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
"lisa-cdk": {
|
|
257
|
+
displayName: "Lisa CDK",
|
|
258
|
+
description: "AWS CDK-specific Lisa plugin.",
|
|
259
|
+
shortDescription: "AWS CDK workflows",
|
|
260
|
+
longDescription:
|
|
261
|
+
"Lisa plugin metadata for AWS CDK-focused project workflows.",
|
|
262
|
+
category: "Coding",
|
|
263
|
+
capabilities: ["Interactive", "Write"],
|
|
264
|
+
keywords: ["aws", "cdk", "infrastructure"],
|
|
265
|
+
defaultPrompt: ["Review this CDK change"],
|
|
266
|
+
},
|
|
267
|
+
"lisa-rails": {
|
|
268
|
+
displayName: "Lisa Rails",
|
|
269
|
+
description:
|
|
270
|
+
"Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
|
|
271
|
+
shortDescription: "Ruby on Rails workflows",
|
|
272
|
+
longDescription:
|
|
273
|
+
"Lisa skills and lifecycle checks for Rails conventions, code improvement, linting, and operations workflows.",
|
|
274
|
+
category: "Coding",
|
|
275
|
+
capabilities: ["Interactive", "Write"],
|
|
276
|
+
keywords: ["rails", "ruby", "rubocop", "hooks"],
|
|
277
|
+
defaultPrompt: [
|
|
278
|
+
"Review this Rails controller",
|
|
279
|
+
"Improve this Rails model",
|
|
280
|
+
],
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
return (
|
|
284
|
+
map[pluginName] ?? {
|
|
285
|
+
displayName: pluginName,
|
|
286
|
+
shortDescription: claudeManifest.description,
|
|
287
|
+
longDescription: claudeManifest.description,
|
|
288
|
+
category: "Coding",
|
|
289
|
+
capabilities: ["Interactive", "Write"],
|
|
290
|
+
keywords: ["lisa"],
|
|
291
|
+
defaultPrompt: [`Use ${pluginName}`],
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
}
|