monoai 0.2.1 → 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
+
@@ -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.1",
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,77 +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
-
10
- // Production URLs
11
- const CONVEX_SITE_URL = 'https://majestic-crane-609.convex.site';
12
- const WEB_URL = 'https://monoai.space';
13
-
14
- export const loginCommand = new Command('login')
15
- .description('Authenticate with MonoAI')
16
- .action(async () => {
17
- console.log(chalk.blue('🔐 Starting MonoAI Login...'));
18
-
19
- try {
20
- // 1. Init Session
21
- const initSpinner = ora('Initializing auth session...').start();
22
- const initRes = await axios.post(`${CONVEX_SITE_URL}/cli/auth/init`, {
23
- deviceDescription: process.platform
24
- });
25
- initSpinner.succeed();
26
-
27
- const { tempCode } = initRes.data;
28
- const loginUrl = `${WEB_URL}/auth/cli?code=${tempCode}`;
29
-
30
- console.log(chalk.yellow(`\n👉 Verification Code: ${chalk.bold(tempCode)}`));
31
- console.log(chalk.dim(` Opening browser... if it doesn't open, visit:`));
32
- console.log(chalk.underline(loginUrl));
33
- console.log('\n');
34
-
35
- await open(loginUrl);
36
-
37
- // 2. Poll Status
38
- const pollSpinner = ora('Waiting for approval in browser...').start();
39
-
40
- let attempts = 0;
41
- const maxAttempts = 60; // 2 minutes (2s * 60)
42
-
43
- const pollInterval = setInterval(async () => {
44
- try {
45
- const pollRes = await axios.post(`${CONVEX_SITE_URL}/cli/auth/poll`, { tempCode });
46
- const { status, token, userId } = pollRes.data;
47
-
48
- if (status === 'approved' && token) {
49
- clearInterval(pollInterval);
50
- config.set('auth_token', token);
51
- config.set('user_id', userId);
52
- config.set('convex_url', CONVEX_SITE_URL); // Store for future use
53
-
54
- pollSpinner.succeed(chalk.green('✅ Login Successful!'));
55
- console.log(chalk.dim(` Token saved to ${config.path}`));
56
- process.exit(0);
57
- } else if (status === 'expired' || status === 'rejected') {
58
- clearInterval(pollInterval);
59
- pollSpinner.fail(chalk.red(`❌ Session ${status}. Please try again.`));
60
- process.exit(1);
61
- } else {
62
- attempts++;
63
- if (attempts >= maxAttempts) {
64
- clearInterval(pollInterval);
65
- pollSpinner.fail(chalk.red('❌ Timed out.'));
66
- process.exit(1);
67
- }
68
- }
69
- } catch (err) {
70
- // Ignore poll errors (network blips)
71
- }
72
- }, 2000);
73
-
74
- } catch (error: any) {
75
- console.error(chalk.red('\n❌ Login failed:'), error.message);
76
- }
77
- });
@@ -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
- }