@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
|
@@ -1,24 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-nestjs",
|
|
3
|
-
"version": "2.124.
|
|
3
|
+
"version": "2.124.5",
|
|
4
4
|
"description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": [
|
|
9
9
|
"lisa-typescript"
|
|
10
|
-
]
|
|
11
|
-
"hooks": {
|
|
12
|
-
"PreToolUse": [
|
|
13
|
-
{
|
|
14
|
-
"matcher": "Write|Edit",
|
|
15
|
-
"hooks": [
|
|
16
|
-
{
|
|
17
|
-
"type": "command",
|
|
18
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/block-migration-edits.sh"
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
}
|
|
10
|
+
]
|
|
24
11
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.124.
|
|
3
|
+
"version": "2.124.5",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.124.
|
|
3
|
+
"version": "2.124.5",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.124.
|
|
3
|
+
"version": "2.124.5",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.124.
|
|
3
|
+
"version": "2.124.5",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.124.
|
|
3
|
+
"version": "2.124.5",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,28 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-rails",
|
|
3
|
-
"version": "2.124.
|
|
3
|
+
"version": "2.124.5",
|
|
4
4
|
"description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": [
|
|
9
9
|
"lisa"
|
|
10
|
-
]
|
|
11
|
-
"hooks": {
|
|
12
|
-
"PostToolUse": [
|
|
13
|
-
{
|
|
14
|
-
"matcher": "Write|Edit",
|
|
15
|
-
"hooks": [
|
|
16
|
-
{
|
|
17
|
-
"type": "command",
|
|
18
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/rubocop-on-edit.sh"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"type": "command",
|
|
22
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/sg-scan-on-edit.sh"
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
}
|
|
26
|
-
]
|
|
27
|
-
}
|
|
10
|
+
]
|
|
28
11
|
}
|
|
@@ -1,43 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-typescript",
|
|
3
|
-
"version": "2.124.
|
|
3
|
+
"version": "2.124.5",
|
|
4
4
|
"description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": [
|
|
9
9
|
"lisa"
|
|
10
|
-
]
|
|
11
|
-
"hooks": {
|
|
12
|
-
"PreToolUse": [
|
|
13
|
-
{
|
|
14
|
-
"matcher": "Write|Edit",
|
|
15
|
-
"hooks": [
|
|
16
|
-
{
|
|
17
|
-
"type": "command",
|
|
18
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/block-suppress-directives.sh"
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
}
|
|
22
|
-
],
|
|
23
|
-
"PostToolUse": [
|
|
24
|
-
{
|
|
25
|
-
"matcher": "Write|Edit",
|
|
26
|
-
"hooks": [
|
|
27
|
-
{
|
|
28
|
-
"type": "command",
|
|
29
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/format-on-edit.sh"
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"type": "command",
|
|
33
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/sg-scan-on-edit.sh"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"type": "command",
|
|
37
|
-
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/lint-on-edit.sh"
|
|
38
|
-
}
|
|
39
|
-
]
|
|
40
|
-
}
|
|
41
|
-
]
|
|
42
|
-
}
|
|
10
|
+
]
|
|
43
11
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"hooks": {
|
|
4
|
+
"preToolUse": [
|
|
5
|
+
{
|
|
6
|
+
"command": "./hooks/block-suppress-directives.sh",
|
|
7
|
+
"matcher": "Write|Edit"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"postToolUse": [
|
|
11
|
+
{
|
|
12
|
+
"command": "./hooks/format-on-edit.sh",
|
|
13
|
+
"matcher": "Write|Edit"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"command": "./hooks/sg-scan-on-edit.sh",
|
|
17
|
+
"matcher": "Write|Edit"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"command": "./hooks/lint-on-edit.sh",
|
|
21
|
+
"matcher": "Write|Edit"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -2,13 +2,32 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Generate the Cursor variant of a Lisa plugin from the built Claude artifact.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* The variant is reshaped to match the official Cursor plugin spec (issue
|
|
6
|
+
* #1055). Rule application was verified empirically by loading the generated
|
|
7
|
+
* `.mdc` at Cursor's canonical `.cursor/rules/` location (NEW `.mdc` = applied,
|
|
8
|
+
* OLD nested `.md` = UNKNOWN, both with 0 tool calls). Note: the
|
|
9
|
+
* `cursor-agent --plugin-dir` headless flag loads a plugin's skills/agents/
|
|
10
|
+
* commands but does NOT inject its `rules/*.mdc` into model context, so it
|
|
11
|
+
* cannot be used to prove rule application — see evidence/cursor-rule-probe-1055.md:
|
|
12
|
+
*
|
|
13
|
+
* - Manifest: keep `.claude-plugin/plugin.json` — `cursor-agent --plugin-dir`
|
|
14
|
+
* loads it (CLI scope). The IDE marketplace's `.cursor-plugin/` + a Cursor
|
|
15
|
+
* `marketplace.json` is a separate, untested follow-up and is NOT emitted here.
|
|
16
|
+
* - Rules: Cursor discovers `rules/*.mdc` files carrying YAML frontmatter and
|
|
17
|
+
* ignores plain `.md`. The built Claude artifact ships a nested
|
|
18
|
+
* `rules/eager/*.md` + `rules/reference/*.md` tree, so the variant flattens it
|
|
19
|
+
* to top-level `rules/<name>.mdc` (eager → `alwaysApply: true`) and
|
|
20
|
+
* `rules/<name>-reference.mdc` (reference → `alwaysApply: false`). The
|
|
21
|
+
* `-reference` suffix avoids a same-path collision: eager and reference share
|
|
22
|
+
* all base names.
|
|
23
|
+
* - Hooks: emitted as a Cursor-native `hooks/hooks.json` (flattened schema,
|
|
24
|
+
* camelCase event names, relative `./hooks/` command paths) — NOT inline in
|
|
25
|
+
* the manifest. `inject-rules.sh` is dropped because rules now ship as native
|
|
26
|
+
* `.mdc` (the single delivery path; injecting would double-deliver);
|
|
27
|
+
* `enforce-team-first.sh` (Claude-team-specific) and the
|
|
28
|
+
* `entire hooks claude-code *` analytics calls (Claude-only) are dropped too.
|
|
29
|
+
* - MCP: renamed from `.mcp.json` to `mcp.json` — Cursor auto-discovers the
|
|
30
|
+
* un-dotted filename.
|
|
12
31
|
*
|
|
13
32
|
* The variant's `hooks/` directory mirrors the surviving script ship-list.
|
|
14
33
|
*
|
|
@@ -25,7 +44,7 @@ import fs from "node:fs";
|
|
|
25
44
|
import path from "node:path";
|
|
26
45
|
import { fileURLToPath } from "node:url";
|
|
27
46
|
import {
|
|
28
|
-
|
|
47
|
+
buildCursorHooksJson,
|
|
29
48
|
filterScriptsForAgent,
|
|
30
49
|
} from "./lib/per-agent-hook-filter.mjs";
|
|
31
50
|
|
|
@@ -82,6 +101,167 @@ function copyDir(src, dst, keep = () => true) {
|
|
|
82
101
|
walk(src, "");
|
|
83
102
|
}
|
|
84
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Titleize a rule slug for a fallback frontmatter description.
|
|
106
|
+
*
|
|
107
|
+
* @param {string} name e.g. "base-rules"
|
|
108
|
+
* @returns {string} e.g. "Base Rules"
|
|
109
|
+
*/
|
|
110
|
+
function titleizeRuleName(name) {
|
|
111
|
+
return name
|
|
112
|
+
.split("-")
|
|
113
|
+
.map(part => (part ? part[0].toUpperCase() + part.slice(1) : part))
|
|
114
|
+
.join(" ")
|
|
115
|
+
.trim();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Derive a single-line frontmatter description from a rule body.
|
|
120
|
+
*
|
|
121
|
+
* Prefers the first markdown H1 heading; falls back to the titleized slug. The
|
|
122
|
+
* result is always a non-empty single line (Cursor frontmatter `description`).
|
|
123
|
+
*
|
|
124
|
+
* @param {string} body Rule markdown content.
|
|
125
|
+
* @param {string} name Rule slug.
|
|
126
|
+
* @returns {string}
|
|
127
|
+
*/
|
|
128
|
+
function deriveRuleDescription(body, name) {
|
|
129
|
+
// Strip fenced code blocks first so a `#`-prefixed line inside one (a shell
|
|
130
|
+
// comment, a Markdown example, etc.) is not mistaken for the rule's H1.
|
|
131
|
+
const withoutFences = body.replace(/^(```|~~~).*$[\s\S]*?^\1.*$/gm, "");
|
|
132
|
+
const h1 = /^#\s+(.+?)\s*$/m.exec(withoutFences);
|
|
133
|
+
const text = (h1 ? h1[1] : "").replace(/\s+/g, " ").trim();
|
|
134
|
+
return text || titleizeRuleName(name);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* YAML-quote a description value for `.mdc` frontmatter.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} value
|
|
141
|
+
* @returns {string}
|
|
142
|
+
*/
|
|
143
|
+
function yamlQuote(value) {
|
|
144
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Rewrite intra-rule cross-link URLs from the nested `.md` layout to the flat
|
|
149
|
+
* `.mdc` layout so links in flattened rules still resolve.
|
|
150
|
+
*
|
|
151
|
+
* Only the URL inside a Markdown `](...)` link is rewritten — the link TEXT is
|
|
152
|
+
* left untouched (so `[reference/base-rules.md](../reference/base-rules.md)`
|
|
153
|
+
* keeps its readable label while the target becomes `base-rules-reference.mdc`).
|
|
154
|
+
* Two URL shapes are handled:
|
|
155
|
+
* - tier-prefixed: `(../)?eager/<slug>.md` → `<slug>.mdc`;
|
|
156
|
+
* `(../)?reference/<slug>.md` → `<slug>-reference.mdc`
|
|
157
|
+
* - bare same-dir: `<slug>.md` (no slash) → `<slug>.mdc`
|
|
158
|
+
* Optional `#fragment` suffixes are preserved. Constraint: a bare same-dir link
|
|
159
|
+
* is assumed to target the eager / top-level rule of that slug — the real eager
|
|
160
|
+
* bodies always use an explicit `../reference/<name>.md` URL for the reference
|
|
161
|
+
* tier, so bare links never need the `-reference` suffix. URLs with any other
|
|
162
|
+
* shape (deeper paths, external `.md`, http links) are left as-is.
|
|
163
|
+
*
|
|
164
|
+
* @param {string} body Rule markdown content.
|
|
165
|
+
* @returns {string}
|
|
166
|
+
*/
|
|
167
|
+
function rewriteRuleLinks(body) {
|
|
168
|
+
return body.replace(/\]\(([^)]+)\)/g, (match, url) => {
|
|
169
|
+
const tiered =
|
|
170
|
+
/^(?:\.\.\/)?(eager|reference)\/([A-Za-z0-9._-]+)\.md(#[^)]*)?$/.exec(
|
|
171
|
+
url
|
|
172
|
+
);
|
|
173
|
+
if (tiered) {
|
|
174
|
+
const [, tier, slug, fragment = ""] = tiered;
|
|
175
|
+
const base = tier === "reference" ? `${slug}-reference` : slug;
|
|
176
|
+
return `](${base}.mdc${fragment})`;
|
|
177
|
+
}
|
|
178
|
+
const bare = /^([A-Za-z0-9._-]+)\.md(#[^)]*)?$/.exec(url);
|
|
179
|
+
if (bare) {
|
|
180
|
+
const [, slug, fragment = ""] = bare;
|
|
181
|
+
return `](${slug}.mdc${fragment})`;
|
|
182
|
+
}
|
|
183
|
+
return match;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Transform the copied nested `rules/` tree into flat Cursor-native
|
|
189
|
+
* `rules/<name>.mdc` files with YAML frontmatter.
|
|
190
|
+
*
|
|
191
|
+
* The base plugin splits rules into `rules/eager/*.md` (always-on) and
|
|
192
|
+
* `rules/reference/*.md` (on-request); stack plugins instead ship a single
|
|
193
|
+
* always-on `rules/<name>.md` at the top level. Both layouts are normalized:
|
|
194
|
+
* - eager rule → `rules/<name>.mdc` with `alwaysApply: true`
|
|
195
|
+
* - reference rule → `rules/<name>-reference.mdc` with `alwaysApply: false`
|
|
196
|
+
* (the `-reference` suffix prevents a same-path collision, since eager and
|
|
197
|
+
* reference share base names)
|
|
198
|
+
* - top-level stack rule → `rules/<name>.mdc` with `alwaysApply: true`
|
|
199
|
+
* Plain top-level `.md` rules are rewritten in place to `.mdc`; the nested
|
|
200
|
+
* `rules/eager/` and `rules/reference/` subdirs are removed afterward.
|
|
201
|
+
*
|
|
202
|
+
* @param {string} outDir Cursor variant output directory.
|
|
203
|
+
*/
|
|
204
|
+
function transformRules(outDir) {
|
|
205
|
+
const rulesDir = path.join(outDir, "rules");
|
|
206
|
+
if (!fs.existsSync(rulesDir)) return;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Write one `.mdc` rule with frontmatter, rewriting intra-rule links.
|
|
210
|
+
*
|
|
211
|
+
* @param {string} srcFile Absolute path of the source `.md` rule.
|
|
212
|
+
* @param {string} slug Output rule slug (filename without extension).
|
|
213
|
+
* @param {boolean} alwaysApply Frontmatter `alwaysApply` value.
|
|
214
|
+
*/
|
|
215
|
+
const writeMdc = (srcFile, slug, alwaysApply) => {
|
|
216
|
+
const body = fs.readFileSync(srcFile, "utf8");
|
|
217
|
+
const frontmatter = `---\ndescription: ${yamlQuote(
|
|
218
|
+
deriveRuleDescription(body, slug)
|
|
219
|
+
)}\nalwaysApply: ${alwaysApply}\n---\n\n`;
|
|
220
|
+
fs.writeFileSync(
|
|
221
|
+
path.join(rulesDir, `${slug}.mdc`),
|
|
222
|
+
frontmatter + rewriteRuleLinks(body)
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// Eager / reference subdir layout (base plugin).
|
|
227
|
+
const tiers = [
|
|
228
|
+
{ sub: "eager", alwaysApply: true, suffix: "" },
|
|
229
|
+
{ sub: "reference", alwaysApply: false, suffix: "-reference" },
|
|
230
|
+
];
|
|
231
|
+
for (const { sub, alwaysApply, suffix } of tiers) {
|
|
232
|
+
const subDir = path.join(rulesDir, sub);
|
|
233
|
+
if (!fs.existsSync(subDir)) continue;
|
|
234
|
+
for (const entry of fs.readdirSync(subDir)) {
|
|
235
|
+
if (!entry.endsWith(".md")) continue;
|
|
236
|
+
const slug = entry.slice(0, -".md".length);
|
|
237
|
+
writeMdc(path.join(subDir, entry), `${slug}${suffix}`, alwaysApply);
|
|
238
|
+
}
|
|
239
|
+
fs.rmSync(subDir, { recursive: true, force: true });
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Top-level stack rules (e.g. lisa-rails, lisa-harper-fabric): single
|
|
243
|
+
// always-on `.md` → `.mdc` with alwaysApply:true.
|
|
244
|
+
for (const entry of fs.readdirSync(rulesDir)) {
|
|
245
|
+
if (!entry.endsWith(".md")) continue;
|
|
246
|
+
const srcFile = path.join(rulesDir, entry);
|
|
247
|
+
if (!fs.statSync(srcFile).isFile()) continue;
|
|
248
|
+
writeMdc(srcFile, entry.slice(0, -".md".length), true);
|
|
249
|
+
fs.rmSync(srcFile);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Rename a copied `.mcp.json` to Cursor's auto-discovered `mcp.json`.
|
|
255
|
+
*
|
|
256
|
+
* @param {string} outDir Cursor variant output directory.
|
|
257
|
+
*/
|
|
258
|
+
function renameMcpFile(outDir) {
|
|
259
|
+
const dotMcp = path.join(outDir, ".mcp.json");
|
|
260
|
+
if (fs.existsSync(dotMcp)) {
|
|
261
|
+
fs.renameSync(dotMcp, path.join(outDir, "mcp.json"));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
85
265
|
/**
|
|
86
266
|
* Generate the Cursor variant.
|
|
87
267
|
*
|
|
@@ -105,11 +285,10 @@ export function generateCursorVariant(srcDir, outDir, version) {
|
|
|
105
285
|
// Drop the `.codex-plugin/` directory — Cursor does not consume it.
|
|
106
286
|
if (relPath.startsWith(".codex-plugin/") || relPath === ".codex-plugin")
|
|
107
287
|
return false;
|
|
108
|
-
//
|
|
109
|
-
// hooks
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// The surviving .sh scripts in hooks/ are kept below.
|
|
288
|
+
// Drop any source-provided hooks/hooks.json (e.g. a Codex-shaped leak from a
|
|
289
|
+
// regression; the base build emits Codex hooks under .codex-plugin/, stripped
|
|
290
|
+
// above). We emit our OWN Cursor-shaped hooks/hooks.json below from the
|
|
291
|
+
// manifest's hook block, so any copied one would be wrong.
|
|
113
292
|
if (relPath === path.join("hooks", "hooks.json")) return false;
|
|
114
293
|
// Drop Codex-specific per-skill openai.yaml artifacts — Cursor does not use them.
|
|
115
294
|
// These live at skills/<n>/agents/openai.yaml and are generated by the Codex
|
|
@@ -135,18 +314,31 @@ export function generateCursorVariant(srcDir, outDir, version) {
|
|
|
135
314
|
}
|
|
136
315
|
}
|
|
137
316
|
|
|
138
|
-
//
|
|
317
|
+
// 1b. Flatten the nested rules/ tree into Cursor-native rules/*.mdc files.
|
|
318
|
+
transformRules(outDir);
|
|
319
|
+
|
|
320
|
+
// 1c. Rename .mcp.json → mcp.json (Cursor auto-discovers the un-dotted name).
|
|
321
|
+
renameMcpFile(outDir);
|
|
322
|
+
|
|
323
|
+
// 2. Read the manifest, stamp the version, and strip the inline hook block.
|
|
324
|
+
// Cursor reads hooks from hooks/hooks.json (emitted in 2a), never inline.
|
|
139
325
|
const manifestPath = path.join(outDir, ".claude-plugin", "plugin.json");
|
|
140
326
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
141
327
|
manifest.version = version;
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
manifest.hooks = filteredHooks;
|
|
145
|
-
} else {
|
|
146
|
-
delete manifest.hooks;
|
|
147
|
-
}
|
|
328
|
+
const cursorHooks = buildCursorHooksJson(manifest.hooks ?? {});
|
|
329
|
+
delete manifest.hooks;
|
|
148
330
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
149
331
|
|
|
332
|
+
// 2a. Emit hooks/hooks.json in Cursor's native shape when any hooks survive.
|
|
333
|
+
if (cursorHooks) {
|
|
334
|
+
const cursorHooksDir = path.join(outDir, "hooks");
|
|
335
|
+
fs.mkdirSync(cursorHooksDir, { recursive: true });
|
|
336
|
+
fs.writeFileSync(
|
|
337
|
+
path.join(cursorHooksDir, "hooks.json"),
|
|
338
|
+
JSON.stringify(cursorHooks, null, 2) + "\n"
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
150
342
|
// 3. Filter the hooks/ directory to match the script ship-list.
|
|
151
343
|
const hooksDir = path.join(outDir, "hooks");
|
|
152
344
|
if (fs.existsSync(hooksDir)) {
|