gent-cli 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/commit.js +28 -3
- package/src/commands/init.js +63 -84
- package/src/utils/auth-storage.js +4 -3
package/package.json
CHANGED
package/src/commands/commit.js
CHANGED
|
@@ -8,6 +8,7 @@ const chalk = require('chalk');
|
|
|
8
8
|
const inquirer = require('inquirer');
|
|
9
9
|
const ora = require('ora');
|
|
10
10
|
const { getGentPath, readJSON, writeJSON } = require('../utils/fileSystem');
|
|
11
|
+
const authStorage = require('../utils/auth-storage');
|
|
11
12
|
const { STAGING_FILE, COMMITS_FILE, CONFIG_FILE } = require('../utils/constants');
|
|
12
13
|
const { generateCommitHash, getFileHash } = require('../utils/helpers');
|
|
13
14
|
|
|
@@ -49,17 +50,41 @@ async function commit(options) {
|
|
|
49
50
|
|
|
50
51
|
const spinner = ora('Creating commit...').start();
|
|
51
52
|
|
|
52
|
-
// Read config and repository
|
|
53
53
|
const config = await readJSON(path.join(gentPath, CONFIG_FILE));
|
|
54
54
|
const repository = await readJSON(path.join(gentPath, COMMITS_FILE));
|
|
55
55
|
|
|
56
|
+
// Resolve author identity
|
|
57
|
+
let authorName = config.user.name;
|
|
58
|
+
let authorEmail = config.user.email;
|
|
59
|
+
|
|
60
|
+
// Fallback to global auth if local config is empty
|
|
61
|
+
if (!authorName || !authorEmail) {
|
|
62
|
+
const globalUser = await authStorage.getUser();
|
|
63
|
+
if (globalUser) {
|
|
64
|
+
if (!authorName) {
|
|
65
|
+
authorName = [globalUser.first_name, globalUser.last_name].filter(Boolean).join(' ');
|
|
66
|
+
}
|
|
67
|
+
if (!authorEmail) {
|
|
68
|
+
authorEmail = globalUser.email;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Fail if still no identity
|
|
74
|
+
if (!authorName || !authorEmail) {
|
|
75
|
+
spinner.stop();
|
|
76
|
+
console.error(chalk.red('Author identity unknown'));
|
|
77
|
+
console.log(chalk.yellow('Please run "gent login" to set your identity globally'));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
56
81
|
// Create commit object
|
|
57
82
|
const commit = {
|
|
58
83
|
hash: generateCommitHash(),
|
|
59
84
|
message: message,
|
|
60
85
|
author: {
|
|
61
|
-
name:
|
|
62
|
-
email:
|
|
86
|
+
name: authorName,
|
|
87
|
+
email: authorEmail
|
|
63
88
|
},
|
|
64
89
|
timestamp: new Date().toISOString(),
|
|
65
90
|
parent: repository.branches[repository.currentBranch] || null,
|
package/src/commands/init.js
CHANGED
|
@@ -6,10 +6,8 @@
|
|
|
6
6
|
const fs = require('fs').promises;
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const chalk = require('chalk');
|
|
9
|
-
const inquirer = require('inquirer');
|
|
10
|
-
const ora = require('ora');
|
|
11
|
-
const boxen = require('boxen');
|
|
12
9
|
const { ensureDir, writeJSON, pathExists } = require('../utils/fileSystem');
|
|
10
|
+
const authStorage = require('../utils/auth-storage');
|
|
13
11
|
const { GENT_DIR, CONFIG_FILE, STAGING_FILE, COMMITS_FILE } = require('../utils/constants');
|
|
14
12
|
|
|
15
13
|
/**
|
|
@@ -17,24 +15,38 @@ const { GENT_DIR, CONFIG_FILE, STAGING_FILE, COMMITS_FILE } = require('../utils/
|
|
|
17
15
|
* @param {Object} options - Command options
|
|
18
16
|
*/
|
|
19
17
|
async function init(options) {
|
|
20
|
-
const spinner = ora('Initializing gent repository...').start();
|
|
21
|
-
|
|
22
18
|
try {
|
|
23
19
|
const cwd = process.cwd();
|
|
24
20
|
const gentPath = path.join(cwd, GENT_DIR);
|
|
21
|
+
let isReinit = false;
|
|
25
22
|
|
|
26
23
|
// Check if already initialized
|
|
27
24
|
if (await pathExists(gentPath)) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
isReinit = true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Get authenticated user profile if available
|
|
29
|
+
let defaultName = '';
|
|
30
|
+
let defaultEmail = '';
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const user = await authStorage.getUser();
|
|
34
|
+
if (user) {
|
|
35
|
+
if (user.first_name || user.last_name) {
|
|
36
|
+
defaultName = [user.first_name, user.last_name].filter(Boolean).join(' ');
|
|
37
|
+
}
|
|
38
|
+
if (user.email) {
|
|
39
|
+
defaultEmail = user.email;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
// Ignore auth errors
|
|
31
44
|
}
|
|
32
45
|
|
|
33
|
-
// Get user configuration if not using defaults
|
|
34
46
|
let config = {
|
|
35
47
|
user: {
|
|
36
|
-
name:
|
|
37
|
-
email:
|
|
48
|
+
name: defaultName,
|
|
49
|
+
email: defaultEmail
|
|
38
50
|
},
|
|
39
51
|
repository: {
|
|
40
52
|
name: path.basename(cwd),
|
|
@@ -43,96 +55,63 @@ async function init(options) {
|
|
|
43
55
|
}
|
|
44
56
|
};
|
|
45
57
|
|
|
46
|
-
if (!options.yes) {
|
|
47
|
-
spinner.stop();
|
|
48
|
-
|
|
49
|
-
const answers = await inquirer.prompt([
|
|
50
|
-
{
|
|
51
|
-
type: 'input',
|
|
52
|
-
name: 'userName',
|
|
53
|
-
message: 'Enter your name:',
|
|
54
|
-
default: 'Anonymous'
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
type: 'input',
|
|
58
|
-
name: 'userEmail',
|
|
59
|
-
message: 'Enter your email:',
|
|
60
|
-
default: 'anonymous@example.com',
|
|
61
|
-
validate: (input) => {
|
|
62
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
63
|
-
return emailRegex.test(input) || 'Please enter a valid email';
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
type: 'input',
|
|
68
|
-
name: 'repoName',
|
|
69
|
-
message: 'Repository name:',
|
|
70
|
-
default: path.basename(cwd)
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
type: 'input',
|
|
74
|
-
name: 'repoDescription',
|
|
75
|
-
message: 'Repository description:',
|
|
76
|
-
default: 'A gent repository'
|
|
77
|
-
}
|
|
78
|
-
]);
|
|
79
|
-
|
|
80
|
-
config.user.name = answers.userName;
|
|
81
|
-
config.user.email = answers.userEmail;
|
|
82
|
-
config.repository.name = answers.repoName;
|
|
83
|
-
config.repository.description = answers.repoDescription;
|
|
84
|
-
|
|
85
|
-
spinner.start('Creating repository structure...');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
58
|
// Create directory structure
|
|
89
59
|
await ensureDir(gentPath);
|
|
90
60
|
await ensureDir(path.join(gentPath, 'objects'));
|
|
91
61
|
await ensureDir(path.join(gentPath, 'refs', 'heads'));
|
|
92
62
|
await ensureDir(path.join(gentPath, 'refs', 'tags'));
|
|
93
63
|
|
|
94
|
-
// Create
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
64
|
+
// Create/Update configuration
|
|
65
|
+
// Only write config if it doesn't exist OR if we have valid user info to update
|
|
66
|
+
const configPath = path.join(gentPath, CONFIG_FILE);
|
|
67
|
+
if (!(await pathExists(configPath)) || (defaultName && defaultEmail)) {
|
|
68
|
+
// If re-init, we might want to preserve existing config unless we have better info?
|
|
69
|
+
// Git re-init doesn't overwrite config usually.
|
|
70
|
+
// But for now, let's write ensuring we have a config file.
|
|
71
|
+
if (!isReinit || !(await pathExists(configPath))) {
|
|
72
|
+
await writeJSON(configPath, config);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Create initial files only if they don't exist
|
|
77
|
+
const stagingPath = path.join(gentPath, STAGING_FILE);
|
|
78
|
+
if (!(await pathExists(stagingPath))) {
|
|
79
|
+
await writeJSON(stagingPath, { files: [] });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const commitsPath = path.join(gentPath, COMMITS_FILE);
|
|
83
|
+
if (!(await pathExists(commitsPath))) {
|
|
84
|
+
await writeJSON(commitsPath, { commits: [], branches: { main: null }, currentBranch: 'main' });
|
|
85
|
+
}
|
|
98
86
|
|
|
99
|
-
// Create HEAD file
|
|
100
|
-
|
|
87
|
+
// Create HEAD file only if it doesn't exist
|
|
88
|
+
const headPath = path.join(gentPath, 'HEAD');
|
|
89
|
+
if (!(await pathExists(headPath))) {
|
|
90
|
+
await fs.writeFile(headPath, 'ref: refs/heads/main\n');
|
|
91
|
+
}
|
|
101
92
|
|
|
102
|
-
// Create .gentignore
|
|
103
|
-
const
|
|
93
|
+
// Create .gentignore only if it doesn't exist
|
|
94
|
+
const ignorePath = path.join(cwd, '.gentignore');
|
|
95
|
+
if (!(await pathExists(ignorePath))) {
|
|
96
|
+
const gentignore = `# Gent ignore patterns
|
|
104
97
|
node_modules/
|
|
105
98
|
.DS_Store
|
|
106
99
|
*.log
|
|
107
100
|
.env
|
|
108
101
|
.gent/
|
|
109
102
|
`;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
spinner.succeed(chalk.green('✓ Gent repository initialized successfully!'));
|
|
113
|
-
|
|
114
|
-
// Display success message
|
|
115
|
-
const message = chalk.white(`
|
|
116
|
-
${chalk.bold('Repository:')} ${config.repository.name}
|
|
117
|
-
${chalk.bold('User:')} ${config.user.name} <${config.user.email}>
|
|
118
|
-
${chalk.bold('Branch:')} main
|
|
119
|
-
|
|
120
|
-
${chalk.cyan('Next steps:')}
|
|
121
|
-
${chalk.gray('•')} gent add <files> - Add files to staging
|
|
122
|
-
${chalk.gray('•')} gent commit - Commit your changes
|
|
123
|
-
${chalk.gray('•')} gent status - View repository status
|
|
124
|
-
`);
|
|
103
|
+
await fs.writeFile(ignorePath, gentignore);
|
|
104
|
+
}
|
|
125
105
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}));
|
|
106
|
+
if (isReinit) {
|
|
107
|
+
console.log(chalk.gray(`Reinitialized existing Gent repository in ${gentPath}`));
|
|
108
|
+
} else {
|
|
109
|
+
console.log(chalk.gray(`Initialized empty Gent repository in ${gentPath}`));
|
|
110
|
+
}
|
|
132
111
|
|
|
133
112
|
} catch (error) {
|
|
134
|
-
|
|
135
|
-
console.error(chalk.red('
|
|
113
|
+
console.error(chalk.red('Failed to initialize repository'));
|
|
114
|
+
console.error(chalk.red('Error:'), error.message);
|
|
136
115
|
process.exit(1);
|
|
137
116
|
}
|
|
138
117
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const fs = require('fs').promises;
|
|
7
7
|
const path = require('path');
|
|
8
|
+
const os = require('os');
|
|
8
9
|
const CryptoJS = require('crypto-js');
|
|
9
10
|
const { GENT_DIR, AUTH_FILE } = require('./constants');
|
|
10
11
|
|
|
@@ -16,7 +17,7 @@ const ENCRYPTION_KEY = 'gent-cli-secret-key-v1';
|
|
|
16
17
|
* @returns {string} Path to auth.json file
|
|
17
18
|
*/
|
|
18
19
|
function getAuthFilePath() {
|
|
19
|
-
return path.join(
|
|
20
|
+
return path.join(os.homedir(), GENT_DIR, AUTH_FILE);
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -48,7 +49,7 @@ function decrypt(encryptedData) {
|
|
|
48
49
|
*/
|
|
49
50
|
async function saveTokens(accessToken, refreshToken, user) {
|
|
50
51
|
const authFilePath = getAuthFilePath();
|
|
51
|
-
const gentDir = path.join(
|
|
52
|
+
const gentDir = path.join(os.homedir(), GENT_DIR);
|
|
52
53
|
|
|
53
54
|
const authData = {
|
|
54
55
|
accessToken,
|
|
@@ -149,7 +150,7 @@ async function updateAccessToken(newAccessToken) {
|
|
|
149
150
|
authData.timestamp = new Date().toISOString();
|
|
150
151
|
|
|
151
152
|
const authFilePath = getAuthFilePath();
|
|
152
|
-
const gentDir = path.join(
|
|
153
|
+
const gentDir = path.join(os.homedir(), GENT_DIR);
|
|
153
154
|
const encryptedData = encrypt(authData);
|
|
154
155
|
|
|
155
156
|
// Ensure .gent directory exists
|