prituscode 2.0.1 → 2.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/package.json +1 -1
- package/prituscode.js +242 -178
package/package.json
CHANGED
package/prituscode.js
CHANGED
|
@@ -38,8 +38,9 @@ loadEnv()
|
|
|
38
38
|
const VERSION = '1.0.0'
|
|
39
39
|
const DEFAULT_API_URL = process.env.PRITUS_API_URL || 'https://pritusai.netlify.app'
|
|
40
40
|
const DEFAULT_MODEL = process.env.PRITUS_MODEL || 'pro'
|
|
41
|
-
const
|
|
42
|
-
const
|
|
41
|
+
const CONFIG_DIR = path.join(os.homedir(), '.prituscode')
|
|
42
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json')
|
|
43
|
+
const AUTH_FILE = path.join(CONFIG_DIR, 'auth.json')
|
|
43
44
|
|
|
44
45
|
const MODELS = {
|
|
45
46
|
1: { id: 'flash', label: 'Flash', desc: 'Fast & lightweight' },
|
|
@@ -48,6 +49,13 @@ const MODELS = {
|
|
|
48
49
|
4: { id: 'ultra', label: 'Ultra Code', desc: 'Elite code generation' }
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
const THEMES = {
|
|
53
|
+
1: { id: 'dark', label: 'Dark', desc: 'Vibrant dark mode (Default)' },
|
|
54
|
+
2: { id: 'light', label: 'Light', desc: 'Clean light mode' },
|
|
55
|
+
3: { id: 'daltonized', label: 'Colorblind Friendly', desc: 'High contrast blue & yellow' },
|
|
56
|
+
4: { id: 'monochrome', label: 'Monochrome', desc: 'Sleek black & white' }
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
const AGENT_SYSTEM = `You are Pritus Code, an AI coding agent in a terminal. You CREATE and MODIFY real files.
|
|
52
60
|
When asked to build something, respond in EXACT format:
|
|
53
61
|
THINKING: <one sentence>
|
|
@@ -57,11 +65,33 @@ CREATE_FILE: <filename>
|
|
|
57
65
|
\`\`\`
|
|
58
66
|
`
|
|
59
67
|
|
|
60
|
-
// ---
|
|
68
|
+
// --- CONFIG & AUTH STORAGE HELPERS ---
|
|
69
|
+
function loadConfig() {
|
|
70
|
+
try {
|
|
71
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
72
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'))
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// ignore
|
|
76
|
+
}
|
|
77
|
+
return { theme: 'dark', model: DEFAULT_MODEL }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function saveConfig(cfg) {
|
|
81
|
+
try {
|
|
82
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
83
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true })
|
|
84
|
+
}
|
|
85
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), 'utf8')
|
|
86
|
+
} catch (e) {
|
|
87
|
+
// ignore
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
61
91
|
function saveAuthSession(sessionData) {
|
|
62
92
|
try {
|
|
63
|
-
if (!fs.existsSync(
|
|
64
|
-
fs.mkdirSync(
|
|
93
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
94
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true })
|
|
65
95
|
}
|
|
66
96
|
fs.writeFileSync(AUTH_FILE, JSON.stringify(sessionData, null, 2), 'utf8')
|
|
67
97
|
return true
|
|
@@ -104,7 +134,7 @@ function openBrowser(url) {
|
|
|
104
134
|
}
|
|
105
135
|
}
|
|
106
136
|
|
|
107
|
-
// --- ANSI &
|
|
137
|
+
// --- ANSI & THEME HELPERS ---
|
|
108
138
|
function stripAnsi(str) {
|
|
109
139
|
return str.replace(/\x1B\[[0-9;]{0,}[A-Za-z]/g, '')
|
|
110
140
|
}
|
|
@@ -117,20 +147,25 @@ function dim(str) {
|
|
|
117
147
|
return '\x1b[2m' + str + '\x1b[22m'
|
|
118
148
|
}
|
|
119
149
|
|
|
120
|
-
function
|
|
121
|
-
return '\x1b[
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
return '\x1b[32m' + str + '\x1b[39m'
|
|
150
|
+
function colorPrimary(themeId, str) {
|
|
151
|
+
if (themeId === 'light') return '\x1b[34m' + str + '\x1b[39m'
|
|
152
|
+
if (themeId === 'daltonized') return '\x1b[34m' + str + '\x1b[39m'
|
|
153
|
+
if (themeId === 'monochrome') return '\x1b[37m' + str + '\x1b[39m'
|
|
154
|
+
return '\x1b[36m' + str + '\x1b[39m' // dark
|
|
126
155
|
}
|
|
127
156
|
|
|
128
|
-
function
|
|
129
|
-
return '\x1b[35m' + str + '\x1b[39m'
|
|
157
|
+
function colorSecondary(themeId, str) {
|
|
158
|
+
if (themeId === 'light') return '\x1b[35m' + str + '\x1b[39m'
|
|
159
|
+
if (themeId === 'daltonized') return '\x1b[33m' + str + '\x1b[39m'
|
|
160
|
+
if (themeId === 'monochrome') return '\x1b[90m' + str + '\x1b[39m'
|
|
161
|
+
return '\x1b[95m' + str + '\x1b[39m' // dark
|
|
130
162
|
}
|
|
131
163
|
|
|
132
|
-
function
|
|
133
|
-
return '\x1b[
|
|
164
|
+
function colorSuccess(themeId, str) {
|
|
165
|
+
if (themeId === 'light') return '\x1b[32m' + str + '\x1b[39m'
|
|
166
|
+
if (themeId === 'daltonized') return '\x1b[33m' + str + '\x1b[39m'
|
|
167
|
+
if (themeId === 'monochrome') return '\x1b[37m' + str + '\x1b[39m'
|
|
168
|
+
return '\x1b[32m' + str + '\x1b[39m' // dark
|
|
134
169
|
}
|
|
135
170
|
|
|
136
171
|
function ansi256(code, str) {
|
|
@@ -145,139 +180,28 @@ function formatPath(p) {
|
|
|
145
180
|
return p
|
|
146
181
|
}
|
|
147
182
|
|
|
148
|
-
// --- LOGO &
|
|
149
|
-
function
|
|
150
|
-
const lines = [
|
|
151
|
-
ansi256(39, '██'),
|
|
152
|
-
' ' + ansi256(75, '██'),
|
|
153
|
-
' ' + ansi256(99, '██'),
|
|
154
|
-
' ' + ansi256(141, '██'),
|
|
155
|
-
' ' + ansi256(177, '██'),
|
|
156
|
-
ansi256(207, '██'),
|
|
157
|
-
ansi256(207, '████████')
|
|
158
|
-
]
|
|
159
|
-
return lines
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const LETTERS = {
|
|
163
|
-
P: [
|
|
164
|
-
'██████╗ ',
|
|
165
|
-
'██╔══██╗',
|
|
166
|
-
'██████╔╝',
|
|
167
|
-
'██╔═══╝ ',
|
|
168
|
-
'██║ ',
|
|
169
|
-
'╚═╝ '
|
|
170
|
-
],
|
|
171
|
-
R: [
|
|
172
|
-
'██████╗ ',
|
|
173
|
-
'██╔══██╗',
|
|
174
|
-
'██████╔╝',
|
|
175
|
-
'██╔██╗ ',
|
|
176
|
-
'██║╚██╗ ',
|
|
177
|
-
'╚═╝ ╚═╝ '
|
|
178
|
-
],
|
|
179
|
-
I: [
|
|
180
|
-
'██╗',
|
|
181
|
-
'██║',
|
|
182
|
-
'██║',
|
|
183
|
-
'██║',
|
|
184
|
-
'██║',
|
|
185
|
-
'╚═╝'
|
|
186
|
-
],
|
|
187
|
-
T: [
|
|
188
|
-
'████████╗',
|
|
189
|
-
'╚══██╔══╝',
|
|
190
|
-
' ██║ ',
|
|
191
|
-
' ██║ ',
|
|
192
|
-
' ██║ ',
|
|
193
|
-
' ╚═╝ '
|
|
194
|
-
],
|
|
195
|
-
U: [
|
|
196
|
-
'██╗ ██╗',
|
|
197
|
-
'██║ ██║',
|
|
198
|
-
'██║ ██║',
|
|
199
|
-
'██║ ██║',
|
|
200
|
-
'╚██████╔╝',
|
|
201
|
-
' ╚═════╝ '
|
|
202
|
-
],
|
|
203
|
-
S: [
|
|
204
|
-
'███████╗',
|
|
205
|
-
'██╔════╝',
|
|
206
|
-
'███████╗',
|
|
207
|
-
'╚════██║',
|
|
208
|
-
'███████║',
|
|
209
|
-
'╚══════╝'
|
|
210
|
-
],
|
|
211
|
-
C: [
|
|
212
|
-
'███████╗',
|
|
213
|
-
'██╔════╝',
|
|
214
|
-
'██║ ',
|
|
215
|
-
'██║ ',
|
|
216
|
-
'╚██████╗',
|
|
217
|
-
' ╚═════╝'
|
|
218
|
-
],
|
|
219
|
-
O: [
|
|
220
|
-
' █████╗ ',
|
|
221
|
-
'██╔══██╗',
|
|
222
|
-
'██║ ██║',
|
|
223
|
-
'██║ ██║',
|
|
224
|
-
'╚██████╔╝',
|
|
225
|
-
' ╚═════╝ '
|
|
226
|
-
],
|
|
227
|
-
D: [
|
|
228
|
-
'██████╗ ',
|
|
229
|
-
'██╔══██╗',
|
|
230
|
-
'██║ ██║',
|
|
231
|
-
'██║ ██║',
|
|
232
|
-
'╚██████╗',
|
|
233
|
-
' ╚═════╝ '
|
|
234
|
-
],
|
|
235
|
-
E: [
|
|
236
|
-
'███████╗',
|
|
237
|
-
'██╔════╝',
|
|
238
|
-
'███████╗',
|
|
239
|
-
'██╔════╝',
|
|
240
|
-
'███████╝',
|
|
241
|
-
' '
|
|
242
|
-
]
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function getBigBanner() {
|
|
246
|
-
const rows = []
|
|
247
|
-
for (let r = 0; r < 6; r++) {
|
|
248
|
-
const pritusLine = LETTERS.P[r] + ' ' + LETTERS.R[r] + ' ' + LETTERS.I[r] + ' ' + LETTERS.T[r] + ' ' + LETTERS.U[r] + ' ' + LETTERS.S[r]
|
|
249
|
-
const codeLine = LETTERS.C[r] + ' ' + LETTERS.O[r] + ' ' + LETTERS.D[r] + ' ' + LETTERS.E[r]
|
|
250
|
-
rows.push(pritusLine + ' ' + codeLine)
|
|
251
|
-
}
|
|
252
|
-
return rows
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function renderHeader() {
|
|
256
|
-
const diamond = getDiamondLogo()
|
|
257
|
-
const banner = getBigBanner()
|
|
258
|
-
|
|
259
|
-
console.log('')
|
|
260
|
-
// Render Diamond Logo cleanly
|
|
261
|
-
for (let i = 0; i < diamond.length; i++) {
|
|
262
|
-
console.log(diamond[i])
|
|
263
|
-
}
|
|
183
|
+
// --- LOGO & HEADER RENDERER ---
|
|
184
|
+
function renderHeader(themeId) {
|
|
264
185
|
console.log('')
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
186
|
+
// Sleek glowing diamond crystal icon + clean typography
|
|
187
|
+
const row1 = ansi256(39, ' ▲ ') + ' ' + bold(colorPrimary(themeId, 'P R I T U S C O D E'))
|
|
188
|
+
const row2 = ansi256(99, ' ◄ ◈ ► ') + ' ' + dim('Terminal AI Coding Agent ') + colorSecondary(themeId, 'v' + VERSION)
|
|
189
|
+
const row3 = ansi256(207, ' ▼ ') + ' ' + ansi256(207, '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
|
|
190
|
+
|
|
191
|
+
console.log(row1)
|
|
192
|
+
console.log(row2)
|
|
193
|
+
console.log(row3)
|
|
270
194
|
console.log('')
|
|
271
195
|
}
|
|
272
196
|
|
|
273
|
-
function renderWelcomeBox(cwd, authSession) {
|
|
197
|
+
function renderWelcomeBox(cwd, authSession, themeId) {
|
|
274
198
|
const displayCwd = formatPath(cwd)
|
|
275
|
-
const userText = authSession && authSession.email ? 'Logged in as: ' + authSession.email : 'Not logged in
|
|
199
|
+
const userText = authSession && authSession.email ? 'Logged in as: ' + authSession.email : 'Not logged in'
|
|
276
200
|
|
|
277
201
|
const lines = [
|
|
278
202
|
'Welcome to Pritus Code!',
|
|
279
203
|
userText,
|
|
280
|
-
'/help for help, /status for
|
|
204
|
+
'/help for help, /status for config, /theme to change colors',
|
|
281
205
|
'cwd: ' + displayCwd
|
|
282
206
|
]
|
|
283
207
|
|
|
@@ -292,39 +216,39 @@ function renderWelcomeBox(cwd, authSession) {
|
|
|
292
216
|
const topBorder = '┌' + '─'.repeat(innerWidth) + '┐'
|
|
293
217
|
const bottomBorder = '└' + '─'.repeat(innerWidth) + '┘'
|
|
294
218
|
|
|
295
|
-
console.log(
|
|
219
|
+
console.log(colorPrimary(themeId, topBorder))
|
|
296
220
|
|
|
297
221
|
const pad1 = ' '.repeat(innerWidth - lines[0].length - 2)
|
|
298
|
-
console.log(
|
|
222
|
+
console.log(colorPrimary(themeId, '│ ') + bold(lines[0]) + pad1 + colorPrimary(themeId, ' │'))
|
|
299
223
|
|
|
300
224
|
const pad2 = ' '.repeat(innerWidth - lines[1].length - 2)
|
|
301
|
-
const formattedUserLine = authSession && authSession.email ?
|
|
302
|
-
console.log(
|
|
225
|
+
const formattedUserLine = authSession && authSession.email ? colorSuccess(themeId, lines[1]) : dim(lines[1])
|
|
226
|
+
console.log(colorPrimary(themeId, '│ ') + formattedUserLine + pad2 + colorPrimary(themeId, ' │'))
|
|
303
227
|
|
|
304
228
|
const pad3 = ' '.repeat(innerWidth - lines[2].length - 2)
|
|
305
|
-
console.log(
|
|
229
|
+
console.log(colorPrimary(themeId, '│ ') + dim(lines[2]) + pad3 + colorPrimary(themeId, ' │'))
|
|
306
230
|
|
|
307
231
|
const pad4 = ' '.repeat(innerWidth - lines[3].length - 2)
|
|
308
|
-
console.log(
|
|
232
|
+
console.log(colorPrimary(themeId, '│ ') + dim(lines[3]) + pad4 + colorPrimary(themeId, ' │'))
|
|
309
233
|
|
|
310
|
-
console.log(
|
|
234
|
+
console.log(colorPrimary(themeId, bottomBorder))
|
|
311
235
|
console.log('')
|
|
312
236
|
}
|
|
313
237
|
|
|
314
|
-
function renderTips() {
|
|
238
|
+
function renderTips(themeId) {
|
|
315
239
|
console.log(bold('Tips for getting started:'))
|
|
316
240
|
console.log('1. Ask Pritus to build something — it will create actual files in your project')
|
|
317
241
|
console.log('2. Use @filename to include a file contents in your message')
|
|
318
242
|
console.log('3. Be as specific as you would with another engineer for the best results')
|
|
319
|
-
console.log(
|
|
243
|
+
console.log(colorSuccess(themeId, '✓') + ' Run /init to create a PRITUS.md with project instructions')
|
|
320
244
|
console.log('')
|
|
321
245
|
}
|
|
322
246
|
|
|
323
|
-
function renderStatusBar(currentModelId, cwd, authSession) {
|
|
247
|
+
function renderStatusBar(currentModelId, cwd, authSession, themeId) {
|
|
324
248
|
const displayCwd = formatPath(cwd)
|
|
325
249
|
const selectedObj = getModelInfo(currentModelId)
|
|
326
250
|
const authStatus = authSession && authSession.email ? authSession.email : 'guest'
|
|
327
|
-
const barText = 'workspace (' + displayCwd + ') /model ' + selectedObj.id + ' user: ' + authStatus + ' ? for
|
|
251
|
+
const barText = 'workspace (' + displayCwd + ') /model ' + selectedObj.id + ' theme: ' + themeId + ' user: ' + authStatus + ' ? for help'
|
|
328
252
|
console.log(dim(barText))
|
|
329
253
|
console.log('')
|
|
330
254
|
}
|
|
@@ -339,11 +263,23 @@ function getModelInfo(keyOrId) {
|
|
|
339
263
|
return MODELS[2]
|
|
340
264
|
}
|
|
341
265
|
|
|
266
|
+
function getThemeInfo(keyOrId) {
|
|
267
|
+
const str = String(keyOrId).toLowerCase()
|
|
268
|
+
for (const k in THEMES) {
|
|
269
|
+
if (String(k) === str || THEMES[k].id === str) {
|
|
270
|
+
return THEMES[k]
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return THEMES[1]
|
|
274
|
+
}
|
|
275
|
+
|
|
342
276
|
// --- GOOGLE OAUTH LOGIN SERVER ---
|
|
277
|
+
const DEFAULT_GOOGLE_AUTH_URL = 'https://accounts.google.com/v3/signin/accountchooser?client_id=428748183111-03pulj6olh5cp74t3f6s8p6cg5ksne0a.apps.googleusercontent.com&context_uri=https%3A%2F%2Fpritusai.netlify.app&redirect_uri=https%3A%2F%2Fpritus-ai-2c54c.firebaseapp.com%2F__%2Fauth%2Fhandler&response_type=code&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+profile'
|
|
278
|
+
|
|
343
279
|
function startGoogleOAuthFlow(apiUrl, onComplete) {
|
|
344
280
|
const port = 8585
|
|
345
281
|
const redirectUri = 'http://localhost:' + port + '/callback'
|
|
346
|
-
const loginUrl =
|
|
282
|
+
const loginUrl = process.env.PRITUS_AUTH_URL || DEFAULT_GOOGLE_AUTH_URL
|
|
347
283
|
|
|
348
284
|
const server = http.createServer((req, res) => {
|
|
349
285
|
const reqUrl = new URL(req.url, 'http://localhost:' + port)
|
|
@@ -409,8 +345,104 @@ function startGoogleOAuthFlow(apiUrl, onComplete) {
|
|
|
409
345
|
})
|
|
410
346
|
}
|
|
411
347
|
|
|
348
|
+
function enforceCompulsoryLogin(state, callback) {
|
|
349
|
+
if (state.authSession && state.authSession.email) {
|
|
350
|
+
return callback(state.authSession)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
console.log(bold('\x1b[33m[Authentication Required]\x1b[39m'))
|
|
354
|
+
console.log('Pritus Code requires a free Google sign-in to continue.')
|
|
355
|
+
process.stdout.write('Press Enter to sign in with Google in your browser... ')
|
|
356
|
+
|
|
357
|
+
if (process.stdin.isTTY) {
|
|
358
|
+
process.stdin.setRawMode(true)
|
|
359
|
+
process.stdin.resume()
|
|
360
|
+
process.stdin.once('data', () => {
|
|
361
|
+
process.stdin.setRawMode(false)
|
|
362
|
+
process.stdin.pause()
|
|
363
|
+
console.log('')
|
|
364
|
+
startGoogleOAuthFlow(state.apiUrl, (err, session) => {
|
|
365
|
+
if (err || !session) {
|
|
366
|
+
console.log('\x1b[31mAuthentication failed. Pritus Code requires Google Login to proceed.\x1b[39m')
|
|
367
|
+
process.exit(1)
|
|
368
|
+
}
|
|
369
|
+
state.authSession = session
|
|
370
|
+
console.log(colorSuccess(state.currentTheme, '✓') + ' Signed in as ' + session.email + '\n')
|
|
371
|
+
callback(session)
|
|
372
|
+
})
|
|
373
|
+
})
|
|
374
|
+
} else {
|
|
375
|
+
const rlTemp = readline.createInterface({
|
|
376
|
+
input: process.stdin,
|
|
377
|
+
output: process.stdout
|
|
378
|
+
})
|
|
379
|
+
rlTemp.question('', () => {
|
|
380
|
+
rlTemp.close()
|
|
381
|
+
startGoogleOAuthFlow(state.apiUrl, (err, session) => {
|
|
382
|
+
if (err || !session) {
|
|
383
|
+
console.log('\x1b[31mAuthentication failed. Pritus Code requires Google Login to proceed.\x1b[39m')
|
|
384
|
+
process.exit(1)
|
|
385
|
+
}
|
|
386
|
+
state.authSession = session
|
|
387
|
+
console.log(colorSuccess(state.currentTheme, '✓') + ' Signed in as ' + session.email + '\n')
|
|
388
|
+
callback(session)
|
|
389
|
+
})
|
|
390
|
+
})
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// --- CLAUDE CODE STYLE THEME SELECTOR ---
|
|
395
|
+
function promptThemeSelection(currentThemeId, callback) {
|
|
396
|
+
console.log('Select a color theme:')
|
|
397
|
+
for (const k in THEMES) {
|
|
398
|
+
const t = THEMES[k]
|
|
399
|
+
const isSelected = t.id === currentThemeId
|
|
400
|
+
const pointer = isSelected ? '▸ ' : ' '
|
|
401
|
+
const num = k + ' '
|
|
402
|
+
console.log(pointer + num + bold(t.label) + ' — ' + t.desc)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const defaultThemeObj = getThemeInfo(currentThemeId)
|
|
406
|
+
process.stdout.write('Press 1-4, or Enter for ' + defaultThemeObj.label + ': ')
|
|
407
|
+
|
|
408
|
+
if (process.stdin.isTTY) {
|
|
409
|
+
process.stdin.setRawMode(true)
|
|
410
|
+
process.stdin.resume()
|
|
411
|
+
process.stdin.once('data', (data) => {
|
|
412
|
+
process.stdin.setRawMode(false)
|
|
413
|
+
process.stdin.pause()
|
|
414
|
+
const char = data.toString('utf8').trim()
|
|
415
|
+
console.log(char)
|
|
416
|
+
|
|
417
|
+
let choiceObj = defaultThemeObj
|
|
418
|
+
if (THEMES[char]) {
|
|
419
|
+
choiceObj = THEMES[char]
|
|
420
|
+
}
|
|
421
|
+
console.log(colorSuccess(choiceObj.id, '✓') + ' Applied ' + choiceObj.label + ' theme')
|
|
422
|
+
console.log('')
|
|
423
|
+
callback(choiceObj.id)
|
|
424
|
+
})
|
|
425
|
+
} else {
|
|
426
|
+
const rlTemp = readline.createInterface({
|
|
427
|
+
input: process.stdin,
|
|
428
|
+
output: process.stdout
|
|
429
|
+
})
|
|
430
|
+
rlTemp.question('', (ans) => {
|
|
431
|
+
rlTemp.close()
|
|
432
|
+
const trimmed = ans.trim()
|
|
433
|
+
let choiceObj = defaultThemeObj
|
|
434
|
+
if (THEMES[trimmed]) {
|
|
435
|
+
choiceObj = THEMES[trimmed]
|
|
436
|
+
}
|
|
437
|
+
console.log(colorSuccess(choiceObj.id, '✓') + ' Applied ' + choiceObj.label + ' theme')
|
|
438
|
+
console.log('')
|
|
439
|
+
callback(choiceObj.id)
|
|
440
|
+
})
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
412
444
|
// --- MODEL SELECTOR ---
|
|
413
|
-
function promptModelSelection(currentModelId, callback) {
|
|
445
|
+
function promptModelSelection(currentModelId, themeId, callback) {
|
|
414
446
|
console.log('Select a model:')
|
|
415
447
|
for (const k in MODELS) {
|
|
416
448
|
const m = MODELS[k]
|
|
@@ -436,7 +468,7 @@ function promptModelSelection(currentModelId, callback) {
|
|
|
436
468
|
if (MODELS[char]) {
|
|
437
469
|
choiceObj = MODELS[char]
|
|
438
470
|
}
|
|
439
|
-
console.log(
|
|
471
|
+
console.log(colorSuccess(themeId, '✓') + ' Switched to ' + choiceObj.label)
|
|
440
472
|
console.log('')
|
|
441
473
|
callback(choiceObj.id)
|
|
442
474
|
})
|
|
@@ -452,7 +484,7 @@ function promptModelSelection(currentModelId, callback) {
|
|
|
452
484
|
if (MODELS[trimmed]) {
|
|
453
485
|
choiceObj = MODELS[trimmed]
|
|
454
486
|
}
|
|
455
|
-
console.log(
|
|
487
|
+
console.log(colorSuccess(themeId, '✓') + ' Switched to ' + choiceObj.label)
|
|
456
488
|
console.log('')
|
|
457
489
|
callback(choiceObj.id)
|
|
458
490
|
})
|
|
@@ -460,7 +492,7 @@ function promptModelSelection(currentModelId, callback) {
|
|
|
460
492
|
}
|
|
461
493
|
|
|
462
494
|
// --- FILE PARSER & AGENT OPERATIONS ---
|
|
463
|
-
function processFileOperations(aiResponse) {
|
|
495
|
+
function processFileOperations(aiResponse, themeId) {
|
|
464
496
|
let modifiedAny = false
|
|
465
497
|
const createPattern = /CREATE_FILE:\s{0,}([^\n]+)\n```[a-z]{0,}\n([\s\S]+?)```/gi
|
|
466
498
|
const modifyPattern = /MODIFY_FILE:\s{0,}([^\n]+)\n```[a-z]{0,}\n([\s\S]+?)```/gi
|
|
@@ -474,7 +506,7 @@ function processFileOperations(aiResponse) {
|
|
|
474
506
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true })
|
|
475
507
|
fs.writeFileSync(targetPath, content, 'utf8')
|
|
476
508
|
const bytes = Buffer.byteLength(content, 'utf8')
|
|
477
|
-
console.log(
|
|
509
|
+
console.log(colorSuccess(themeId, '✓ Created ') + fileName + ' (' + bytes + ' bytes)')
|
|
478
510
|
modifiedAny = true
|
|
479
511
|
} catch (err) {
|
|
480
512
|
console.log('\x1b[31mError creating file ' + fileName + ': ' + err.message + '\x1b[39m')
|
|
@@ -489,7 +521,7 @@ function processFileOperations(aiResponse) {
|
|
|
489
521
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true })
|
|
490
522
|
fs.writeFileSync(targetPath, content, 'utf8')
|
|
491
523
|
const bytes = Buffer.byteLength(content, 'utf8')
|
|
492
|
-
console.log(
|
|
524
|
+
console.log(colorSuccess(themeId, '✓ Modified ') + fileName + ' (' + bytes + ' bytes)')
|
|
493
525
|
modifiedAny = true
|
|
494
526
|
} catch (err) {
|
|
495
527
|
console.log('\x1b[31mError modifying file ' + fileName + ': ' + err.message + '\x1b[39m')
|
|
@@ -550,12 +582,16 @@ function sendChatRequest(apiUrl, modelId, userPrompt, messagesHistory, authSessi
|
|
|
550
582
|
return callback(new Error('Invalid URL'))
|
|
551
583
|
}
|
|
552
584
|
|
|
585
|
+
const allMessages = (messagesHistory || []).concat([
|
|
586
|
+
{ role: 'user', content: expandedPrompt }
|
|
587
|
+
])
|
|
588
|
+
|
|
553
589
|
const httpModule = parsedUrl.protocol === 'https:' ? require('https') : require('http')
|
|
554
590
|
const payload = JSON.stringify({
|
|
555
591
|
prompt: expandedPrompt,
|
|
556
592
|
model: modelId,
|
|
557
593
|
system: AGENT_SYSTEM,
|
|
558
|
-
messages:
|
|
594
|
+
messages: allMessages
|
|
559
595
|
})
|
|
560
596
|
|
|
561
597
|
const reqHeaders = {
|
|
@@ -620,6 +656,7 @@ function handleSlashCommand(input, state, rl) {
|
|
|
620
656
|
case '?':
|
|
621
657
|
console.log('')
|
|
622
658
|
console.log(bold('PRITUS CODE Commands & Shortcuts:'))
|
|
659
|
+
console.log(' /theme Change color theme (Dark, Light, Colorblind, Monochrome)')
|
|
623
660
|
console.log(' /login Sign in with Google authentication')
|
|
624
661
|
console.log(' /logout Sign out of current account')
|
|
625
662
|
console.log(' /whoami Display active user profile')
|
|
@@ -634,11 +671,28 @@ function handleSlashCommand(input, state, rl) {
|
|
|
634
671
|
rl.prompt()
|
|
635
672
|
return true
|
|
636
673
|
|
|
674
|
+
case '/theme':
|
|
675
|
+
if (arg) {
|
|
676
|
+
const info = getThemeInfo(arg)
|
|
677
|
+
state.currentTheme = info.id
|
|
678
|
+
saveConfig({ theme: state.currentTheme, model: state.currentModel })
|
|
679
|
+
console.log(colorSuccess(state.currentTheme, '✓') + ' Applied ' + info.label + ' theme')
|
|
680
|
+
console.log('')
|
|
681
|
+
rl.prompt()
|
|
682
|
+
} else {
|
|
683
|
+
promptThemeSelection(state.currentTheme, (newTheme) => {
|
|
684
|
+
state.currentTheme = newTheme
|
|
685
|
+
saveConfig({ theme: state.currentTheme, model: state.currentModel })
|
|
686
|
+
rl.prompt()
|
|
687
|
+
})
|
|
688
|
+
}
|
|
689
|
+
return true
|
|
690
|
+
|
|
637
691
|
case '/login':
|
|
638
692
|
startGoogleOAuthFlow(state.apiUrl, (err, session) => {
|
|
639
693
|
if (!err && session) {
|
|
640
694
|
state.authSession = session
|
|
641
|
-
console.log(
|
|
695
|
+
console.log(colorSuccess(state.currentTheme, '✓ Logged in as ') + session.email + ' (' + session.name + ')')
|
|
642
696
|
}
|
|
643
697
|
console.log('')
|
|
644
698
|
rl.prompt()
|
|
@@ -648,7 +702,7 @@ function handleSlashCommand(input, state, rl) {
|
|
|
648
702
|
case '/logout':
|
|
649
703
|
clearAuthSession()
|
|
650
704
|
state.authSession = null
|
|
651
|
-
console.log(
|
|
705
|
+
console.log(colorSuccess(state.currentTheme, '✓ Successfully logged out.'))
|
|
652
706
|
console.log('')
|
|
653
707
|
rl.prompt()
|
|
654
708
|
return true
|
|
@@ -670,6 +724,7 @@ function handleSlashCommand(input, state, rl) {
|
|
|
670
724
|
console.log('')
|
|
671
725
|
console.log(bold('Current Setup:'))
|
|
672
726
|
console.log(' Version: ' + VERSION)
|
|
727
|
+
console.log(' Theme: ' + getThemeInfo(state.currentTheme).label + ' (' + state.currentTheme + ')')
|
|
673
728
|
console.log(' Model: ' + getModelInfo(state.currentModel).label + ' (' + state.currentModel + ')')
|
|
674
729
|
console.log(' User: ' + (state.authSession ? state.authSession.email : 'Not logged in'))
|
|
675
730
|
console.log(' API URL: ' + state.apiUrl)
|
|
@@ -692,7 +747,7 @@ Describe your project and architectural guidelines here for Pritus Code.
|
|
|
692
747
|
`
|
|
693
748
|
try {
|
|
694
749
|
fs.writeFileSync(docPath, docContent, 'utf8')
|
|
695
|
-
console.log(
|
|
750
|
+
console.log(colorSuccess(state.currentTheme, '✓ Created PRITUS.md with project instructions'))
|
|
696
751
|
} catch (err) {
|
|
697
752
|
console.log('\x1b[31mError creating PRITUS.md: ' + err.message + '\x1b[39m')
|
|
698
753
|
}
|
|
@@ -705,12 +760,14 @@ Describe your project and architectural guidelines here for Pritus Code.
|
|
|
705
760
|
if (arg) {
|
|
706
761
|
const info = getModelInfo(arg)
|
|
707
762
|
state.currentModel = info.id
|
|
708
|
-
|
|
763
|
+
saveConfig({ theme: state.currentTheme, model: state.currentModel })
|
|
764
|
+
console.log(colorSuccess(state.currentTheme, '✓ Switched to ') + info.label)
|
|
709
765
|
console.log('')
|
|
710
766
|
rl.prompt()
|
|
711
767
|
} else {
|
|
712
|
-
promptModelSelection(state.currentModel, (newModel) => {
|
|
768
|
+
promptModelSelection(state.currentModel, state.currentTheme, (newModel) => {
|
|
713
769
|
state.currentModel = newModel
|
|
770
|
+
saveConfig({ theme: state.currentTheme, model: state.currentModel })
|
|
714
771
|
rl.prompt()
|
|
715
772
|
})
|
|
716
773
|
}
|
|
@@ -718,8 +775,8 @@ Describe your project and architectural guidelines here for Pritus Code.
|
|
|
718
775
|
|
|
719
776
|
case '/clear':
|
|
720
777
|
console.clear()
|
|
721
|
-
renderHeader()
|
|
722
|
-
renderStatusBar(state.currentModel, process.cwd(), state.authSession)
|
|
778
|
+
renderHeader(state.currentTheme)
|
|
779
|
+
renderStatusBar(state.currentModel, process.cwd(), state.authSession, state.currentTheme)
|
|
723
780
|
rl.prompt()
|
|
724
781
|
return true
|
|
725
782
|
|
|
@@ -779,7 +836,7 @@ function startRepl(state) {
|
|
|
779
836
|
console.log(responseText)
|
|
780
837
|
console.log('')
|
|
781
838
|
|
|
782
|
-
const opsExecuted = processFileOperations(responseText)
|
|
839
|
+
const opsExecuted = processFileOperations(responseText, state.currentTheme)
|
|
783
840
|
if (opsExecuted) {
|
|
784
841
|
console.log('')
|
|
785
842
|
}
|
|
@@ -800,10 +857,12 @@ function startRepl(state) {
|
|
|
800
857
|
// --- ENTRY POINT ---
|
|
801
858
|
function main() {
|
|
802
859
|
const args = process.argv.slice(2)
|
|
860
|
+
const userConfig = loadConfig()
|
|
803
861
|
|
|
804
862
|
const state = {
|
|
805
863
|
apiUrl: DEFAULT_API_URL,
|
|
806
|
-
currentModel: DEFAULT_MODEL,
|
|
864
|
+
currentModel: userConfig.model || DEFAULT_MODEL,
|
|
865
|
+
currentTheme: userConfig.theme || 'dark',
|
|
807
866
|
authSession: loadAuthSession()
|
|
808
867
|
}
|
|
809
868
|
|
|
@@ -830,7 +889,7 @@ function main() {
|
|
|
830
889
|
if (err) {
|
|
831
890
|
process.exit(1)
|
|
832
891
|
}
|
|
833
|
-
console.log(
|
|
892
|
+
console.log(colorSuccess(state.currentTheme, '✓ Logged in as ') + session.email + ' (' + session.name + ')')
|
|
834
893
|
process.exit(0)
|
|
835
894
|
})
|
|
836
895
|
return
|
|
@@ -865,21 +924,26 @@ function main() {
|
|
|
865
924
|
process.exit(1)
|
|
866
925
|
}
|
|
867
926
|
console.log(responseText)
|
|
868
|
-
processFileOperations(responseText)
|
|
927
|
+
processFileOperations(responseText, state.currentTheme)
|
|
869
928
|
process.exit(0)
|
|
870
929
|
})
|
|
871
930
|
return
|
|
872
931
|
}
|
|
873
932
|
|
|
874
933
|
// Interactive mode startup
|
|
875
|
-
renderHeader()
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
state.
|
|
881
|
-
|
|
882
|
-
|
|
934
|
+
renderHeader(state.currentTheme)
|
|
935
|
+
|
|
936
|
+
// Compulsory Login Enforcement
|
|
937
|
+
enforceCompulsoryLogin(state, () => {
|
|
938
|
+
renderWelcomeBox(process.cwd(), state.authSession, state.currentTheme)
|
|
939
|
+
renderTips(state.currentTheme)
|
|
940
|
+
|
|
941
|
+
promptModelSelection(state.currentModel, state.currentTheme, (selectedModel) => {
|
|
942
|
+
state.currentModel = selectedModel
|
|
943
|
+
saveConfig({ theme: state.currentTheme, model: state.currentModel })
|
|
944
|
+
renderStatusBar(state.currentModel, process.cwd(), state.authSession, state.currentTheme)
|
|
945
|
+
startRepl(state)
|
|
946
|
+
})
|
|
883
947
|
})
|
|
884
948
|
}
|
|
885
949
|
|