@codyswann/lisa 2.124.4 → 2.124.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/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 +212 -20
- package/scripts/lib/per-agent-hook-filter.mjs +133 -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, relative ./hooks/ command paths) via
|
|
18
|
+
* 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,66 @@ 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-relative form: the
|
|
118
|
+
* `${CLAUDE_PLUGIN_ROOT}/` prefix becomes `./` (e.g.
|
|
119
|
+
* `${CLAUDE_PLUGIN_ROOT}/hooks/block-no-verify.sh` → `./hooks/block-no-verify.sh`).
|
|
120
|
+
*
|
|
121
|
+
* Path-resolution caveat (issue #1055 security review): Cursor exposes NO
|
|
122
|
+
* plugin-root token for hook commands — its hooks reference documents only
|
|
123
|
+
* `CURSOR_PROJECT_DIR` / `CLAUDE_PROJECT_DIR` (workspace root), and is silent on
|
|
124
|
+
* how plugin-bundled `hooks/hooks.json` commands resolve. `./` is therefore the
|
|
125
|
+
* only plugin-relative form available, and is what the Cursor plugin structure
|
|
126
|
+
* implies for a plugin-bundled file. Plugin-hook FIRING (and thus the exact CWD
|
|
127
|
+
* these resolve against) is not verifiable via `cursor-agent --plugin-dir` — it
|
|
128
|
+
* is an IDE/marketplace concern tracked as a PR follow-up. If a future Cursor
|
|
129
|
+
* release resolves plugin-hook `./` against the project root rather than the
|
|
130
|
+
* plugin root, a malicious repo could shadow a guard hook; revisit this then.
|
|
131
|
+
*
|
|
132
|
+
* @param {string} command
|
|
133
|
+
* @returns {string}
|
|
134
|
+
*/
|
|
135
|
+
const toCursorCommandPath = command =>
|
|
136
|
+
typeof command === "string"
|
|
137
|
+
? command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}\//g, "./")
|
|
138
|
+
: command;
|
|
139
|
+
|
|
140
|
+
/** Claude PascalCase → Copilot event-name map (per Copilot's docs). */
|
|
141
|
+
const COPILOT_EVENTS = {
|
|
142
|
+
PreToolUse: "preToolUse",
|
|
143
|
+
PostToolUse: "postToolUse",
|
|
144
|
+
SessionStart: "sessionStart",
|
|
145
|
+
SessionEnd: "sessionEnd",
|
|
146
|
+
UserPromptSubmit: "userPromptSubmitted",
|
|
147
|
+
Stop: "agentStop",
|
|
148
|
+
SubagentStart: "subagentStart", // not supported but include for symmetry
|
|
149
|
+
SubagentStop: "subagentStop",
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Claude PascalCase → Cursor event-name map. Verified against the official
|
|
154
|
+
* Cursor hooks reference (issue #1055; the prior "keep PascalCase, loader
|
|
155
|
+
* auto-normalizes" assumption was wrong).
|
|
156
|
+
*/
|
|
157
|
+
const CURSOR_EVENTS = {
|
|
158
|
+
PreToolUse: "preToolUse",
|
|
159
|
+
PostToolUse: "postToolUse",
|
|
160
|
+
SessionStart: "sessionStart",
|
|
161
|
+
SessionEnd: "sessionEnd",
|
|
162
|
+
UserPromptSubmit: "beforeSubmitPrompt",
|
|
163
|
+
Stop: "stop",
|
|
164
|
+
SubagentStart: "subagentStart",
|
|
165
|
+
SubagentStop: "subagentStop",
|
|
166
|
+
PreCompact: "preCompact",
|
|
167
|
+
};
|
|
168
|
+
|
|
113
169
|
/**
|
|
114
170
|
* Translate Claude PascalCase event names to a target agent's native casing.
|
|
115
171
|
*
|
|
116
172
|
* Per the Wave 1 audit's Wave 3 contract step 4 + step 6:
|
|
117
|
-
* - Cursor:
|
|
118
|
-
*
|
|
173
|
+
* - Cursor: rewrite to Cursor camelCase event names (preToolUse, postToolUse,
|
|
174
|
+
* sessionStart, beforeSubmitPrompt, stop, …)
|
|
175
|
+
* - Codex / agy: keep PascalCase
|
|
119
176
|
* - Copilot: rewrite to lowercase / camelCase per Copilot's docs
|
|
120
177
|
*
|
|
121
178
|
* @param {string} eventName Claude event name (e.g. "PreToolUse")
|
|
@@ -123,18 +180,9 @@ const scriptNameFromCommand = cmd => {
|
|
|
123
180
|
* @returns {string} Translated event name
|
|
124
181
|
*/
|
|
125
182
|
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;
|
|
183
|
+
if (agent === "copilot") return COPILOT_EVENTS[eventName] ?? eventName;
|
|
184
|
+
if (agent === "cursor") return CURSOR_EVENTS[eventName] ?? eventName;
|
|
185
|
+
return eventName;
|
|
138
186
|
}
|
|
139
187
|
|
|
140
188
|
/**
|
|
@@ -204,12 +252,15 @@ export function shouldShipHook(hook, _eventName, agent, opts = {}) {
|
|
|
204
252
|
* Returns the new hook block (or undefined when the block ends up empty after
|
|
205
253
|
* filtering, which means the manifest should omit the hooks field entirely).
|
|
206
254
|
*
|
|
207
|
-
* This function
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* agy
|
|
255
|
+
* This function returns the Claude-NESTED block shape (with translated event
|
|
256
|
+
* keys) and is used by the Copilot generator. Cursor does NOT use it — Cursor
|
|
257
|
+
* needs the flattened hooks/hooks.json schema and goes through
|
|
258
|
+
* buildCursorHooksJson instead. The "agy" branch still works (3 universal
|
|
259
|
+
* scripts survive, PascalCase events) and is exercised by unit tests as
|
|
260
|
+
* conceptual ship-list documentation, but agy hooks are NOT emitted through this
|
|
261
|
+
* path — they ship as a plugin-bundled root hooks.json built by
|
|
262
|
+
* generate-agy-plugin-artifacts.mjs (only block-no-verify is portable; agy lacks
|
|
263
|
+
* SessionStart).
|
|
213
264
|
*
|
|
214
265
|
* @param {Record<string, Array<{ matcher?: string, hooks: Array<object> }>>} hookBlock
|
|
215
266
|
* The Claude-format hook block from .claude-plugin/plugin.json.
|
|
@@ -258,3 +309,62 @@ export function filterHooksForAgent(hookBlock, agent, opts = {}) {
|
|
|
258
309
|
export function filterScriptsForAgent(scriptFilenames, agent) {
|
|
259
310
|
return scriptFilenames.filter(name => shouldShipScript(name, agent));
|
|
260
311
|
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Build the Cursor-native `hooks/hooks.json` structure from a Claude-format hook
|
|
315
|
+
* block.
|
|
316
|
+
*
|
|
317
|
+
* Cursor's hooks file uses a flattened schema that differs from Claude's nested
|
|
318
|
+
* `.claude-plugin/plugin.json` block:
|
|
319
|
+
*
|
|
320
|
+
* Claude: { "<ClaudeEvent>": [ { matcher, hooks: [ { type: "command", command } ] } ] }
|
|
321
|
+
* Cursor: { version: 1, hooks: { "<cursorEvent>": [ { command, matcher? } ] } }
|
|
322
|
+
*
|
|
323
|
+
* The transformation:
|
|
324
|
+
* 1. Filters each handler for the cursor agent (drops inject-rules.sh,
|
|
325
|
+
* enforce-team-first.sh, and `entire hooks claude-code *` calls).
|
|
326
|
+
* 2. Translates Claude PascalCase event names to Cursor camelCase.
|
|
327
|
+
* 3. Flattens each matcher-group into one `{ command, matcher? }` per surviving
|
|
328
|
+
* handler (unwrapping Claude's `{ type: "command", command }`).
|
|
329
|
+
* 4. Rewrites `${CLAUDE_PLUGIN_ROOT}/hooks/<x>` command paths to the
|
|
330
|
+
* Cursor-relative `./hooks/<x>`.
|
|
331
|
+
*
|
|
332
|
+
* Note: this intentionally re-walks the hook block rather than sharing a
|
|
333
|
+
* skeleton with `filterHooksForAgent`. The DRY extraction was deferred (issue
|
|
334
|
+
* #1055 review): `filterHooksForAgent` is on the Copilot path and emits the
|
|
335
|
+
* Claude-NESTED shape, whereas this emits Cursor's FLAT shape — unifying the
|
|
336
|
+
* walk would risk a sibling-generator regression for marginal gain.
|
|
337
|
+
*
|
|
338
|
+
* @param {Record<string, Array<{ matcher?: string, hooks: Array<{ type?: string, command: string }> }>>} hookBlock
|
|
339
|
+
* The Claude-format hook block from `.claude-plugin/plugin.json`.
|
|
340
|
+
* @returns {{ version: number, hooks: Record<string, Array<{ command: string, matcher?: string }>> } | undefined}
|
|
341
|
+
* The Cursor hooks structure, or undefined when no hooks survive (the caller
|
|
342
|
+
* then omits `hooks/hooks.json` entirely).
|
|
343
|
+
*/
|
|
344
|
+
export function buildCursorHooksJson(hookBlock) {
|
|
345
|
+
if (!hookBlock || typeof hookBlock !== "object") return undefined;
|
|
346
|
+
|
|
347
|
+
/** @type {Record<string, Array<{ command: string, matcher?: string }>>} */
|
|
348
|
+
const hooks = {};
|
|
349
|
+
|
|
350
|
+
for (const [claudeEventName, entries] of Object.entries(hookBlock)) {
|
|
351
|
+
if (!Array.isArray(entries)) continue;
|
|
352
|
+
const flattened = [];
|
|
353
|
+
for (const entry of entries) {
|
|
354
|
+
const handlerArray = entry?.hooks;
|
|
355
|
+
if (!Array.isArray(handlerArray)) continue;
|
|
356
|
+
for (const handler of handlerArray) {
|
|
357
|
+
if (!shouldShipHook(handler, claudeEventName, "cursor")) continue;
|
|
358
|
+
const command = toCursorCommandPath(handler.command);
|
|
359
|
+
flattened.push(
|
|
360
|
+
entry.matcher ? { command, matcher: entry.matcher } : { command }
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (flattened.length > 0) {
|
|
365
|
+
hooks[translateEventName(claudeEventName, "cursor")] = flattened;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return Object.keys(hooks).length > 0 ? { version: 1, hooks } : undefined;
|
|
370
|
+
}
|
|
File without changes
|