bitcompass 0.2.9 → 0.3.1

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/README.md CHANGED
@@ -72,7 +72,26 @@ cd packages/bitcompass-cli && npm run build && bitcompass login
72
72
 
73
73
  **Manual (local path):** Settings → MCP → stdio, Command **node**, Args **path/to/packages/bitcompass-cli/dist/index.js** **mcp** **start**.
74
74
 
75
- Tools: `search-rules`, `search-solutions`, `post-rules`. Prompts: `share_new_rule`, `share_problem_solution`.
75
+ ### MCP Tools
76
+
77
+ **Rules & Solutions:**
78
+ - `search-rules` - Search rules by query (with optional kind filter)
79
+ - `search-solutions` - Search solutions by query
80
+ - `get-rule` - Get full rule/solution details by ID
81
+ - `list-rules` - List all rules/solutions (with optional kind filter and limit)
82
+ - `post-rules` - Create/publish a new rule or solution
83
+ - `update-rule` - Update an existing rule or solution
84
+ - `delete-rule` - Delete a rule or solution by ID
85
+ - `pull-rule` - Pull a rule/solution to a file in the project rules directory
86
+
87
+ **Activity Logs:**
88
+ - `create-activity-log` - Create activity log from git repo (day/week/month)
89
+ - `list-activity-logs` - List user's activity logs (with optional filters)
90
+ - `get-activity-log` - Get activity log details by ID
91
+
92
+ **Prompts:**
93
+ - `share_new_rule` - Guide to collect and publish a reusable rule
94
+ - `share_problem_solution` - Guide to collect and publish a problem solution
76
95
 
77
96
  ## Publish (maintainers)
78
97
 
@@ -1,5 +1,5 @@
1
1
  import { type SupabaseClient } from '@supabase/supabase-js';
2
- import type { ActivityLog, ActivityLogInsert, Rule, RuleInsert } from '../types.js';
2
+ import type { ActivityLog, ActivityLogInsert, Rule, RuleInsert, RuleKind } from '../types.js';
3
3
  /** Shown when MCP is used before logging in; instructs user to login and restart MCP. */
4
4
  export declare const AUTH_REQUIRED_MSG = "BitCompass needs authentication. Run `bitcompass login`, then restart the MCP server in your editor.";
5
5
  /** Shown when Supabase URL/key are not set; instructs config then login then restart MCP. */
@@ -8,9 +8,9 @@ export declare const NOT_CONFIGURED_MSG = "BitCompass is not configured. Run `bi
8
8
  export declare const getSupabaseClient: () => SupabaseClient | null;
9
9
  /** Client for public read-only (rules/solutions). Works without login when RLS allows public select. */
10
10
  export declare const getSupabaseClientForRead: () => SupabaseClient | null;
11
- export declare const fetchRules: (kind?: "rule" | "solution") => Promise<Rule[]>;
11
+ export declare const fetchRules: (kind?: RuleKind) => Promise<Rule[]>;
12
12
  export declare const searchRules: (queryText: string, options?: {
13
- kind?: "rule" | "solution";
13
+ kind?: RuleKind;
14
14
  limit?: number;
15
15
  }) => Promise<Rule[]>;
16
16
  export declare const getRuleById: (id: string) => Promise<Rule | null>;
@@ -18,3 +18,8 @@ export declare const insertRule: (rule: RuleInsert) => Promise<Rule>;
18
18
  export declare const updateRule: (id: string, updates: Partial<RuleInsert>) => Promise<Rule>;
19
19
  export declare const deleteRule: (id: string) => Promise<void>;
20
20
  export declare const insertActivityLog: (payload: ActivityLogInsert) => Promise<ActivityLog>;
21
+ export declare const fetchActivityLogs: (options?: {
22
+ limit?: number;
23
+ time_frame?: "day" | "week" | "month";
24
+ }) => Promise<ActivityLog[]>;
25
+ export declare const getActivityLogById: (id: string) => Promise<ActivityLog | null>;
@@ -135,3 +135,31 @@ export const insertActivityLog = async (payload) => {
135
135
  throw new Error(isAuthError(error) ? AUTH_REQUIRED_MSG : error.message);
136
136
  return data;
137
137
  };
