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.
- package/package.json +1 -1
- package/prituscode.js +239 -177
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,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(
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
state.
|
|
881
|
-
|
|
882
|
-
|
|
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
|
|