aibridge-context 1.1.0 → 1.2.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.
package/README.md CHANGED
@@ -4,6 +4,12 @@
4
4
 
5
5
  Think of it as Git for AI context.
6
6
 
7
+ ## ⚠️ Public AI Access Warning
8
+
9
+ When GitHub sync is enabled, your project context becomes publicly accessible via a URL.
10
+
11
+ Do NOT use this tool with sensitive data.
12
+
7
13
  ## Quick Start
8
14
 
9
15
  ```bash
@@ -12,6 +18,14 @@ npx aibridge-context link-github
12
18
  npx aibridge-context start
13
19
  ```
14
20
 
21
+ ## How It Works
22
+
23
+ 1. Tracks project changes
24
+ 2. Generates structured AI context
25
+ 3. Syncs to GitHub (optional)
26
+ 4. Creates a public URL
27
+ 5. AI tools read this URL for context
28
+
15
29
  ## Usage
16
30
 
17
31
  This package exposes a CLI command:
@@ -60,7 +74,7 @@ If published to npm, the package exposes the `aibridge` binary.
60
74
 
61
75
  `ai-context` is supported as a legacy alias.
62
76
 
63
- ## Use With AI
77
+ ## Using With AI
64
78
 
65
79
  After enabling GitHub sync, use:
66
80
 
@@ -70,6 +84,13 @@ https://raw.githubusercontent.com/<user>/<repo>/main/.ai-context/state.json
70
84
 
71
85
  This URL always returns the latest project state.
72
86
 
87
+ Paste this into any AI:
88
+
89
+ ```text
90
+ Use this as source of truth:
91
+ https://raw.githubusercontent.com/<user>/<repo>/main/.ai-context/state.json
92
+ ```
93
+
73
94
  You can also share:
74
95
 
75
96
  ```text
@@ -155,7 +176,10 @@ Default configuration:
155
176
  "gitSync": {
156
177
  "enabled": false,
157
178
  "push": true,
158
- "commitMessage": "auto: update AI context"
179
+ "commitMessage": "auto: update AI context",
180
+ "remote": "origin",
181
+ "branch": "main",
182
+ "repoUrl": ""
159
183
  }
160
184
  }
161
185
  ```