138
+ export const fetchActivityLogs = async (options) => {
139
+ const client = getSupabaseClient();
140
+ if (!client)
141
+ throw new Error(NOT_CONFIGURED_MSG);
142
+ let query = client.from('activity_logs').select('*').order('created_at', { ascending: false });
143
+ if (options?.time_frame) {
144
+ query = query.eq('time_frame', options.time_frame);
145
+ }
146
+ if (options?.limit) {
147
+ query = query.limit(options.limit);
148
+ }
149
+ const { data, error } = await query;
150
+ if (error)
151
+ throw new Error(isAuthError(error) ? AUTH_REQUIRED_MSG : error.message);
152
+ return (data ?? []);
153
+ };
154
+ export const getActivityLogById = async (id) => {
155
+ const client = getSupabaseClient();
156
+ if (!client)
157
+ throw new Error(NOT_CONFIGURED_MSG);
158
+ const { data, error } = await client.from('activity_logs').select('*').eq('id', id).single();
159
+ if (error) {
160
+ if (error.code === 'PGRST116')
161
+ return null;
162
+ throw new Error(isAuthError(error) ? AUTH_REQUIRED_MSG : error.message);
163
+ }
164
+ return data;
165
+ };
@@ -1,8 +1,8 @@
1
- import inquirer from 'inquirer';
2
1
  import chalk from 'chalk';
3
- import { existsSync, readFileSync, writeFileSync } from 'fs';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
3
+ import inquirer from 'inquirer';
4
4
  import { join } from 'path';
