@codyswann/lisa 2.124.4 → 2.124.6
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/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -34
- package/plugins/lisa-cursor/hooks/hooks.json +20 -0
- package/plugins/lisa-cursor/rules/{reference/base-rules.md → base-rules-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/base-rules.md → base-rules.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/coding-philosophy.md → coding-philosophy-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/coding-philosophy.md → coding-philosophy.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/config-resolution.md → config-resolution-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/config-resolution.md → config-resolution.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/documentation-source-paths.md → documentation-source-paths-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/documentation-source-paths.md → documentation-source-paths.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/empirical-inquiry.md → empirical-inquiry-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/empirical-inquiry.md → empirical-inquiry.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/intent-routing.md → intent-routing-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/intent-routing.md → intent-routing.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/leaf-only-lifecycle.md → leaf-only-lifecycle-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/leaf-only-lifecycle.md → leaf-only-lifecycle.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/prd-lifecycle-rollup.md → prd-lifecycle-rollup-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/prd-lifecycle-rollup.md → prd-lifecycle-rollup.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/repo-scope-split.md → repo-scope-split-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/repo-scope-split.md → repo-scope-split.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/security-audit-handling.md → security-audit-handling-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/security-audit-handling.md → security-audit-handling.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/usage-accounting.md → usage-accounting-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/usage-accounting.md → usage-accounting.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/verification.md → verification-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/verification.md → verification.mdc} +6 -1
- package/plugins/lisa-cursor/rules/{reference/wiki-knowledge-source.md → wiki-knowledge-source-reference.mdc} +5 -0
- package/plugins/lisa-cursor/rules/{eager/wiki-knowledge-source.md → wiki-knowledge-source.mdc} +6 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/rules/{harper-fabric.md → harper-fabric.mdc} +5 -0
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +2 -15
- package/plugins/lisa-nestjs-cursor/hooks/hooks.json +11 -0
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +2 -19
- package/plugins/lisa-rails-cursor/hooks/hooks.json +15 -0
- package/plugins/lisa-rails-cursor/rules/{rails-conventions.md → rails-conventions.mdc} +5 -0
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +2 -34
- package/plugins/lisa-typescript-cursor/hooks/hooks.json +25 -0
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/scripts/generate-cursor-plugin-artifacts.mjs +214 -20
- package/scripts/lib/per-agent-hook-filter.mjs +138 -23
- /package/plugins/lisa-expo-cursor/{.mcp.json → mcp.json} +0 -0
|
@@ -13,8 +13,11 @@
|
|
|
13
13
|
* - Codex: ship universal + SubagentStart (handled by the Codex generator's own
|
|
14
14
|
* ship list — this module is not invoked for Codex; Codex uses the existing
|
|
15
15
|
* generate-codex-plugin-artifacts.mjs path)
|
|
16
|
-
* - Cursor:
|
|
17
|
-
*
|
|
16
|
+
* - Cursor: emit hooks to a Cursor-native hooks/hooks.json (camelCase events,
|
|
17
|
+
* flattened {command, matcher} entries, ${CURSOR_PLUGIN_ROOT}/hooks/ command
|
|
18
|
+
* paths) via buildCursorHooksJson; strip inject-rules.sh (rules ship as native
|
|
19
|
+
* rules/*.mdc — the single delivery path; injecting would double-deliver),
|
|
20
|
+
* Claude-team-specific scripts, and `entire hooks claude-code *` calls
|
|
18
21
|
* - agy: this filter is NOT consumed for agy. agy hooks ship as a
|
|
19
22
|
* plugin-bundled ROOT hooks.json emitted by
|
|
20
23
|
* generate-agy-plugin-artifacts.mjs (its own AGY_PLUGIN_HOOKS map is the
|
|
@@ -50,7 +53,7 @@ const SCRIPT_RULES = {
|
|
|
50
53
|
"inject-rules.sh": {
|
|
51
54
|
claude: true,
|
|
52
55
|
codex: true,
|
|
53
|
-
cursor: false, //
|
|
56
|
+
cursor: false, // rules ship as native rules/*.mdc (single delivery path); injecting would double-deliver
|
|
54
57
|
agy: false, // rules delivered via AGENTS.md bake, not a hook (rules-once invariant)
|
|
55
58
|
copilot: true, // conservative default; conditionally stripped if rules-auto-load probe positive
|
|
56
59
|
},
|
|
@@ -110,12 +113,69 @@ const scriptNameFromCommand = cmd => {
|
|
|
110
113
|
return match ? match[1] : null;
|
|
111
114
|
};
|
|
112
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Rewrite a Claude hook command to the Cursor plugin-root form: the
|
|
118
|
+
* `${CLAUDE_PLUGIN_ROOT}/` (or `${CURSOR_PLUGIN_ROOT}/`) prefix is normalized to
|
|
119
|
+
* `${CURSOR_PLUGIN_ROOT}/` (e.g. `${CLAUDE_PLUGIN_ROOT}/hooks/block-no-verify.sh`
|
|
120
|
+
* → `${CURSOR_PLUGIN_ROOT}/hooks/block-no-verify.sh`).
|
|
121
|
+
*
|
|
122
|
+
* Why the token, not a bare `./` (issue #1055, CodeRabbit security review):
|
|
123
|
+
* Cursor plugin hook commands execute with the OPENED PROJECT ROOT as their cwd
|
|
124
|
+
* — NOT the plugin directory (confirmed by a Cursor maintainer on the forum:
|
|
125
|
+
* https://forum.cursor.com/t/inconsistent-working-directory-for-plugin-hook-commands/153236).
|
|
126
|
+
* A bare `./hooks/<script>.sh` would therefore (a) fail to resolve to the
|
|
127
|
+
* plugin's bundled script and (b) let a malicious repo shadow a guard hook with
|
|
128
|
+
* its own project-root `./hooks/*`. `${CURSOR_PLUGIN_ROOT}` is the maintainer-
|
|
129
|
+
* endorsed placeholder for the plugin install dir (`${CLAUDE_PLUGIN_ROOT}` also
|
|
130
|
+
* works in Cursor, but we normalize to the Cursor-native name).
|
|
131
|
+
*
|
|
132
|
+
* @param {string} command
|
|
133
|
+
* @returns {string}
|
|
134
|
+
*/
|
|
135
|
+
const toCursorCommandPath = command =>
|
|
136
|
+
typeof command === "string"
|
|
137
|
+
? command.replace(
|
|
138
|
+
/\$\{(?:CLAUDE|CURSOR)_PLUGIN_ROOT\}\//g,
|
|
139
|
+
"${CURSOR_PLUGIN_ROOT}/"
|
|
140
|
+
)
|
|
141
|
+
: command;
|
|
142
|
+
|
|
143
|
+
/** Claude PascalCase → Copilot event-name map (per Copilot's docs). */
|
|
144
|
+
const COPILOT_EVENTS = {
|
|
145
|
+
PreToolUse: "preToolUse",
|
|
146
|
+
PostToolUse: "postToolUse",
|
|
147
|
+
SessionStart: "sessionStart",
|
|
148
|
+
SessionEnd: "sessionEnd",
|
|
149
|
+
UserPromptSubmit: "userPromptSubmitted",
|
|
150
|
+
Stop: "agentStop",
|
|
151
|
+
SubagentStart: "subagentStart", // not supported but include for symmetry
|
|
152
|
+
SubagentStop: "subagentStop",
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Claude PascalCase → Cursor event-name map. Verified against the official
|
|
157
|
+
* Cursor hooks reference (issue #1055; the prior "keep PascalCase, loader
|
|
158
|
+
* auto-normalizes" assumption was wrong).
|
|
159
|
+
*/
|
|
160
|
+
const CURSOR_EVENTS = {
|
|
161
|
+
PreToolUse: "preToolUse",
|
|
162
|
+
PostToolUse: "postToolUse",
|
|
163
|
+
SessionStart: "sessionStart",
|
|
164
|
+
SessionEnd: "sessionEnd",
|
|
165
|
+
UserPromptSubmit: "beforeSubmitPrompt",
|
|
166
|
+
Stop: "stop",
|
|
167
|
+
SubagentStart: "subagentStart",
|
|
168
|
+
SubagentStop: "subagentStop",
|
|
169
|
+
PreCompact: "preCompact",
|
|
170
|
+
};
|
|
171
|
+
|
|
113
172
|
/**
|
|
114
173
|
* Translate Claude PascalCase event names to a target agent's native casing.
|
|
115
174
|
*
|
|
116
175
|
* Per the Wave 1 audit's Wave 3 contract step 4 + step 6:
|
|
117
|
-
* - Cursor:
|
|
118
|
-
*
|
|
176
|
+
* - Cursor: rewrite to Cursor camelCase event names (preToolUse, postToolUse,
|
|
177
|
+
* sessionStart, beforeSubmitPrompt, stop, …)
|
|
178
|
+
* - Codex / agy: keep PascalCase
|
|
119
179
|
* - Copilot: rewrite to lowercase / camelCase per Copilot's docs
|
|
120
180
|
*
|
|
121
181
|
* @param {string} eventName Claude event name (e.g. "PreToolUse")
|
|
@@ -123,18 +183,9 @@ const scriptNameFromCommand = cmd => {
|
|
|
123
183
|
* @returns {string} Translated event name
|
|
124
184
|
*/
|
|
125
185
|
export function translateEventName(eventName, agent) {
|
|
126
|
-
if (agent
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
PostToolUse: "postToolUse",
|
|
130
|
-
SessionStart: "sessionStart",
|
|
131
|
-
SessionEnd: "sessionEnd",
|
|
132
|
-
UserPromptSubmit: "userPromptSubmitted",
|
|
133
|
-
Stop: "agentStop",
|
|
134
|
-
SubagentStart: "subagentStart", // not supported but include for symmetry
|
|
135
|
-
SubagentStop: "subagentStop",
|
|
136
|
-
};
|
|
137
|
-
return COPILOT_EVENTS[eventName] ?? eventName;
|
|
186
|
+
if (agent === "copilot") return COPILOT_EVENTS[eventName] ?? eventName;
|
|
187
|
+
if (agent === "cursor") return CURSOR_EVENTS[eventName] ?? eventName;
|
|
188
|
+
return eventName;
|
|
138
189
|
}
|
|
139
190
|
|
|
140
191
|
/**
|
|
@@ -204,12 +255,15 @@ export function shouldShipHook(hook, _eventName, agent, opts = {}) {
|
|
|
204
255
|
* Returns the new hook block (or undefined when the block ends up empty after
|
|
205
256
|
* filtering, which means the manifest should omit the hooks field entirely).
|
|
206
257
|
*
|
|
207
|
-
* This function
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* agy
|
|
258
|
+
* This function returns the Claude-NESTED block shape (with translated event
|
|
259
|
+
* keys) and is used by the Copilot generator. Cursor does NOT use it — Cursor
|
|
260
|
+
* needs the flattened hooks/hooks.json schema and goes through
|
|
261
|
+
* buildCursorHooksJson instead. The "agy" branch still works (3 universal
|
|
262
|
+
* scripts survive, PascalCase events) and is exercised by unit tests as
|
|
263
|
+
* conceptual ship-list documentation, but agy hooks are NOT emitted through this
|
|
264
|
+
* path — they ship as a plugin-bundled root hooks.json built by
|
|
265
|
+
* generate-agy-plugin-artifacts.mjs (only block-no-verify is portable; agy lacks
|
|
266
|
+
* SessionStart).
|
|
213
267
|
*
|
|
214
268
|
* @param {Record<string, Array<{ matcher?: string, hooks: Array<object> }>>} hookBlock
|
|
215
269
|
* The Claude-format hook block from .claude-plugin/plugin.json.
|
|
@@ -258,3 +312,64 @@ export function filterHooksForAgent(hookBlock, agent, opts = {}) {
|
|
|
258
312
|
export function filterScriptsForAgent(scriptFilenames, agent) {
|
|
259
313
|
return scriptFilenames.filter(name => shouldShipScript(name, agent));
|
|
260
314
|
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Build the Cursor-native `hooks/hooks.json` structure from a Claude-format hook
|
|
318
|
+
* block.
|
|
319
|
+
*
|
|
320
|
+
* Cursor's hooks file uses a flattened schema that differs from Claude's nested
|
|
321
|
+
* `.claude-plugin/plugin.json` block:
|
|
322
|
+
*
|
|
323
|
+
* Claude: { "<ClaudeEvent>": [ { matcher, hooks: [ { type: "command", command } ] } ] }
|
|
324
|
+
* Cursor: { version: 1, hooks: { "<cursorEvent>": [ { command, matcher? } ] } }
|
|
325
|
+
*
|
|
326
|
+
* The transformation:
|
|
327
|
+
* 1. Filters each handler for the cursor agent (drops inject-rules.sh,
|
|
328
|
+
* enforce-team-first.sh, and `entire hooks claude-code *` calls).
|
|
329
|
+
* 2. Translates Claude PascalCase event names to Cursor camelCase.
|
|
330
|
+
* 3. Flattens each matcher-group into one `{ command, matcher? }` per surviving
|
|
331
|
+
* handler (unwrapping Claude's `{ type: "command", command }`).
|
|
332
|
+
* 4. Rewrites `${CLAUDE_PLUGIN_ROOT}/hooks/<x>` command paths to the
|
|
333
|
+
* Cursor plugin-root form `${CURSOR_PLUGIN_ROOT}/hooks/<x>` (plugin hooks
|
|
334
|
+
* run with the project root as cwd, so a bare `./` would not resolve to the
|
|
335
|
+
* bundled script and could be shadowed by a repo-local `./hooks/*`).
|
|
336
|
+
*
|
|
337
|
+
* Note: this intentionally re-walks the hook block rather than sharing a
|
|
338
|
+
* skeleton with `filterHooksForAgent`. The DRY extraction was deferred (issue
|
|
339
|
+
* #1055 review): `filterHooksForAgent` is on the Copilot path and emits the
|
|
340
|
+
* Claude-NESTED shape, whereas this emits Cursor's FLAT shape — unifying the
|
|
341
|
+
* walk would risk a sibling-generator regression for marginal gain.
|
|
342
|
+
*
|
|
343
|
+
* @param {Record<string, Array<{ matcher?: string, hooks: Array<{ type?: string, command: string }> }>>} hookBlock
|
|
344
|
+
* The Claude-format hook block from `.claude-plugin/plugin.json`.
|
|
345
|
+
* @returns {{ version: number, hooks: Record<string, Array<{ command: string, matcher?: string }>> } | undefined}
|
|
346
|
+
* The Cursor hooks structure, or undefined when no hooks survive (the caller
|
|
347
|
+
* then omits `hooks/hooks.json` entirely).
|
|
348
|
+
*/
|
|
349
|
+
export function buildCursorHooksJson(hookBlock) {
|
|
350
|
+
if (!hookBlock || typeof hookBlock !== "object") return undefined;
|
|
351
|
+
|
|
352
|
+
/** @type {Record<string, Array<{ command: string, matcher?: string }>>} */
|
|
353
|
+
const hooks = {};
|
|
354
|
+
|
|
355
|
+
for (const [claudeEventName, entries] of Object.entries(hookBlock)) {
|
|
356
|
+
if (!Array.isArray(entries)) continue;
|
|
357
|
+
const flattened = [];
|
|
358
|
+
for (const entry of entries) {
|
|
359
|
+
const handlerArray = entry?.hooks;
|
|
360
|
+
if (!Array.isArray(handlerArray)) continue;
|
|
361
|
+
for (const handler of handlerArray) {
|
|
362
|
+
if (!shouldShipHook(handler, claudeEventName, "cursor")) continue;
|
|
363
|
+
const command = toCursorCommandPath(handler.command);
|
|
364
|
+
flattened.push(
|
|
365
|
+
entry.matcher ? { command, matcher: entry.matcher } : { command }
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (flattened.length > 0) {
|
|
370
|
+
hooks[translateEventName(claudeEventName, "cursor")] = flattened;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return Object.keys(hooks).length > 0 ? { version: 1, hooks } : undefined;
|
|
375
|
+
}
|
|
File without changes
|