monoai 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/README.md CHANGED
@@ -56,4 +56,19 @@ coverage
56
56
  *.log
57
57
  ```
58
58
 
59
+ ## .monoaiwhitelist (optional, include-only)
60
+
61
+ `monoai push` can also read `.monoaiwhitelist` as an include-only filter.
62
+ When this file exists and has rules, only matched paths are scanned and uploaded.
63
+
64
+ Use this when you run `monoai push` from a monorepo root but want to sync only one app (for example `step4.vite-web-migration`).
65
+
66
+ ```gitignore
67
+ # Example: sync only step4 project
68
+ step4.vite-web-migration/**
69
+ ```
70
+
71
+ Behavior summary:
72
+ - `.monoaiwhitelist` limits scope first (include-only).
73
+ - `.gitignore` and `.monoaiignore` still exclude paths inside that scope.
59
74
 
@@ -11,25 +11,25 @@ const CONVEX_SITE_URL = process.env.MONOAI_CONVEX_SITE_URL ||
11
11
  'https://majestic-crane-609.convex.site';
12
12
  const WEB_URL = 'https://monoai.space';
13
13
  export const loginCommand = new Command('login')
14
- .description('Authenticate with MonoAI')
14
+ .description('Sign in to MonoAI')
15
15
  .action(async () => {
16
- console.log(chalk.blue('šŸ” Starting MonoAI Login...'));
16
+ console.log(chalk.blue('šŸ” Starting sign-in...'));
17
17
  try {
18
18
  // 1. Init Session
19
- const initSpinner = ora('Initializing auth session...').start();
19
+ const initSpinner = ora('Preparing secure sign-in...').start();
20
20
  const initRes = await axios.post(`${CONVEX_SITE_URL}/cli/auth/init`, {
21
21
  deviceDescription: process.platform
22
22
  });
23
23
  initSpinner.succeed();
24
24
  const { tempCode } = initRes.data;
25
25
  const loginUrl = `${WEB_URL}/auth/cli?code=${tempCode}`;
26
- console.log(chalk.yellow(`\nšŸ‘‰ Verification Code: ${chalk.bold(tempCode)}`));
27
- console.log(chalk.dim(` Opening browser... if it doesn't open, visit:`));
26
+ console.log(chalk.yellow(`\nšŸ‘‰ Verification code: ${chalk.bold(tempCode)}`));
27
+ console.log(chalk.dim(` Opening your browser. If it does not open, visit:`));
28
28
  console.log(chalk.underline(loginUrl));
29
29
  console.log('\n');
30
30
  await open(loginUrl);
31
31
  // 2. Poll Status
32
- const pollSpinner = ora('Waiting for approval in browser...').start();
32
+ const pollSpinner = ora('Waiting for approval in your browser...').start();
33
33
  let attempts = 0;
34
34
  const maxAttempts = 60; // 2 minutes (2s * 60)
35
35
  const pollInterval = setInterval(async () => {
@@ -41,30 +41,30 @@ export const loginCommand = new Command('login')
41
41
  config.set('auth_token', token);
42
42
  config.set('user_id', userId);
43
43
  config.set('convex_url', CONVEX_SITE_URL); // Store for future use
44
- pollSpinner.succeed(chalk.green('āœ… Login Successful!'));
45
- console.log(chalk.dim(` Token saved to ${config.path}`));
44
+ pollSpinner.succeed(chalk.green('āœ… Sign-in complete.'));
45
+ console.log(chalk.dim(` Credentials saved to ${config.path}`));
46
46
  process.exit(0);
47
47
  }
48
48
  else if (status === 'expired' || status === 'rejected') {
49
49
  clearInterval(pollInterval);
50
- pollSpinner.fail(chalk.red(`āŒ Session ${status}. Please try again.`));
50
+ pollSpinner.fail(chalk.red(`āŒ Sign-in ${status}. Please try again.`));
51
51
  process.exit(1);
52
52
  }
53
53
  else {
54
54
  attempts++;
55
55
  if (attempts >= maxAttempts) {
56
56
  clearInterval(pollInterval);
57
- pollSpinner.fail(chalk.red('āŒ Timed out.'));
57
+ pollSpinner.fail(chalk.red('āŒ Sign-in timed out.'));
58
58
  process.exit(1);
59
59
  }
60
60
  }
61
61
  }
62
62
  catch (err) {
63
- // Ignore poll errors (network blips)
63
+ // Ignore temporary polling errors
64
64
  }
65
65
  }, 2000);
66
66
  }
67
67
  catch (error) {
68
- console.error(chalk.red('\nāŒ Login failed:'), error.message);
68
+ console.error(chalk.red('\nāŒ Sign-in failed:'), error.message);
69
69
  }
70
70
  });
@@ -10,6 +10,7 @@ import { extractSkeleton } from '../utils/ast-extractor.js';
10
10
  const git = simpleGit();
11
11
  const config = new Conf({ projectName: 'monoai' });
12
12
  const MONOAIIGNORE_FILENAME = '.monoaiignore';
13
+ const MONOAIWHITELIST_FILENAME = '.monoaiwhitelist';
13
14
  const DEFAULT_MONOAIIGNORE = `# MonoAI AST scan ignore rules
14
15
  # Uses .gitignore-style patterns.
15
16
 
@@ -26,67 +27,180 @@ coverage
26
27
  **/.agent/**
27
28
  *.log
28
29
  `;
30
+ function loadMonoaiWhitelist(cwd) {
31
+ const whitelistPath = path.join(cwd, MONOAIWHITELIST_FILENAME);
32
+ if (!fs.existsSync(whitelistPath)) {
33
+ return { matcher: null, ruleCount: 0 };
34
+ }
35
+ const rules = fs.readFileSync(whitelistPath, 'utf8')
36
+ .split(/\r?\n/g)
37
+ .map((line) => line.trim())
38
+ .filter((line) => line.length > 0 && !line.startsWith('#'));
39
+ if (rules.length === 0) {
40
+ return { matcher: null, ruleCount: 0 };
41
+ }
42
+ return {
43
+ matcher: ignore().add(rules),
44
+ ruleCount: rules.length,
45
+ };
46
+ }
47
+ function normalizeGitFilePath(value) {
48
+ return String(value || "")
49
+ .trim()
50
+ .replace(/\\/g, "/")
51
+ .replace(/^\.?\//, "");
52
+ }
53
+ function scopePrefix(filePath, depth) {
54
+ const parts = normalizeGitFilePath(filePath).split("/").filter(Boolean);
55
+ if (parts.length === 0)
56
+ return "";
57
+ return parts.slice(0, Math.min(depth, parts.length)).join("/");
58
+ }
59
+ function asNumber(value) {
60
+ const n = Number(value);
61
+ if (!Number.isFinite(n))
62
+ return 0;
63
+ return Math.max(0, Math.round(n));
64
+ }
65
+ function buildChangedFileSignals(diffFiles) {
66
+ const rows = Array.isArray(diffFiles) ? diffFiles : [];
67
+ return rows
68
+ .map((file) => {
69
+ const normalizedPath = normalizeGitFilePath(String(file?.file || ""));
70
+ if (!normalizedPath)
71
+ return null;
72
+ const insertions = asNumber(file?.insertions);
73
+ const deletions = asNumber(file?.deletions);
74
+ const changes = Math.max(asNumber(file?.changes), insertions + deletions);
75
+ const ext = path.extname(normalizedPath).replace(".", "").toLowerCase() || "none";
76
+ return {
77
+ path: normalizedPath,
78
+ insertions,
79
+ deletions,
80
+ changes,
81
+ scope1: scopePrefix(normalizedPath, 1),
82
+ scope2: scopePrefix(normalizedPath, 2),
83
+ extension: ext,
84
+ };
85
+ })
86
+ .filter(Boolean);
87
+ }
88
+ function buildGraphInsightsFromChanges(files) {
89
+ if (!Array.isArray(files) || files.length === 0)
90
+ return [];
91
+ const byScope = new Map();
92
+ const byExtension = new Map();
93
+ for (const row of files) {
94
+ const scope = row.scope2 || row.scope1 || "root";
95
+ const churn = Math.max(1, row.changes);
96
+ const currentScope = byScope.get(scope) || { files: 0, churn: 0 };
97
+ currentScope.files += 1;
98
+ currentScope.churn += churn;
99
+ byScope.set(scope, currentScope);
100
+ const extCount = byExtension.get(row.extension) || 0;
101
+ byExtension.set(row.extension, extCount + 1);
102
+ }
103
+ const hotFiles = files
104
+ .slice()
105
+ .sort((a, b) => b.changes - a.changes)
106
+ .slice(0, 8)
107
+ .map((row) => `changed_file:${row.path} (+${row.insertions}/-${row.deletions})`);
108
+ const hotScopes = Array.from(byScope.entries())
109
+ .sort((a, b) => {
110
+ if (b[1].churn !== a[1].churn)
111
+ return b[1].churn - a[1].churn;
112
+ return b[1].files - a[1].files;
113
+ })
114
+ .slice(0, 6)
115
+ .map(([scope, stat]) => `changed_scope:${scope} files=${stat.files} churn=${stat.churn}`);
116
+ const extensionMix = Array.from(byExtension.entries())
117
+ .sort((a, b) => b[1] - a[1])
118
+ .slice(0, 4)
119
+ .map(([ext, count]) => `extension_mix:${ext}=${count}`);
120
+ return Array.from(new Set([
121
+ `changed_total_files:${files.length}`,
122
+ ...hotScopes,
123
+ ...hotFiles,
124
+ ...extensionMix,
125
+ ])).slice(0, 24);
126
+ }
29
127
  export const pushCommand = new Command('push')
30
- .description('Push codebase integrity and AST skeleton to MonoAI')
31
- .action(async () => {
128
+ .description('Sync your codebase structure to MonoAI')
129
+ .option('-v, --verbose', 'Show internal pipeline logs')
130
+ .option('-f, --force', 'Force sync even if the same commit was already uploaded')
131
+ .action(async (options) => {
132
+ const verbose = !!options?.verbose;
133
+ const force = !!options?.force;
32
134
  const totalStart = Date.now();
33
135
  const stageTimes = [];
136
+ const logDetail = (message) => {
137
+ if (verbose)
138
+ console.log(chalk.dim(message));
139
+ };
34
140
  const track = (stage, fn) => {
35
141
  const start = Date.now();
36
142
  return fn().then((result) => {
37
143
  const ms = Date.now() - start;
38
144
  stageTimes.push({ stage, ms });
39
- console.log(chalk.dim(` ā± ${stage}: ${(ms / 1000).toFixed(2)}s`));
145
+ logDetail(` ā± ${stage}: ${(ms / 1000).toFixed(2)}s`);
40
146
  return result;
41
147
  });
42
148
  };
43
149
  try {
44
- console.log(chalk.blue('šŸŽļø Starting MonoAI Strategic Push...'));
150
+ console.log(chalk.blue('šŸŽļø Starting codebase sync...'));
45
151
  // 0. Auth Check
46
152
  const token = config.get('auth_token');
47
153
  if (!token) {
48
- console.error(chalk.red('āŒ Not Authenticated. Please run:'));
154
+ console.error(chalk.red('āŒ You are not signed in. Please run:'));
49
155
  console.error(chalk.white(' npx monoai login'));
50
156
  return;
51
157
  }
52
158
  const isRepo = await git.checkIsRepo();
53
159
  if (!isRepo) {
54
- console.error(chalk.red('āŒ Not a git repository.'));
160
+ console.error(chalk.red('āŒ This folder is not a Git repository.'));
55
161
  return;
56
162
  }
163
+ const { matcher: whitelistMatcher, ruleCount: whitelistRuleCount } = loadMonoaiWhitelist(process.cwd());
164
+ const isWhitelisted = (relativePath) => !whitelistMatcher || whitelistMatcher.ignores(normalizeGitFilePath(relativePath));
165
+ if (whitelistMatcher) {
166
+ console.log(chalk.blue(`šŸŽÆ Applying ${MONOAIWHITELIST_FILENAME} (${whitelistRuleCount} rule${whitelistRuleCount > 1 ? 's' : ''})`));
167
+ }
57
168
  // 1. Git Metadata (Zero-HITL Intent)
58
- const { lastCommit, branch, changedScopes } = await track('git metadata', async () => {
169
+ const { lastCommit, branch, changedScopes, graphInsights } = await track('git metadata', async () => {
59
170
  const log = await git.log({ maxCount: 1 });
60
171
  const lastCommit = log.latest;
61
172
  const branch = await git.revparse(['--abbrev-ref', 'HEAD']);
62
173
  if (!lastCommit) {
63
174
  throw new Error('No commits found.');
64
175
  }
65
- let changedScopes = [];
176
+ let changedFiles = [];
66
177
  try {
67
178
  const diffSummary = await git.diffSummary(['HEAD~1', 'HEAD']);
68
- changedScopes = Array.from(new Set(diffSummary.files.map((f) => {
69
- const parts = f.file.split('/');
70
- return parts.length > 1 ? parts[0] : f.file;
71
- })));
179
+ changedFiles = buildChangedFileSignals(diffSummary?.files || []);
72
180
  }
73
181
  catch {
74
- changedScopes = [];
182
+ changedFiles = [];
75
183
  }
76
- return { lastCommit, branch, changedScopes };
184
+ changedFiles = changedFiles.filter((row) => isWhitelisted(row.path));
185
+ const changedScopes = Array.from(new Set(changedFiles
186
+ .flatMap((row) => [row.scope2, row.scope1])
187
+ .map((row) => String(row || "").trim())
188
+ .filter(Boolean))).slice(0, 24);
189
+ const graphInsights = buildGraphInsightsFromChanges(changedFiles);
190
+ return { lastCommit, branch, changedScopes, graphInsights };
77
191
  });
78
192
  const shortCommitId = lastCommit.hash.substring(0, 7);
79
193
  const snapshotId = `${branch}@${shortCommitId}`;
80
- console.log(chalk.dim(` Branch: ${chalk.white(branch)}`));
81
- console.log(chalk.dim(` Commit: ${chalk.white(shortCommitId)}`));
82
- // 2. Scan & Extract AST Skeleton
83
- console.log(chalk.blue('šŸ” Analyzing structural integrity (AST)...'));
84
- const { skeleton } = await track('ast extraction', async () => {
194
+ logDetail(` Branch: ${chalk.white(branch)}`);
195
+ logDetail(` Commit: ${chalk.white(shortCommitId)}`);
196
+ // 2. Scan and extract source structure
197
+ console.log(chalk.blue('šŸ” Scanning codebase structure...'));
198
+ const { skeleton } = await track('structure scan', async () => {
85
199
  const ig = ignore();
86
200
  const monoaiIgnorePath = path.join(process.cwd(), MONOAIIGNORE_FILENAME);
87
201
  if (!fs.existsSync(monoaiIgnorePath)) {
88
202
  fs.writeFileSync(monoaiIgnorePath, DEFAULT_MONOAIIGNORE, 'utf8');
89
- console.log(chalk.dim(` Created ${MONOAIIGNORE_FILENAME} template`));
203
+ logDetail(` Created ${MONOAIIGNORE_FILENAME} template`);
90
204
  }
91
205
  if (fs.existsSync('.gitignore')) {
92
206
  ig.add(fs.readFileSync('.gitignore').toString());
@@ -101,30 +215,32 @@ export const pushCommand = new Command('push')
101
215
  const items = fs.readdirSync(dir);
102
216
  for (const item of items) {
103
217
  const fullPath = path.join(dir, item);
104
- const relativePath = path.relative(process.cwd(), fullPath);
218
+ const relativePath = normalizeGitFilePath(path.relative(process.cwd(), fullPath));
105
219
  if (ig.ignores(relativePath))
106
220
  continue;
107
221
  if (fs.statSync(fullPath).isDirectory()) {
108
222
  scanDir(fullPath);
109
223
  }
110
224
  else if (/\.(ts|tsx|js|jsx)$/.test(item)) {
225
+ if (!isWhitelisted(relativePath))
226
+ continue;
111
227
  filesToAnalyze.push(fullPath);
112
228
  }
113
229
  }
114
230
  };
115
231
  scanDir(process.cwd());
116
232
  const skeleton = extractSkeleton(filesToAnalyze);
117
- console.log(chalk.dim(` Files analyzed: ${filesToAnalyze.length}`));
233
+ console.log(chalk.dim(` Files scanned: ${filesToAnalyze.length}`));
118
234
  return { skeleton };
119
235
  });
120
236
  const CONVEX_SITE_URL = process.env.MONOAI_CONVEX_SITE_URL ||
121
237
  process.env.MONOAI_CONVEX_URL ||
122
238
  config.get('convex_url') ||
123
239
  'https://majestic-crane-609.convex.site';
124
- // 3. AST-only upload. Knowledge graph processing is handled server-side.
125
- console.log(chalk.blue('šŸ“¦ Preparing AST payload for server-side graph pipeline...'));
240
+ // 3. AST-only upload. Knowledge Graph processing is handled server-side.
241
+ console.log(chalk.blue('šŸ“¦ Preparing data...'));
126
242
  // 4. Payload Construction
127
- const payload = await track('payload build', async () => ({
243
+ const payload = {
128
244
  name: path.basename(process.cwd()),
129
245
  snapshotId,
130
246
  branch: branch,
@@ -132,13 +248,15 @@ export const pushCommand = new Command('push')
132
248
  commitMessage: lastCommit.message,
133
249
  structure: JSON.stringify(skeleton), // Structured AST
134
250
  changedScopes,
251
+ graphInsights,
135
252
  syncStatus: 'processing',
136
- }));
253
+ };
137
254
  // 5. Send to Navigator (Convex)
138
- console.log(chalk.blue('šŸ“” Transmitting to Value Engine...'));
255
+ console.log(chalk.blue('šŸ“” Uploading to MonoAI...'));
139
256
  const transmitResult = await track('transmit', async () => {
140
257
  const response = await axios.post(`${CONVEX_SITE_URL}/cli/git-commit`, {
141
- codebaseData: payload
258
+ codebaseData: payload,
259
+ force,
142
260
  }, {
143
261
  headers: {
144
262
  'Authorization': `Bearer ${token}`
@@ -146,12 +264,24 @@ export const pushCommand = new Command('push')
146
264
  });
147
265
  return response.data;
148
266
  });
267
+ if (transmitResult?.deduped) {
268
+ console.log(chalk.yellow('⚠ This commit was already synced. No new snapshot was created.'));
269
+ if (transmitResult.message && verbose) {
270
+ console.log(chalk.dim(` ${transmitResult.message}`));
271
+ }
272
+ const totalMs = Date.now() - totalStart;
273
+ console.log(chalk.blue(`ā± Total time: ${(totalMs / 1000).toFixed(2)}s`));
274
+ return;
275
+ }
276
+ if (force) {
277
+ logDetail(' Force mode enabled: duplicate commit dedupe was bypassed.');
278
+ }
149
279
  if (transmitResult?.graphJobId) {
150
280
  const terminalStatuses = new Set(['done', 'error']);
151
281
  const waitStart = Date.now();
152
282
  const timeoutMs = 180000;
153
283
  const pollIntervalMs = 2000;
154
- console.log(chalk.blue(`🧠 Waiting for KG pipeline... (job: ${transmitResult.graphJobId})`));
284
+ console.log(chalk.blue('🧠 Building Knowledge Graph...'));
155
285
  let lastStatus = 'uploaded';
156
286
  let finalJob = null;
157
287
  while (Date.now() - waitStart < timeoutMs) {
@@ -160,54 +290,59 @@ export const pushCommand = new Command('push')
160
290
  if (!job)
161
291
  break;
162
292
  finalJob = job;
163
- if (job.status !== lastStatus) {
164
- console.log(chalk.dim(` ↳ KG status: ${job.status}`));
165
- lastStatus = job.status;
293
+ if (job.status !== lastStatus && verbose) {
294
+ console.log(chalk.dim(` ↳ Knowledge Graph status: ${job.status}`));
166
295
  }
296
+ lastStatus = job.status;
167
297
  if (terminalStatuses.has(job.status)) {
168
298
  break;
169
299
  }
170
300
  await new Promise((r) => setTimeout(r, pollIntervalMs));
171
301
  }
172
302
  const waitMs = Date.now() - waitStart;
173
- stageTimes.push({ stage: 'kg pipeline wait', ms: waitMs });
174
- console.log(chalk.dim(` ā± kg pipeline wait: ${(waitMs / 1000).toFixed(2)}s`));
303
+ stageTimes.push({ stage: 'knowledge graph wait', ms: waitMs });
304
+ logDetail(` ā± knowledge graph wait: ${(waitMs / 1000).toFixed(2)}s`);
175
305
  if (finalJob) {
176
306
  const fmt = (v) => (typeof v === 'number' ? `${(v / 1000).toFixed(2)}s` : 'n/a');
177
- console.log(chalk.blue('šŸ“Š KG timing'));
178
- console.log(chalk.dim(` - queue wait: ${fmt(finalJob.queueWaitMs)}`));
179
- console.log(chalk.dim(` - cognee/graph build: ${fmt(finalJob.cogneeMs)}`));
180
- console.log(chalk.dim(` - callback: ${fmt(finalJob.callbackMs)}`));
181
- console.log(chalk.dim(` - worker total: ${fmt(finalJob.workerTotalMs)}`));
182
- console.log(chalk.dim(` - pipeline total: ${fmt(finalJob.totalPipelineMs)}`));
307
+ if (verbose) {
308
+ console.log(chalk.blue('šŸ“Š Knowledge Graph timing'));
309
+ console.log(chalk.dim(` - queue wait: ${fmt(finalJob.queueWaitMs)}`));
310
+ console.log(chalk.dim(` - graph build: ${fmt(finalJob.cogneeMs)}`));
311
+ console.log(chalk.dim(` - callback: ${fmt(finalJob.callbackMs)}`));
312
+ console.log(chalk.dim(` - service total: ${fmt(finalJob.workerTotalMs)}`));
313
+ console.log(chalk.dim(` - total service time: ${fmt(finalJob.totalPipelineMs)}`));
314
+ }
183
315
  if (finalJob.status === 'error' && finalJob.error) {
184
- console.log(chalk.red(` - pipeline error: ${finalJob.error}`));
316
+ console.log(chalk.red(`āŒ Could not build Knowledge Graph: ${finalJob.error}`));
317
+ }
318
+ if (finalJob.status === 'done') {
319
+ console.log(chalk.green('āœ… Knowledge Graph is ready.'));
185
320
  }
186
321
  }
187
322
  else {
188
- console.log(chalk.yellow(' ⚠ KG timing unavailable (job status not returned)'));
323
+ console.log(chalk.yellow('⚠ Knowledge Graph status is unavailable. It may still be processing in the background.'));
189
324
  }
190
325
  }
191
- console.log(chalk.green('✨ [Navigator] Push complete! Check your dashboard for Alignment Analysis.'));
192
- console.log(chalk.dim(` Message: ${lastCommit.message.split('\n')[0]}`));
326
+ console.log(chalk.green('✨ Sync complete. Check your dashboard for updated insights.'));
327
+ logDetail(` Message: ${lastCommit.message.split('\n')[0]}`);
193
328
  const totalMs = Date.now() - totalStart;
194
- console.log(chalk.blue(`ā± Total: ${(totalMs / 1000).toFixed(2)}s`));
329
+ console.log(chalk.blue(`ā± Total time: ${(totalMs / 1000).toFixed(2)}s`));
195
330
  }
196
331
  catch (error) {
197
332
  const totalMs = Date.now() - totalStart;
198
333
  if (error.response?.status === 401) {
199
- console.error(chalk.red('āŒ Authentication Expired. Please run:'));
334
+ console.error(chalk.red('āŒ Your sign-in has expired. Please run:'));
200
335
  console.error(chalk.white(' npx monoai login'));
201
336
  }
202
337
  else {
203
338
  console.error(chalk.red('āŒ Sync failed:'), error.message);
204
339
  }
205
- if (stageTimes.length > 0) {
206
- console.log(chalk.yellow('\nā± Stage timing summary'));
340
+ if (verbose && stageTimes.length > 0) {
341
+ console.log(chalk.yellow('\nā± Stage timing details'));
207
342
  for (const item of stageTimes) {
208
343
  console.log(chalk.dim(` - ${item.stage}: ${(item.ms / 1000).toFixed(2)}s`));
209
344
  }
210
345
  }
211
- console.log(chalk.blue(`ā± Total: ${(totalMs / 1000).toFixed(2)}s`));
346
+ console.log(chalk.blue(`ā± Total time: ${(totalMs / 1000).toFixed(2)}s`));
212
347
  }
213
348
  });
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ const program = new Command();
6
6
  program
7
7
  .name('monoai')
8
8
  .description('MonoAI CLI - Strategic Navigator')
9
- .version('0.2.4');
9
+ .version('0.2.6');
10
10
  program.addCommand(loginCommand);
11
11
  program.addCommand(pushCommand);
12
12
  program.parse();
@@ -7,6 +7,29 @@ const SECRET_PATTERNS = [
7
7
  /AIza[0-9A-Za-z-_]{35}/g, // Google Cloud style
8
8
  /ghp_[a-zA-Z0-9]{36}/g // GitHub Personal Access Token
9
9
  ];
10
+ function toRepoRelativePath(rawPath) {
11
+ const normalized = String(rawPath || "").replace(/\\/g, "/").trim();
12
+ if (!normalized)
13
+ return "";
14
+ const cwd = process.cwd().replace(/\\/g, "/");
15
+ if (normalized.startsWith(cwd + "/")) {
16
+ return normalized.slice(cwd.length + 1);
17
+ }
18
+ if (normalized === cwd) {
19
+ return ".";
20
+ }
21
+ const relative = path.relative(process.cwd(), rawPath).replace(/\\/g, "/");
22
+ if (relative && !relative.startsWith("../") && relative !== "..") {
23
+ return relative.replace(/^\.?\//, "");
24
+ }
25
+ // Fallback for older absolute paths captured under this mono repo.
26
+ const repoMarker = "/Web_AIChat/";
27
+ const markerIdx = normalized.lastIndexOf(repoMarker);
28
+ if (markerIdx >= 0) {
29
+ return normalized.slice(markerIdx + repoMarker.length).replace(/^\.?\//, "");
30
+ }
31
+ return normalized.replace(/^\.?\//, "");
32
+ }
10
33
  export function extractSkeleton(filePaths) {
11
34
  const project = new Project();
12
35
  // šŸ›”ļø Security: File Filter
@@ -46,7 +69,7 @@ export function extractSkeleton(filePaths) {
46
69
  }
47
70
  }
48
71
  });
49
- const filePath = sourceFile.getFilePath();
72
+ const filePath = toRepoRelativePath(sourceFile.getFilePath());
50
73
  const skeleton = {
51
74
  functions: [],
52
75
  classes: [],
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "monoai",
3
3
  "type": "module",
4
- "version": "0.2.6",
4
+ "version": "0.2.8",
5
5
  "description": "MonoAI CLI for syncing codebase history",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
- "monoai": "./dist/index.js"
8
+ "monoai": "dist/index.js"
9
9
  },
10
10
  "files": [
11
11
  "dist",
@@ -22,6 +22,7 @@
22
22
  "commander": "^11.1.0",
23
23
  "conf": "^15.1.0",
24
24
  "ignore": "^7.0.5",
25
+ "monoai": "^0.2.7",
25
26
  "open": "^9.1.0",
26
27
  "ora": "^7.0.1",
27
28
  "simple-git": "^3.21.0",