5
- import { getEditorDefaultPath, loadProjectConfig, saveProjectConfig, getProjectConfigDir, } from '../auth/project-config.js';
5
+ import { getEditorDefaultPath, getProjectConfigDir, loadProjectConfig, saveProjectConfig, } from '../auth/project-config.js';
6
6
  const EDITOR_CHOICES = [
7
7
  { name: 'VSCode', value: 'vscode' },
8
8
  { name: 'Cursor', value: 'cursor' },
@@ -48,9 +48,158 @@ export const runInit = async () => {
48
48
  };
49
49
  saveProjectConfig(config);
50
50
  ensureGitignoreEntry();
51
+ // Create rule file about BitCompass MCP and CLI usage
52
+ const outputDir = join(process.cwd(), config.outputPath);
53
+ mkdirSync(outputDir, { recursive: true });
54
+ const rulePath = join(outputDir, 'rule-bitcompass-mcp-and-cli-usage.md');
55
+ const ruleContent = `# BitCompass MCP and CLI Usage
56
+
57
+ This project uses BitCompass for managing rules, solutions, and activity logs. LLMs should use the available MCP tools and CLI commands when appropriate.
58
+
59
+ ## MCP Tools (Available in AI Editor)
60
+
61
+ Use these tools directly from the AI editor when available:
62
+
63
+ ### search-rules
64
+ Search BitCompass rules by query.
65
+
66
+ **When to use:** When you need to find existing rules or patterns that might help solve a problem.
67
+
68
+ **Parameters:**
69
+ - \`query\` (required): Search query string
70
+ - \`kind\` (optional): Filter by 'rule' or 'solution'
71
+ - \`limit\` (optional): Maximum number of results (default: 20)
72
+
73
+ **Example:** Search for rules about "authentication" or "error handling"
74
+
75
+ ### search-solutions
76
+ Search BitCompass solutions by query.
77
+
78
+ **When to use:** When you need to find solutions to specific problems.
79
+
80
+ **Parameters:**
81
+ - \`query\` (required): Search query string
82
+ - \`limit\` (optional): Maximum number of results (default: 20)
83
+
84
+ **Example:** Search for solutions about "database connection issues"
85
+
86
+ ### post-rules
87
+ Publish a new rule or solution to BitCompass.
88
+
89
+ **When to use:** When you've created a reusable pattern, best practice, or solution that should be shared with the team.
90
+
91
+ **Parameters:**
92
+ - \`kind\` (required): 'rule' or 'solution'
93
+ - \`title\` (required): Title of the rule/solution
94
+ - \`body\` (required): Full content of the rule/solution
95
+ - \`description\` (optional): Short description
96
+ - \`context\` (optional): Additional context
97
+ - \`examples\` (optional): Array of example strings
98
+ - \`technologies\` (optional): Array of technology tags
99
+
100
+ **Example:** After implementing a new pattern, publish it as a rule so others can reuse it.
101
+
102
+ ### create-activity-log
103
+ Collect repository summary and git activity, then push to activity logs.
104
+
105
+ **When to use:** When the user asks to log their work or track activity for a specific time period.
106
+
107
+ **Parameters:**
108
+ - \`time_frame\` (required): 'day', 'week', or 'month'
109
+ - \`repo_path\` (optional): Path to git repo (defaults to current directory)
110
+
111
+ **Example:** User asks "log my work for this week" → call with time_frame: 'week'
112
+
113
+ ## CLI Commands (Run via Terminal)
114
+
115
+ Use these commands when you need to interact with BitCompass from the terminal:
116
+
117
+ ### Authentication
118
+ - \`bitcompass login\` - Log in with Google (opens browser)
119
+ - \`bitcompass logout\` - Remove stored credentials
120
+ - \`bitcompass whoami\` - Show current user (email)
121
+
122
+ **When to use:** When authentication is required or to verify login status.
123
+
124
+ ### Project Configuration
125
+ - \`bitcompass init\` - Configure project: editor/AI provider and output folder
126
+
127
+ **When to use:** Run once per project to set up BitCompass configuration.
128
+
129
+ ### Rules Management
130
+ - \`bitcompass rules search [query]\` - Search rules interactively
131
+ - \`bitcompass rules list\` - List all available rules
132
+ - \`bitcompass rules pull [id]\` - Pull a rule by ID (saves to project rules folder)
133
+ - Use \`--global\` flag to install globally to ~/.cursor/rules/
134
+ - \`bitcompass rules push [file]\` - Push a rule file to BitCompass
135
+
136
+ **When to use:**
137
+ - \`pull\`: When you want to download and use a specific rule in your project
138
+ - \`push\`: When you've created a rule file and want to share it
139
+ - \`search\`/\`list\`: When browsing available rules
140
+
141
+ ### Solutions Management
142
+ - \`bitcompass solutions search [query]\` - Search solutions interactively
143
+ - \`bitcompass solutions pull [id]\` - Pull a solution by ID
144
+ - Use \`--global\` flag to install globally
145
+ - \`bitcompass solutions push [file]\` - Push a solution file to BitCompass
146
+
147
+ **When to use:** Similar to rules, but for problem-solution pairs.
148
+
149
+ ### Activity Logs
150
+ - \`bitcompass log\` - Collect repo summary and git activity, push to activity logs
151
+ - \`bitcompass log YYYY-MM-DD\` - Log activity for a specific date
152
+ - \`bitcompass log YYYY-MM-DD YYYY-MM-DD\` - Log activity for a date range
153
+
154
+ **When to use:** When the user wants to track or log their development activity.
155
+
156
+ ### Configuration
157
+ - \`bitcompass config list\` - List all config values
158
+ - \`bitcompass config get <key>\` - Get a specific config value
159
+ - \`bitcompass config set <key> <value>\` - Set config value (supabaseUrl, supabaseAnonKey, apiUrl)
160
+
161
+ **When to use:** When you need to check or modify BitCompass configuration.
162
+
163
+ ### MCP Server
164
+ - \`bitcompass mcp start\` - Start MCP server (stdio mode for AI editors)
165
+ - \`bitcompass mcp status\` - Show MCP server status
166
+
167
+ **When to use:** Usually configured automatically by the AI editor. Use \`status\` to verify MCP is working.
168
+
169
+ ## Decision Guide
170
+
171
+ **Use MCP tools when:**
172
+ - You're working in the AI editor and need to search or publish rules/solutions
173
+ - The user asks you to search for existing patterns or solutions
174
+ - You've created something reusable and want to share it immediately
175
+ - The user wants to log their activity
176
+
177
+ **Use CLI commands when:**
178
+ - You need to authenticate or verify authentication
179
+ - The user explicitly asks to run a CLI command
180
+ - You need to pull rules/solutions to the project (for version control)
181
+ - You need to check or modify configuration
182
+ - The MCP tool is not available or returns an authentication error
183
+
184
+ ## Authentication Notes
185
+
186
+ - Most MCP tools require authentication (except search-rules and search-solutions)
187
+ - If you get an authentication error, instruct the user to run \`bitcompass login\` and restart the MCP server
188
+ - CLI commands that require auth will prompt the user to login
189
+
190
+ ## Best Practices
191
+
192
+ 1. **Search before creating:** Before publishing a new rule, search to see if something similar exists
193
+ 2. **Use descriptive titles:** When posting rules/solutions, use clear, searchable titles
194
+ 3. **Include context:** Add context, examples, and technologies when posting to make rules more discoverable
195
+ 4. **Pull for version control:** Use \`rules pull\` or \`solutions pull\` to add rules to your project's rules folder (tracked in git)
196
+ 5. **Global vs project:** Use \`--global\` flag when you want a rule available across all projects, otherwise use project-specific rules
197
+ `;
198
+ writeFileSync(rulePath, ruleContent, 'utf-8');
51
199
  console.log(chalk.green('Project configured.'));
52
200
  console.log(chalk.dim('Config:'), join(getProjectConfigDir(), 'config.json'));
53
201
  console.log(chalk.dim('Editor:'), config.editor);
54
202
  console.log(chalk.dim('Output path:'), config.outputPath);
55
203
  console.log(chalk.dim('.gitignore:'), GITIGNORE_ENTRY, 'added or already present.');
204
+ console.log(chalk.green('Rule created:'), rulePath);
56
205
  };
