@anirudh242/contextbridge 0.1.0

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.
@@ -0,0 +1,128 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { getConfigDir } from './config.js';
4
+ const STORE_PATH = path.join(getConfigDir(), 'store.json');
5
+ const RAW_DIR = path.join(getConfigDir(), 'raw');
6
+ function createEmptyStore() {
7
+ return {
8
+ projects: {},
9
+ };
10
+ }
11
+ function getErrorCode(error) {
12
+ return error instanceof Error && 'code' in error
13
+ ? String(error.code)
14
+ : undefined;
15
+ }
16
+ export function getStorePath() {
17
+ return STORE_PATH;
18
+ }
19
+ export async function saveRawCapture(id, raw) {
20
+ await fs.mkdir(RAW_DIR, { recursive: true });
21
+ await fs.writeFile(path.join(RAW_DIR, `${id}.json`), `${JSON.stringify(raw, null, 2)}\n`, 'utf8');
22
+ }
23
+ export async function loadRawCapture(id) {
24
+ try {
25
+ const content = await fs.readFile(path.join(RAW_DIR, `${id}.json`), 'utf8');
26
+ return JSON.parse(content);
27
+ }
28
+ catch (error) {
29
+ if (getErrorCode(error) === 'ENOENT')
30
+ return null;
31
+ throw error;
32
+ }
33
+ }
34
+ export async function saveStore(store) {
35
+ await fs.mkdir(path.dirname(STORE_PATH), { recursive: true });
36
+ await fs.writeFile(STORE_PATH, `${JSON.stringify(store, null, 2)}\n`, 'utf8');
37
+ }
38
+ export async function ensureStore() {
39
+ await fs.mkdir(path.dirname(STORE_PATH), { recursive: true });
40
+ try {
41
+ const existing = await fs.readFile(STORE_PATH, 'utf8');
42
+ // NEW: Safely parse the JSON to catch corruption
43
+ let parsed;
44
+ try {
45
+ parsed = JSON.parse(existing);
46
+ }
47
+ catch (parseError) {
48
+ // If the file is corrupt, back it up and start fresh
49
+ const backupPath = `${STORE_PATH}.corrupted.${Date.now()}.bak`;
50
+ await fs.rename(STORE_PATH, backupPath);
51
+ console.warn(`\n[ContextBridge] Warning: Local store was corrupted. Backed up to: ${backupPath}\n`);
52
+ const initial = createEmptyStore();
53
+ await saveStore(initial);
54
+ return initial;
55
+ }
56
+ if (!parsed.projects) {
57
+ const normalised = createEmptyStore();
58
+ await saveStore(normalised);
59
+ return normalised;
60
+ }
61
+ let migrated = false;
62
+ for (const project of Object.values(parsed.projects)) {
63
+ if (!project.entries)
64
+ continue;
65
+ for (const entry of project.entries) {
66
+ if ('raw' in entry) {
67
+ const raw = entry.raw;
68
+ await saveRawCapture(entry.id, raw);
69
+ delete entry.raw;
70
+ migrated = true;
71
+ }
72
+ }
73
+ }
74
+ if (migrated) {
75
+ await saveStore(parsed);
76
+ }
77
+ return parsed;
78
+ }
79
+ catch (error) {
80
+ if (getErrorCode(error) !== 'ENOENT') {
81
+ throw error;
82
+ }
83
+ // This handles the first-run case where store.json doesn't exist yet
84
+ const initial = createEmptyStore();
85
+ await saveStore(initial);
86
+ return initial;
87
+ }
88
+ }
89
+ export async function loadStore() {
90
+ return ensureStore();
91
+ }
92
+ export async function ensureProjectStore(projectPath, projectName = path.basename(projectPath)) {
93
+ const store = await ensureStore();
94
+ if (!store.projects[projectPath]) {
95
+ store.projects[projectPath] = {
96
+ name: projectName,
97
+ head: null,
98
+ entries: [],
99
+ };
100
+ await saveStore(store);
101
+ }
102
+ return store.projects[projectPath];
103
+ }
104
+ export async function getProjectStore(projectPath) {
105
+ const store = await ensureStore();
106
+ return store.projects[projectPath] ?? null;
107
+ }
108
+ export async function addEntryToProjectStore(projectPath, entry, raw) {
109
+ await saveRawCapture(entry.id, raw);
110
+ const store = await ensureStore();
111
+ const project = store.projects[projectPath] ?? {
112
+ name: path.basename(projectPath),
113
+ head: null,
114
+ entries: [],
115
+ };
116
+ project.entries.push(entry);
117
+ project.head = entry.id;
118
+ store.projects[projectPath] = project;
119
+ await saveStore(store);
120
+ return entry;
121
+ }
122
+ export async function getHeadEntry(projectPath) {
123
+ const project = await getProjectStore(projectPath);
124
+ if (!project?.head) {
125
+ return null;
126
+ }
127
+ return project.entries.find((entry) => entry.id === project.head) ?? null;
128
+ }
package/dist/tsc ADDED
File without changes
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,57 @@
1
+ import boxen from 'boxen';
2
+ import chalk from 'chalk';
3
+ const colorMap = {
4
+ blue: chalk.blue,
5
+ cyan: chalk.cyan,
6
+ green: chalk.green,
7
+ yellow: chalk.yellow,
8
+ };
9
+ export function formatRelativeTime(input) {
10
+ const value = typeof input === 'string' ? new Date(input) : input;
11
+ const diffMs = Date.now() - value.getTime();
12
+ const diffMinutes = Math.floor(diffMs / (1000 * 60));
13
+ if (diffMinutes < 1) {
14
+ return 'just now';
15
+ }
16
+ if (diffMinutes < 60) {
17
+ return `${diffMinutes} minute${diffMinutes === 1 ? '' : 's'} ago`;
18
+ }
19
+ const diffHours = Math.floor(diffMinutes / 60);
20
+ if (diffHours < 24) {
21
+ return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`;
22
+ }
23
+ const diffDays = Math.floor(diffHours / 24);
24
+ return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`;
25
+ }
26
+ export function printPanel(content, color = 'cyan') {
27
+ console.log(boxen(colorMap[color](content), {
28
+ padding: 1,
29
+ borderColor: color,
30
+ borderStyle: 'round',
31
+ }));
32
+ }
33
+ export function printSuccess(title, lines = []) {
34
+ const content = [chalk.bold(title), ...lines].join('\n');
35
+ printPanel(content, 'green');
36
+ }
37
+ export function printWarning(message) {
38
+ printPanel(message, 'yellow');
39
+ }
40
+ export function printInfo(message) {
41
+ console.log(chalk.cyan(message));
42
+ }
43
+ export function printInfoPanel(title, lines = []) {
44
+ const content = [chalk.bold(title), ...lines].join('\n');
45
+ printPanel(content, 'blue');
46
+ }
47
+ export function formatSummaryLines(summary) {
48
+ return [
49
+ `Project: ${summary.projectGoal ?? 'Unknown'}`,
50
+ `Direction: ${summary.currentDirection ?? 'Unknown'}`,
51
+ `Focus: ${summary.currentFocus ?? 'Unknown'}`,
52
+ `Decisions: ${summary.decisions.join(' | ') || 'Unknown'}`,
53
+ `Blockers: ${summary.blockers.join(' | ') || 'None'}`,
54
+ `Stack: ${summary.stack ?? 'Unknown'}`,
55
+ `Next steps: ${summary.nextSteps.join(' | ') || 'Unknown'}`,
56
+ ];
57
+ }
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@anirudh242/contextbridge",
3
+ "version": "0.1.0",
4
+ "description": "Persistent, versioned AI project context for every coding tool you use.",
5
+ "type": "module",
6
+ "bin": {
7
+ "contextbridge": "dist/index.js",
8
+ "cb": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "package.json"
14
+ ],
15
+ "engines": {
16
+ "node": ">=18"
17
+ },
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "prepack": "npm run build",
21
+ "start": "node dist/index.js",
22
+ "check": "tsc --noEmit -p tsconfig.json"
23
+ },
24
+ "keywords": [
25
+ "cli",
26
+ "ai",
27
+ "context",
28
+ "developer-tools",
29
+ "cursor",
30
+ "codex",
31
+ "antigravity"
32
+ ],
33
+ "author": "anirudh242",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/anirudh242/contextbridge.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/anirudh242/contextbridge/issues"
40
+ },
41
+ "license": "MIT",
42
+ "dependencies": {
43
+ "boxen": "^8.0.1",
44
+ "chalk": "^5.3.0",
45
+ "clipboardy": "^4.0.0",
46
+ "commander": "^12.1.0",
47
+ "ora": "^8.0.1",
48
+ "simple-git": "^3.27.0",
49
+ "zod": "^3.24.1",
50
+ "zod-to-json-schema": "^3.24.6"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^22.15.29",
54
+ "typescript": "^5.8.3"
55
+ }
56
+ }