@taj-special/dravix-code 1.1.27 → 1.2.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.
- package/dist/cli/commands.js +34 -8
- package/dist/cli/index.js +24 -84
- package/dist/cli/repl.js +215 -65
- package/dist/services/ai.js +89 -11
- package/dist/services/auth.js +2 -2
- package/dist/services/context.js +107 -8
- package/dist/services/executor.js +244 -8
- package/dist/services/undo.js +181 -0
- package/dist/utils/display.js +58 -47
- package/package.json +1 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
const UNDO_DIR = path.join(os.homedir(), '.dravix-code', 'undo');
|
|
5
|
+
let currentSession = null;
|
|
6
|
+
// Initialize undo directory
|
|
7
|
+
function ensureUndoDir() {
|
|
8
|
+
fs.mkdirSync(UNDO_DIR, { recursive: true });
|
|
9
|
+
}
|
|
10
|
+
// Generate unique session ID
|
|
11
|
+
function generateSessionId() {
|
|
12
|
+
return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
13
|
+
}
|
|
14
|
+
// Start a new undo session (before a batch of operations)
|
|
15
|
+
export function startUndoSession() {
|
|
16
|
+
ensureUndoDir();
|
|
17
|
+
const id = generateSessionId();
|
|
18
|
+
currentSession = {
|
|
19
|
+
id,
|
|
20
|
+
timestamp: new Date().toISOString(),
|
|
21
|
+
operations: [],
|
|
22
|
+
};
|
|
23
|
+
return id;
|
|
24
|
+
}
|
|
25
|
+
// Backup a file before modification
|
|
26
|
+
export function backupFile(filePath, cwd) {
|
|
27
|
+
try {
|
|
28
|
+
if (!fs.existsSync(filePath))
|
|
29
|
+
return null;
|
|
30
|
+
const relPath = path.relative(cwd, filePath).replace(/\\/g, '/');
|
|
31
|
+
const backupId = currentSession?.id ?? generateSessionId();
|
|
32
|
+
const backupDir = path.join(UNDO_DIR, backupId);
|
|
33
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
34
|
+
// Create backup with path-based naming
|
|
35
|
+
const backupName = relPath.replace(/[/\\]/g, '__');
|
|
36
|
+
const backupPath = path.join(backupDir, backupName);
|
|
37
|
+
fs.copyFileSync(filePath, backupPath);
|
|
38
|
+
return backupPath;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Record a write operation for undo
|
|
45
|
+
export function recordWrite(filePath, backupPath) {
|
|
46
|
+
if (!currentSession)
|
|
47
|
+
return;
|
|
48
|
+
currentSession.operations.push({
|
|
49
|
+
type: 'write',
|
|
50
|
+
path: filePath,
|
|
51
|
+
backupPath: backupPath ?? undefined,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// Record an edit operation for undo
|
|
55
|
+
export function recordEdit(filePath, backupPath) {
|
|
56
|
+
if (!currentSession)
|
|
57
|
+
return;
|
|
58
|
+
currentSession.operations.push({
|
|
59
|
+
type: 'edit',
|
|
60
|
+
path: filePath,
|
|
61
|
+
backupPath: backupPath ?? undefined,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// Record a delete operation for undo
|
|
65
|
+
export function recordDelete(filePath) {
|
|
66
|
+
if (!currentSession)
|
|
67
|
+
return;
|
|
68
|
+
let content = '';
|
|
69
|
+
try {
|
|
70
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
71
|
+
}
|
|
72
|
+
catch { /* ignore */ }
|
|
73
|
+
currentSession.operations.push({
|
|
74
|
+
type: 'delete',
|
|
75
|
+
path: filePath,
|
|
76
|
+
deletedContent: content || undefined,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// Save the current undo session
|
|
80
|
+
export function saveUndoSession() {
|
|
81
|
+
if (!currentSession || currentSession.operations.length === 0)
|
|
82
|
+
return;
|
|
83
|
+
ensureUndoDir();
|
|
84
|
+
const sessionPath = path.join(UNDO_DIR, `${currentSession.id}.json`);
|
|
85
|
+
fs.writeFileSync(sessionPath, JSON.stringify(currentSession, null, 2), 'utf-8');
|
|
86
|
+
}
|
|
87
|
+
// Undo the last session
|
|
88
|
+
export function undoLastSession(cwd) {
|
|
89
|
+
ensureUndoDir();
|
|
90
|
+
// Find the most recent session file
|
|
91
|
+
const files = fs.readdirSync(UNDO_DIR)
|
|
92
|
+
.filter(f => f.endsWith('.json'))
|
|
93
|
+
.sort()
|
|
94
|
+
.reverse();
|
|
95
|
+
if (files.length === 0) {
|
|
96
|
+
return { success: false, message: 'No undo history found' };
|
|
97
|
+
}
|
|
98
|
+
const sessionPath = path.join(UNDO_DIR, files[0]);
|
|
99
|
+
const session = JSON.parse(fs.readFileSync(sessionPath, 'utf-8'));
|
|
100
|
+
let restored = 0;
|
|
101
|
+
let errors = 0;
|
|
102
|
+
// Process operations in reverse order
|
|
103
|
+
for (const op of session.operations.reverse()) {
|
|
104
|
+
try {
|
|
105
|
+
if (op.type === 'write' || op.type === 'edit') {
|
|
106
|
+
// Restore from backup
|
|
107
|
+
if (op.backupPath && fs.existsSync(op.backupPath)) {
|
|
108
|
+
fs.copyFileSync(op.backupPath, op.path);
|
|
109
|
+
restored++;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
errors++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (op.type === 'delete') {
|
|
116
|
+
// Recreate the deleted file
|
|
117
|
+
if (op.deletedContent !== undefined) {
|
|
118
|
+
fs.mkdirSync(path.dirname(op.path), { recursive: true });
|
|
119
|
+
fs.writeFileSync(op.path, op.deletedContent, 'utf-8');
|
|
120
|
+
restored++;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
errors++;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
errors++;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Clean up session file and backup directory
|
|
132
|
+
try {
|
|
133
|
+
fs.unlinkSync(sessionPath);
|
|
134
|
+
const backupDir = path.join(UNDO_DIR, session.id);
|
|
135
|
+
if (fs.existsSync(backupDir)) {
|
|
136
|
+
fs.rmSync(backupDir, { recursive: true });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch { /* ignore */ }
|
|
140
|
+
const msg = `Undone: ${restored} operation(s) restored` +
|
|
141
|
+
(errors > 0 ? `, ${errors} error(s)` : '') +
|
|
142
|
+
` from ${new Date(session.timestamp).toLocaleString()}`;
|
|
143
|
+
return { success: restored > 0, message: msg };
|
|
144
|
+
}
|
|
145
|
+
// Get list of available undo sessions
|
|
146
|
+
export function listUndoSessions() {
|
|
147
|
+
ensureUndoDir();
|
|
148
|
+
const files = fs.readdirSync(UNDO_DIR).filter(f => f.endsWith('.json'));
|
|
149
|
+
return files.map(f => {
|
|
150
|
+
try {
|
|
151
|
+
const session = JSON.parse(fs.readFileSync(path.join(UNDO_DIR, f), 'utf-8'));
|
|
152
|
+
return {
|
|
153
|
+
id: session.id,
|
|
154
|
+
timestamp: session.timestamp,
|
|
155
|
+
ops: session.operations.length,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return { id: f.replace('.json', ''), timestamp: '', ops: 0 };
|
|
160
|
+
}
|
|
161
|
+
}).sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
162
|
+
}
|
|
163
|
+
// Clean up old undo sessions (older than 7 days)
|
|
164
|
+
export function cleanupOldSessions() {
|
|
165
|
+
ensureUndoDir();
|
|
166
|
+
const files = fs.readdirSync(UNDO_DIR).filter(f => f.endsWith('.json'));
|
|
167
|
+
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
168
|
+
for (const f of files) {
|
|
169
|
+
try {
|
|
170
|
+
const session = JSON.parse(fs.readFileSync(path.join(UNDO_DIR, f), 'utf-8'));
|
|
171
|
+
if (new Date(session.timestamp).getTime() < cutoff) {
|
|
172
|
+
fs.unlinkSync(path.join(UNDO_DIR, f));
|
|
173
|
+
const backupDir = path.join(UNDO_DIR, session.id);
|
|
174
|
+
if (fs.existsSync(backupDir)) {
|
|
175
|
+
fs.rmSync(backupDir, { recursive: true });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch { /* ignore */ }
|
|
180
|
+
}
|
|
181
|
+
}
|
package/dist/utils/display.js
CHANGED
|
@@ -7,12 +7,18 @@ const _pkg = JSON.parse(readFileSync(join(_dir, '../../package.json'), 'utf-8'))
|
|
|
7
7
|
export const VERSION = _pkg.version;
|
|
8
8
|
export const colors = {
|
|
9
9
|
primary: chalk.hex('#6366f1'),
|
|
10
|
+
accent: chalk.hex('#818cf8'),
|
|
10
11
|
success: chalk.hex('#34d399'),
|
|
11
12
|
error: chalk.hex('#f87171'),
|
|
12
13
|
warn: chalk.hex('#fbbf24'),
|
|
13
14
|
muted: chalk.hex('#6b7280'),
|
|
14
15
|
ai: chalk.hex('#c4b5fd'),
|
|
15
16
|
dim: chalk.hex('#374151'),
|
|
17
|
+
bg: chalk.hex('#0d1117'),
|
|
18
|
+
surface: chalk.hex('#161b22'),
|
|
19
|
+
border: chalk.hex('#30363d'),
|
|
20
|
+
text: chalk.hex('#e2e8f0'),
|
|
21
|
+
subtext: chalk.hex('#94a3b8'),
|
|
16
22
|
};
|
|
17
23
|
export function banner() {
|
|
18
24
|
console.log(colors.primary.bold(`
|
|
@@ -22,57 +28,69 @@ export function banner() {
|
|
|
22
28
|
██║ ██║██╔══██╗██╔══██║╚██╗ ██╔╝██║ ██╔██╗
|
|
23
29
|
██████╔╝██║ ██║██║ ██║ ╚████╔╝ ██║██╔╝ ██╗
|
|
24
30
|
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝`));
|
|
25
|
-
console.log(
|
|
26
|
-
console.log(
|
|
31
|
+
console.log(colors.primary.bold(' Code') + colors.muted(` v${VERSION} · `) + colors.accent('AI-powered coding assistant'));
|
|
32
|
+
console.log(colors.dim(' ─────────────────────────────────────────────\n'));
|
|
27
33
|
}
|
|
28
34
|
export function printAI(chunk) {
|
|
29
35
|
process.stdout.write(colors.ai(chunk));
|
|
30
36
|
}
|
|
31
37
|
export function printOpResult(result) {
|
|
32
38
|
if (result.type === 'created') {
|
|
33
|
-
console.log(colors.success(' ✓') +
|
|
39
|
+
console.log(colors.success(' ✓') + colors.text(' Created ') + colors.muted(result.path ?? ''));
|
|
34
40
|
}
|
|
35
41
|
else if (result.type === 'skipped') {
|
|
36
|
-
console.log(colors.muted(' ·') +
|
|
42
|
+
console.log(colors.muted(' ·') + colors.text(' Unchanged ') + colors.muted(result.path ?? ''));
|
|
37
43
|
}
|
|
38
44
|
else if (result.type === 'folder_created') {
|
|
39
|
-
console.log(colors.success(' ✓') +
|
|
45
|
+
console.log(colors.success(' ✓') + colors.text(' Created dir ') + colors.muted(result.path ?? ''));
|
|
40
46
|
}
|
|
41
47
|
else if (result.type === 'deleted') {
|
|
42
|
-
console.log(colors.error(' ✗') +
|
|
48
|
+
console.log(colors.error(' ✗') + colors.text(' Deleted ') + colors.muted(result.path ?? ''));
|
|
43
49
|
}
|
|
44
50
|
else if (result.type === 'modified') {
|
|
45
51
|
const diff = result.diff ?? [];
|
|
46
52
|
const adds = diff.filter(d => d.kind === 'add').length;
|
|
47
53
|
const removes = diff.filter(d => d.kind === 'remove').length;
|
|
48
54
|
const cols = process.stdout.columns ?? 80;
|
|
49
|
-
// ── Header ──────────────────────────────────────────────────
|
|
50
|
-
// For large files (>1500 lines), diff is empty — use raw line counts
|
|
51
55
|
const displayAdds = adds > 0 ? adds : (result.linesAfter ?? 0) - Math.min(result.linesBefore ?? 0, result.linesAfter ?? 0);
|
|
52
56
|
const displayRemoves = removes > 0 ? removes : (result.linesBefore ?? 0) - Math.min(result.linesBefore ?? 0, result.linesAfter ?? 0);
|
|
53
57
|
const statStr = [
|
|
54
|
-
(adds > 0 || result.linesAfter !== undefined) && displayAdds > 0 ?
|
|
55
|
-
(removes > 0 || result.linesBefore !== undefined) && displayRemoves > 0 ?
|
|
58
|
+
(adds > 0 || result.linesAfter !== undefined) && displayAdds > 0 ? colors.success.bold(`+${displayAdds}`) : '',
|
|
59
|
+
(removes > 0 || result.linesBefore !== undefined) && displayRemoves > 0 ? colors.error.bold(`-${displayRemoves}`) : '',
|
|
56
60
|
].filter(Boolean).join(' ');
|
|
57
|
-
const pathStr =
|
|
58
|
-
const iconStr =
|
|
59
|
-
const labelStr =
|
|
61
|
+
const pathStr = colors.text(result.path ?? '');
|
|
62
|
+
const iconStr = colors.warn(' ~');
|
|
63
|
+
const labelStr = colors.subtext(' Updated ');
|
|
60
64
|
const statPad = statStr ? ' ' + statStr : '';
|
|
61
65
|
console.log(iconStr + labelStr + pathStr + statPad);
|
|
62
|
-
//
|
|
66
|
+
// Compact mode: only show diff if changes are small (<=10 lines)
|
|
67
|
+
// For larger changes, just show summary
|
|
68
|
+
const totalChanges = displayAdds + displayRemoves;
|
|
69
|
+
if (totalChanges > 10) {
|
|
70
|
+
// Large change — show compact summary only
|
|
71
|
+
if (result.linesBefore !== undefined && result.linesAfter !== undefined) {
|
|
72
|
+
const delta = result.linesAfter - result.linesBefore;
|
|
73
|
+
const deltaStr = delta > 0 ? colors.success(`+${delta}`) : delta < 0 ? colors.error(`${delta}`) : '=0';
|
|
74
|
+
console.log(colors.muted(` ${result.linesBefore} → ${result.linesAfter} lines (${deltaStr})`));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.log(colors.muted(` ${totalChanges} lines changed`));
|
|
78
|
+
}
|
|
79
|
+
console.log(colors.muted(` ${colors.muted('···')} ${colors.success(`+${displayAdds}`)} added, ${colors.error(`-${displayRemoves}`)} removed ${colors.muted('(more lines not shown)')}`));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
63
82
|
if (diff.length === 0 && result.linesBefore !== undefined) {
|
|
64
83
|
const delta = (result.linesAfter ?? 0) - (result.linesBefore ?? 0);
|
|
65
|
-
const deltaStr = delta > 0 ?
|
|
66
|
-
console.log(
|
|
84
|
+
const deltaStr = delta > 0 ? colors.success(`+${delta} lines`) : delta < 0 ? colors.error(`${delta} lines`) : 'no line count change';
|
|
85
|
+
console.log(colors.muted(` ${result.linesBefore} → ${result.linesAfter} lines (${deltaStr})`));
|
|
67
86
|
return;
|
|
68
87
|
}
|
|
69
88
|
if (diff.length === 0 || (adds === 0 && removes === 0)) {
|
|
70
|
-
console.log(
|
|
89
|
+
console.log(colors.muted(' (formatting / whitespace change)'));
|
|
71
90
|
return;
|
|
72
91
|
}
|
|
73
|
-
//
|
|
74
|
-
const
|
|
75
|
-
const MAX_SIDE = 40; // max removes and max adds shown (each independently)
|
|
92
|
+
const CONTEXT = 2; // Reduced from 3 to 2 for more compact diffs
|
|
93
|
+
const MAX_SIDE = 20; // Reduced from 40 to 20 for more compact diffs
|
|
76
94
|
const show = new Uint8Array(diff.length);
|
|
77
95
|
for (let i = 0; i < diff.length; i++) {
|
|
78
96
|
if (diff[i].kind !== 'context') {
|
|
@@ -85,18 +103,17 @@ export function printOpResult(result) {
|
|
|
85
103
|
const renderLine = (line) => {
|
|
86
104
|
const num = String(line.lineNo).padStart(5, ' ');
|
|
87
105
|
if (line.kind === 'remove') {
|
|
88
|
-
const content = ` ${num} ${
|
|
89
|
-
console.log(' ' +
|
|
106
|
+
const content = ` ${num} ${colors.error('-')} ${clip(line.text)}`;
|
|
107
|
+
console.log(' ' + colors.error('▌') + chalk.bgHex('#1c0a0a')(content.padEnd(cols - 3)));
|
|
90
108
|
}
|
|
91
109
|
else if (line.kind === 'add') {
|
|
92
|
-
const content = ` ${num} ${
|
|
93
|
-
console.log(' ' +
|
|
110
|
+
const content = ` ${num} ${colors.success('+')} ${clip(line.text)}`;
|
|
111
|
+
console.log(' ' + colors.success('▌') + chalk.bgHex('#021a0e')(content.padEnd(cols - 3)));
|
|
94
112
|
}
|
|
95
113
|
else {
|
|
96
|
-
console.log(
|
|
114
|
+
console.log(colors.dim(` ${num} ${clip(line.text)}`));
|
|
97
115
|
}
|
|
98
116
|
};
|
|
99
|
-
// Build hunks (contiguous visible sections)
|
|
100
117
|
const hunks = [];
|
|
101
118
|
let curHunk = [];
|
|
102
119
|
let lastIdx = -1;
|
|
@@ -112,14 +129,13 @@ export function printOpResult(result) {
|
|
|
112
129
|
}
|
|
113
130
|
if (curHunk.length > 0)
|
|
114
131
|
hunks.push(curHunk);
|
|
115
|
-
// Per-hunk rendering: leading context → all removes → all adds → trailing context
|
|
116
132
|
let shownRemoves = 0;
|
|
117
133
|
let shownAdds = 0;
|
|
118
134
|
for (let h = 0; h < hunks.length; h++) {
|
|
119
135
|
if (shownRemoves >= MAX_SIDE && shownAdds >= MAX_SIDE)
|
|
120
136
|
break;
|
|
121
137
|
if (h > 0)
|
|
122
|
-
console.log(
|
|
138
|
+
console.log(colors.dim(' ╌╌╌'));
|
|
123
139
|
const hunk = hunks[h];
|
|
124
140
|
const before = [];
|
|
125
141
|
const hRemoves = [];
|
|
@@ -163,26 +179,26 @@ export function printOpResult(result) {
|
|
|
163
179
|
const hiddenAdds = adds - shownAdds;
|
|
164
180
|
if (hiddenRemoves > 0 || hiddenAdds > 0) {
|
|
165
181
|
const parts = [
|
|
166
|
-
hiddenRemoves > 0 ?
|
|
167
|
-
hiddenAdds > 0 ?
|
|
182
|
+
hiddenRemoves > 0 ? colors.error(`-${hiddenRemoves}`) : '',
|
|
183
|
+
hiddenAdds > 0 ? colors.success(`+${hiddenAdds}`) : '',
|
|
168
184
|
].filter(Boolean).join(' ');
|
|
169
|
-
console.log(
|
|
185
|
+
console.log(colors.muted(` … ${parts} more lines not shown`));
|
|
170
186
|
}
|
|
171
187
|
}
|
|
172
188
|
else if (result.type === 'run') {
|
|
173
189
|
const cmd = result.message ?? '';
|
|
174
190
|
const cols = process.stdout.columns ?? 80;
|
|
175
|
-
console.log(colors.primary(' $') + ' ' +
|
|
191
|
+
console.log(colors.primary(' $') + ' ' + colors.text(cmd));
|
|
176
192
|
if (result.output) {
|
|
177
193
|
const outLines = result.output.split('\n').filter(l => l.trim()).slice(0, 20);
|
|
178
194
|
if (outLines.length > 0) {
|
|
179
195
|
const maxW = Math.min(Math.max(...outLines.map(l => l.length), 10) + 2, cols - 8);
|
|
180
|
-
console.log(
|
|
196
|
+
console.log(colors.dim(' ╭' + '─'.repeat(maxW)));
|
|
181
197
|
for (const line of outLines) {
|
|
182
198
|
const clipped = line.length > maxW - 2 ? line.slice(0, maxW - 3) + '…' : line;
|
|
183
|
-
console.log(
|
|
199
|
+
console.log(colors.dim(' │') + ' ' + colors.subtext(clipped));
|
|
184
200
|
}
|
|
185
|
-
console.log(
|
|
201
|
+
console.log(colors.dim(' ╰' + '─'.repeat(maxW)));
|
|
186
202
|
}
|
|
187
203
|
}
|
|
188
204
|
}
|
|
@@ -191,31 +207,31 @@ export function printOpResult(result) {
|
|
|
191
207
|
const cols = process.stdout.columns ?? 80;
|
|
192
208
|
const errLines = msg.split('\n').filter(l => l.trim());
|
|
193
209
|
if (errLines.length <= 1) {
|
|
194
|
-
console.log(colors.error(' ✗') +
|
|
210
|
+
console.log(colors.error(' ✗') + colors.text(' ' + msg));
|
|
195
211
|
}
|
|
196
212
|
else {
|
|
197
|
-
console.log(colors.error(' ✗') +
|
|
213
|
+
console.log(colors.error(' ✗') + colors.text(' ' + errLines[0]));
|
|
198
214
|
const rest = errLines.slice(1);
|
|
199
215
|
const maxW = Math.min(Math.max(...rest.map(l => l.length), 10) + 2, cols - 8);
|
|
200
216
|
console.log(chalk.hex('#450a0a')(' ╭' + '─'.repeat(maxW)));
|
|
201
217
|
for (const line of rest) {
|
|
202
218
|
const clipped = line.length > maxW - 2 ? line.slice(0, maxW - 3) + '…' : line;
|
|
203
|
-
console.log(chalk.hex('#450a0a')(' │') + ' ' +
|
|
219
|
+
console.log(chalk.hex('#450a0a')(' │') + ' ' + colors.error(clipped));
|
|
204
220
|
}
|
|
205
221
|
console.log(chalk.hex('#450a0a')(' ╰' + '─'.repeat(maxW)));
|
|
206
222
|
}
|
|
207
223
|
}
|
|
208
224
|
}
|
|
209
225
|
export function printError(msg) {
|
|
210
|
-
console.log('\n' + colors.error(' ✗ ') +
|
|
226
|
+
console.log('\n' + colors.error(' ✗ ') + colors.text(msg) + '\n');
|
|
211
227
|
}
|
|
212
228
|
export function printInfo(msg) {
|
|
213
229
|
console.log(colors.muted(' ' + msg));
|
|
214
230
|
}
|
|
215
231
|
export function printHelp() {
|
|
216
|
-
const C =
|
|
217
|
-
const DIM =
|
|
218
|
-
const W =
|
|
232
|
+
const C = colors.primary;
|
|
233
|
+
const DIM = colors.muted;
|
|
234
|
+
const W = colors.text;
|
|
219
235
|
const cols = process.stdout.columns ?? 80;
|
|
220
236
|
const line = DIM(' ' + '─'.repeat(Math.min(cols - 4, 60)));
|
|
221
237
|
console.log(`
|
|
@@ -243,11 +259,6 @@ ${line}
|
|
|
243
259
|
${C('Space')} ${W('Select item in file picker')}
|
|
244
260
|
${C('Esc')} ${W('Close picker')}
|
|
245
261
|
|
|
246
|
-
${colors.primary.bold(' File picker triggers')}
|
|
247
|
-
${line}
|
|
248
|
-
${C('@filename')} ${W('Search and insert a file reference')}
|
|
249
|
-
${C('Tab')} ${W('Autocomplete path at cursor position')}
|
|
250
|
-
|
|
251
262
|
${colors.primary.bold(' Tips')}
|
|
252
263
|
${line}
|
|
253
264
|
${colors.muted('·')} ${W('Start typing while AI responds — your message queues automatically')}
|