package/bin/cli.js CHANGED
@@ -34,7 +34,12 @@ async function run() {
34
34
 
35
35
  try {
36
36
  if (command === 'init') {
37
- await initProject(projectRoot, { logger });
37
+ await initProject(projectRoot, {
38
+ logger,
39
+ requestPublicSyncConsent: true,
40
+ promptForPublicSyncConsent: () =>
41
+ promptYesNo('[aibridge] Do you want to enable GitHub sync and public AI access? (y/n) ')
42
+ });
38
43
  return;
39
44
  }
40
45
 
@@ -59,6 +64,19 @@ async function run() {
59
64
 
60
65
  if (command === 'link-github') {
61
66
  await initProject(projectRoot, { logger });
67
+ logger.warn('You are about to connect a GitHub repository.');
68
+ logger.info('This will:');
69
+ logger.info('* Push .ai-context to GitHub');
70
+ logger.info('* Make project context publicly accessible');
71
+ logger.info('* Allow AI systems to read your project state');
72
+
73
+ const shouldContinue = await promptYesNo('[aibridge] Continue? (y/n) ');
74
+
75
+ if (!shouldContinue) {
76
+ logger.info('GitHub linking cancelled. Public sync remains unchanged.');
77
+ return;
78
+ }
79
+
62
80
  const repoUrl = process.argv[3] || (await promptForRepoUrl());
63
81
 
64
82
  if (!repoUrl) {
@@ -88,11 +106,23 @@ async function run() {
88
106
  if (command === 'start') {
89
107
  await initProject(projectRoot, { logger });
90
108
  const config = await loadRuntimeConfig(projectRoot);
109
+ const port = Number(process.env.AI_CONTEXT_PORT || config.port || 3333);
110
+ logger.info('Watching project for changes...');
91
111
  const serverHandle = await startServer({
92
112
  projectRoot,
93
- port: Number(process.env.AI_CONTEXT_PORT || config.port || 3333),
113
+ port,
94
114
  logger
95
115
  });
116
+ logger.info(`Local server: http://localhost:${port}`);
117
+
118
+ if (config.gitSync.enabled) {
119
+ logger.info('Auto-sync to GitHub is ENABLED');
120
+ logger.warn('Changes will be publicly available to AI');
121
+ } else {
122
+ logger.info('GitHub sync is DISABLED');
123
+ logger.info('Data is only available locally');
124
+ }
125
+
96
126
  const watcherHandle = await startWatcher(projectRoot, { logger });
97
127
 
98
128
  const shutdown = async (signal) => {
@@ -131,17 +161,14 @@ async function run() {
131
161
  function printHelp() {
132
162
  console.log(`aibridge-context
133
163
 
134
- Usage:
135
- aibridge init
136
- aibridge link-github
137
- aibridge start
138
- aibridge update
139
-
140
164
  Commands:
141
- init Create .ai-context and initialize AI context files
142
- link-github Connect a GitHub remote and enable public AI sync
143
- start Start the watcher and local server on port 3333
144
- update Trigger a manual state update
165
+ init Initialize AI context (with optional public sync)
166
+ link-github Connect GitHub for public AI access
167
+ start Start watcher and server
168
+ update Manually update context
169
+
170
+ Description:
171
+ This tool creates an AI-readable version of your project and can expose it via a public URL for AI tools.
145
172
  `);
146
173
  }
147
174
 
@@ -152,11 +179,29 @@ async function promptForRepoUrl() {
152
179
  });
153
180
 
154
181
  try {
155
- const response = await rl.question('GitHub repository URL: ');
182
+ const response = await rl.question('[aibridge] GitHub repository URL: ');
156
183
  return response.trim();
157
184
  } finally {
158
185
  rl.close();
159
186
  }
160
187
  }
161
188
 
189
+ async function promptYesNo(question) {
190
+ if (!stdin.isTTY || !stdout.isTTY) {
191
+ return false;
192
+ }
193
+
194
+ const rl = readline.createInterface({
195
+ input: stdin,
196
+ output: stdout
197
+ });
198
+
199
+ try {
200
+ const response = await rl.question(question);
201
+ return response.trim().toLowerCase() === 'y';
202
+ } finally {
203
+ rl.close();
204
+ }
205
+ }
206
+
162
207
  run();
package/core/gitSync.js CHANGED
@@ -74,6 +74,13 @@ async function hasStagedContextChanges(projectRoot) {
74
74
  }
75
75
  }
76
76
 
77
+ function formatGitError(error) {
78
+ const stderr = typeof error.stderr === 'string' ? error.stderr.trim() : '';
79
+ const stdout = typeof error.stdout === 'string' ? error.stdout.trim() : '';
80
+ const message = stderr || stdout || error.message || 'Unknown git error.';
81
+ return message.split('\n')[0];
82
+ }
83
+
77
84
  async function ensureGitInitialized(projectRoot, logger) {
78
85
  const repositoryReady = await isGitRepository(projectRoot);
79
86
 
@@ -93,6 +100,7 @@ async function ensureGitInitialized(projectRoot, logger) {
93
100
  throw error;
94
101
  }
95
102
  }
103
+
96
104
  await ensureMainBranch(projectRoot);
97
105
 
98
106
  if (logger) {
@@ -104,7 +112,7 @@ async function ensureGitInitialized(projectRoot, logger) {
104
112
  };
105
113
  } catch (error) {
106
114
  if (logger) {
107
- logger.warn(`Git initialization failed gracefully: ${error.message}`);
115
+ logger.warn(`Git initialization failed gracefully: ${formatGitError(error)}`);
108
116
  }
109
117
 
110
118
  return {
@@ -156,7 +164,7 @@ function logMissingRemoteInstructions(logger) {
156
164
  return;
157
165
  }
158
166
 
159
- logger.warn('No GitHub remote detected.');
167
+ logger.warn('GitHub remote not found.');
160
168
  logger.info('Run:');
161
169
  logger.info('git remote add origin <repo-url>');
162
170
  logger.info('git push -u origin main');
@@ -167,10 +175,15 @@ function logPublicAiEndpoints(logger, urls) {
167
175
  return;
168
176
  }
169
177
 
170
- logger.info('Public AI endpoint:');
178
+ logger.info('\u2705 AI Context Synced Successfully');
179
+ logger.info('\u{1F310} Public AI Endpoint:');
171
180
  logger.info(urls.stateUrl);
172
- logger.info('Use this with AI:');
181
+ logger.info('\u{1F9E0} AI Instructions Endpoint:');
173
182
  logger.info(urls.brainUrl);
183
+ logger.warn('\u26A0\uFE0F IMPORTANT:');
184
+ logger.warn('This data is PUBLIC. Anyone with this link can access it.');
185
+ logger.info('\u{1F916} To use with AI:');
186
+ logger.info('"Use this URL as the source of truth for my project."');
174
187
  }
175
188
 
176
189
  async function linkGithubRepository(projectRoot, repoUrl, logger) {
@@ -189,16 +202,13 @@ async function linkGithubRepository(projectRoot, repoUrl, logger) {
189
202
  await ensureMainBranch(projectRoot);
190
203
  await runGit(projectRoot, ['push', '-u', 'origin', 'main']);
191
204
 
192
- const urls = buildPublicAiUrls(normalizedRepoUrl, 'main');
193
- logPublicAiEndpoints(logger, urls);
194
-
195
205
  return {
196
206
  ok: true,
197
- urls
207
+ urls: buildPublicAiUrls(normalizedRepoUrl, 'main')
198
208
  };
199
209
  } catch (error) {
200
210
  if (logger) {
201
- logger.warn(`GitHub link failed gracefully: ${error.message}`);
211
+ logger.warn(`GitHub link failed gracefully: ${formatGitError(error)}`);
202
212
  }
203
213
 
204
214
  return {
@@ -211,7 +221,7 @@ async function linkGithubRepository(projectRoot, repoUrl, logger) {
211
221
  async function syncContextToGit(projectRoot, config, logger) {
212
222
  const settings = Object.assign(
213
223
  {
214
- enabled: true,
224
+ enabled: false,
215
225
  push: true,
216
226
  commitMessage: 'auto: update AI context',
217
227
  remote: 'origin',
@@ -285,11 +295,6 @@ async function syncContextToGit(projectRoot, config, logger) {
285
295
  }
286
296
 
287
297
  const urls = buildPublicAiUrls(remoteUrl, branchName);
288
-
289
- if (logger) {
290
- logger.info('Synced .ai-context changes to git.');
291
- }
292
-
293
298
  logPublicAiEndpoints(logger, urls);
294
299
 
295
300
  return {
@@ -299,7 +304,7 @@ async function syncContextToGit(projectRoot, config, logger) {
299
304
  };
300
305
  } catch (error) {
301
306
  if (logger) {
302
- logger.warn(`Git sync failed gracefully: ${error.message}`);
307
+ logger.warn(`Git sync failed gracefully: ${formatGitError(error)}`);
303
308
  }
304
309
 
305
310
  return {
package/core/init.js CHANGED
@@ -18,7 +18,15 @@ const {
18
18
  const { ensureGitInitialized } = require('./gitSync');
19
19
 
20
20
  async function initProject(projectRoot, options) {
21
- const settings = Object.assign({ logger: null, force: false }, options);
21
+ const settings = Object.assign(
22
+ {
23
+ logger: null,
24
+ force: false,
25
+ requestPublicSyncConsent: false,
26
+ promptForPublicSyncConsent: null
27
+ },
28
+ options
29
+ );
22
30
  const logger = settings.logger;
23
31
  const contextDir = await ensureContextDirectory(projectRoot);
24
32
  const paths = getContextPaths(projectRoot);
@@ -36,6 +44,7 @@ async function initProject(projectRoot, options) {
36
44
  const existingChangelog = await readJsonFile(paths.changelogFile, null);
37
45
  const templateState = JSON.parse(stateTemplate);
38
46
  const templateChangelog = JSON.parse(changelogTemplate);
47
+ let enableGitSync = false;
39
48
 
40
49
  const initialState = Object.assign({}, templateState, existingState || createDefaultState(projectRoot), {
41
50
  project: metadata.project,
@@ -62,18 +71,18 @@ async function initProject(projectRoot, options) {
62
71
  await writeTextAtomic(paths.contextFile, initialContext);
63
72
  }
64
73
 
65
- await updateRuntimeConfig(
66
- projectRoot,
67
- existingConfig
68
- ? null
69
- : {
70
- gitSync: {
71
- enabled: true,
72
- push: true,
73
- commitMessage: 'auto: update AI context'
74
- }
75
- }
76
- );
74
+ if (!existingConfig && settings.requestPublicSyncConsent) {
75
+ enableGitSync = await requestPublicSyncConsent(
76
+ logger,
77
+ settings.promptForPublicSyncConsent
78
+ );
79
+ }
80
+
81
+ await updateRuntimeConfig(projectRoot, existingConfig ? null : {
82
+ gitSync: {
83
+ enabled: enableGitSync
84
+ }
85
+ });
77
86
 
78
87
  await ensureGitInitialized(projectRoot, logger);
79
88
 
@@ -87,6 +96,29 @@ async function initProject(projectRoot, options) {
87
96
  };
88
97
  }
89
98
 
99
+ async function requestPublicSyncConsent(logger, promptForPublicSyncConsent) {
100
+ if (logger) {
101
+ logger.warn('This tool can make parts of your project publicly accessible for AI tools.');
102
+ logger.info('It will:');
103
+ logger.info('* Track project changes');
104
+ logger.info('* Generate AI-readable context');
105
+ logger.info('* Optionally sync to GitHub');
106
+ logger.info('* Create a PUBLIC URL accessible by AI systems');
107
+ logger.warn('WARNING:');
108
+ logger.warn('Anyone with the URL can read this data.');
109
+ }
110
+
111
+ if (typeof promptForPublicSyncConsent !== 'function') {
112
+ if (logger) {
113
+ logger.info('GitHub sync will remain disabled until you explicitly enable it.');
114
+ }
115
+
116
+ return false;
117
+ }
118
+
119
+ return Boolean(await promptForPublicSyncConsent());
120
+ }
121
+
90
122
  async function fileExists(filePath) {
91
123
  try {
92
124
  await fsp.access(filePath);
@@ -12,7 +12,7 @@ const DEFAULT_CONFIG = {
12
12
  port: 3333,
13
13
  debounceMs: 600,
14
14
  gitSync: {
15
- enabled: true,
15
+ enabled: false,
16
16
  push: true,
17
17
  commitMessage: 'auto: update AI context',
18
18
  remote: 'origin',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aibridge-context",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Zero-config CLI and library for generating AI-readable project context, serving it locally, and syncing it with git.",
5
5
  "main": "./index.js",
6
6
  "bin": {
package/server/server.js CHANGED
@@ -22,10 +22,6 @@ async function startServer(options) {
22
22
  instance.on('error', reject);
23
23
  });
24
24
 
25
- if (settings.logger) {
26
- settings.logger.info(`AI context server is running on http://localhost:${settings.port}`);
27
- }
28
-
29
25
  return {
30
26
  app,
31
27
  server,