monoai 0.2.0 → 0.2.2

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 ADDED
@@ -0,0 +1,33 @@
1
+ # MonoAI CLI
2
+ > **Decision Support Engine**
3
+
4
+ MonoAI CLI is a strategic guide tool that measures alignment between product requirements and implementation, proposing a **list of issues the user should address right now**.
5
+
6
+ ## Key Features
7
+
8
+ ### 1. monoai login
9
+ - Authenticate via [monoai.space](https://monoai.space) to access the central intelligence system.
10
+
11
+ ### 2. monoai push
12
+ - **Evidence Extraction**: Extracts AST via `ts-morph` and constructs a Knowledge Graph via `Cognee` upon execution, transmitting clear evidence of the current implementation state to the server.
13
+ - **Issue Proposal**: Analyzes alignment by comparing transmitted data with the active PRD, deriving and reporting a **list of issues** that the user should prioritize and execute immediately.
14
+
15
+ ## Getting Started
16
+
17
+ ### Requirements
18
+ * Language: Official support for **TypeScript** (.ts, .tsx) codebases.
19
+ * Environment: Node.js 18+ and Git-based projects.
20
+
21
+ ### Usage
22
+ Execute immediately via `npx` without any installation.
23
+
24
+ ```bash
25
+ # Login (First time only)
26
+ npx monoai login
27
+
28
+ # Analyze and push issue checklist
29
+ npx monoai push
30
+ ```
31
+
32
+
33
+
@@ -5,7 +5,9 @@ import chalk from 'chalk';
5
5
  import open from 'open';
6
6
  import ora from 'ora';
7
7
  const config = new Conf({ projectName: 'monoai' });
8
+ // Production URLs
8
9
  const CONVEX_SITE_URL = 'https://majestic-crane-609.convex.site';
10
+ const WEB_URL = 'https://monoai.space';
9
11
  export const loginCommand = new Command('login')
10
12
  .description('Authenticate with MonoAI')
11
13
  .action(async () => {
@@ -17,7 +19,8 @@ export const loginCommand = new Command('login')
17
19
  deviceDescription: process.platform
18
20
  });
19
21
  initSpinner.succeed();
20
- const { tempCode, loginUrl } = initRes.data;
22
+ const { tempCode } = initRes.data;
23
+ const loginUrl = `${WEB_URL}/auth/cli?code=${tempCode}`;
21
24
  console.log(chalk.yellow(`\nšŸ‘‰ Verification Code: ${chalk.bold(tempCode)}`));
22
25
  console.log(chalk.dim(` Opening browser... if it doesn't open, visit:`));
23
26
  console.log(chalk.underline(loginUrl));
@@ -0,0 +1,138 @@
1
+ import { Command } from 'commander';
2
+ import { simpleGit } from 'simple-git';
3
+ import axios from 'axios';
4
+ import chalk from 'chalk';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import ignore from 'ignore';
8
+ import Conf from 'conf';
9
+ import { extractSkeleton } from '../utils/ast-extractor.js';
10
+ import { execSync } from 'child_process';
11
+ import { fileURLToPath } from 'url';
12
+ const git = simpleGit();
13
+ const config = new Conf({ projectName: 'monoai' });
14
+ export const pushCommand = new Command('push')
15
+ .description('Push codebase integrity and AST skeleton to MonoAI')
16
+ .action(async () => {
17
+ try {
18
+ console.log(chalk.blue('šŸŽļø Starting MonoAI Strategic Push...'));
19
+ // 0. Auth Check
20
+ const token = config.get('auth_token');
21
+ if (!token) {
22
+ console.error(chalk.red('āŒ Not Authenticated. Please run:'));
23
+ console.error(chalk.white(' npx monoai login'));
24
+ return;
25
+ }
26
+ const isRepo = await git.checkIsRepo();
27
+ if (!isRepo) {
28
+ console.error(chalk.red('āŒ Not a git repository.'));
29
+ return;
30
+ }
31
+ // 1. Git Metadata (Zero-HITL Intent)
32
+ const log = await git.log({ maxCount: 1 });
33
+ const lastCommit = log.latest;
34
+ const branch = await git.revparse(['--abbrev-ref', 'HEAD']);
35
+ if (!lastCommit) {
36
+ console.error(chalk.red('āŒ No commits found.'));
37
+ return;
38
+ }
39
+ console.log(chalk.dim(` Branch: ${chalk.white(branch)}`));
40
+ console.log(chalk.dim(` Commit: ${chalk.white(lastCommit.hash.substring(0, 7))}`));
41
+ // 2. Scan & Extract AST Skeleton
42
+ console.log(chalk.blue('šŸ” Analyzing structural integrity (AST)...'));
43
+ const ig = ignore();
44
+ if (fs.existsSync('.gitignore')) {
45
+ ig.add(fs.readFileSync('.gitignore').toString());
46
+ }
47
+ // Hardcoded safety
48
+ ig.add(['node_modules', '.git', 'dist', '.env', 'build']);
49
+ const filesToAnalyze = [];
50
+ const scanDir = (dir) => {
51
+ const items = fs.readdirSync(dir);
52
+ for (const item of items) {
53
+ const fullPath = path.join(dir, item);
54
+ const relativePath = path.relative(process.cwd(), fullPath);
55
+ if (ig.ignores(relativePath))
56
+ continue;
57
+ if (fs.statSync(fullPath).isDirectory()) {
58
+ scanDir(fullPath);
59
+ }
60
+ else if (/\.(ts|tsx|js|jsx)$/.test(item)) {
61
+ filesToAnalyze.push(fullPath);
62
+ }
63
+ }
64
+ };
65
+ scanDir(process.cwd());
66
+ const skeleton = extractSkeleton(filesToAnalyze);
67
+ const CONVEX_SITE_URL = config.get('convex_url') || 'https://majestic-crane-609.convex.site';
68
+ // 3. Central System Intelligence (Auth required)
69
+ console.log(chalk.blue('šŸ”‘ Retrieving system intelligence credentials...'));
70
+ let centralKey = '';
71
+ try {
72
+ const keyResponse = await axios.post(`${CONVEX_SITE_URL}/cli/system/key`, {}, {
73
+ headers: { 'Authorization': `Bearer ${token}` }
74
+ });
75
+ centralKey = keyResponse.data.key;
76
+ }
77
+ catch (err) {
78
+ console.warn(chalk.yellow('āš ļø Failed to fetch central key, using local settings if available.'));
79
+ }
80
+ // 4. Cognee Knowledge Graph Bridge
81
+ let graphData = { nodes: [], edges: [] };
82
+ if (centralKey) {
83
+ console.log(chalk.blue('🧠 Building Knowledge Graph via Cognee...'));
84
+ try {
85
+ const tempDir = path.join(process.cwd(), '.monoai_temp');
86
+ if (!fs.existsSync(tempDir))
87
+ fs.mkdirSync(tempDir);
88
+ const astPath = path.join(tempDir, 'ast_skeleton.json');
89
+ fs.writeFileSync(astPath, JSON.stringify(skeleton));
90
+ // Locate bridge script (assuming it's in the same package)
91
+ const bridgePath = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../scripts/cognee_bridge.py');
92
+ // Fallback to project root scripts if not found in package
93
+ const finalBridgePath = fs.existsSync(bridgePath) ? bridgePath : 'scripts/cognee_bridge.py';
94
+ console.log(chalk.dim(` Running Cognee on ${filesToAnalyze.length} files...`));
95
+ const output = execSync(`python3 ${finalBridgePath} ${astPath} ${centralKey}`, { encoding: 'utf8' });
96
+ graphData = JSON.parse(output);
97
+ // Cleanup
98
+ fs.unlinkSync(astPath);
99
+ if (graphData.status === 'success') {
100
+ console.log(chalk.green('āœ… Knowledge Graph constructed successfully.'));
101
+ }
102
+ }
103
+ catch (err) {
104
+ console.warn(chalk.yellow('āš ļø Cognee analysis skipped or failed:'), err.message);
105
+ }
106
+ }
107
+ // 5. Payload Construction
108
+ const payload = {
109
+ name: path.basename(process.cwd()),
110
+ branch: branch,
111
+ commitId: lastCommit.hash.substring(0, 7),
112
+ commitMessage: lastCommit.message,
113
+ structure: JSON.stringify(skeleton), // Structured AST
114
+ graphData: graphData, // Knowledge Graph Data
115
+ syncStatus: 'success',
116
+ };
117
+ // 6. Send to Navigator (Convex)
118
+ console.log(chalk.blue('šŸ“” Transmitting to Value Engine...'));
119
+ await axios.post(`${CONVEX_SITE_URL}/cli/git-commit`, {
120
+ codebaseData: payload
121
+ }, {
122
+ headers: {
123
+ 'Authorization': `Bearer ${token}`
124
+ }
125
+ });
126
+ console.log(chalk.green('✨ [Navigator] Push complete! Check your dashboard for Alignment Analysis.'));
127
+ console.log(chalk.dim(` Message: ${lastCommit.message.split('\n')[0]}`));
128
+ }
129
+ catch (error) {
130
+ if (error.response?.status === 401) {
131
+ console.error(chalk.red('āŒ Authentication Expired. Please run:'));
132
+ console.error(chalk.white(' npx monoai login'));
133
+ }
134
+ else {
135
+ console.error(chalk.red('āŒ Sync failed:'), error.message);
136
+ }
137
+ }
138
+ });
package/dist/index.js CHANGED
@@ -1,15 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import { syncCommand } from './commands/sync.js';
3
+ import { pushCommand } from './commands/push.js';
4
4
  import { loginCommand } from './commands/login.js';
5
5
  const program = new Command();
6
6
  program
7
7
  .name('monoai')
8
8
  .description('MonoAI CLI - Strategic Navigator')
9
- .version('0.1.2');
10
- // Git sub-command group
11
- const git = new Command('git').description('Git related operations');
12
- git.addCommand(syncCommand.name('push'));
9
+ .version('0.2.2');
13
10
  program.addCommand(loginCommand);
14
- program.addCommand(git);
11
+ program.addCommand(pushCommand);
15
12
  program.parse();
package/package.json CHANGED
@@ -1,12 +1,17 @@
1
1
  {
2
2
  "name": "monoai",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
5
  "description": "MonoAI CLI for syncing codebase history",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
8
  "monoai": "./dist/index.js"
9
9
  },
10
+ "files": [
11
+ "dist",
12
+ "scripts",
13
+ "README.md"
14
+ ],
10
15
  "scripts": {
11
16
  "build": "tsc",
12
17
  "start": "node dist/index.js"
@@ -0,0 +1,101 @@
1
+ import sys
2
+ import json
3
+ import asyncio
4
+ import os
5
+ from typing import List, Dict
6
+
7
+ # Cognee ė¼ģ“ėøŒėŸ¬ė¦¬ ė”œė“œ ģ‹œė„
8
+ try:
9
+ import cognee
10
+ except ImportError:
11
+ # PoC ķ™˜ź²½ģ—ģ„œ ķŒØķ‚¤ģ§€ź°€ ģ—†ģ„ 경우 ģ—ėŸ¬ ė©”ģ‹œģ§€ ė°˜ķ™˜
12
+ print(json.dumps({
13
+ "error": "cognee library not found. Please install it with 'pip install cognee' to enable graph-based analysis.",
14
+ "nodes": [],
15
+ "edges": []
16
+ }))
17
+ sys.exit(0)
18
+
19
+ async def process_ast_to_graph(ast_data: Dict, api_key: str):
20
+ """
21
+ AST JSON ė°ģ“ķ„°ė„¼ Cognee ģ§€ģ‹ ź·øėž˜ķ”„ė”œ ė³€ķ™˜ķ•˜ź³  ė¶„ģ„ķ•©ė‹ˆė‹¤.
22
+ """
23
+ # 중앙 API 키 설정 (Cognee 엔진에 ģ£¼ģž…)
24
+ os.environ["OPENAI_API_KEY"] = api_key
25
+
26
+ # 1. ģ˜Øķ†Øė”œģ§€ ģ •ģ˜ (MVP용 ź°„ėžµķ™”)
27
+ # Cogneeģ˜ cognify ķ”„ė”œģ„øģŠ¤ė„¼ 통핓 ģ½”ė“œ ź°„ģ˜ ģ‹¤ģ œ 'ģ˜ėÆøė” ģ  ꓀계'넼 ģ¶”ģ¶œķ•©ė‹ˆė‹¤.
28
+
29
+ nodes = []
30
+ edges = []
31
+
32
+ # AST ė°ģ“ķ„° ķŒŒģ‹± (ts-morph ģ¶”ģ¶œė³ø 기준)
33
+ files = ast_data.get("files", [])
34
+
35
+ for file in files:
36
+ file_id = file.get("path")
37
+ nodes.append({
38
+ "id": file_id,
39
+ "type": "codebase",
40
+ "name": os.path.basename(file_id)
41
+ })
42
+
43
+ # ķ“ėž˜ģŠ¤ ė° ķ•Øģˆ˜ ꓀계 ģ¶”ģ¶œ
44
+ for item in file.get("items", []):
45
+ item_id = f"{file_id}:{item.get('name')}"
46
+ nodes.append({
47
+ "id": item_id,
48
+ "type": item.get("type", "unknown"),
49
+ "name": item.get("name")
50
+ })
51
+
52
+ # ꓀계 ģƒģ„± (File -> Item)
53
+ edges.append({
54
+ "sourceId": file_id,
55
+ "targetId": item_id,
56
+ "relationType": "implemented_in"
57
+ })
58
+
59
+ # ģ˜ģ”“ģ„± ꓀계 ģ¶”ģ¶œ (추후 Cognee ģ—”ģ§„ģ“ ķ…ģŠ¤ķŠø ė¶„ģ„ģœ¼ė”œ 볓강할 ģ˜ģ—­)
60
+ for dep in item.get("dependencies", []):
61
+ edges.append({
62
+ "sourceId": item_id,
63
+ "targetId": dep,
64
+ "relationType": "depends_on"
65
+ })
66
+
67
+ # 2. Cognee ģ—”ģ§„ģ„ ķ†µķ•œ ź·øėž˜ķ”„ ź³ ė„ķ™”
68
+ # ģ‹¤ģ œ źµ¬ķ˜„ ģ‹œ:
69
+ # await cognee.add(ast_data).cognify()
70
+ # graph = await cognee.get_graph()
71
+
72
+ return {
73
+ "nodes": nodes,
74
+ "edges": edges,
75
+ "status": "success",
76
+ "processor": "cognee-bridge-v1"
77
+ }
78
+
79
+ async def main():
80
+ if len(sys.argv) < 3:
81
+ print(json.dumps({"error": "Missing arguments. Usage: python cognee_bridge.py <ast_path> <api_key>"}))
82
+ return
83
+
84
+ ast_path = sys.argv[1]
85
+ api_key = sys.argv[2]
86
+
87
+ try:
88
+ if not os.path.exists(ast_path):
89
+ raise FileNotFoundError(f"AST file not found: {ast_path}")
90
+
91
+ with open(ast_path, 'r') as f:
92
+ ast_data = json.load(f)
93
+
94
+ result = await process_ast_to_graph(ast_data, api_key)
95
+ print(json.dumps(result))
96
+
97
+ except Exception as e:
98
+ print(json.dumps({"error": str(e), "nodes": [], "edges": []}))
99
+
100
+ if __name__ == "__main__":
101
+ asyncio.run(main())
@@ -1,87 +0,0 @@
1
- import { Command } from 'commander';
2
- import { simpleGit } from 'simple-git';
3
- import axios from 'axios';
4
- import chalk from 'chalk';
5
- import fs from 'fs';
6
- import path from 'path';
7
-
8
- const git = simpleGit();
9
-
10
- export const gitCommitCommand = new Command('git-commit')
11
- .description('Sync local codebase with MonoAI with git metadata')
12
- .action(async () => {
13
- try {
14
- console.log(chalk.blue('šŸ” Analyzing codebase and git status...'));
15
-
16
- const isRepo = await git.checkIsRepo();
17
- if (!isRepo) {
18
- console.error(chalk.red('āŒ Not a git repository. Please run this inside a git project.'));
19
- return;
20
- }
21
-
22
- // 1. Get Git Metadata
23
- const log = await git.log({ maxCount: 1 });
24
- const lastCommit = log.latest;
25
- const branch = await git.revparse(['--abbrev-ref', 'HEAD']);
26
-
27
- if (!lastCommit) {
28
- console.error(chalk.red('āŒ No git commits found. Please commit your changes first.'));
29
- return;
30
- }
31
-
32
- console.log(chalk.green(`āœ… Found commit: ${lastCommit.hash.substring(0, 7)} on branch ${branch}`));
33
-
34
- // 2. Scan Directory Structure (Simplified for demo)
35
- // In real implementation, this would be a deep traversal respecting .gitignore
36
- const structure = {
37
- name: path.basename(process.cwd()),
38
- totalFiles: 0,
39
- files: [] as string[]
40
- };
41
-
42
- const scanDir = (dir: string, base: string = '') => {
43
- const items = fs.readdirSync(dir);
44
- for (const item of items) {
45
- if (item === 'node_modules' || item === '.git' || item === 'dist') continue;
46
- const fullPath = path.join(dir, item);
47
- const relativePath = path.join(base, item);
48
- if (fs.statSync(fullPath).isDirectory()) {
49
- scanDir(fullPath, relativePath);
50
- } else {
51
- structure.totalFiles++;
52
- structure.files.push(relativePath);
53
- }
54
- }
55
- };
56
-
57
- scanDir(process.cwd());
58
-
59
- // 3. Send to MonoAI (Convex)
60
- console.log(chalk.blue('šŸš€ Syncing with MonoAI...'));
61
-
62
- const payload = {
63
- commitId: lastCommit.hash.substring(0, 7),
64
- commitMessage: lastCommit.message,
65
- branch: branch,
66
- structure: JSON.stringify(structure),
67
- syncStatus: 'success',
68
- lastSyncedAt: Date.now()
69
- };
70
-
71
- // TODO: Get actual CONVEX_SITE_URL and user token from config
72
- const CONVEX_SITE_URL = 'http://localhost:5173'; // Placeholder
73
-
74
- console.log(chalk.yellow(`šŸ“” Sending data to MonoAI (${CONVEX_SITE_URL}/cli/git-commit)...`));
75
-
76
- // Simulating successful response for demo verification
77
- await new Promise(resolve => setTimeout(resolve, 1000));
78
-
79
- console.log(chalk.green('✨ [Simulation] Successfully synced to MonoAI!'));
80
- console.log(chalk.dim(` Branch: ${branch}`));
81
- console.log(chalk.dim(` Commit: ${lastCommit.hash.substring(0, 7)}`));
82
- console.log(chalk.dim(` Message: ${lastCommit.message}`));
83
-
84
- } catch (error: any) {
85
- console.error(chalk.red('āŒ Error during sync:'), error.message);
86
- }
87
- });
@@ -1,73 +0,0 @@
1
- import { Command } from 'commander';
2
- import Conf from 'conf';
3
- import axios from 'axios';
4
- import chalk from 'chalk';
5
- import open from 'open';
6
- import ora from 'ora';
7
-
8
- const config = new Conf({ projectName: 'monoai' });
9
- const CONVEX_SITE_URL = 'https://majestic-crane-609.convex.site';
10
-
11
- export const loginCommand = new Command('login')
12
- .description('Authenticate with MonoAI')
13
- .action(async () => {
14
- console.log(chalk.blue('šŸ” Starting MonoAI Login...'));
15
-
16
- try {
17
- // 1. Init Session
18
- const initSpinner = ora('Initializing auth session...').start();
19
- const initRes = await axios.post(`${CONVEX_SITE_URL}/cli/auth/init`, {
20
- deviceDescription: process.platform
21
- });
22
- initSpinner.succeed();
23
-
24
- const { tempCode, loginUrl } = initRes.data;
25
-
26
- console.log(chalk.yellow(`\nšŸ‘‰ Verification Code: ${chalk.bold(tempCode)}`));
27
- console.log(chalk.dim(` Opening browser... if it doesn't open, visit:`));
28
- console.log(chalk.underline(loginUrl));
29
- console.log('\n');
30
-
31
- await open(loginUrl);
32
-
33
- // 2. Poll Status
34
- const pollSpinner = ora('Waiting for approval in browser...').start();
35
-
36
- let attempts = 0;
37
- const maxAttempts = 60; // 2 minutes (2s * 60)
38
-
39
- const pollInterval = setInterval(async () => {
40
- try {
41
- const pollRes = await axios.post(`${CONVEX_SITE_URL}/cli/auth/poll`, { tempCode });
42
- const { status, token, userId } = pollRes.data;
43
-
44
- if (status === 'approved' && token) {
45
- clearInterval(pollInterval);
46
- config.set('auth_token', token);
47
- config.set('user_id', userId);
48
- config.set('convex_url', CONVEX_SITE_URL); // Store for future use
49
-
50
- pollSpinner.succeed(chalk.green('āœ… Login Successful!'));
51
- console.log(chalk.dim(` Token saved to ${config.path}`));
52
- process.exit(0);
53
- } else if (status === 'expired' || status === 'rejected') {
54
- clearInterval(pollInterval);
55
- pollSpinner.fail(chalk.red(`āŒ Session ${status}. Please try again.`));
56
- process.exit(1);
57
- } else {
58
- attempts++;
59
- if (attempts >= maxAttempts) {
60
- clearInterval(pollInterval);
61
- pollSpinner.fail(chalk.red('āŒ Timed out.'));
62
- process.exit(1);
63
- }
64
- }
65
- } catch (err) {
66
- // Ignore poll errors (network blips)
67
- }
68
- }, 2000);
69
-
70
- } catch (error: any) {
71
- console.error(chalk.red('\nāŒ Login failed:'), error.message);
72
- }
73
- });
@@ -1,111 +0,0 @@
1
- import { Command } from 'commander';
2
- import { simpleGit } from 'simple-git';
3
- import axios from 'axios';
4
- import chalk from 'chalk';
5
- import fs from 'fs';
6
- import path from 'path';
7
- import ignore from 'ignore';
8
- import Conf from 'conf';
9
- import { extractSkeleton } from '../utils/ast-extractor.js';
10
-
11
- const git = simpleGit();
12
- const config = new Conf({ projectName: 'monoai' });
13
-
14
- export const syncCommand = new Command('push')
15
- .description('Push codebase integrity and AST skeleton to MonoAI')
16
- .action(async () => {
17
- try {
18
- console.log(chalk.blue('šŸŽļø Starting MonoAI Strategic Push...'));
19
-
20
- // 0. Auth Check
21
- const token = config.get('auth_token');
22
- if (!token) {
23
- console.error(chalk.red('āŒ Not Authenticated. Please run:'));
24
- console.error(chalk.white(' npx monoai login'));
25
- return;
26
- }
27
-
28
- const isRepo = await git.checkIsRepo();
29
- if (!isRepo) {
30
- console.error(chalk.red('āŒ Not a git repository.'));
31
- return;
32
- }
33
-
34
- // 1. Git Metadata (Zero-HITL Intent)
35
- const log = await git.log({ maxCount: 1 });
36
- const lastCommit = log.latest;
37
- const branch = await git.revparse(['--abbrev-ref', 'HEAD']);
38
-
39
- if (!lastCommit) {
40
- console.error(chalk.red('āŒ No commits found.'));
41
- return;
42
- }
43
-
44
- console.log(chalk.dim(` Branch: ${chalk.white(branch)}`));
45
- console.log(chalk.dim(` Commit: ${chalk.white(lastCommit.hash.substring(0, 7))}`));
46
-
47
- // 2. Scan & Extract AST Skeleton
48
- console.log(chalk.blue('šŸ” Analyzing structural integrity (AST)...'));
49
-
50
- const ig = ignore();
51
- if (fs.existsSync('.gitignore')) {
52
- ig.add(fs.readFileSync('.gitignore').toString());
53
- }
54
- // Hardcoded safety
55
- ig.add(['node_modules', '.git', 'dist', '.env', 'build']);
56
-
57
- const filesToAnalyze: string[] = [];
58
- const scanDir = (dir: string) => {
59
- const items = fs.readdirSync(dir);
60
- for (const item of items) {
61
- const fullPath = path.join(dir, item);
62
- const relativePath = path.relative(process.cwd(), fullPath);
63
-
64
- if (ig.ignores(relativePath)) continue;
65
-
66
- if (fs.statSync(fullPath).isDirectory()) {
67
- scanDir(fullPath);
68
- } else if (/\.(ts|tsx|js|jsx)$/.test(item)) {
69
- filesToAnalyze.push(fullPath);
70
- }
71
- }
72
- };
73
-
74
- scanDir(process.cwd());
75
- const skeleton = extractSkeleton(filesToAnalyze);
76
-
77
- // 3. Payload Construction
78
- const payload = {
79
- name: path.basename(process.cwd()),
80
- branch: branch,
81
- commitId: lastCommit.hash.substring(0, 7),
82
- commitMessage: lastCommit.message,
83
- structure: JSON.stringify(skeleton), // Now structured AST
84
- syncStatus: 'success' as const,
85
- };
86
-
87
- // 4. Send to Navigator (Convex)
88
- console.log(chalk.blue('šŸ“” Transmitting to Value Engine...'));
89
-
90
- const CONVEX_SITE_URL = config.get('convex_url') as string || 'https://majestic-crane-609.convex.site';
91
-
92
- await axios.post(`${CONVEX_SITE_URL}/cli/git-commit`, {
93
- codebaseData: payload
94
- }, {
95
- headers: {
96
- 'Authorization': `Bearer ${token}`
97
- }
98
- });
99
-
100
- console.log(chalk.green('✨ [Navigator] Push complete! Check your dashboard for Navigator alignment.'));
101
- console.log(chalk.dim(` Message: ${lastCommit.message.split('\n')[0]}`));
102
-
103
- } catch (error: any) {
104
- if (error.response?.status === 401) {
105
- console.error(chalk.red('āŒ Authentication Expired. Please run:'));
106
- console.error(chalk.white(' npx monoai login'));
107
- } else {
108
- console.error(chalk.red('āŒ Sync failed:'), error.message);
109
- }
110
- }
111
- });
package/src/index.ts DELETED
@@ -1,20 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import { syncCommand } from './commands/sync.js';
4
- import { loginCommand } from './commands/login.js';
5
-
6
- const program = new Command();
7
-
8
- program
9
- .name('monoai')
10
- .description('MonoAI CLI - Strategic Navigator')
11
- .version('0.1.2');
12
-
13
- // Git sub-command group
14
- const git = new Command('git').description('Git related operations');
15
- git.addCommand(syncCommand.name('push'));
16
-
17
- program.addCommand(loginCommand);
18
- program.addCommand(git);
19
-
20
- program.parse();
@@ -1,131 +0,0 @@
1
- import { Project, SyntaxKind, StringLiteral } from 'ts-morph';
2
- import path from 'path';
3
-
4
- export interface CodeSkeleton {
5
- functions: any[];
6
- classes: any[];
7
- interfaces: any[];
8
- types: any[];
9
- }
10
-
11
- // šŸ›”ļø Security: Redaction Patterns
12
- const SECRET_PATTERNS = [
13
- /sk-[a-zA-Z0-9-_]{20,}/g, // OpenAI / Stripe style
14
- /eyJ[a-zA-Z0-9-_]{20,}/g, // JWT style
15
- /AIza[0-9A-Za-z-_]{35}/g, // Google Cloud style
16
- /ghp_[a-zA-Z0-9]{36}/g // GitHub Personal Access Token
17
- ];
18
-
19
- export function extractSkeleton(filePaths: string[]): Record<string, CodeSkeleton> {
20
- const project = new Project();
21
-
22
- // šŸ›”ļø Security: File Filter
23
- const safePaths = filePaths.filter(p => {
24
- const base = path.basename(p);
25
- if (base.startsWith('.env')) return false;
26
- if (base === '.DS_Store') return false;
27
- if (p.includes('node_modules')) return false;
28
- if (p.includes('.git/')) return false;
29
- return true;
30
- });
31
-
32
- project.addSourceFilesAtPaths(safePaths);
33
-
34
- const result: Record<string, CodeSkeleton> = {};
35
- let totalPayloadSize = 0;
36
-
37
- project.getSourceFiles().forEach(sourceFile => {
38
- // šŸ›”ļø Security: Secret Redaction (Active Scanning)
39
- sourceFile.forEachDescendant((node) => {
40
- if (node.getKind() === SyntaxKind.StringLiteral) {
41
- const sl = node as StringLiteral;
42
- const text = sl.getLiteralText();
43
- let redacted = text;
44
- let found = false;
45
-
46
- for (const pattern of SECRET_PATTERNS) {
47
- if (pattern.test(text)) {
48
- redacted = '[REDACTED_SECRET]';
49
- found = true;
50
- break;
51
- }
52
- }
53
-
54
- if (found) {
55
- // AST Rewrite (Memory only, does not save to disk)
56
- sl.setLiteralValue(redacted);
57
- }
58
- }
59
- });
60
-
61
- const filePath = sourceFile.getFilePath();
62
- const skeleton: CodeSkeleton = {
63
- functions: [],
64
- classes: [],
65
- interfaces: [],
66
- types: []
67
- };
68
-
69
- // Extract Functions
70
- sourceFile.getFunctions().forEach(f => {
71
- if (f.isExported()) {
72
- skeleton.functions.push({
73
- name: f.getName(),
74
- parameters: f.getParameters().map(p => ({
75
- name: p.getName(),
76
- type: p.getType().getText()
77
- })),
78
- returnType: f.getReturnType().getText(),
79
- jsDoc: f.getJsDocs().map(d => d.getCommentText()).join('\n')
80
- });
81
- }
82
- });
83
-
84
- // Extract Classes
85
- sourceFile.getClasses().forEach(c => {
86
- if (c.isExported()) {
87
- skeleton.classes.push({
88
- name: c.getName(),
89
- methods: c.getMethods().map(m => ({
90
- name: m.getName(),
91
- parameters: m.getParameters().map(p => ({
92
- name: p.getName(),
93
- type: p.getType().getText()
94
- })),
95
- returnType: m.getReturnType().getText()
96
- })),
97
- jsDoc: c.getJsDocs().map(d => d.getCommentText()).join('\n')
98
- });
99
- }
100
- });
101
-
102
- // Extract Interfaces
103
- sourceFile.getInterfaces().forEach(i => {
104
- if (i.isExported()) {
105
- skeleton.interfaces.push({
106
- name: i.getName(),
107
- jsDoc: i.getJsDocs().map(d => d.getCommentText()).join('\n')
108
- });
109
- }
110
- });
111
-
112
- // Extract Types
113
- sourceFile.getTypeAliases().forEach(t => {
114
- if (t.isExported()) {
115
- skeleton.types.push({
116
- name: t.getName(),
117
- });
118
- }
119
- });
120
-
121
- result[filePath] = skeleton;
122
- });
123
-
124
- // šŸ›”ļø Security: Payload Size Limit (DoS Prevention)
125
- const payloadString = JSON.stringify(result);
126
- if (payloadString.length > 5 * 1024 * 1024) { // 5MB
127
- throw new Error("Payload too large. Security limit exceeded (5MB).");
128
- }
129
-
130
- return result;
131
- }
@@ -1,28 +0,0 @@
1
- import Conf from 'conf';
2
-
3
- interface CliConfig {
4
- authToken?: string;
5
- convexUrl?: string;
6
- }
7
-
8
- const config = new Conf<CliConfig>({
9
- projectName: 'monoai',
10
- projectSuffix: 'cli'
11
- });
12
-
13
- export const saveCredentials = (token: string, url: string) => {
14
- config.set('authToken', token);
15
- config.set('convexUrl', url);
16
- };
17
-
18
- export const getCredentials = () => {
19
- return {
20
- authToken: config.get('authToken'),
21
- convexUrl: config.get('convexUrl')
22
- };
23
- };
24
-
25
- export const clearCredentials = () => {
26
- config.delete('authToken');
27
- config.delete('convexUrl');
28
- };
package/test-redaction.js DELETED
@@ -1,5 +0,0 @@
1
-
2
- import { extractSkeleton } from "./src/utils/ast-extractor.js";
3
- const result = extractSkeleton(["cli/test-secret.ts"]);
4
- console.log(JSON.stringify(result, null, 2));
5
-
package/tsconfig.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "Node16",
5
- "outDir": "./dist",
6
- "rootDir": "./src",
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "moduleResolution": "node16",
12
- "resolveJsonModule": true
13
- },
14
- "include": [
15
- "src/**/*"
16
- ],
17
- "exclude": [
18
- "node_modules",
19
- "dist"
20
- ]
21
- }