@@ -0,0 +1,11 @@
1
+ export interface PullRuleOptions {
2
+ /** Install globally to ~/.cursor/rules/ for all projects */
3
+ global?: boolean;
4
+ /** Custom output path (overrides project config and global) */
5
+ outputPath?: string;
6
+ }
7
+ /**
8
+ * Pulls a rule or solution to a file. Returns the file path where it was written.
9
+ * Throws if rule not found or if authentication is required.
10
+ */
11
+ export declare const pullRuleToFile: (id: string, options?: PullRuleOptions) => Promise<string>;
@@ -0,0 +1,39 @@
1
+ import { mkdirSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ import { getRuleById } from '../api/client.js';
5
+ import { getProjectConfig } from '../auth/project-config.js';
6
+ import { ruleFilename, solutionFilename } from './slug.js';
7
+ /**
8
+ * Pulls a rule or solution to a file. Returns the file path where it was written.
9
+ * Throws if rule not found or if authentication is required.
10
+ */
11
+ export const pullRuleToFile = async (id, options = {}) => {
12
+ const rule = await getRuleById(id);
13
+ if (!rule) {
14
+ throw new Error(`Rule or solution with ID ${id} not found.`);
15
+ }
16
+ let outDir;
17
+ if (options.outputPath) {
18
+ // Custom output path takes precedence
19
+ outDir = options.outputPath.startsWith('/') ? options.outputPath : join(process.cwd(), options.outputPath);
20
+ }
21
+ else if (options.global) {
22
+ // Use global location: ~/.cursor/rules/
23
+ outDir = join(homedir(), '.cursor', 'rules');
24
+ }
25
+ else {
26
+ // Use project config (default behavior)
27
+ const { outputPath } = getProjectConfig({ warnIfMissing: true });
28
+ outDir = join(process.cwd(), outputPath);
29
+ }
30
+ mkdirSync(outDir, { recursive: true });
31
+ const filename = rule.kind === 'solution'
32
+ ? join(outDir, solutionFilename(rule.title, rule.id))
33
+ : join(outDir, ruleFilename(rule.title, rule.id));
34
+ const content = rule.kind === 'solution'
35
+ ? `# ${rule.title}\n\n${rule.description}\n\n## Solution\n\n${rule.body}\n`
36
+ : `# ${rule.title}\n\n${rule.description}\n\n${rule.body}\n`;
37
+ writeFileSync(filename, content);
38
+ return filename;
39
+ };
@@ -1,7 +1,8 @@
1
- import { AUTH_REQUIRED_MSG, insertRule, searchRules } from '../api/client.js';
1
+ import { AUTH_REQUIRED_MSG, insertRule, searchRules, getRuleById, fetchRules, updateRule, deleteRule, fetchActivityLogs, getActivityLogById, } from '../api/client.js';
2
2
  import { buildAndPushActivityLog } from '../commands/log.js';
3
3
  import { loadCredentials } from '../auth/config.js';
4
4
  import { getProjectConfig } from '../auth/project-config.js';
5
+ import { pullRuleToFile } from '../lib/rule-file-ops.js';
5
6
  /** When token is missing, we fail initialize so Cursor shows "Needs authentication" (yellow) instead of success (green). */
6
7
  const NEEDS_AUTH_ERROR_MESSAGE = 'Needs authentication';
7
8
  const NEEDS_AUTH_ERROR_CODE = -32001; // Server error: auth required
@@ -59,7 +60,7 @@ function createStdioServer() {
59
60
  description: 'Search BitCompass rules by query',
60
61
  inputSchema: {
61
62
  type: 'object',
62
- properties: { query: { type: 'string' }, kind: { type: 'string', enum: ['rule', 'solution'] }, limit: { type: 'number' } },
63
+ properties: { query: { type: 'string' }, kind: { type: 'string', enum: ['rule', 'solution', 'skill', 'command'] }, limit: { type: 'number' } },
63
64
  required: ['query'],
64
65
  },
65
66
  },
@@ -78,7 +79,7 @@ function createStdioServer() {
78
79
  inputSchema: {
79
80
  type: 'object',
80
81
  properties: {
81
- kind: { type: 'string', enum: ['rule', 'solution'] },
82
+ kind: { type: 'string', enum: ['rule', 'solution', 'skill', 'command'] },
82
83
  title: { type: 'string' },
83
84
  description: { type: 'string' },
84
85
  body: { type: 'string' },
@@ -101,6 +102,92 @@ function createStdioServer() {
101
102
  required: ['time_frame'],
102
103
  },
103
104
  },
105
+ {
106
+ name: 'get-rule',
107
+ description: 'Get full details of a rule or solution by ID',
108
+ inputSchema: {
109
+ type: 'object',
110
+ properties: {
111
+ id: { type: 'string', description: 'Rule/solution ID' },
112
+ kind: { type: 'string', enum: ['rule', 'solution'], description: 'Optional: filter by kind' },
113
+ },
114
+ required: ['id'],
115
+ },
116
+ },
117
+ {
118
+ name: 'list-rules',
119
+ description: 'List all rules and solutions (with optional filtering by kind)',
120
+ inputSchema: {
121
+ type: 'object',
122
+ properties: {
123
+ kind: { type: 'string', enum: ['rule', 'solution'], description: 'Optional: filter by kind' },
124
+ limit: { type: 'number', description: 'Optional: maximum number of results (default: 50)' },
125
+ },
126
+ },
127
+ },
128
+ {
129
+ name: 'update-rule',
130
+ description: 'Update an existing rule or solution',
131
+ inputSchema: {
132
+ type: 'object',
133
+ properties: {
134
+ id: { type: 'string', description: 'Rule/solution ID to update' },
135
+ title: { type: 'string', description: 'Updated title' },
136
+ description: { type: 'string', description: 'Updated description' },
137
+ body: { type: 'string', description: 'Updated body content' },
138
+ context: { type: 'string', description: 'Updated context' },
139
+ examples: { type: 'array', items: { type: 'string' }, description: 'Updated examples array' },
140
+ technologies: { type: 'array', items: { type: 'string' }, description: 'Updated technologies array' },
141
+ },
142
+ required: ['id'],
143
+ },
144
+ },
145
+ {
146
+ name: 'delete-rule',
147
+ description: 'Delete a rule or solution by ID',
148
+ inputSchema: {
149
+ type: 'object',
150
+ properties: {
151
+ id: { type: 'string', description: 'Rule/solution ID to delete' },
152
+ },
153
+ required: ['id'],
154
+ },
155
+ },
156
+ {
157
+ name: 'pull-rule',
158
+ description: 'Pull a rule or solution to a file in the project rules directory',
159
+ inputSchema: {
160
+ type: 'object',
161
+ properties: {
162
+ id: { type: 'string', description: 'Rule/solution ID to pull' },
163
+ output_path: { type: 'string', description: 'Optional: custom output path' },
164
+ global: { type: 'boolean', description: 'Install globally to ~/.cursor/rules/' },
165
+ },
166
+ required: ['id'],
167
+ },
168
+ },
169
+ {
170
+ name: 'list-activity-logs',
171
+ description: "List user's activity logs",
172
+ inputSchema: {
173
+ type: 'object',
174
+ properties: {
175
+ limit: { type: 'number', description: 'Optional: maximum number of results (default: 20)' },
176
+ time_frame: { type: 'string', enum: ['day', 'week', 'month'], description: 'Optional: filter by time frame' },
177
+ },
178
+ },
179
+ },
180
+ {
181
+ name: 'get-activity-log',
182
+ description: 'Get activity log details by ID',
183
+ inputSchema: {
184
+ type: 'object',
185
+ properties: {
186
+ id: { type: 'string', description: 'Activity log ID' },
187
+ },
188
+ required: ['id'],
189
+ },
190
+ },
104
191
  ],
105
192
  },
106
193
  });
