bitcompass 0.2.6 → 0.2.8
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/auth/project-config.d.ts +16 -0
- package/dist/auth/project-config.js +63 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +56 -0
- package/dist/commands/rules.js +8 -2
- package/dist/commands/solutions.js +8 -2
- package/dist/index.js +5 -0
- package/dist/lib/slug.d.ts +15 -0
- package/dist/lib/slug.js +33 -0
- package/dist/mcp/server.js +2 -0
- package/dist/types.d.ts +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EditorProvider, ProjectConfig } from '../types.js';
|
|
2
|
+
declare const EDITOR_DEFAULT_PATHS: Record<EditorProvider, string>;
|
|
3
|
+
declare const DEFAULT_EDITOR: EditorProvider;
|
|
4
|
+
declare const DEFAULT_OUTPUT_PATH: string;
|
|
5
|
+
export declare const getProjectConfigDir: () => string;
|
|
6
|
+
export declare const getEditorDefaultPath: (editor: EditorProvider) => string;
|
|
7
|
+
export declare const loadProjectConfig: () => ProjectConfig | null;
|
|
8
|
+
export declare const saveProjectConfig: (config: ProjectConfig) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Returns project config, or defaults if not configured.
|
|
11
|
+
* When warnIfMissing is true and no config exists, prints a small warning once to stderr and proceeds with defaults.
|
|
12
|
+
*/
|
|
13
|
+
export declare const getProjectConfig: (options?: {
|
|
14
|
+
warnIfMissing?: boolean;
|
|
15
|
+
}) => ProjectConfig;
|
|
16
|
+
export { EDITOR_DEFAULT_PATHS, DEFAULT_EDITOR, DEFAULT_OUTPUT_PATH };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
const PROJECT_CONFIG_DIR = '.bitcompass';
|
|
4
|
+
const PROJECT_CONFIG_FILE = 'config.json';
|
|
5
|
+
const EDITOR_DEFAULT_PATHS = {
|
|
6
|
+
vscode: '.vscode/rules',
|
|
7
|
+
cursor: '.cursor/rules',
|
|
8
|
+
antigrativity: '.antigrativity/rules',
|
|
9
|
+
claudecode: '.claude/rules',
|
|
10
|
+
};
|
|
11
|
+
const DEFAULT_EDITOR = 'cursor';
|
|
12
|
+
const DEFAULT_OUTPUT_PATH = EDITOR_DEFAULT_PATHS[DEFAULT_EDITOR];
|
|
13
|
+
const getProjectConfigPath = () => {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
return join(cwd, PROJECT_CONFIG_DIR, PROJECT_CONFIG_FILE);
|
|
16
|
+
};
|
|
17
|
+
export const getProjectConfigDir = () => join(process.cwd(), PROJECT_CONFIG_DIR);
|
|
18
|
+
export const getEditorDefaultPath = (editor) => EDITOR_DEFAULT_PATHS[editor];
|
|
19
|
+
export const loadProjectConfig = () => {
|
|
20
|
+
const path = getProjectConfigPath();
|
|
21
|
+
if (!existsSync(path))
|
|
22
|
+
return null;
|
|
23
|
+
try {
|
|
24
|
+
const raw = readFileSync(path, 'utf-8');
|
|
25
|
+
const data = JSON.parse(raw);
|
|
26
|
+
const editor = data.editor;
|
|
27
|
+
const outputPath = typeof data.outputPath === 'string' ? data.outputPath : undefined;
|
|
28
|
+
if (editor && Object.keys(EDITOR_DEFAULT_PATHS).includes(editor) && outputPath) {
|
|
29
|
+
return { editor, outputPath };
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const ensureProjectConfigDir = () => {
|
|
38
|
+
const dir = join(process.cwd(), PROJECT_CONFIG_DIR);
|
|
39
|
+
if (!existsSync(dir)) {
|
|
40
|
+
mkdirSync(dir, { mode: 0o755, recursive: true });
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
export const saveProjectConfig = (config) => {
|
|
44
|
+
ensureProjectConfigDir();
|
|
45
|
+
const path = getProjectConfigPath();
|
|
46
|
+
writeFileSync(path, JSON.stringify(config, null, 2), { mode: 0o644 });
|
|
47
|
+
};
|
|
48
|
+
let warnedMissing = false;
|
|
49
|
+
/**
|
|
50
|
+
* Returns project config, or defaults if not configured.
|
|
51
|
+
* When warnIfMissing is true and no config exists, prints a small warning once to stderr and proceeds with defaults.
|
|
52
|
+
*/
|
|
53
|
+
export const getProjectConfig = (options) => {
|
|
54
|
+
const config = loadProjectConfig();
|
|
55
|
+
if (config)
|
|
56
|
+
return config;
|
|
57
|
+
if (options?.warnIfMissing && !warnedMissing) {
|
|
58
|
+
warnedMissing = true;
|
|
59
|
+
process.stderr.write('[bitcompass] No project config found (.bitcompass/config.json). Using defaults. Run "bitcompass init" to configure.\n');
|
|
60
|
+
}
|
|
61
|
+
return { editor: DEFAULT_EDITOR, outputPath: DEFAULT_OUTPUT_PATH };
|
|
62
|
+
};
|
|
63
|
+
export { EDITOR_DEFAULT_PATHS, DEFAULT_EDITOR, DEFAULT_OUTPUT_PATH };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const runInit: () => Promise<void>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
import { getEditorDefaultPath, loadProjectConfig, saveProjectConfig, getProjectConfigDir, } from '../auth/project-config.js';
|
|
6
|
+
const EDITOR_CHOICES = [
|
|
7
|
+
{ name: 'VSCode', value: 'vscode' },
|
|
8
|
+
{ name: 'Cursor', value: 'cursor' },
|
|
9
|
+
{ name: 'Antigrativity', value: 'antigrativity' },
|
|
10
|
+
{ name: 'Claude Code', value: 'claudecode' },
|
|
11
|
+
];
|
|
12
|
+
const GITIGNORE_ENTRY = '.bitcompass';
|
|
13
|
+
const ensureGitignoreEntry = () => {
|
|
14
|
+
const gitignorePath = join(process.cwd(), '.gitignore');
|
|
15
|
+
if (!existsSync(gitignorePath)) {
|
|
16
|
+
writeFileSync(gitignorePath, `${GITIGNORE_ENTRY}\n`, 'utf-8');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const content = readFileSync(gitignorePath, 'utf-8');
|
|
20
|
+
const lines = content.split(/\r?\n/);
|
|
21
|
+
const hasEntry = lines.some((line) => line.trim() === GITIGNORE_ENTRY);
|
|
22
|
+
if (hasEntry)
|
|
23
|
+
return;
|
|
24
|
+
const trimmed = content.trimEnd();
|
|
25
|
+
const suffix = trimmed ? '\n' : '';
|
|
26
|
+
writeFileSync(gitignorePath, `${trimmed}${suffix}\n${GITIGNORE_ENTRY}\n`, 'utf-8');
|
|
27
|
+
};
|
|
28
|
+
export const runInit = async () => {
|
|
29
|
+
const existing = loadProjectConfig();
|
|
30
|
+
const answers = await inquirer.prompt([
|
|
31
|
+
{
|
|
32
|
+
name: 'editor',
|
|
33
|
+
message: 'Editor / AI provider',
|
|
34
|
+
type: 'list',
|
|
35
|
+
choices: EDITOR_CHOICES,
|
|
36
|
+
default: existing?.editor ?? 'cursor',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'outputPath',
|
|
40
|
+
message: 'Folder for rules/docs/commands output',
|
|
41
|
+
type: 'input',
|
|
42
|
+
default: ({ editor }) => getEditorDefaultPath(editor),
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
const config = {
|
|
46
|
+
editor: answers.editor,
|
|
47
|
+
outputPath: answers.outputPath.trim() || getEditorDefaultPath(answers.editor),
|
|
48
|
+
};
|
|
49
|
+
saveProjectConfig(config);
|
|
50
|
+
ensureGitignoreEntry();
|
|
51
|
+
console.log(chalk.green('Project configured.'));
|
|
52
|
+
console.log(chalk.dim('Config:'), join(getProjectConfigDir(), 'config.json'));
|
|
53
|
+
console.log(chalk.dim('Editor:'), config.editor);
|
|
54
|
+
console.log(chalk.dim('Output path:'), config.outputPath);
|
|
55
|
+
console.log(chalk.dim('.gitignore:'), GITIGNORE_ENTRY, 'added or already present.');
|
|
56
|
+
};
|
package/dist/commands/rules.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { writeFileSync } from 'fs';
|
|
4
|
+
import { mkdirSync, writeFileSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
5
6
|
import { loadCredentials } from '../auth/config.js';
|
|
7
|
+
import { getProjectConfig } from '../auth/project-config.js';
|
|
6
8
|
import { searchRules, fetchRules, getRuleById, insertRule } from '../api/client.js';
|
|
9
|
+
import { ruleFilename } from '../lib/slug.js';
|
|
7
10
|
export const runRulesSearch = async (query) => {
|
|
8
11
|
if (!loadCredentials()) {
|
|
9
12
|
console.error(chalk.red('Not logged in. Run bitcompass login.'));
|
|
@@ -67,7 +70,10 @@ export const runRulesPull = async (id) => {
|
|
|
67
70
|
console.error(chalk.red('Rule not found.'));
|
|
68
71
|
process.exit(1);
|
|
69
72
|
}
|
|
70
|
-
const
|
|
73
|
+
const { outputPath } = getProjectConfig({ warnIfMissing: true });
|
|
74
|
+
const outDir = join(process.cwd(), outputPath);
|
|
75
|
+
mkdirSync(outDir, { recursive: true });
|
|
76
|
+
const filename = join(outDir, ruleFilename(rule.title, rule.id));
|
|
71
77
|
const content = `# ${rule.title}\n\n${rule.description}\n\n${rule.body}\n`;
|
|
72
78
|
writeFileSync(filename, content);
|
|
73
79
|
console.log(chalk.green('Wrote'), filename);
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { writeFileSync } from 'fs';
|
|
4
|
+
import { mkdirSync, writeFileSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
5
6
|
import { loadCredentials } from '../auth/config.js';
|
|
7
|
+
import { getProjectConfig } from '../auth/project-config.js';
|
|
6
8
|
import { searchRules, fetchRules, getRuleById, insertRule } from '../api/client.js';
|
|
9
|
+
import { solutionFilename } from '../lib/slug.js';
|
|
7
10
|
export const runSolutionsSearch = async (query) => {
|
|
8
11
|
if (!loadCredentials()) {
|
|
9
12
|
console.error(chalk.red('Not logged in. Run bitcompass login.'));
|
|
@@ -55,7 +58,10 @@ export const runSolutionsPull = async (id) => {
|
|
|
55
58
|
console.error(chalk.red('Solution not found.'));
|
|
56
59
|
process.exit(1);
|
|
57
60
|
}
|
|
58
|
-
const
|
|
61
|
+
const { outputPath } = getProjectConfig({ warnIfMissing: true });
|
|
62
|
+
const outDir = join(process.cwd(), outputPath);
|
|
63
|
+
mkdirSync(outDir, { recursive: true });
|
|
64
|
+
const filename = join(outDir, solutionFilename(rule.title, rule.id));
|
|
59
65
|
const content = `# ${rule.title}\n\n${rule.description}\n\n## Solution\n\n${rule.body}\n`;
|
|
60
66
|
writeFileSync(filename, content);
|
|
61
67
|
console.log(chalk.green('Wrote'), filename);
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import 'dotenv/config';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
5
|
import { runConfigGet, runConfigList, runConfigSet } from './commands/config-cmd.js';
|
|
6
|
+
import { runInit } from './commands/init.js';
|
|
6
7
|
import { runLogin } from './commands/login.js';
|
|
7
8
|
import { runLogout } from './commands/logout.js';
|
|
8
9
|
import { runMcpStart, runMcpStatus } from './commands/mcp.js';
|
|
@@ -27,6 +28,10 @@ program
|
|
|
27
28
|
.command('whoami')
|
|
28
29
|
.description('Show current user (email)')
|
|
29
30
|
.action(runWhoami);
|
|
31
|
+
program
|
|
32
|
+
.command('init')
|
|
33
|
+
.description('Configure project: editor/AI provider and output folder for rules/docs/commands')
|
|
34
|
+
.action(() => runInit().catch(handleErr));
|
|
30
35
|
program
|
|
31
36
|
.command('log [dates...]')
|
|
32
37
|
.description('Collect repo summary and git activity, then push to your activity logs. Optional: bitcompass log YYYY-MM-DD or bitcompass log YYYY-MM-DD YYYY-MM-DD')
|
|
@@ -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;
|
package/dist/lib/slug.js
ADDED
|
@@ -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/dist/mcp/server.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AUTH_REQUIRED_MSG, insertRule, searchRules } from '../api/client.js';
|
|
2
2
|
import { buildAndPushActivityLog } from '../commands/log.js';
|
|
3
3
|
import { loadCredentials } from '../auth/config.js';
|
|
4
|
+
import { getProjectConfig } from '../auth/project-config.js';
|
|
4
5
|
/** When token is missing, we fail initialize so Cursor shows "Needs authentication" (yellow) instead of success (green). */
|
|
5
6
|
const NEEDS_AUTH_ERROR_MESSAGE = 'Needs authentication';
|
|
6
7
|
const NEEDS_AUTH_ERROR_CODE = -32001; // Server error: auth required
|
|
@@ -276,6 +277,7 @@ function createStdioServer() {
|
|
|
276
277
|
};
|
|
277
278
|
}
|
|
278
279
|
export const startMcpServer = async () => {
|
|
280
|
+
getProjectConfig({ warnIfMissing: true });
|
|
279
281
|
const server = createStdioServer();
|
|
280
282
|
await server.connect();
|
|
281
283
|
// Do not exit when not logged in: Cursor needs the process alive to complete
|
package/dist/types.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ export interface StoredCredentials {
|
|
|
30
30
|
email?: string;
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
+
export type EditorProvider = 'vscode' | 'cursor' | 'antigrativity' | 'claudecode';
|
|
34
|
+
export interface ProjectConfig {
|
|
35
|
+
editor: EditorProvider;
|
|
36
|
+
/** Folder for rules/docs/commands output (e.g. .cursor/rules/) */
|
|
37
|
+
outputPath: string;
|
|
38
|
+
}
|
|
33
39
|
export interface BitcompassConfig {
|
|
34
40
|
apiUrl?: string;
|
|
35
41
|
supabaseUrl?: string;
|