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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/prituscode.js +242 -178
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prituscode",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "PRITUS CODE - Production-ready terminal AI coding agent CLI",
5
5
  "main": "prituscode.js",
6
6
  "bin": {
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 AUTH_DIR = path.join(os.homedir(), '.prituscode')
42
- const AUTH_FILE = path.join(AUTH_DIR, 'auth.json')
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
- // --- AUTHENTICATION STORAGE HELPERS ---
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(AUTH_DIR)) {
64
- fs.mkdirSync(AUTH_DIR, { recursive: true })
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 & FORMATTING HELPERS ---
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 cyan(str) {
121
- return '\x1b[36m' + str + '\x1b[39m'
122
- }
123
-
124
- function green(str) {
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 magenta(str) {
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 brightMagenta(str) {
133
- return '\x1b[95m' + str + '\x1b[39m'
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 & ASCII ART GENERATORS ---
149
- function getDiamondLogo() {
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
- // Render ASCII Banner stacked cleanly on its own lines
266
- for (let r = 0; r < banner.length; r++) {
267
- console.log(cyan(banner[r]))
268
- }
269
- console.log(brightMagenta(bold('PRITUS CODE v' + VERSION)))
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 (type /login to authenticate with Google)'
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 your current setup',
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(cyan(topBorder))
219
+ console.log(colorPrimary(themeId, topBorder))
296
220
 
297
221
  const pad1 = ' '.repeat(innerWidth - lines[0].length - 2)
298
- console.log(cyan('│ ') + bold(lines[0]) + pad1 + cyan(' │'))
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 ? green(lines[1]) : dim(lines[1])
302
- console.log(cyan('│ ') + formattedUserLine + pad2 + cyan(' │'))
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(cyan('│ ') + dim(lines[2]) + pad3 + cyan(' │'))
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(cyan('│ ') + dim(lines[3]) + pad4 + cyan(' │'))
232
+ console.log(colorPrimary(themeId, '│ ') + dim(lines[3]) + pad4 + colorPrimary(themeId, ' │'))
309
233
 
310
- console.log(cyan(bottomBorder))
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(green('✓') + ' Run /init to create a PRITUS.md with project instructions')
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 shortcuts'
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 = apiUrl + '/api/auth/google?redirect_uri=' + encodeURIComponent(redirectUri)
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(green('✓') + ' Switched to ' + choiceObj.label)
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(green('✓') + ' Switched to ' + choiceObj.label)
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(green('✓ Created ') + fileName + ' (' + bytes + ' bytes)')
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(green('✓ Modified ') + fileName + ' (' + bytes + ' bytes)')
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: messagesHistory
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(green('✓ Logged in as ') + session.email + ' (' + session.name + ')')
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(green('✓ Successfully logged out.'))
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(green('✓ Created PRITUS.md with project instructions'))
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
- console.log(green('✓ Switched to ') + info.label)
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(green('✓ Logged in as ') + session.email + ' (' + session.name + ')')
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
- renderWelcomeBox(process.cwd(), state.authSession)
877
- renderTips()
878
-
879
- promptModelSelection(state.currentModel, (selectedModel) => {
880
- state.currentModel = selectedModel
881
- renderStatusBar(state.currentModel, process.cwd(), state.authSession)
882
- startRepl(state)
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