@taj-special/dravix-code 1.3.8 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,3 @@
1
- import * as path from 'path';
2
1
  import { execFileSync } from 'child_process';
3
2
  import { buildContext } from '../services/context.js';
4
3
  import { banner, printHelp, printInfo, colors } from '../utils/display.js';
@@ -38,7 +37,7 @@ export async function handleCommand(cmd, cwd, clearHistory, addFileToContext, ge
38
37
  }
39
38
  if (name === '/clear' || name === '/new') {
40
39
  clearHistory();
41
- process.stdout.write(`\x1b]0;Dravix Code — ${path.basename(cwd)}\x07`);
40
+ process.stdout.write(`\x1b]0;Dravix Code — AI-powered coding assistant\x07`);
42
41
  console.clear();
43
42
  banner();
44
43
  const { name: userName } = getSavedUser();
package/dist/cli/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import * as path from 'path';
3
- import { randomBytes } from 'crypto';
3
+ import * as fs from 'fs';
4
+ import * as os from 'os';
5
+ import { randomBytes, createHash } from 'crypto';
4
6
  import { execSync } from 'child_process';
5
7
  import chalk from 'chalk';
6
8
  import { banner, printHelp, printError, colors } from '../utils/display.js';
@@ -9,6 +11,71 @@ import { isLoggedIn, logout, getSavedUser, saveConfig, checkPlan, AUTH_URL } fro
9
11
  import { checkUsage, usageBar, fmtNum, formatResetTime } from '../services/usage.js';
10
12
  const POLL_URL = 'https://dravix.app/cli-auth.php';
11
13
  const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
14
+ const VISITED_FILE = path.join(os.homedir(), '.dravix-code', 'visited.json');
15
+ // ── Project visit tracking ─────────────────────────────────────
16
+ function projectHash(cwd) {
17
+ return createHash('sha256').update(path.resolve(cwd)).digest('hex').slice(0, 10);
18
+ }
19
+ function getVisitedProjects() {
20
+ try {
21
+ return JSON.parse(fs.readFileSync(VISITED_FILE, 'utf-8'));
22
+ }
23
+ catch {
24
+ return [];
25
+ }
26
+ }
27
+ function markProjectVisited(hash) {
28
+ const visited = getVisitedProjects();
29
+ if (!visited.includes(hash)) {
30
+ visited.push(hash);
31
+ fs.mkdirSync(path.dirname(VISITED_FILE), { recursive: true });
32
+ fs.writeFileSync(VISITED_FILE, JSON.stringify(visited), 'utf-8');
33
+ }
34
+ }
35
+ async function showFirstTimeDialog(projectName) {
36
+ const cols = process.stdout.columns ?? 80;
37
+ const W = Math.min(cols - 4, 58);
38
+ const BC = chalk.hex('#6366f1');
39
+ const Wt = chalk.hex('#e2e8f0');
40
+ const Dim = chalk.hex('#6b7280');
41
+ const G = chalk.hex('#34d399');
42
+ const R = chalk.hex('#f87171');
43
+ console.clear();
44
+ console.log('');
45
+ console.log(' ' + BC('╭' + '─'.repeat(W) + '╮'));
46
+ console.log(' ' + BC('│') + ' ' + chalk.hex('#a78bfa').bold('Dravix Code') + Dim(' — first time in this project') + ' '.repeat(Math.max(W - 44, 0)) + BC('│'));
47
+ console.log(' ' + BC('├' + '─'.repeat(W) + '┤'));
48
+ console.log(' ' + BC('│') + ' ' + Wt('Open Dravix Code in:') + ' '.repeat(Math.max(W - 22, 0)) + BC('│'));
49
+ console.log(' ' + BC('│') + ' ' + chalk.hex('#c4b5fd').bold(` ${projectName}`) + ' '.repeat(Math.max(W - projectName.length - 8, 0)) + BC('│'));
50
+ console.log(' ' + BC('├' + '─'.repeat(W) + '┤'));
51
+ console.log(' ' + BC('│') + ' ' + ' '.repeat(Math.max(W - 28, 0)) + BC('│'));
52
+ console.log(' ' + BC('│') + ' ' + G.bold(' [ Enter ] ') + Wt(' Yes, open here') + ' '.repeat(Math.max(W - 40, 0)) + BC('│'));
53
+ console.log(' ' + BC('│') + ' ' + R.bold(' [ Esc ] ') + Wt(' No, exit') + ' '.repeat(Math.max(W - 39, 0)) + BC('│'));
54
+ console.log(' ' + BC('│') + ' ' + ' '.repeat(Math.max(W - 28, 0)) + BC('│'));
55
+ console.log(' ' + BC('╰' + '─'.repeat(W) + '╯'));
56
+ console.log('');
57
+ return new Promise((resolve) => {
58
+ process.stdin.setRawMode(true);
59
+ process.stdin.resume();
60
+ process.stdin.setEncoding('utf8');
61
+ function onKey(data) {
62
+ if (data === '\r' || data === '\n') {
63
+ cleanup();
64
+ resolve(true);
65
+ }
66
+ else if (data === '\x1b' || data === '\x03') {
67
+ cleanup();
68
+ resolve(false);
69
+ }
70
+ }
71
+ function cleanup() {
72
+ process.stdin.removeListener('data', onKey);
73
+ process.stdin.setRawMode(false);
74
+ process.stdin.pause();
75
+ }
76
+ process.stdin.on('data', onKey);
77
+ });
78
+ }
12
79
  function link(url) {
13
80
  return `\x1b]8;;${url}\x1b\\${url}\x1b]8;;\x1b\\`;
14
81
  }
@@ -221,6 +288,22 @@ async function main() {
221
288
  process.on('exit', cleanup);
222
289
  process.on('SIGINT', () => { cleanup(); process.exit(0); });
223
290
  process.on('SIGTERM', () => { cleanup(); process.exit(0); });
291
+ // ── First-time project check ──────────────────────────────
292
+ const pHash = projectHash(cwd);
293
+ const visited = getVisitedProjects();
294
+ if (!visited.includes(pHash)) {
295
+ process.stdin.setRawMode(false);
296
+ process.stdin.pause();
297
+ const open = await showFirstTimeDialog(path.basename(cwd));
298
+ if (!open) {
299
+ console.log(chalk.hex('#6b7280')('\n Exited.\n'));
300
+ process.exit(0);
301
+ }
302
+ markProjectVisited(pHash);
303
+ // Restore terminal for upcoming banner + REPL
304
+ process.stdin.setRawMode(true);
305
+ process.stdin.resume();
306
+ }
224
307
  await startRepl(cwd);
225
308
  }
226
309
  main().catch((err) => {
package/dist/cli/repl.js CHANGED
@@ -1707,8 +1707,8 @@ function timeAgoShort(iso) {
1707
1707
  return `${d}d`;
1708
1708
  return `${Math.floor(d / 30)}mo`;
1709
1709
  }
1710
- async function showConversationPicker() {
1711
- const allConvs = listConversations(100);
1710
+ async function showConversationPicker(cwdForList) {
1711
+ const allConvs = listConversations(cwdForList, 100);
1712
1712
  if (allConvs.length === 0) {
1713
1713
  printInfo('No saved conversations yet.');
1714
1714
  return null;
@@ -2149,7 +2149,7 @@ export async function startRepl(cwd) {
2149
2149
  function setTerminalTitle(title) {
2150
2150
  process.stdout.write(`\x1b]0;Dravix Code — ${title}\x07`);
2151
2151
  }
2152
- setTerminalTitle(path.basename(cwd));
2152
+ setTerminalTitle('AI-powered coding assistant');
2153
2153
  const token = getToken() ?? '';
2154
2154
  let SYSTEM_PROMPT = `You are Dravix Code, an interactive CLI coding agent powered by DeepSeek that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
2155
2155
 
@@ -2415,9 +2415,9 @@ The summary should feel like a natural conversation close — informative, brief
2415
2415
  lastUserLine = line;
2416
2416
  // ── Slash commands ──────────────────────────────────────────
2417
2417
  if (!skipInput && line.trim() === '/resume') {
2418
- const convId = await showConversationPicker();
2418
+ const convId = await showConversationPicker(activeCwd);
2419
2419
  if (convId) {
2420
- const conv = loadConversation(convId);
2420
+ const conv = loadConversation(convId, activeCwd);
2421
2421
  if (conv) {
2422
2422
  // Restore the original working directory so file ops work correctly
2423
2423
  activeCwd = conv.cwd;
@@ -1,9 +1,18 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import * as os from 'os';
4
- const CONV_DIR = path.join(os.homedir(), '.dravix-code', 'conversations');
5
- function ensureDir() {
6
- fs.mkdirSync(CONV_DIR, { recursive: true });
4
+ import { createHash } from 'crypto';
5
+ const BASE_DIR = path.join(os.homedir(), '.dravix-code', 'projects');
6
+ function projectDir(cwd) {
7
+ const hash = createHash('sha256').update(cwd).digest('hex').slice(0, 12);
8
+ return path.join(BASE_DIR, hash);
9
+ }
10
+ function convDir(cwd) {
11
+ return path.join(projectDir(cwd), 'conversations');
12
+ }
13
+ function ensureDir(cwd) {
14
+ const dir = convDir(cwd);
15
+ fs.mkdirSync(dir, { recursive: true });
7
16
  }
8
17
  export function generateId() {
9
18
  return Date.now().toString(36) + Math.random().toString(36).slice(2, 9) + Math.random().toString(36).slice(2, 5);
@@ -18,7 +27,7 @@ export function saveConversation(id, title, cwd, messages) {
18
27
  const chat = messages.filter(m => m.role === 'user' || m.role === 'assistant');
19
28
  if (chat.length === 0)
20
29
  return;
21
- ensureDir();
30
+ ensureDir(cwd);
22
31
  const data = {
23
32
  id, title, cwd,
24
33
  updatedAt: new Date().toISOString(),
@@ -26,26 +35,34 @@ export function saveConversation(id, title, cwd, messages) {
26
35
  messages,
27
36
  };
28
37
  try {
29
- fs.writeFileSync(path.join(CONV_DIR, `${id}.json`), JSON.stringify(data), 'utf-8');
38
+ fs.writeFileSync(path.join(convDir(cwd), `${id}.json`), JSON.stringify(data), 'utf-8');
30
39
  }
31
40
  catch { /* ignore write errors */ }
32
41
  }
33
- export function loadConversation(id) {
42
+ export function loadConversation(id, cwd) {
34
43
  try {
35
- return JSON.parse(fs.readFileSync(path.join(CONV_DIR, `${id}.json`), 'utf-8'));
44
+ const fp = path.join(convDir(cwd), `${id}.json`);
45
+ if (!fs.existsSync(fp)) {
46
+ // Try legacy global conversations directory
47
+ const legacyPath = path.join(os.homedir(), '.dravix-code', 'conversations', `${id}.json`);
48
+ if (fs.existsSync(legacyPath))
49
+ return JSON.parse(fs.readFileSync(legacyPath, 'utf-8'));
50
+ return null;
51
+ }
52
+ return JSON.parse(fs.readFileSync(fp, 'utf-8'));
36
53
  }
37
54
  catch {
38
55
  return null;
39
56
  }
40
57
  }
41
- export function listConversations(limit = 30) {
42
- ensureDir();
58
+ export function listConversations(cwd, limit = 30) {
59
+ ensureDir(cwd);
43
60
  try {
44
- return fs.readdirSync(CONV_DIR)
61
+ return fs.readdirSync(convDir(cwd))
45
62
  .filter(f => f.endsWith('.json'))
46
63
  .map(f => {
47
64
  try {
48
- const c = JSON.parse(fs.readFileSync(path.join(CONV_DIR, f), 'utf-8'));
65
+ const c = JSON.parse(fs.readFileSync(path.join(convDir(cwd), f), 'utf-8'));
49
66
  return { id: c.id, title: c.title, cwd: c.cwd, updatedAt: c.updatedAt, messageCount: c.messageCount };
50
67
  }
51
68
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taj-special/dravix-code",
3
- "version": "1.3.8",
3
+ "version": "1.4.0",
4
4
  "description": "AI-powered coding assistant CLI — Dravix Code",
5
5
  "type": "module",
6
6
  "bin": {