prituscode 2.0.1 → 2.0.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/prituscode.js +239 -177
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prituscode",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
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,6 +263,16 @@ 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 ---
343
277
  function startGoogleOAuthFlow(apiUrl, onComplete) {
344
278
  const port = 8585
@@ -409,8 +343,104 @@ function startGoogleOAuthFlow(apiUrl, onComplete) {
409
343
  })
410
344
  }
411
345
 
346
+ function enforceCompulsoryLogin(state, callback) {
347
+ if (state.authSession && state.authSession.email) {
348
+ return callback(state.authSession)
349
+ }
350
+
351
+ console.log(bold('\x1b[33m[Authentication Required]\x1b[39m'))
352
+ console.log('Pritus Code requires a free Google sign-in to continue.')
353
+ process.stdout.write('Press Enter to sign in with Google in your browser... ')
354
+
355
+ if (process.stdin.isTTY) {
356
+ process.stdin.setRawMode(true)
357
+ process.stdin.resume()
358
+ process.stdin.once('data', () => {
359
+ process.stdin.setRawMode(false)
360
+ process.stdin.pause()
361
+ console.log('')
362
+ startGoogleOAuthFlow(state.apiUrl, (err, session) => {
363
+ if (err || !session) {
364
+ console.log('\x1b[31mAuthentication failed. Pritus Code requires Google Login to proceed.\x1b[39m')
365
+ process.exit(1)
366
+ }
367
+ state.authSession = session
368
+ console.log(colorSuccess(state.currentTheme, '✓') + ' Signed in as ' + session.email + '\n')
369
+ callback(session)
370
+ })
371
+ })
372
+ } else {
373
+ const rlTemp = readline.createInterface({
374
+ input: process.stdin,
375
+ output: process.stdout
376
+ })
377
+ rlTemp.question('', () => {
378
+ rlTemp.close()
379
+ startGoogleOAuthFlow(state.apiUrl, (err, session) => {
380
+ if (err || !session) {
381
+ console.log('\x1b[31mAuthentication failed. Pritus Code requires Google Login to proceed.\x1b[39m')
382
+ process.exit(1)
383
+ }
384
+ state.authSession = session
385
+ console.log(colorSuccess(state.currentTheme, '✓') + ' Signed in as ' + session.email + '\n')
386
+ callback(session)
387
+ })
388
+ })
389
+ }
390
+ }
391
+
392
+ // --- CLAUDE CODE STYLE THEME SELECTOR ---
393
+ function promptThemeSelection(currentThemeId, callback) {
394
+ console.log('Select a color theme:')
395
+ for (const k in THEMES) {
396
+ const t = THEMES[k]
397
+ const isSelected = t.id === currentThemeId
398
+ const pointer = isSelected ? '▸ ' : ' '
399
+ const num = k + ' '
400
+ console.log(pointer + num + bold(t.label) + ' — ' + t.desc)
401
+ }
402
+
403
+ const defaultThemeObj = getThemeInfo(currentThemeId)
404
+ process.stdout.write('Press 1-4, or Enter for ' + defaultThemeObj.label + ': ')
405
+
406
+ if (process.stdin.isTTY) {
407
+ process.stdin.setRawMode(true)
408
+ process.stdin.resume()
409
+ process.stdin.once('data', (data) => {
410
+ process.stdin.setRawMode(false)
411
+ process.stdin.pause()
412
+ const char = data.toString('utf8').trim()
413
+ console.log(char)
414
+
415
+ let choiceObj = defaultThemeObj
416
+ if (THEMES[char]) {
417
+ choiceObj = THEMES[char]
418
+ }
419
+ console.log(colorSuccess(choiceObj.id, '✓') + ' Applied ' + choiceObj.label + ' theme')
420
+ console.log('')
421
+ callback(choiceObj.id)
422
+ })
423
+ } else {
424
+ const rlTemp = readline.createInterface({
425
+ input: process.stdin,
426
+ output: process.stdout
427
+ })
428
+ rlTemp.question('', (ans) => {
429
+ rlTemp.close()
430
+ const trimmed = ans.trim()
431
+ let choiceObj = defaultThemeObj
432
+ if (THEMES[trimmed]) {
433
+ choiceObj = THEMES[trimmed]
434
+ }
435
+ console.log(colorSuccess(choiceObj.id, '✓') + ' Applied ' + choiceObj.label + ' theme')
436
+ console.log('')
437
+ callback(choiceObj.id)
438
+ })
439
+ }
440
+ }
441
+
412
442
  // --- MODEL SELECTOR ---
