@codecora/cli 0.0.3
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 +242 -0
- package/bin/cora.js +183 -0
- package/dist/api/client.js +221 -0
- package/dist/api/types.js +33 -0
- package/dist/commands/auth.js +255 -0
- package/dist/commands/hook.js +160 -0
- package/dist/commands/index.js +8 -0
- package/dist/commands/review.js +215 -0
- package/dist/config/storage.js +166 -0
- package/dist/git/diff.js +162 -0
- package/dist/index.js +12 -0
- package/dist/utils/exec.js +76 -0
- package/dist/version.js +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Configuration Storage
|
|
3
|
+
*
|
|
4
|
+
* Manages ~/.codecora/ directory for storing:
|
|
5
|
+
* - config.yml: User configuration
|
|
6
|
+
* - auth.json: Encrypted auth token (in production)
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import * as os from 'node:os';
|
|
11
|
+
import { existsSync } from 'node:fs';
|
|
12
|
+
/**
|
|
13
|
+
* CORA CLI config directory
|
|
14
|
+
*/
|
|
15
|
+
export const CORA_CONFIG_DIR = path.join(os.homedir(), '.codecora');
|
|
16
|
+
/**
|
|
17
|
+
* Config file path
|
|
18
|
+
*/
|
|
19
|
+
export const CORA_CONFIG_FILE = path.join(CORA_CONFIG_DIR, 'config.json');
|
|
20
|
+
/**
|
|
21
|
+
* Auth file path
|
|
22
|
+
*/
|
|
23
|
+
export const CORA_AUTH_FILE = path.join(CORA_CONFIG_DIR, 'auth.json');
|
|
24
|
+
/**
|
|
25
|
+
* Default configuration
|
|
26
|
+
*/
|
|
27
|
+
const DEFAULT_CONFIG = {
|
|
28
|
+
auth: {
|
|
29
|
+
serverUrl: 'https://codecora.dev',
|
|
30
|
+
sessionToken: '',
|
|
31
|
+
expiresAt: '',
|
|
32
|
+
},
|
|
33
|
+
preferences: {
|
|
34
|
+
autoReview: true,
|
|
35
|
+
blockOnCritical: true,
|
|
36
|
+
blockOnMajor: false,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Ensure config directory exists
|
|
41
|
+
*/
|
|
42
|
+
export function ensureConfigDir() {
|
|
43
|
+
if (!existsSync(CORA_CONFIG_DIR)) {
|
|
44
|
+
fs.mkdirSync(CORA_CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Read local configuration
|
|
49
|
+
*
|
|
50
|
+
* Returns merged config from files and defaults.
|
|
51
|
+
*/
|
|
52
|
+
export async function getLocalConfig() {
|
|
53
|
+
const config = { ...DEFAULT_CONFIG };
|
|
54
|
+
// Read auth file
|
|
55
|
+
if (existsSync(CORA_AUTH_FILE)) {
|
|
56
|
+
try {
|
|
57
|
+
const authData = fs.readFileSync(CORA_AUTH_FILE, 'utf-8');
|
|
58
|
+
const auth = JSON.parse(authData);
|
|
59
|
+
config.auth = { ...config.auth, ...auth };
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
// If auth file is corrupted, ignore it
|
|
63
|
+
console.warn(`Warning: Failed to read auth file: ${error}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Read config file
|
|
67
|
+
if (existsSync(CORA_CONFIG_FILE)) {
|
|
68
|
+
try {
|
|
69
|
+
const configData = fs.readFileSync(CORA_CONFIG_FILE, 'utf-8');
|
|
70
|
+
const userConfig = JSON.parse(configData);
|
|
71
|
+
Object.assign(config, userConfig);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
console.warn(`Warning: Failed to read config file: ${error}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Write local configuration
|
|
81
|
+
*/
|
|
82
|
+
export async function setLocalConfig(config) {
|
|
83
|
+
ensureConfigDir();
|
|
84
|
+
// Split auth and user config
|
|
85
|
+
const auth = config.auth;
|
|
86
|
+
const userConfig = { ...config };
|
|
87
|
+
delete userConfig.auth;
|
|
88
|
+
// Write auth file
|
|
89
|
+
if (auth) {
|
|
90
|
+
fs.writeFileSync(CORA_AUTH_FILE, JSON.stringify(auth, null, 2), { mode: 0o600 });
|
|
91
|
+
}
|
|
92
|
+
// Write config file
|
|
93
|
+
if (Object.keys(userConfig).length > 0) {
|
|
94
|
+
fs.writeFileSync(CORA_CONFIG_FILE, JSON.stringify(userConfig, null, 2), { mode: 0o644 });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Update specific config values
|
|
99
|
+
*/
|
|
100
|
+
export async function updateLocalConfig(updates) {
|
|
101
|
+
const config = await getLocalConfig();
|
|
102
|
+
const merged = deepMerge(config, updates);
|
|
103
|
+
await setLocalConfig(merged);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Set auth token
|
|
107
|
+
*/
|
|
108
|
+
export async function setAuthToken(sessionToken, serverUrl, userId, email, expiresAt) {
|
|
109
|
+
await updateLocalConfig({
|
|
110
|
+
auth: {
|
|
111
|
+
sessionToken,
|
|
112
|
+
serverUrl,
|
|
113
|
+
expiresAt,
|
|
114
|
+
},
|
|
115
|
+
user: {
|
|
116
|
+
id: userId,
|
|
117
|
+
email,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Clear auth token
|
|
123
|
+
*/
|
|
124
|
+
export async function clearAuthToken() {
|
|
125
|
+
if (existsSync(CORA_AUTH_FILE)) {
|
|
126
|
+
fs.unlinkSync(CORA_AUTH_FILE);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check if user is authenticated
|
|
131
|
+
*/
|
|
132
|
+
export async function isAuthenticated() {
|
|
133
|
+
const config = await getLocalConfig();
|
|
134
|
+
return !!config.auth?.sessionToken;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get auth token
|
|
138
|
+
*/
|
|
139
|
+
export async function getAuthToken() {
|
|
140
|
+
const config = await getLocalConfig();
|
|
141
|
+
return config.auth?.sessionToken;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Deep merge objects
|
|
145
|
+
*/
|
|
146
|
+
function deepMerge(target, source) {
|
|
147
|
+
const output = { ...target };
|
|
148
|
+
for (const key in source) {
|
|
149
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
150
|
+
const sourceValue = source[key];
|
|
151
|
+
const targetValue = output[key];
|
|
152
|
+
if (sourceValue &&
|
|
153
|
+
typeof sourceValue === 'object' &&
|
|
154
|
+
!Array.isArray(sourceValue) &&
|
|
155
|
+
targetValue &&
|
|
156
|
+
typeof targetValue === 'object' &&
|
|
157
|
+
!Array.isArray(targetValue)) {
|
|
158
|
+
output[key] = deepMerge(targetValue, sourceValue);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
output[key] = sourceValue;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return output;
|
|
166
|
+
}
|
package/dist/git/diff.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Diff Utilities for CLI
|
|
3
|
+
*
|
|
4
|
+
* Functions to capture git diffs in various formats.
|
|
5
|
+
* Uses safe execFile to prevent command injection vulnerabilities.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { execFileSafe } from '../utils/exec.js';
|
|
10
|
+
/**
|
|
11
|
+
* Check if current directory is a git repository
|
|
12
|
+
*/
|
|
13
|
+
export function isGitRepo(cwd = process.cwd()) {
|
|
14
|
+
return existsSync(path.join(cwd, '.git'));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get git diff for staged changes
|
|
18
|
+
*
|
|
19
|
+
* Returns diff of files that are staged for commit.
|
|
20
|
+
*/
|
|
21
|
+
export function getStagedDiff(cwd = process.cwd()) {
|
|
22
|
+
const result = execFileSafe('git', ['diff', '--cached'], { cwd });
|
|
23
|
+
if (result.status !== 0) {
|
|
24
|
+
throw new Error(`Failed to get staged diff: ${result.stderr || result.error?.message}`);
|
|
25
|
+
}
|
|
26
|
+
return result.stdout;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get git diff for unstaged changes
|
|
30
|
+
*
|
|
31
|
+
* Returns diff of files that are modified but not staged.
|
|
32
|
+
*/
|
|
33
|
+
export function getUnstagedDiff(cwd = process.cwd()) {
|
|
34
|
+
const result = execFileSafe('git', ['diff'], { cwd });
|
|
35
|
+
if (result.status !== 0) {
|
|
36
|
+
throw new Error(`Failed to get unstaged diff: ${result.stderr || result.error?.message}`);
|
|
37
|
+
}
|
|
38
|
+
return result.stdout;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get git diff for specific files
|
|
42
|
+
*
|
|
43
|
+
* Returns diff of specified files (staged or unstaged).
|
|
44
|
+
*/
|
|
45
|
+
export function getFilesDiff(files, staged = true, cwd = process.cwd()) {
|
|
46
|
+
const args = ['diff', ...(staged ? ['--cached'] : []), '--', ...files];
|
|
47
|
+
const result = execFileSafe('git', args, { cwd });
|
|
48
|
+
if (result.status !== 0) {
|
|
49
|
+
throw new Error(`Failed to get files diff: ${result.stderr || result.error?.message}`);
|
|
50
|
+
}
|
|
51
|
+
return result.stdout;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get combined diff (staged + unstaged)
|
|
55
|
+
*
|
|
56
|
+
* Returns all changes including both staged and unstaged.
|
|
57
|
+
*/
|
|
58
|
+
export function getCombinedDiff(cwd = process.cwd()) {
|
|
59
|
+
// Get staged diff
|
|
60
|
+
const staged = getStagedDiff(cwd);
|
|
61
|
+
// Get unstaged diff
|
|
62
|
+
const unstaged = getUnstagedDiff(cwd);
|
|
63
|
+
// Combine them
|
|
64
|
+
if (staged && unstaged) {
|
|
65
|
+
return `${staged}\n${unstaged}`;
|
|
66
|
+
}
|
|
67
|
+
else if (staged) {
|
|
68
|
+
return staged;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
return unstaged;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get repository information
|
|
76
|
+
*/
|
|
77
|
+
export function getRepoInfo(cwd = process.cwd()) {
|
|
78
|
+
// Get remote URL
|
|
79
|
+
const remoteResult = execFileSafe('git', ['config', '--get', 'remote.origin.url'], { cwd });
|
|
80
|
+
if (remoteResult.status !== 0 || !remoteResult.stdout) {
|
|
81
|
+
// If we can't get repo info, return defaults
|
|
82
|
+
return {
|
|
83
|
+
owner: 'unknown',
|
|
84
|
+
repo: 'unknown',
|
|
85
|
+
branch: 'main',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const remoteUrl = remoteResult.stdout.trim();
|
|
89
|
+
// Parse owner/repo from URL
|
|
90
|
+
// Supports: git@github.com:owner/repo.git
|
|
91
|
+
// https://github.com/owner/repo.git
|
|
92
|
+
let owner = '';
|
|
93
|
+
let repo = '';
|
|
94
|
+
const sshMatch = remoteUrl.match(/git@github\.com:([^/]+)\/(.+?)\.git/);
|
|
95
|
+
const httpsMatch = remoteUrl.match(/github\.com[\/:]([^/]+)\/(.+?)(\.git|$)/);
|
|
96
|
+
if (sshMatch && sshMatch[1] && sshMatch[2]) {
|
|
97
|
+
owner = sshMatch[1];
|
|
98
|
+
repo = sshMatch[2];
|
|
99
|
+
}
|
|
100
|
+
else if (httpsMatch && httpsMatch[1] && httpsMatch[2]) {
|
|
101
|
+
owner = httpsMatch[1];
|
|
102
|
+
repo = httpsMatch[2];
|
|
103
|
+
}
|
|
104
|
+
// Get current branch
|
|
105
|
+
const branchResult = execFileSafe('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd });
|
|
106
|
+
const branch = branchResult.status === 0 ? branchResult.stdout.trim() : 'main';
|
|
107
|
+
return { owner, repo, branch };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get list of changed files
|
|
111
|
+
*
|
|
112
|
+
* Returns files that have been modified (staged or unstaged).
|
|
113
|
+
*/
|
|
114
|
+
export function getChangedFiles(cwd = process.cwd()) {
|
|
115
|
+
const files = [];
|
|
116
|
+
// Get staged files
|
|
117
|
+
const stagedResult = execFileSafe('git', ['diff', '--cached', '--name-only'], { cwd });
|
|
118
|
+
if (stagedResult.status === 0) {
|
|
119
|
+
files.push(...stagedResult.stdout.split('\n').filter(Boolean));
|
|
120
|
+
}
|
|
121
|
+
// Get unstaged files
|
|
122
|
+
const unstagedResult = execFileSafe('git', ['diff', '--name-only'], { cwd });
|
|
123
|
+
if (unstagedResult.status === 0) {
|
|
124
|
+
files.push(...unstagedResult.stdout.split('\n').filter(Boolean));
|
|
125
|
+
}
|
|
126
|
+
// Return unique files
|
|
127
|
+
return Array.from(new Set(files));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Validate diff size
|
|
131
|
+
*
|
|
132
|
+
* Checks if diff exceeds maximum size limit.
|
|
133
|
+
*/
|
|
134
|
+
export function validateDiffSize(diff, maxSize = 10 * 1024 * 1024 // 10MB default
|
|
135
|
+
) {
|
|
136
|
+
const size = Buffer.byteLength(diff, 'utf-8');
|
|
137
|
+
if (size > maxSize) {
|
|
138
|
+
return {
|
|
139
|
+
valid: false,
|
|
140
|
+
size,
|
|
141
|
+
error: `Diff size (${(size / 1024 / 1024).toFixed(2)}MB) exceeds maximum (${(maxSize / 1024 / 1024).toFixed(2)}MB)`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
return { valid: true, size };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get diff for review
|
|
148
|
+
*
|
|
149
|
+
* Main function to get diff based on options.
|
|
150
|
+
*/
|
|
151
|
+
export function getReviewDiff(options) {
|
|
152
|
+
const { staged = true, files, cwd = process.cwd() } = options;
|
|
153
|
+
if (files && files.length > 0) {
|
|
154
|
+
return getFilesDiff(files, staged, cwd);
|
|
155
|
+
}
|
|
156
|
+
else if (staged) {
|
|
157
|
+
return getStagedDiff(cwd);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
return getUnstagedDiff(cwd);
|
|
161
|
+
}
|
|
162
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORA CLI Package
|
|
3
|
+
*
|
|
4
|
+
* AI-powered code review for git commits.
|
|
5
|
+
*/
|
|
6
|
+
export * from './api/types.js';
|
|
7
|
+
export * from './api/client.js';
|
|
8
|
+
export * from './config/storage.js';
|
|
9
|
+
export * from './git/diff.js';
|
|
10
|
+
export * from './utils/exec.js';
|
|
11
|
+
export * from './commands/index.js';
|
|
12
|
+
// Test change
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safe process execution utilities
|
|
3
|
+
*
|
|
4
|
+
* Uses execFile instead of exec to prevent command injection vulnerabilities.
|
|
5
|
+
* Handles Windows compatibility and provides proper error handling.
|
|
6
|
+
*/
|
|
7
|
+
import { execFileSync } from 'node:child_process';
|
|
8
|
+
/**
|
|
9
|
+
* Execute command synchronously with safety guarantees
|
|
10
|
+
*
|
|
11
|
+
* Uses execFile which doesn't go through shell, preventing command injection.
|
|
12
|
+
*
|
|
13
|
+
* @param command - Command to execute
|
|
14
|
+
* @param args - Command arguments (will be safely escaped)
|
|
15
|
+
* @param options - Child process options
|
|
16
|
+
* @returns Execution result with stdout, stderr, and status
|
|
17
|
+
*/
|
|
18
|
+
export function execFileSafe(command, args = [], options = {}) {
|
|
19
|
+
const { cwd = process.cwd(), encoding = 'utf-8' } = options;
|
|
20
|
+
try {
|
|
21
|
+
const stdout = execFileSync(command, args, {
|
|
22
|
+
cwd,
|
|
23
|
+
encoding: encoding,
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
stdout,
|
|
27
|
+
stderr: '',
|
|
28
|
+
status: 0,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const err = error;
|
|
33
|
+
return {
|
|
34
|
+
stdout: err.stdout || '',
|
|
35
|
+
stderr: err.stderr || '',
|
|
36
|
+
status: err.status ?? null,
|
|
37
|
+
error: error,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Execute command asynchronously with safety guarantees
|
|
43
|
+
*
|
|
44
|
+
* Uses execFile which doesn't go through shell, preventing command injection.
|
|
45
|
+
*
|
|
46
|
+
* @param command - Command to execute
|
|
47
|
+
* @param args - Command arguments (will be safely escaped)
|
|
48
|
+
* @param options - Child process options
|
|
49
|
+
* @returns Execution result with stdout, stderr, and status
|
|
50
|
+
*/
|
|
51
|
+
export async function execFileSafeAsync(command, args = [], options = {}) {
|
|
52
|
+
const { cwd = process.cwd(), encoding = 'utf-8' } = options;
|
|
53
|
+
try {
|
|
54
|
+
const { execFile: execFileAsync } = await import('node:child_process');
|
|
55
|
+
const { promisify } = await import('node:util');
|
|
56
|
+
const asyncExecFile = promisify(execFileAsync);
|
|
57
|
+
const { stdout, stderr } = await asyncExecFile(command, args, {
|
|
58
|
+
cwd,
|
|
59
|
+
encoding: encoding,
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
stdout,
|
|
63
|
+
stderr,
|
|
64
|
+
status: 0,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const err = error;
|
|
69
|
+
return {
|
|
70
|
+
stdout: err.stdout || '',
|
|
71
|
+
stderr: err.stderr || '',
|
|
72
|
+
status: err.status ?? null,
|
|
73
|
+
error: error,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const VERSION = '0.0.3';
|
package/package.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codecora/cli",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "CORA CLI - AI code review before commit",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./api": {
|
|
14
|
+
"types": "./dist/api/types.d.ts",
|
|
15
|
+
"import": "./dist/api/types.js"
|
|
16
|
+
},
|
|
17
|
+
"./api/client": {
|
|
18
|
+
"types": "./dist/api/client.d.ts",
|
|
19
|
+
"import": "./dist/api/client.js"
|
|
20
|
+
},
|
|
21
|
+
"./config/storage": {
|
|
22
|
+
"types": "./dist/config/storage.d.ts",
|
|
23
|
+
"import": "./dist/config/storage.js"
|
|
24
|
+
},
|
|
25
|
+
"./git/diff": {
|
|
26
|
+
"types": "./dist/git/diff.d.ts",
|
|
27
|
+
"import": "./dist/git/diff.js"
|
|
28
|
+
},
|
|
29
|
+
"./utils/exec": {
|
|
30
|
+
"types": "./dist/utils/exec.d.ts",
|
|
31
|
+
"import": "./dist/utils/exec.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"bin": {
|
|
35
|
+
"cora": "./bin/cora.js"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"bin",
|
|
39
|
+
"dist",
|
|
40
|
+
"README.md"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc",
|
|
44
|
+
"build:binary": "tsc && pkg . --targets node18-macos-x64,node18-macos-arm64,node18-linux-x64,node18-win-x64 --output ./binaries/cora",
|
|
45
|
+
"dev": "tsc --watch",
|
|
46
|
+
"check-types": "tsc --noEmit",
|
|
47
|
+
"test": "vitest",
|
|
48
|
+
"test:unit": "vitest tests/unit"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"chalk": "^5.3.0",
|
|
52
|
+
"commander": "^12.1.0",
|
|
53
|
+
"ora": "^8.1.0",
|
|
54
|
+
"cli-table3": "^0.6.5",
|
|
55
|
+
"open": "^10.1.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/node": "^20.17.0",
|
|
59
|
+
"typescript": "^5.7.2",
|
|
60
|
+
"vitest": "^2.1.0",
|
|
61
|
+
"pkg": "^5.8.1"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"registry": "https://registry.npmjs.org",
|
|
65
|
+
"access": "public"
|
|
66
|
+
},
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=20.0.0",
|
|
69
|
+
"bun": ">=1.0.0"
|
|
70
|
+
},
|
|
71
|
+
"keywords": [
|
|
72
|
+
"cli",
|
|
73
|
+
"code-review",
|
|
74
|
+
"ai",
|
|
75
|
+
"git",
|
|
76
|
+
"pre-commit"
|
|
77
|
+
],
|
|
78
|
+
"pkg": {
|
|
79
|
+
"scripts": [
|
|
80
|
+
"dist/**/*.js"
|
|
81
|
+
],
|
|
82
|
+
"assets": [
|
|
83
|
+
"package.json"
|
|
84
|
+
],
|
|
85
|
+
"targets": [
|
|
86
|
+
"node18-macos-x64",
|
|
87
|
+
"node18-macos-arm64",
|
|
88
|
+
"node18-linux-x64",
|
|
89
|
+
"node18-win-x64"
|
|
90
|
+
],
|
|
91
|
+
"outputPath": "./binaries"
|
|
92
|
+
},
|
|
93
|
+
"author": "CORA",
|
|
94
|
+
"license": "MIT"
|
|
95
|
+
}
|