bitcompass 0.2.7 → 0.2.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.
@@ -1,4 +1,6 @@
1
1
  export declare const runRulesSearch: (query?: string) => Promise<void>;
2
2
  export declare const runRulesList: () => Promise<void>;
3
- export declare const runRulesPull: (id?: string) => Promise<void>;
3
+ export declare const runRulesPull: (id?: string, options?: {
4
+ global?: boolean;
5
+ }) => Promise<void>;
4
6
  export declare const runRulesPush: (file?: string) => Promise<void>;
@@ -3,9 +3,11 @@ import ora from 'ora';
3
3
  import chalk from 'chalk';
4
4
  import { mkdirSync, writeFileSync } from 'fs';
5
5
  import { join } from 'path';
6
+ import { homedir } from 'os';
6
7
  import { loadCredentials } from '../auth/config.js';
7
8
  import { getProjectConfig } from '../auth/project-config.js';
8
9
  import { searchRules, fetchRules, getRuleById, insertRule } from '../api/client.js';
10
+ import { ruleFilename } from '../lib/slug.js';
9
11
  export const runRulesSearch = async (query) => {
10
12
  if (!loadCredentials()) {
11
13
  console.error(chalk.red('Not logged in. Run bitcompass login.'));
@@ -45,7 +47,7 @@ export const runRulesList = async () => {
45
47
  if (list.length === 0)
46
48
  console.log(chalk.yellow('No rules yet.'));
47
49
  };
48
- export const runRulesPull = async (id) => {
50
+ export const runRulesPull = async (id, options) => {
49
51
  if (!loadCredentials()) {
50
52
  console.error(chalk.red('Not logged in. Run bitcompass login.'));
51
53
  process.exit(1);
@@ -69,13 +71,24 @@ export const runRulesPull = async (id) => {
69
71
  console.error(chalk.red('Rule not found.'));
70
72
  process.exit(1);
71
73
  }
72
- const { outputPath } = getProjectConfig({ warnIfMissing: true });
73
- const outDir = join(process.cwd(), outputPath);
74
+ let outDir;
75
+ if (options?.global) {
76
+ // Use global location: ~/.cursor/rules/
77
+ outDir = join(homedir(), '.cursor', 'rules');
78
+ }
79
+ else {
80
+ // Use project config (default behavior)
81
+ const { outputPath } = getProjectConfig({ warnIfMissing: true });
82
+ outDir = join(process.cwd(), outputPath);
83
+ }
74
84
  mkdirSync(outDir, { recursive: true });
75
- const filename = join(outDir, `rule-${rule.id}.md`);
85
+ const filename = join(outDir, ruleFilename(rule.title, rule.id));
76
86
  const content = `# ${rule.title}\n\n${rule.description}\n\n${rule.body}\n`;
77
87
  writeFileSync(filename, content);
78
88
  console.log(chalk.green('Wrote'), filename);
89
+ if (options?.global) {
90
+ console.log(chalk.dim('Installed globally for all projects'));
91
+ }
79
92
  };
80
93
  export const runRulesPush = async (file) => {
81
94
  if (!loadCredentials()) {
@@ -1,3 +1,5 @@
1
1
  export declare const runSolutionsSearch: (query?: string) => Promise<void>;
2
- export declare const runSolutionsPull: (id?: string) => Promise<void>;
2
+ export declare const runSolutionsPull: (id?: string, options?: {
3
+ global?: boolean;
4
+ }) => Promise<void>;
3
5
  export declare const runSolutionsPush: (file?: string) => Promise<void>;
@@ -3,9 +3,11 @@ import ora from 'ora';
3
3
  import chalk from 'chalk';
4
4
  import { mkdirSync, writeFileSync } from 'fs';
5
5
  import { join } from 'path';
6
+ import { homedir } from 'os';
6
7
  import { loadCredentials } from '../auth/config.js';
7
8
  import { getProjectConfig } from '../auth/project-config.js';
8
9
  import { searchRules, fetchRules, getRuleById, insertRule } from '../api/client.js';
10
+ import { solutionFilename } from '../lib/slug.js';
9
11
  export const runSolutionsSearch = async (query) => {
10
12
  if (!loadCredentials()) {
11
13
  console.error(chalk.red('Not logged in. Run bitcompass login.'));
@@ -33,7 +35,7 @@ export const runSolutionsSearch = async (query) => {
33
35
  console.log(rule.body);
34
36
  }
35
37
  };
36
- export const runSolutionsPull = async (id) => {
38
+ export const runSolutionsPull = async (id, options) => {
37
39
  if (!loadCredentials()) {
38
40
  console.error(chalk.red('Not logged in. Run bitcompass login.'));
39
41
  process.exit(1);
@@ -57,13 +59,24 @@ export const runSolutionsPull = async (id) => {
57
59
  console.error(chalk.red('Solution not found.'));
58
60
  process.exit(1);
59
61
  }
60
- const { outputPath } = getProjectConfig({ warnIfMissing: true });
61
- const outDir = join(process.cwd(), outputPath);
62
+ let outDir;
63
+ if (options?.global) {
64
+ // Use global location: ~/.cursor/rules/
65
+ outDir = join(homedir(), '.cursor', 'rules');
66
+ }
67
+ else {
68
+ // Use project config (default behavior)
69
+ const { outputPath } = getProjectConfig({ warnIfMissing: true });
70
+ outDir = join(process.cwd(), outputPath);
71
+ }
62
72
  mkdirSync(outDir, { recursive: true });
63
- const filename = join(outDir, `solution-${rule.id}.md`);
73
+ const filename = join(outDir, solutionFilename(rule.title, rule.id));
64
74
  const content = `# ${rule.title}\n\n${rule.description}\n\n## Solution\n\n${rule.body}\n`;
65
75
  writeFileSync(filename, content);
66
76
  console.log(chalk.green('Wrote'), filename);
77
+ if (options?.global) {
78
+ console.log(chalk.dim('Installed globally for all projects'));
79
+ }
67
80
  };
68
81
  export const runSolutionsPush = async (file) => {
69
82
  if (!loadCredentials()) {
package/dist/index.js CHANGED
@@ -45,12 +45,20 @@ configCmd.command('get <key>').description('Get a config value').action((key) =>
45
45
  const rules = program.command('rules').description('Manage rules');
46
46
  rules.command('search [query]').description('Search rules').action((query) => runRulesSearch(query).catch(handleErr));
47
47
  rules.command('list').description('List rules').action(() => runRulesList().catch(handleErr));
48
- rules.command('pull [id]').description('Pull a rule by ID or choose from list').action((id) => runRulesPull(id).catch(handleErr));
48
+ rules
49
+ .command('pull [id]')
50
+ .description('Pull a rule by ID or choose from list')
51
+ .option('-g, --global', 'Install globally to ~/.cursor/rules/ for all projects')
52
+ .action((id, options) => runRulesPull(id, options).catch(handleErr));
49
53
  rules.command('push [file]').description('Push a rule (file or interactive)').action((file) => runRulesPush(file).catch(handleErr));
50
54
  // solutions
51
55
  const solutions = program.command('solutions').description('Manage solutions');
52
56
  solutions.command('search [query]').description('Search solutions').action((query) => runSolutionsSearch(query).catch(handleErr));
53
- solutions.command('pull [id]').description('Pull a solution by ID or choose from list').action((id) => runSolutionsPull(id).catch(handleErr));
57
+ solutions
58
+ .command('pull [id]')
59
+ .description('Pull a solution by ID or choose from list')
60
+ .option('-g, --global', 'Install globally to ~/.cursor/rules/ for all projects')
61
+ .action((id, options) => runSolutionsPull(id, options).catch(handleErr));
54
62
  solutions.command('push [file]').description('Push a solution (file or interactive)').action((file) => runSolutionsPush(file).catch(handleErr));
55
63
  // mcp
56
64
  const mcp = program.command('mcp').description('MCP server');
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Converts a rule/solution title to a standardized filename slug.
3
+ * e.g. "# Strava API Authentication Flow" -> "strava-api-authentication-flow"
4
+ */
5
+ export declare const titleToSlug: (title: string) => string;
6
+ /**
7
+ * Returns the rule filename (e.g. rule-strava-api-authentication-flow.md).
8
+ * Falls back to id if slug is empty.
9
+ */
10
+ export declare const ruleFilename: (title: string, id: string) => string;
11
+ /**
12
+ * Returns the solution filename (e.g. solution-strava-api-authentication-flow.md).
13
+ * Falls back to id if slug is empty.
14
+ */
15
+ export declare const solutionFilename: (title: string, id: string) => string;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Converts a rule/solution title to a standardized filename slug.
3
+ * e.g. "# Strava API Authentication Flow" -> "strava-api-authentication-flow"
4
+ */
5
+ export const titleToSlug = (title) => {
6
+ const trimmed = (title ?? '').trim().replace(/^#\s*/, '');
7
+ if (!trimmed)
8
+ return '';
9
+ return trimmed
10
+ .toLowerCase()
11
+ .replace(/\s+/g, '-')
12
+ .replace(/[^a-z0-9-]/g, '')
13
+ .replace(/-+/g, '-')
14
+ .replace(/^-|-$/g, '');
15
+ };
16
+ /**
17
+ * Returns the rule filename (e.g. rule-strava-api-authentication-flow.md).
18
+ * Falls back to id if slug is empty.
19
+ */
20
+ export const ruleFilename = (title, id) => {
21
+ const slug = titleToSlug(title);
22
+ const base = slug ? `rule-${slug}` : `rule-${id}`;
23
+ return `${base}.md`;
24
+ };
25
+ /**
26
+ * Returns the solution filename (e.g. solution-strava-api-authentication-flow.md).
27
+ * Falls back to id if slug is empty.
28
+ */
29
+ export const solutionFilename = (title, id) => {
30
+ const slug = titleToSlug(title);
31
+ const base = slug ? `solution-${slug}` : `solution-${id}`;
32
+ return `${base}.md`;
33
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitcompass",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "BitCompass CLI - rules, solutions, and MCP server",
5
5
  "type": "module",
6
6
  "bin": {