413
- function promptModelSelection(currentModelId, callback) {
443
+ function promptModelSelection(currentModelId, themeId, callback) {
414
444
  console.log('Select a model:')
415
445
  for (const k in MODELS) {
416
446
  const m = MODELS[k]
@@ -436,7 +466,7 @@ function promptModelSelection(currentModelId, callback) {
436
466
  if (MODELS[char]) {
437
467
  choiceObj = MODELS[char]
438
468
  }
439
- console.log(green('✓') + ' Switched to ' + choiceObj.label)
469
+ console.log(colorSuccess(themeId, '✓') + ' Switched to ' + choiceObj.label)
440
470
  console.log('')
441
471
  callback(choiceObj.id)
442
472
  })
@@ -452,7 +482,7 @@ function promptModelSelection(currentModelId, callback) {
452
482
  if (MODELS[trimmed]) {
453
483
  choiceObj = MODELS[trimmed]
454
484
  }
455
- console.log(green('✓') + ' Switched to ' + choiceObj.label)
485
+ console.log(colorSuccess(themeId, '✓') + ' Switched to ' + choiceObj.label)
456
486
  console.log('')
457
487
  callback(choiceObj.id)
458
488
  })
@@ -460,7 +490,7 @@ function promptModelSelection(currentModelId, callback) {
460
490
  }
461
491
 
462
492
  // --- FILE PARSER & AGENT OPERATIONS ---
463
- function processFileOperations(aiResponse) {
493
+ function processFileOperations(aiResponse, themeId) {
464
494
  let modifiedAny = false
465
495
  const createPattern = /CREATE_FILE:\s{0,}([^\n]+)\n```[a-z]{0,}\n([\s\S]+?)```/gi
466
496
  const modifyPattern = /MODIFY_FILE:\s{0,}([^\n]+)\n```[a-z]{0,}\n([\s\S]+?)```/gi
@@ -474,7 +504,7 @@ function processFileOperations(aiResponse) {
474
504
  fs.mkdirSync(path.dirname(targetPath), { recursive: true })
475
505
  fs.writeFileSync(targetPath, content, 'utf8')
476
506
  const bytes = Buffer.byteLength(content, 'utf8')
477
- console.log(green('✓ Created ') + fileName + ' (' + bytes + ' bytes)')
507
+ console.log(colorSuccess(themeId, '✓ Created ') + fileName + ' (' + bytes + ' bytes)')
478
508
  modifiedAny = true
479
509
  } catch (err) {
480
510
  console.log('\x1b[31mError creating file ' + fileName + ': ' + err.message + '\x1b[39m')
@@ -489,7 +519,7 @@ function processFileOperations(aiResponse) {
489
519
  fs.mkdirSync(path.dirname(targetPath), { recursive: true })
490
520
  fs.writeFileSync(targetPath, content, 'utf8')
491
521
  const bytes = Buffer.byteLength(content, 'utf8')
492
- console.log(green('✓ Modified ') + fileName + ' (' + bytes + ' bytes)')
522
+ console.log(colorSuccess(themeId, '✓ Modified ') + fileName + ' (' + bytes + ' bytes)')
493
523
  modifiedAny = true
494
524
  } catch (err) {
495
525
  console.log('\x1b[31mError modifying file ' + fileName + ': ' + err.message + '\x1b[39m')
@@ -550,12 +580,16 @@ function sendChatRequest(apiUrl, modelId, userPrompt, messagesHistory, authSessi
550
580
  return callback(new Error('Invalid URL'))
551
581
  }
552
582
 
583
+ const allMessages = (messagesHistory || []).concat([
584
+ { role: 'user', content: expandedPrompt }
585
+ ])
586
+
553
587
  const httpModule = parsedUrl.protocol === 'https:' ? require('https') : require('http')
554
588
  const payload = JSON.stringify({
555
589
  prompt: expandedPrompt,
556
590
  model: modelId,
557
591
  system: AGENT_SYSTEM,
558
- messages: messagesHistory
592
+ messages: allMessages
559
593
  })
560
594
 
561
595
  const reqHeaders = {
@@ -620,6 +654,7 @@ function handleSlashCommand(input, state, rl) {
620
654
  case '?':
621
655
  console.log('')
622
656
  console.log(bold('PRITUS CODE Commands & Shortcuts:'))
657
+ console.log(' /theme Change color theme (Dark, Light, Colorblind, Monochrome)')
623
658
  console.log(' /login Sign in with Google authentication')
624
659
  console.log(' /logout Sign out of current account')
625
660
  console.log(' /whoami Display active user profile')
@@ -634,11 +669,28 @@ function handleSlashCommand(input, state, rl) {
634
669
  rl.prompt()
635
670
  return true
636
671
 
672
+ case '/theme':
673
+ if (arg) {
674
+ const info = getThemeInfo(arg)
675
+ state.currentTheme = info.id
676
+ saveConfig({ theme: state.currentTheme, model: state.currentModel })
677
+ console.log(colorSuccess(state.currentTheme, '✓') + ' Applied ' + info.label + ' theme')
678
+ console.log('')
679
+ rl.prompt()
680
+ } else {
681
+ promptThemeSelection(state.currentTheme, (newTheme) => {
682
+ state.currentTheme = newTheme
683
+ saveConfig({ theme: state.currentTheme, model: state.currentModel })
684
+ rl.prompt()
685
+ })
686
+ }
687
+ return true
688
+
637
689
  case '/login':
638
690
  startGoogleOAuthFlow(state.apiUrl, (err, session) => {
639
691
  if (!err && session) {
640
692
  state.authSession = session
641
- console.log(green('✓ Logged in as ') + session.email + ' (' + session.name + ')')
693
+ console.log(colorSuccess(state.currentTheme, '✓ Logged in as ') + session.email + ' (' + session.name + ')')
642
694
  }
643
695
  console.log('')
644
696
  rl.prompt()
@@ -648,7 +700,7 @@ function handleSlashCommand(input, state, rl) {
648
700
  case '/logout':
649
701
  clearAuthSession()
650
702
  state.authSession = null
651
- console.log(green('✓ Successfully logged out.'))
703
+ console.log(colorSuccess(state.currentTheme, '✓ Successfully logged out.'))
652
704
  console.log('')
653
705
  rl.prompt()
654
706
  return true
@@ -670,6 +722,7 @@ function handleSlashCommand(input, state, rl) {
670
722
  console.log('')
671
723
  console.log(bold('Current Setup:'))
672
724
  console.log(' Version: ' + VERSION)
725
+ console.log(' Theme: ' + getThemeInfo(state.currentTheme).label + ' (' + state.currentTheme + ')')
673
726
  console.log(' Model: ' + getModelInfo(state.currentModel).label + ' (' + state.currentModel + ')')
674
727
  console.log(' User: ' + (state.authSession ? state.authSession.email : 'Not logged in'))
675
728
  console.log(' API URL: ' + state.apiUrl)
@@ -692,7 +745,7 @@ Describe your project and architectural guidelines here for Pritus Code.
692
745
  `
693
746
  try {
694
747
  fs.writeFileSync(docPath, docContent, 'utf8')
695
- console.log(green('✓ Created PRITUS.md with project instructions'))
748
+ console.log(colorSuccess(state.currentTheme, '✓ Created PRITUS.md with project instructions'))
696
749
  } catch (err) {
697
750
  console.log('\x1b[31mError creating PRITUS.md: ' + err.message + '\x1b[39m')
698
751
  }
@@ -705,12 +758,14 @@ Describe your project and architectural guidelines here for Pritus Code.
705
758
  if (arg) {
706
759
  const info = getModelInfo(arg)
707
760
  state.currentModel = info.id
708
- console.log(green('✓ Switched to ') + info.label)
761
+ saveConfig({ theme: state.currentTheme, model: state.currentModel })
762
+ console.log(colorSuccess(state.currentTheme, '✓ Switched to ') + info.label)
709
763
  console.log('')
710
764
  rl.prompt()
711
765
  } else {
712
- promptModelSelection(state.currentModel, (newModel) => {
766
+ promptModelSelection(state.currentModel, state.currentTheme, (newModel) => {
713
767
  state.currentModel = newModel
768
+ saveConfig({ theme: state.currentTheme, model: state.currentModel })
714
769
  rl.prompt()
715
770
  })
716
771
  }
@@ -718,8 +773,8 @@ Describe your project and architectural guidelines here for Pritus Code.
718
773
 
719
774
  case '/clear':
720
775
  console.clear()
721
- renderHeader()
722
- renderStatusBar(state.currentModel, process.cwd(), state.authSession)
776
+ renderHeader(state.currentTheme)
777
+ renderStatusBar(state.currentModel, process.cwd(), state.authSession, state.currentTheme)
723
778
  rl.prompt()
724
779
  return true
725
780
 
@@ -779,7 +834,7 @@ function startRepl(state) {
779
834
  console.log(responseText)
780
835
  console.log('')
781
836
 
782
- const opsExecuted = processFileOperations(responseText)
837
+ const opsExecuted = processFileOperations(responseText, state.currentTheme)
783
838
  if (opsExecuted) {
784
839
  console.log('')
785
840
  }
@@ -800,10 +855,12 @@ function startRepl(state) {
800
855
  // --- ENTRY POINT ---
801
856
  function main() {
802
857
  const args = process.argv.slice(2)
858
+ const userConfig = loadConfig()
803
859
 
804
860
  const state = {
805
861
  apiUrl: DEFAULT_API_URL,
806
- currentModel: DEFAULT_MODEL,
862
+ currentModel: userConfig.model || DEFAULT_MODEL,
863
+ currentTheme: userConfig.theme || 'dark',
807
864
  authSession: loadAuthSession()
808
865
  }
809
866
 
@@ -830,7 +887,7 @@ function main() {
830
887
  if (err) {
831
888
  process.exit(1)
832
889
  }
833
- console.log(green('✓ Logged in as ') + session.email + ' (' + session.name + ')')
890
+ console.log(colorSuccess(state.currentTheme, '✓ Logged in as ') + session.email + ' (' + session.name + ')')
834
891
  process.exit(0)
835
892
  })
836
893
  return
@@ -865,21 +922,26 @@ function main() {
865
922
  process.exit(1)
866
923
  }
867
924
  console.log(responseText)
868
- processFileOperations(responseText)
925
+ processFileOperations(responseText, state.currentTheme)
869
926
  process.exit(0)
870
927
  })
871
928
  return
872
929
  }
873
930
 
874
931
  // 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)
932
+ renderHeader(state.currentTheme)
933
+
934
+ // Compulsory Login Enforcement
935
+ enforceCompulsoryLogin(state, () => {
936
+ renderWelcomeBox(process.cwd(), state.authSession, state.currentTheme)
937
+ renderTips(state.currentTheme)
938
+
939
+ promptModelSelection(state.currentModel, state.currentTheme, (selectedModel) => {
940
+ state.currentModel = selectedModel
941
+ saveConfig({ theme: state.currentTheme, model: state.currentModel })
942
+ renderStatusBar(state.currentModel, process.cwd(), state.authSession, state.currentTheme)
943
+ startRepl(state)
944
+ })
883
945
  })
884
946
  }
885
947