@@ -270,6 +357,177 @@ function createStdioServer() {
270
357
  return { error: msg };
271
358
  }
272
359
  });
360
+ handlers.set('get-rule', async (args) => {
361
+ const id = args.id;
362
+ if (!id)
363
+ return { error: 'id is required' };
364
+ const kind = args.kind;
365
+ try {
366
+ const rule = await getRuleById(id);
367
+ if (!rule) {
368
+ return { error: `Rule or solution with ID ${id} not found.` };
369
+ }
370
+ if (kind && rule.kind !== kind) {
371
+ return { error: `Rule with ID ${id} is a ${rule.kind}, not a ${kind}.` };
372
+ }
373
+ return {
374
+ id: rule.id,
375
+ kind: rule.kind,
376
+ title: rule.title,
377
+ description: rule.description,
378
+ body: rule.body,
379
+ context: rule.context ?? null,
380
+ examples: rule.examples ?? [],
381
+ technologies: rule.technologies ?? [],
382
+ author: rule.author_display_name ?? null,
383
+ created_at: rule.created_at,
384
+ updated_at: rule.updated_at,
385
+ };
386
+ }
387
+ catch (e) {
388
+ const msg = e instanceof Error ? e.message : 'Failed to get rule.';
389
+ return { error: msg };
390
+ }
391
+ });
392
+ handlers.set('list-rules', async (args) => {
393
+ const kind = args.kind;
394
+ const limit = args.limit ?? 50;
395
+ try {
396
+ const list = await fetchRules(kind);
397
+ const limited = list.slice(0, limit);
398
+ return {
399
+ rules: limited.map((r) => ({
400
+ id: r.id,
401
+ title: r.title,
402
+ kind: r.kind,
403
+ description: r.description,
404
+ author: r.author_display_name ?? null,
405
+ snippet: r.body.slice(0, 200),
406
+ created_at: r.created_at,
407
+ })),
408
+ total: list.length,
409
+ returned: limited.length,
410
+ };
411
+ }
412
+ catch (e) {
413
+ const msg = e instanceof Error ? e.message : 'Failed to list rules.';
414
+ return { error: msg };
415
+ }
416
+ });
417
+ handlers.set('update-rule', async (args) => {
418
+ if (!loadCredentials()?.access_token)
419
+ return { error: AUTH_REQUIRED_MSG };
420
+ const id = args.id;
421
+ if (!id)
422
+ return { error: 'id is required' };
423
+ const updates = {};
424
+ if (args.title !== undefined)
425
+ updates.title = args.title;
426
+ if (args.description !== undefined)
427
+ updates.description = args.description;
428
+ if (args.body !== undefined)
429
+ updates.body = args.body;
430
+ if (args.context !== undefined)
431
+ updates.context = args.context || undefined;
432
+ if (Array.isArray(args.examples))
433
+ updates.examples = args.examples;
434
+ if (Array.isArray(args.technologies))
435
+ updates.technologies = args.technologies;
436
+ try {
437
+ const updated = await updateRule(id, updates);
438
+ return {
439
+ id: updated.id,
440
+ title: updated.title,
441
+ kind: updated.kind,
442
+ success: true,
443
+ };
444
+ }
445
+ catch (e) {
446
+ const msg = e instanceof Error ? e.message : 'Failed to update rule.';
447
+ return { error: msg };
448
+ }
449
+ });
450
+ handlers.set('delete-rule', async (args) => {
451
+ if (!loadCredentials()?.access_token)
452
+ return { error: AUTH_REQUIRED_MSG };
453
+ const id = args.id;
454
+ if (!id)
455
+ return { error: 'id is required' };
456
+ try {
457
+ await deleteRule(id);
458
+ return { success: true, id };
459
+ }
460
+ catch (e) {
461
+ const msg = e instanceof Error ? e.message : 'Failed to delete rule.';
462
+ return { error: msg };
463
+ }
464
+ });
465
+ handlers.set('pull-rule', async (args) => {
466
+ if (!loadCredentials()?.access_token)
467
+ return { error: AUTH_REQUIRED_MSG };
468
+ const id = args.id;
469
+ if (!id)
470
+ return { error: 'id is required' };
471
+ const global = Boolean(args.global);
472
+ const outputPath = typeof args.output_path === 'string' ? args.output_path : undefined;
473
+ try {
474
+ const filePath = await pullRuleToFile(id, { global, outputPath });
475
+ return { success: true, file_path: filePath, id };
476
+ }
477
+ catch (e) {
478
+ const msg = e instanceof Error ? e.message : 'Failed to pull rule.';
479
+ return { error: msg };
480
+ }
481
+ });
482
+ handlers.set('list-activity-logs', async (args) => {
483
+ if (!loadCredentials()?.access_token)
484
+ return { error: AUTH_REQUIRED_MSG };
485
+ const limit = args.limit ?? 20;
486
+ const timeFrame = args.time_frame;
487
+ try {
488
+ const logs = await fetchActivityLogs({ limit, time_frame: timeFrame });
489
+ return {
490
+ logs: logs.map((log) => ({
491
+ id: log.id,
492
+ time_frame: log.time_frame,
493
+ period_start: log.period_start,
494
+ period_end: log.period_end,
495
+ created_at: log.created_at,
496
+ })),
497
+ total: logs.length,
498
+ };
499
+ }
500
+ catch (e) {
501
+ const msg = e instanceof Error ? e.message : 'Failed to list activity logs.';
502
+ return { error: msg };
503
+ }
504
+ });
505
+ handlers.set('get-activity-log', async (args) => {
506
+ if (!loadCredentials()?.access_token)
507
+ return { error: AUTH_REQUIRED_MSG };
508
+ const id = args.id;
509
+ if (!id)
510
+ return { error: 'id is required' };
511
+ try {
512
+ const log = await getActivityLogById(id);
513
+ if (!log) {
514
+ return { error: `Activity log with ID ${id} not found.` };
515
+ }
516
+ return {
517
+ id: log.id,
518
+ time_frame: log.time_frame,
519
+ period_start: log.period_start,
520
+ period_end: log.period_end,
521
+ repo_summary: log.repo_summary,
522
+ git_analysis: log.git_analysis,
523
+ created_at: log.created_at,
524
+ };
525
+ }
526
+ catch (e) {
527
+ const msg = e instanceof Error ? e.message : 'Failed to get activity log.';
528
+ return { error: msg };
529
+ }
530
+ });
273
531
  return {
274
532
  async connect() {
275
533
  // Stdio listener already attached; keep process alive
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type RuleKind = 'rule' | 'solution';
1
+ export type RuleKind = 'rule' | 'solution' | 'skill' | 'command';
2
2
  export interface Rule {
3
3
  id: string;
4
4
  kind: RuleKind;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitcompass",
3
- "version": "0.2.9",
3
+ "version": "0.3.1",
4
4
  "description": "BitCompass CLI - rules, solutions, and MCP server",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,7 @@
10
10
  "build": "tsc",
11
11
  "dev": "tsc --watch",
12
12
  "start": "node dist/index.js",
13
+ "publish": "npm publish --access public",
13
14
  "prepare": "npm run build",
14
15
  "postinstall": "node scripts/postinstall.js"
15
16
  },