bitcompass 0.3.7 → 0.3.9
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/dist/commands/login.js +48 -3
- package/dist/commands/rules.js +18 -3
- package/dist/lib/mdc-format.d.ts +16 -0
- package/dist/lib/mdc-format.js +66 -0
- package/dist/lib/rule-cache.d.ts +2 -2
- package/dist/lib/rule-cache.js +14 -7
- package/dist/lib/rule-file-ops.js +7 -17
- package/dist/lib/slug.d.ts +1 -1
- package/dist/lib/slug.js +2 -2
- package/dist/mcp/server.js +9 -1
- package/dist/types.d.ts +6 -0
- package/package.json +1 -1
package/dist/commands/login.js
CHANGED
|
@@ -6,6 +6,10 @@ import ora from 'ora';
|
|
|
6
6
|
import { getTokenFilePath, loadConfig, saveCredentials } from '../auth/config.js';
|
|
7
7
|
import { DEFAULT_SUPABASE_ANON_KEY, DEFAULT_SUPABASE_URL } from '../auth/defaults.js';
|
|
8
8
|
const CALLBACK_PORT = 38473;
|
|
9
|
+
/** Hardcoded Compass rules URL: prod or local testing. Used for "See rules on Compass" link after login. */
|
|
10
|
+
const COMPASS_RULES_URL_PROD = 'https://bitcompass.vercel.app/rules';
|
|
11
|
+
const COMPASS_RULES_URL_DEV = 'http://localhost:8080/rules';
|
|
12
|
+
const getCompassRulesUrl = () => process.env.NODE_ENV === 'development' ? COMPASS_RULES_URL_DEV : COMPASS_RULES_URL_PROD;
|
|
9
13
|
/** Design tokens matching src/index.css (light theme) */
|
|
10
14
|
const STYLES = {
|
|
11
15
|
background: 'hsl(0, 0%, 98%)',
|
|
@@ -18,7 +22,25 @@ const STYLES = {
|
|
|
18
22
|
radius: '0.625rem',
|
|
19
23
|
shadow: '0 10px 15px -3px hsl(220 13% 11% / 0.08), 0 4px 6px -4px hsl(220 13% 11% / 0.05)',
|
|
20
24
|
};
|
|
21
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Builds the Compass rules URL, optionally with auth hash so the web app can set the session
|
|
27
|
+
* without asking the user to log in again (use-auth.ts reads #access_token and #refresh_token).
|
|
28
|
+
*/
|
|
29
|
+
const compassRulesUrlWithSession = (baseUrl, session) => {
|
|
30
|
+
if (!session?.access_token || !session?.refresh_token)
|
|
31
|
+
return baseUrl;
|
|
32
|
+
const hash = `access_token=${encodeURIComponent(session.access_token)}&refresh_token=${encodeURIComponent(session.refresh_token)}`;
|
|
33
|
+
return `${baseUrl}#${hash}`;
|
|
34
|
+
};
|
|
35
|
+
/** Builds the post-login success (recap) page HTML with Compass rules CTA link. Session in URL hash persists login on the website. */
|
|
36
|
+
const buildCallbackSuccessHtml = (compassRulesUrl, session) => {
|
|
37
|
+
const href = compassRulesUrlWithSession(compassRulesUrl, session);
|
|
38
|
+
const compassCtaBlock = `
|
|
39
|
+
<div class="compass-cta-block">
|
|
40
|
+
<a href="${escapeHtml(href)}" class="compass-cta" target="_blank" rel="noopener noreferrer">See Available Rules on Compass</a>
|
|
41
|
+
<p class="compass-cta-hint">Opens in the same browser; you’ll be signed in automatically.</p>
|
|
42
|
+
</div>`;
|
|
43
|
+
return `<!DOCTYPE html>
|
|
22
44
|
<html lang="en">
|
|
23
45
|
<head>
|
|
24
46
|
<meta charset="UTF-8" />
|
|
@@ -119,6 +141,28 @@ const CALLBACK_SUCCESS_HTML = `<!DOCTYPE html>
|
|
|
119
141
|
}
|
|
120
142
|
.copy-btn:hover { opacity: 0.9; }
|
|
121
143
|
.copy-btn.copied { background: ${STYLES.muted}; cursor: default; }
|
|
144
|
+
.compass-cta-block {
|
|
145
|
+
margin-top: 1.25rem;
|
|
146
|
+
padding-top: 1.25rem;
|
|
147
|
+
border-top: 1px solid ${STYLES.border};
|
|
148
|
+
}
|
|
149
|
+
.compass-cta {
|
|
150
|
+
display: inline-block;
|
|
151
|
+
padding: 0.5rem 1rem;
|
|
152
|
+
background: ${STYLES.primary};
|
|
153
|
+
color: ${STYLES.primaryForeground};
|
|
154
|
+
font-size: 0.875rem;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
text-decoration: none;
|
|
157
|
+
border-radius: 0.5rem;
|
|
158
|
+
transition: opacity 0.15s;
|
|
159
|
+
}
|
|
160
|
+
.compass-cta:hover { opacity: 0.9; }
|
|
161
|
+
.compass-cta-hint {
|
|
162
|
+
margin: 0.5rem 0 0;
|
|
163
|
+
font-size: 0.75rem;
|
|
164
|
+
color: ${STYLES.muted};
|
|
165
|
+
}
|
|
122
166
|
</style>
|
|
123
167
|
</head>
|
|
124
168
|
<body>
|
|
@@ -131,7 +175,7 @@ const CALLBACK_SUCCESS_HTML = `<!DOCTYPE html>
|
|
|
131
175
|
</div>
|
|
132
176
|
<h1>You're all set</h1>
|
|
133
177
|
<p class="muted">You're logged in successfully. You can close this window safely—your credentials are saved and the CLI is ready to use.</p>
|
|
134
|
-
<p class="hint">Return to your terminal to continue.</p
|
|
178
|
+
<p class="hint">Return to your terminal to continue.</p>${compassCtaBlock}
|
|
135
179
|
<div class="verify-block">
|
|
136
180
|
<p class="muted" style="margin:0">Verify in terminal:</p>
|
|
137
181
|
<div class="cmd-row">
|
|
@@ -159,6 +203,7 @@ const CALLBACK_SUCCESS_HTML = `<!DOCTYPE html>
|
|
|
159
203
|
</script>
|
|
160
204
|
</body>
|
|
161
205
|
</html>`;
|
|
206
|
+
};
|
|
162
207
|
const escapeHtml = (s) => s
|
|
163
208
|
.replace(/&/g, '&')
|
|
164
209
|
.replace(/</g, '<')
|
|
@@ -301,7 +346,7 @@ export const runLogin = async () => {
|
|
|
301
346
|
const tokenPath = getTokenFilePath();
|
|
302
347
|
saveCredentials(creds);
|
|
303
348
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
304
|
-
res.end(
|
|
349
|
+
res.end(buildCallbackSuccessHtml(getCompassRulesUrl(), session));
|
|
305
350
|
spinner.succeed(chalk.green('Logged in successfully.'));
|
|
306
351
|
console.log(chalk.dim('Credentials saved to:'), tokenPath);
|
|
307
352
|
server.close();
|
package/dist/commands/rules.js
CHANGED
|
@@ -4,6 +4,7 @@ import chalk from 'chalk';
|
|
|
4
4
|
import { loadCredentials } from '../auth/config.js';
|
|
5
5
|
import { searchRules, fetchRules, getRuleById, insertRule } from '../api/client.js';
|
|
6
6
|
import { pullRuleToFile } from '../lib/rule-file-ops.js';
|
|
7
|
+
import { parseRuleMdcContent } from '../lib/mdc-format.js';
|
|
7
8
|
import { formatList, shouldUseTable } from '../lib/list-format.js';
|
|
8
9
|
export const runRulesSearch = async (query, options) => {
|
|
9
10
|
if (!loadCredentials()) {
|
|
@@ -108,9 +109,23 @@ export const runRulesPush = async (file) => {
|
|
|
108
109
|
payload = JSON.parse(raw);
|
|
109
110
|
}
|
|
110
111
|
catch {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
const parsed = parseRuleMdcContent(raw);
|
|
113
|
+
if (parsed) {
|
|
114
|
+
const titleFromBody = parsed.body.split('\n')[0]?.replace(/^#\s*/, '').trim() || 'Untitled';
|
|
115
|
+
payload = {
|
|
116
|
+
kind: 'rule',
|
|
117
|
+
title: titleFromBody,
|
|
118
|
+
description: parsed.description,
|
|
119
|
+
body: parsed.body,
|
|
120
|
+
globs: parsed.globs ?? undefined,
|
|
121
|
+
always_apply: parsed.alwaysApply,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const lines = raw.split('\n');
|
|
126
|
+
const title = lines[0].replace(/^#\s*/, '') || 'Untitled';
|
|
127
|
+
payload = { kind: 'rule', title, description: '', body: raw };
|
|
128
|
+
}
|
|
114
129
|
}
|
|
115
130
|
}
|
|
116
131
|
else {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Rule } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds Cursor .mdc content for a rule: YAML frontmatter (description, globs, alwaysApply) then body.
|
|
4
|
+
*/
|
|
5
|
+
export declare const buildRuleMdcContent: (rule: Rule) => string;
|
|
6
|
+
export interface ParsedMdcFrontmatter {
|
|
7
|
+
description: string;
|
|
8
|
+
globs?: string;
|
|
9
|
+
alwaysApply: boolean;
|
|
10
|
+
body: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parses .mdc content: frontmatter (description, globs, alwaysApply) and body.
|
|
14
|
+
* Returns null if the file does not start with --- (not frontmatter format).
|
|
15
|
+
*/
|
|
16
|
+
export declare const parseRuleMdcContent: (raw: string) => ParsedMdcFrontmatter | null;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const FRONTMATTER_DELIM = '---';
|
|
2
|
+
/**
|
|
3
|
+
* Builds Cursor .mdc content for a rule: YAML frontmatter (description, globs, alwaysApply) then body.
|
|
4
|
+
*/
|
|
5
|
+
export const buildRuleMdcContent = (rule) => {
|
|
6
|
+
const lines = [FRONTMATTER_DELIM];
|
|
7
|
+
lines.push(`description: ${escapeYamlValue(rule.description ?? '')}`);
|
|
8
|
+
if (rule.globs != null && String(rule.globs).trim() !== '') {
|
|
9
|
+
lines.push(`globs: ${escapeYamlValue(String(rule.globs).trim())}`);
|
|
10
|
+
}
|
|
11
|
+
lines.push(`alwaysApply: ${rule.always_apply === true}`);
|
|
12
|
+
lines.push(FRONTMATTER_DELIM);
|
|
13
|
+
lines.push('');
|
|
14
|
+
lines.push(rule.body.trimEnd());
|
|
15
|
+
if (!rule.body.endsWith('\n')) {
|
|
16
|
+
lines.push('');
|
|
17
|
+
}
|
|
18
|
+
return lines.join('\n');
|
|
19
|
+
};
|
|
20
|
+
const escapeYamlValue = (s) => {
|
|
21
|
+
if (/^[a-z0-9-]+$/i.test(s) && !s.includes(':'))
|
|
22
|
+
return s;
|
|
23
|
+
return `"${s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Parses .mdc content: frontmatter (description, globs, alwaysApply) and body.
|
|
27
|
+
* Returns null if the file does not start with --- (not frontmatter format).
|
|
28
|
+
*/
|
|
29
|
+
export const parseRuleMdcContent = (raw) => {
|
|
30
|
+
const trimmed = raw.trimStart();
|
|
31
|
+
if (!trimmed.startsWith(FRONTMATTER_DELIM)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const rest = trimmed.slice(FRONTMATTER_DELIM.length);
|
|
35
|
+
const endIdx = rest.indexOf('\n' + FRONTMATTER_DELIM);
|
|
36
|
+
if (endIdx === -1) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const frontmatterBlock = rest.slice(0, endIdx).trim();
|
|
40
|
+
const body = rest.slice(endIdx + FRONTMATTER_DELIM.length + 1).trimStart();
|
|
41
|
+
let description = '';
|
|
42
|
+
let globs;
|
|
43
|
+
let alwaysApply = false;
|
|
44
|
+
for (const line of frontmatterBlock.split('\n')) {
|
|
45
|
+
const colonIdx = line.indexOf(':');
|
|
46
|
+
if (colonIdx === -1)
|
|
47
|
+
continue;
|
|
48
|
+
const key = line.slice(0, colonIdx).trim();
|
|
49
|
+
let value = line.slice(colonIdx + 1).trim();
|
|
50
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
51
|
+
value = value.slice(1, -1).replace(/\\n/g, '\n').replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
52
|
+
}
|
|
53
|
+
switch (key) {
|
|
54
|
+
case 'description':
|
|
55
|
+
description = value;
|
|
56
|
+
break;
|
|
57
|
+
case 'globs':
|
|
58
|
+
globs = value;
|
|
59
|
+
break;
|
|
60
|
+
case 'alwaysApply':
|
|
61
|
+
alwaysApply = value === 'true' || value === '1';
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { description, globs, alwaysApply, body };
|
|
66
|
+
};
|
package/dist/lib/rule-cache.d.ts
CHANGED
|
@@ -4,11 +4,11 @@ import type { Rule } from '../types.js';
|
|
|
4
4
|
*/
|
|
5
5
|
export declare const getCacheDir: () => string;
|
|
6
6
|
/**
|
|
7
|
-
* Gets the cached file path for a rule by ID
|
|
7
|
+
* Gets the cached file path for a rule by ID (rules use .mdc, others use .md)
|
|
8
8
|
*/
|
|
9
9
|
export declare const getCachedRulePath: (rule: Rule) => string;
|
|
10
10
|
/**
|
|
11
11
|
* Ensures a rule is cached. Downloads and caches it if not present or outdated.
|
|
12
|
-
* Returns the path to the cached file.
|
|
12
|
+
* Returns the path to the cached file. Rules are stored as .mdc with Cursor frontmatter.
|
|
13
13
|
*/
|
|
14
14
|
export declare const ensureRuleCached: (id: string) => Promise<string>;
|
package/dist/lib/rule-cache.js
CHANGED
|
@@ -2,7 +2,8 @@ import { existsSync, mkdirSync, writeFileSync, statSync } from 'fs';
|
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { getConfigDir } from '../auth/config.js';
|
|
4
4
|
import { getRuleById } from '../api/client.js';
|
|
5
|
-
import { ruleFilename, solutionFilename } from './slug.js';
|
|
5
|
+
import { ruleFilename, solutionFilename, skillFilename, commandFilename } from './slug.js';
|
|
6
|
+
import { buildRuleMdcContent } from './mdc-format.js';
|
|
6
7
|
/**
|
|
7
8
|
* Gets the cache directory for rules (~/.bitcompass/cache/rules/)
|
|
8
9
|
*/
|
|
@@ -14,18 +15,22 @@ export const getCacheDir = () => {
|
|
|
14
15
|
return cacheDir;
|
|
15
16
|
};
|
|
16
17
|
/**
|
|
17
|
-
* Gets the cached file path for a rule by ID
|
|
18
|
+
* Gets the cached file path for a rule by ID (rules use .mdc, others use .md)
|
|
18
19
|
*/
|
|
19
20
|
export const getCachedRulePath = (rule) => {
|
|
20
21
|
const cacheDir = getCacheDir();
|
|
21
22
|
const filename = rule.kind === 'solution'
|
|
22
23
|
? solutionFilename(rule.title, rule.id)
|
|
23
|
-
:
|
|
24
|
+
: rule.kind === 'skill'
|
|
25
|
+
? skillFilename(rule.title, rule.id)
|
|
26
|
+
: rule.kind === 'command'
|
|
27
|
+
? commandFilename(rule.title, rule.id)
|
|
28
|
+
: ruleFilename(rule.title, rule.id);
|
|
24
29
|
return join(cacheDir, `${rule.id}-${filename}`);
|
|
25
30
|
};
|
|
26
31
|
/**
|
|
27
32
|
* Ensures a rule is cached. Downloads and caches it if not present or outdated.
|
|
28
|
-
* Returns the path to the cached file.
|
|
33
|
+
* Returns the path to the cached file. Rules are stored as .mdc with Cursor frontmatter.
|
|
29
34
|
*/
|
|
30
35
|
export const ensureRuleCached = async (id) => {
|
|
31
36
|
const rule = await getRuleById(id);
|
|
@@ -35,9 +40,11 @@ export const ensureRuleCached = async (id) => {
|
|
|
35
40
|
const cachedPath = getCachedRulePath(rule);
|
|
36
41
|
const needsUpdate = !existsSync(cachedPath) || isCacheOutdated(cachedPath, rule);
|
|
37
42
|
if (needsUpdate) {
|
|
38
|
-
const content = rule.kind === '
|
|
39
|
-
?
|
|
40
|
-
:
|
|
43
|
+
const content = rule.kind === 'rule'
|
|
44
|
+
? buildRuleMdcContent(rule)
|
|
45
|
+
: rule.kind === 'solution'
|
|
46
|
+
? `# ${rule.title}\n\n${rule.description}\n\n## Solution\n\n${rule.body}\n`
|
|
47
|
+
: `# ${rule.title}\n\n${rule.description}\n\n${rule.body}\n`;
|
|
41
48
|
writeFileSync(cachedPath, content, 'utf-8');
|
|
42
49
|
}
|
|
43
50
|
return cachedPath;
|
|
@@ -5,6 +5,7 @@ import { getRuleById } from '../api/client.js';
|
|
|
5
5
|
import { getProjectConfig } from '../auth/project-config.js';
|
|
6
6
|
import { ruleFilename, solutionFilename, skillFilename, commandFilename } from './slug.js';
|
|
7
7
|
import { ensureRuleCached } from './rule-cache.js';
|
|
8
|
+
import { buildRuleMdcContent } from './mdc-format.js';
|
|
8
9
|
/**
|
|
9
10
|
* Pulls a rule or solution to a file using symbolic links (like Bun init).
|
|
10
11
|
* Returns the file path where it was written/linked.
|
|
@@ -79,23 +80,12 @@ export const pullRuleToFile = async (id, options = {}) => {
|
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
else {
|
|
82
|
-
// Fallback: copy file content (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
case 'skill':
|
|
89
|
-
content = `# ${rule.title}\n\n${rule.description}\n\n${rule.body}\n`;
|
|
90
|
-
break;
|
|
91
|
-
case 'command':
|
|
92
|
-
content = `# ${rule.title}\n\n${rule.description}\n\n${rule.body}\n`;
|
|
93
|
-
break;
|
|
94
|
-
case 'rule':
|
|
95
|
-
default:
|
|
96
|
-
content = `# ${rule.title}\n\n${rule.description}\n\n${rule.body}\n`;
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
83
|
+
// Fallback: copy file content (rules as .mdc with frontmatter, others as .md)
|
|
84
|
+
const content = rule.kind === 'rule'
|
|
85
|
+
? buildRuleMdcContent(rule)
|
|
86
|
+
: rule.kind === 'solution'
|
|
87
|
+
? `# ${rule.title}\n\n${rule.description}\n\n## Solution\n\n${rule.body}\n`
|
|
88
|
+
: `# ${rule.title}\n\n${rule.description}\n\n${rule.body}\n`;
|
|
99
89
|
writeFileSync(filename, content);
|
|
100
90
|
}
|
|
101
91
|
return filename;
|
package/dist/lib/slug.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export declare const titleToSlug: (title: string) => string;
|
|
6
6
|
/**
|
|
7
|
-
* Returns the rule filename (e.g. rule-strava-api-authentication-flow.
|
|
7
|
+
* Returns the rule filename in Cursor .mdc format (e.g. rule-strava-api-authentication-flow.mdc).
|
|
8
8
|
* Falls back to id if slug is empty.
|
|
9
9
|
*/
|
|
10
10
|
export declare const ruleFilename: (title: string, id: string) => string;
|
package/dist/lib/slug.js
CHANGED
|
@@ -14,13 +14,13 @@ export const titleToSlug = (title) => {
|
|
|
14
14
|
.replace(/^-|-$/g, '');
|
|
15
15
|
};
|
|
16
16
|
/**
|
|
17
|
-
* Returns the rule filename (e.g. rule-strava-api-authentication-flow.
|
|
17
|
+
* Returns the rule filename in Cursor .mdc format (e.g. rule-strava-api-authentication-flow.mdc).
|
|
18
18
|
* Falls back to id if slug is empty.
|
|
19
19
|
*/
|
|
20
20
|
export const ruleFilename = (title, id) => {
|
|
21
21
|
const slug = titleToSlug(title);
|
|
22
22
|
const base = slug ? `rule-${slug}` : `rule-${id}`;
|
|
23
|
-
return `${base}.
|
|
23
|
+
return `${base}.mdc`;
|
|
24
24
|
};
|
|
25
25
|
/**
|
|
26
26
|
* Returns the solution filename (e.g. solution-strava-api-authentication-flow.md).
|
package/dist/mcp/server.js
CHANGED
|
@@ -139,7 +139,7 @@ function createStdioServer() {
|
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
name: 'update-rule',
|
|
142
|
-
description: 'Use when the user wants to edit an existing rule or solution they own. Pass id and any fields to update (title, description, body, context, examples, technologies). Returns updated metadata. Requires authentication.',
|
|
142
|
+
description: 'Use when the user wants to edit an existing rule or solution they own. Pass id and any fields to update (title, description, body, context, examples, technologies, globs, always_apply). Returns updated metadata. Requires authentication.',
|
|
143
143
|
inputSchema: {
|
|
144
144
|
type: 'object',
|
|
145
145
|
properties: {
|
|
@@ -150,6 +150,8 @@ function createStdioServer() {
|
|
|
150
150
|
context: { type: 'string', description: 'Updated context' },
|
|
151
151
|
examples: { type: 'array', items: { type: 'string' }, description: 'Updated examples array' },
|
|
152
152
|
technologies: { type: 'array', items: { type: 'string' }, description: 'Updated technologies array' },
|
|
153
|
+
globs: { type: 'string', description: 'Glob patterns for when rule applies (e.g. "*.ts, *.tsx")' },
|
|
154
|
+
always_apply: { type: 'boolean', description: 'If true, Cursor applies this rule globally' },
|
|
153
155
|
},
|
|
154
156
|
required: ['id'],
|
|
155
157
|
},
|
|
@@ -399,6 +401,8 @@ function createStdioServer() {
|
|
|
399
401
|
context: rule.context ?? null,
|
|
400
402
|
examples: rule.examples ?? [],
|
|
401
403
|
technologies: rule.technologies ?? [],
|
|
404
|
+
globs: rule.globs ?? null,
|
|
405
|
+
always_apply: rule.always_apply ?? false,
|
|
402
406
|
author: rule.author_display_name ?? null,
|
|
403
407
|
created_at: rule.created_at,
|
|
404
408
|
updated_at: rule.updated_at,
|
|
@@ -459,6 +463,10 @@ function createStdioServer() {
|
|
|
459
463
|
updates.examples = args.examples;
|
|
460
464
|
if (Array.isArray(args.technologies))
|
|
461
465
|
updates.technologies = args.technologies;
|
|
466
|
+
if (args.globs !== undefined)
|
|
467
|
+
updates.globs = args.globs || null;
|
|
468
|
+
if (args.always_apply !== undefined)
|
|
469
|
+
updates.always_apply = Boolean(args.always_apply);
|
|
462
470
|
try {
|
|
463
471
|
const updated = await updateRule(id, updates);
|
|
464
472
|
return {
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,10 @@ export interface Rule {
|
|
|
11
11
|
user_id: string;
|
|
12
12
|
author_display_name?: string | null;
|
|
13
13
|
version?: string | null;
|
|
14
|
+
/** Optional glob patterns for when the rule applies (e.g. "*.ts, *.tsx"). Used in .mdc frontmatter. */
|
|
15
|
+
globs?: string | null;
|
|
16
|
+
/** If true, Cursor applies this rule globally. Default false. Used in .mdc frontmatter. */
|
|
17
|
+
always_apply?: boolean;
|
|
14
18
|
created_at: string;
|
|
15
19
|
updated_at: string;
|
|
16
20
|
}
|
|
@@ -23,6 +27,8 @@ export interface RuleInsert {
|
|
|
23
27
|
examples?: string[];
|
|
24
28
|
technologies?: string[];
|
|
25
29
|
version?: string;
|
|
30
|
+
globs?: string | null;
|
|
31
|
+
always_apply?: boolean;
|
|
26
32
|
}
|
|
27
33
|
export interface StoredCredentials {
|
|
28
34
|
access_token: string;
|