@sylix/coworker 1.2.6 → 1.3.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/index.d.ts.map +1 -1
- package/dist/cli/index.js +249 -27
- package/dist/cli/index.js.map +1 -1
- package/dist/core/CoWorkerAgent.d.ts +1 -0
- package/dist/core/CoWorkerAgent.d.ts.map +1 -1
- package/dist/core/CoWorkerAgent.js +86 -10
- package/dist/core/CoWorkerAgent.js.map +1 -1
- package/dist/review/CodeReviewEngine.d.ts +78 -0
- package/dist/review/CodeReviewEngine.d.ts.map +1 -0
- package/dist/review/CodeReviewEngine.js +449 -0
- package/dist/review/CodeReviewEngine.js.map +1 -0
- package/dist/skills/HookAndSkillManager.d.ts +29 -5
- package/dist/skills/HookAndSkillManager.d.ts.map +1 -1
- package/dist/skills/HookAndSkillManager.js +165 -11
- package/dist/skills/HookAndSkillManager.js.map +1 -1
- package/dist/todos/TodoManager.d.ts +19 -0
- package/dist/todos/TodoManager.d.ts.map +1 -0
- package/dist/todos/TodoManager.js +102 -0
- package/dist/todos/TodoManager.js.map +1 -0
- package/dist/tools/NativeTools.d.ts +98 -0
- package/dist/tools/NativeTools.d.ts.map +1 -1
- package/dist/tools/NativeTools.js +571 -0
- package/dist/tools/NativeTools.js.map +1 -1
- package/dist/tools/Schemas.d.ts +1199 -102
- package/dist/tools/Schemas.d.ts.map +1 -1
- package/dist/tools/Schemas.js +358 -40
- package/dist/tools/Schemas.js.map +1 -1
- package/dist/utils/output.d.ts +63 -44
- package/dist/utils/output.d.ts.map +1 -1
- package/dist/utils/output.js +279 -135
- package/dist/utils/output.js.map +1 -1
- package/package.json +1 -1
package/dist/utils/output.js
CHANGED
|
@@ -33,14 +33,16 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.Timer = exports.ThinkingAnimation = exports.theme = void 0;
|
|
36
|
+
exports.Timer = exports.ThinkingAnimation = exports.theme = exports.CW_VERSION = void 0;
|
|
37
37
|
exports.getTermWidth = getTermWidth;
|
|
38
38
|
exports.showBanner = showBanner;
|
|
39
39
|
exports.showStaticBanner = showStaticBanner;
|
|
40
|
-
exports.
|
|
41
|
-
exports.
|
|
40
|
+
exports.showSessionInfo = showSessionInfo;
|
|
41
|
+
exports.renderMarkdown = renderMarkdown;
|
|
42
42
|
exports.printResponseHeader = printResponseHeader;
|
|
43
|
+
exports.printResponseLine = printResponseLine;
|
|
43
44
|
exports.printResponseFooter = printResponseFooter;
|
|
45
|
+
exports.streamResponseChunk = streamResponseChunk;
|
|
44
46
|
exports.printUserPrompt = printUserPrompt;
|
|
45
47
|
exports.printError = printError;
|
|
46
48
|
exports.printSuccess = printSuccess;
|
|
@@ -54,16 +56,22 @@ exports.printCommitBox = printCommitBox;
|
|
|
54
56
|
exports.printDiff = printDiff;
|
|
55
57
|
exports.printHelp = printHelp;
|
|
56
58
|
exports.printCompact = printCompact;
|
|
59
|
+
exports.getSlashCompletions = getSlashCompletions;
|
|
60
|
+
exports.renderSlashMenu = renderSlashMenu;
|
|
57
61
|
exports.printBox = printBox;
|
|
58
62
|
const chalk = __importStar(require("chalk"));
|
|
59
63
|
/**
|
|
60
64
|
* ═══════════════════════════════════════════════════════════════════
|
|
61
|
-
* CoWorker by Sylix — Premium Output System
|
|
65
|
+
* CoWorker v1.3.0 by Sylix — Premium Output System
|
|
62
66
|
* ═══════════════════════════════════════════════════════════════════
|
|
63
67
|
*
|
|
64
|
-
* Claude Code
|
|
65
|
-
*
|
|
68
|
+
* Full Claude Code-level terminal rendering:
|
|
69
|
+
* - Animated line-by-line banner with word-by-word tagline
|
|
70
|
+
* - Thinking animation with rotating messages
|
|
71
|
+
* - Markdown-aware response rendering (code blocks, bold, inline code)
|
|
72
|
+
* - Score cards, commit boxes, diff previews
|
|
66
73
|
*/
|
|
74
|
+
exports.CW_VERSION = '1.3.0';
|
|
67
75
|
// ============================================================================
|
|
68
76
|
// THEME SYSTEM
|
|
69
77
|
// ============================================================================
|
|
@@ -89,6 +97,7 @@ exports.theme = {
|
|
|
89
97
|
dim: hex('#6B7280'), // Gray — secondary info
|
|
90
98
|
white: hex('#F9FAFB'), // Off white
|
|
91
99
|
code: hex('#FCD34D'), // Yellow — code and commands
|
|
100
|
+
purple: hex('#A78BFA'), // Violet for tagline
|
|
92
101
|
};
|
|
93
102
|
// ============================================================================
|
|
94
103
|
// TERMINAL UTILS
|
|
@@ -111,7 +120,7 @@ function showCursor() {
|
|
|
111
120
|
process.stdout.write('\x1b[?25h');
|
|
112
121
|
}
|
|
113
122
|
// ============================================================================
|
|
114
|
-
//
|
|
123
|
+
// PART 1: ANIMATED BANNER — Line-by-line slide-in, word-by-word tagline
|
|
115
124
|
// ============================================================================
|
|
116
125
|
const LOGO_LINES = [
|
|
117
126
|
' ██████╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗███████╗██████╗ ',
|
|
@@ -122,58 +131,76 @@ const LOGO_LINES = [
|
|
|
122
131
|
' ╚═════╝ ╚═════╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝',
|
|
123
132
|
];
|
|
124
133
|
/**
|
|
125
|
-
* Full animated startup banner.
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
134
|
+
* Full animated startup banner for CoWorker v1.3.0.
|
|
135
|
+
*
|
|
136
|
+
* 1. Logo slides in line-by-line (60ms between lines)
|
|
137
|
+
* 2. Tagline fades in word-by-word with semantic coloring
|
|
138
|
+
* 3. Divider draws left-to-right (3ms/char)
|
|
139
|
+
* 4. Info line fades in
|
|
140
|
+
* 5. Session info appears with 50ms delay between lines
|
|
141
|
+
* Total: under 1.5 seconds
|
|
130
142
|
*/
|
|
131
143
|
async function showBanner(version, userEmail, model = 'helix-1.2') {
|
|
132
|
-
if (IS_PIPE)
|
|
144
|
+
if (IS_PIPE || process.env.CW_NO_BANNER === '1')
|
|
133
145
|
return;
|
|
134
146
|
hideCursor();
|
|
135
147
|
console.log('');
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
try {
|
|
149
|
+
// Step 1: Logo — line by line, each types across rapidly
|
|
150
|
+
for (const line of LOGO_LINES) {
|
|
151
|
+
const chars = [...line];
|
|
152
|
+
let buffer = '';
|
|
153
|
+
for (let i = 0; i < chars.length; i++) {
|
|
154
|
+
buffer += chars[i];
|
|
155
|
+
// Batch 6 chars at a time for speed
|
|
156
|
+
if (i % 6 === 5 || i === chars.length - 1) {
|
|
157
|
+
process.stdout.write(exports.theme.brand(buffer));
|
|
158
|
+
buffer = '';
|
|
159
|
+
await sleep(1);
|
|
160
|
+
}
|
|
146
161
|
}
|
|
162
|
+
process.stdout.write('\n');
|
|
163
|
+
await sleep(60); // 60ms between lines
|
|
147
164
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
165
|
+
// Step 2: Tagline — word by word, each with its own color
|
|
166
|
+
console.log('');
|
|
167
|
+
const taglineWords = [
|
|
168
|
+
{ text: 'by', color: exports.theme.dim },
|
|
169
|
+
{ text: 'Sylix', color: exports.theme.brand },
|
|
170
|
+
{ text: '·', color: exports.theme.dim },
|
|
171
|
+
{ text: 'Your', color: exports.theme.white },
|
|
172
|
+
{ text: 'AI', color: exports.theme.purple },
|
|
173
|
+
{ text: 'Coding', color: exports.theme.white },
|
|
174
|
+
{ text: 'Partner', color: exports.theme.white },
|
|
175
|
+
];
|
|
176
|
+
process.stdout.write(' ');
|
|
177
|
+
for (const word of taglineWords) {
|
|
178
|
+
process.stdout.write(word.color(word.text) + ' ');
|
|
179
|
+
await sleep(100);
|
|
180
|
+
}
|
|
181
|
+
console.log('');
|
|
182
|
+
// Step 3: Divider — draws left to right, char by char
|
|
183
|
+
console.log('');
|
|
184
|
+
const lineWidth = Math.min(getTermWidth() - 4, 60);
|
|
185
|
+
process.stdout.write(' ');
|
|
186
|
+
for (let i = 0; i < lineWidth; i++) {
|
|
187
|
+
process.stdout.write(exports.theme.dim('━'));
|
|
188
|
+
if (i % 3 === 0)
|
|
189
|
+
await sleep(3);
|
|
190
|
+
}
|
|
191
|
+
console.log('');
|
|
192
|
+
// Step 4: Info line — fades in all at once
|
|
193
|
+
console.log('');
|
|
194
|
+
const userStr = userEmail ? `Signed in as ${userEmail}` : 'Not signed in';
|
|
195
|
+
console.log(` ${exports.theme.brand('◆')} ${exports.theme.dim(`${userStr} · Model: ${model} · v${version}`)}`);
|
|
196
|
+
console.log('');
|
|
156
197
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
console.log('');
|
|
160
|
-
const lineWidth = Math.min(getTermWidth() - 4, 60);
|
|
161
|
-
process.stdout.write(' ');
|
|
162
|
-
for (let i = 0; i < lineWidth; i++) {
|
|
163
|
-
process.stdout.write(exports.theme.brand('━'));
|
|
164
|
-
if (i % 3 === 0)
|
|
165
|
-
await sleep(3);
|
|
198
|
+
finally {
|
|
199
|
+
showCursor();
|
|
166
200
|
}
|
|
167
|
-
console.log('');
|
|
168
|
-
// Step 4: User + model + version
|
|
169
|
-
console.log('');
|
|
170
|
-
const userStr = userEmail ? `Signed in as ${userEmail}` : 'Not signed in';
|
|
171
|
-
console.log(exports.theme.dim(` ◆ ${userStr} · Model: ${model} · v${version}`));
|
|
172
|
-
console.log('');
|
|
173
|
-
showCursor();
|
|
174
201
|
}
|
|
175
202
|
/**
|
|
176
|
-
* Static banner for non-TTY / piped /
|
|
203
|
+
* Static banner for non-TTY / piped / quick modes.
|
|
177
204
|
*/
|
|
178
205
|
function showStaticBanner(version) {
|
|
179
206
|
console.log('');
|
|
@@ -182,81 +209,190 @@ function showStaticBanner(version) {
|
|
|
182
209
|
}
|
|
183
210
|
console.log(exports.theme.dim(`\n by Sylix · v${version}\n`));
|
|
184
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Session info lines — appear with delay between each.
|
|
214
|
+
*/
|
|
215
|
+
async function showSessionInfo(cwd, sessionId) {
|
|
216
|
+
if (IS_PIPE) {
|
|
217
|
+
console.log(` cwd: ${cwd}`);
|
|
218
|
+
console.log(` session: ${sessionId.substring(0, 8)}...`);
|
|
219
|
+
console.log(` tips: /help for commands, Ctrl+C to exit\n`);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const lines = [
|
|
223
|
+
exports.theme.dim(` cwd: ${cwd}`),
|
|
224
|
+
exports.theme.dim(` session: ${sessionId.substring(0, 8)}...`),
|
|
225
|
+
exports.theme.dim(` tips: /help for commands, Ctrl+C to exit`),
|
|
226
|
+
];
|
|
227
|
+
for (const line of lines) {
|
|
228
|
+
console.log(line);
|
|
229
|
+
await sleep(50);
|
|
230
|
+
}
|
|
231
|
+
console.log('');
|
|
232
|
+
}
|
|
185
233
|
// ============================================================================
|
|
186
|
-
//
|
|
234
|
+
// PART 2: THINKING ANIMATION — Rotating messages with pulsing dots
|
|
187
235
|
// ============================================================================
|
|
188
|
-
const
|
|
189
|
-
'
|
|
190
|
-
'
|
|
191
|
-
'
|
|
192
|
-
'
|
|
193
|
-
'
|
|
194
|
-
'
|
|
236
|
+
const THINKING_MESSAGES = [
|
|
237
|
+
'working',
|
|
238
|
+
'thinking',
|
|
239
|
+
'reading codebase',
|
|
240
|
+
'analyzing',
|
|
241
|
+
'on it',
|
|
242
|
+
'crafting something',
|
|
243
|
+
'processing',
|
|
195
244
|
];
|
|
196
245
|
class ThinkingAnimation {
|
|
197
|
-
constructor() {
|
|
246
|
+
constructor(customMessage) {
|
|
198
247
|
this.interval = null;
|
|
199
248
|
this.frame = 0;
|
|
200
|
-
this.message =
|
|
249
|
+
this.message = customMessage || THINKING_MESSAGES[Math.floor(Math.random() * THINKING_MESSAGES.length)];
|
|
201
250
|
}
|
|
202
251
|
start() {
|
|
203
252
|
if (IS_PIPE)
|
|
204
253
|
return;
|
|
205
254
|
hideCursor();
|
|
206
|
-
const dots = ['◉ ○ ○', '○ ◉ ○', '○ ○ ◉'];
|
|
207
255
|
this.interval = setInterval(() => {
|
|
208
|
-
|
|
209
|
-
|
|
256
|
+
// Pulsing dots: working· working·· working··· working
|
|
257
|
+
const dotCount = (this.frame % 4);
|
|
258
|
+
const dots = '.'.repeat(dotCount);
|
|
259
|
+
const pad = '.'.repeat(3 - dotCount);
|
|
260
|
+
process.stdout.write(`\r ${exports.theme.brand('◆')} ${exports.theme.dim(this.message + dots)}${exports.theme.dim(pad)}` + ' '.repeat(10));
|
|
210
261
|
this.frame++;
|
|
211
|
-
},
|
|
262
|
+
}, 400);
|
|
212
263
|
}
|
|
213
264
|
stop() {
|
|
214
265
|
if (this.interval) {
|
|
215
266
|
clearInterval(this.interval);
|
|
216
267
|
this.interval = null;
|
|
217
268
|
}
|
|
269
|
+
// Clear the line
|
|
218
270
|
process.stdout.write('\r' + ' '.repeat(getTermWidth()) + '\r');
|
|
219
271
|
showCursor();
|
|
220
272
|
}
|
|
221
273
|
}
|
|
222
274
|
exports.ThinkingAnimation = ThinkingAnimation;
|
|
223
275
|
// ============================================================================
|
|
224
|
-
//
|
|
276
|
+
// PART 3: MARKDOWN-AWARE RESPONSE RENDERING
|
|
225
277
|
// ============================================================================
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
278
|
+
/**
|
|
279
|
+
* Renders a block of AI response text with markdown awareness.
|
|
280
|
+
* Handles: code blocks, **bold**, `inline code`, ## headers, - lists
|
|
281
|
+
*/
|
|
282
|
+
function renderMarkdown(text) {
|
|
283
|
+
const lines = text.split('\n');
|
|
284
|
+
const rendered = [];
|
|
285
|
+
let inCodeBlock = false;
|
|
286
|
+
let codeLanguage = '';
|
|
287
|
+
let codeBuffer = [];
|
|
288
|
+
for (const line of lines) {
|
|
289
|
+
// Code block start
|
|
290
|
+
if (line.trim().startsWith('```') && !inCodeBlock) {
|
|
291
|
+
inCodeBlock = true;
|
|
292
|
+
codeLanguage = line.trim().replace('```', '').trim() || 'code';
|
|
293
|
+
codeBuffer = [];
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
// Code block end
|
|
297
|
+
if (line.trim() === '```' && inCodeBlock) {
|
|
298
|
+
inCodeBlock = false;
|
|
299
|
+
// Render the code box
|
|
300
|
+
const maxLen = Math.max(...codeBuffer.map(l => l.length), codeLanguage.length + 4);
|
|
301
|
+
const boxWidth = Math.min(maxLen + 4, getTermWidth() - 10);
|
|
302
|
+
rendered.push('');
|
|
303
|
+
rendered.push(` ${exports.theme.dim(`┌─ ${codeLanguage} ${'─'.repeat(Math.max(0, boxWidth - codeLanguage.length - 5))}┐`)}`);
|
|
304
|
+
for (const codeLine of codeBuffer) {
|
|
305
|
+
const padded = codeLine + ' '.repeat(Math.max(0, boxWidth - codeLine.length - 4));
|
|
306
|
+
rendered.push(` ${exports.theme.dim('│')} ${exports.theme.code(padded)}${exports.theme.dim('│')}`);
|
|
307
|
+
}
|
|
308
|
+
rendered.push(` ${exports.theme.dim(`└${'─'.repeat(boxWidth - 2)}┘`)}`);
|
|
309
|
+
rendered.push('');
|
|
310
|
+
codeBuffer = [];
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
// Inside code block — collect
|
|
314
|
+
if (inCodeBlock) {
|
|
315
|
+
codeBuffer.push(line);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
// ## Headers
|
|
319
|
+
if (line.match(/^#{1,3}\s+/)) {
|
|
320
|
+
const headerText = line.replace(/^#{1,3}\s+/, '');
|
|
321
|
+
rendered.push('');
|
|
322
|
+
rendered.push(exports.theme.brand(headerText));
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
// - List items
|
|
326
|
+
if (line.match(/^\s*[-*]\s+/)) {
|
|
327
|
+
const listText = line.replace(/^\s*[-*]\s+/, '');
|
|
328
|
+
rendered.push(` ${exports.theme.dim('·')} ${renderInline(listText)}`);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
// Regular text with inline formatting
|
|
332
|
+
rendered.push(renderInline(line));
|
|
333
|
+
}
|
|
334
|
+
return rendered.join('\n');
|
|
232
335
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
336
|
+
/**
|
|
337
|
+
* Renders inline markdown: **bold**, `code`
|
|
338
|
+
*/
|
|
339
|
+
function renderInline(text) {
|
|
340
|
+
// **bold** → bright white
|
|
341
|
+
let result = text.replace(/\*\*(.+?)\*\*/g, (_, content) => exports.theme.white(content));
|
|
342
|
+
// `inline code` → yellow
|
|
343
|
+
result = result.replace(/`(.+?)`/g, (_, content) => exports.theme.code(content));
|
|
344
|
+
return result;
|
|
238
345
|
}
|
|
346
|
+
// ── Response box rendering ──
|
|
239
347
|
function printResponseHeader() {
|
|
240
|
-
console.log(
|
|
241
|
-
|
|
348
|
+
console.log(exports.theme.dim(' ╷'));
|
|
349
|
+
}
|
|
350
|
+
function printResponseLine(text) {
|
|
351
|
+
// Wrap text to terminal width
|
|
352
|
+
const maxWidth = getTermWidth() - 6;
|
|
353
|
+
const words = text.split(' ');
|
|
354
|
+
let currentLine = '';
|
|
355
|
+
for (const word of words) {
|
|
356
|
+
if (currentLine.length + word.length + 1 > maxWidth && currentLine.length > 0) {
|
|
357
|
+
console.log(`${exports.theme.dim(' │')} ${exports.theme.ai(currentLine)}`);
|
|
358
|
+
currentLine = word;
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
currentLine = currentLine ? `${currentLine} ${word}` : word;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (currentLine) {
|
|
365
|
+
console.log(`${exports.theme.dim(' │')} ${exports.theme.ai(currentLine)}`);
|
|
366
|
+
}
|
|
242
367
|
}
|
|
243
368
|
function printResponseFooter(elapsed, tokenEstimate) {
|
|
244
|
-
console.log(exports.theme.brand(' │'));
|
|
245
|
-
console.log(boxBottom());
|
|
246
|
-
console.log('');
|
|
247
369
|
const elapsedStr = elapsed < 1000 ? `${elapsed}ms` : `${(elapsed / 1000).toFixed(1)}s`;
|
|
248
|
-
const tokenStr = tokenEstimate ? `
|
|
249
|
-
console.log(exports.theme.dim(`
|
|
370
|
+
const tokenStr = tokenEstimate ? ` · ${tokenEstimate} tokens` : '';
|
|
371
|
+
console.log(exports.theme.dim(` ╵ ✦ ${elapsedStr}${tokenStr}`));
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Streams AI response text into the response box with markdown rendering.
|
|
375
|
+
* Call printResponseHeader() first, then stream chunks, then printResponseFooter().
|
|
376
|
+
*/
|
|
377
|
+
function streamResponseChunk(chunk) {
|
|
378
|
+
// Split by newlines and render each part within the box
|
|
379
|
+
const parts = chunk.split('\n');
|
|
380
|
+
for (let i = 0; i < parts.length; i++) {
|
|
381
|
+
if (i > 0) {
|
|
382
|
+
// Newline within the chunk — start a new box line
|
|
383
|
+
process.stdout.write(`\n${exports.theme.dim(' │')} `);
|
|
384
|
+
}
|
|
385
|
+
process.stdout.write(exports.theme.ai(parts[i]));
|
|
386
|
+
}
|
|
250
387
|
}
|
|
251
388
|
// ============================================================================
|
|
252
389
|
// PROMPT RENDERING
|
|
253
390
|
// ============================================================================
|
|
254
391
|
function printUserPrompt(input) {
|
|
255
|
-
console.log(
|
|
256
|
-
console.log('');
|
|
392
|
+
console.log(`\n ${exports.theme.dim('you')} ${exports.theme.brand('›')} ${input}\n`);
|
|
257
393
|
}
|
|
258
394
|
// ============================================================================
|
|
259
|
-
// ERROR / SUCCESS / WARNING
|
|
395
|
+
// ERROR / SUCCESS / WARNING
|
|
260
396
|
// ============================================================================
|
|
261
397
|
function printError(title, details, hint) {
|
|
262
398
|
console.log('');
|
|
@@ -305,72 +441,49 @@ function printToolError(toolName, error) {
|
|
|
305
441
|
process.stdout.write(`\r ${exports.theme.error('✗')} ${exports.theme.code(toolName)} ${exports.theme.dim(error.substring(0, 60))}` + ' '.repeat(10) + '\n');
|
|
306
442
|
}
|
|
307
443
|
// ============================================================================
|
|
308
|
-
//
|
|
444
|
+
// SCORE CARD (coworker review)
|
|
309
445
|
// ============================================================================
|
|
310
|
-
/**
|
|
311
|
-
* ┌─────────────────────────────────┐
|
|
312
|
-
* │ Code Review · src/index.ts │
|
|
313
|
-
* ├─────────────────────────────────┤
|
|
314
|
-
* │ Quality ████████░░ 8/10 │
|
|
315
|
-
* │ Security ██████████ 10/10 │
|
|
316
|
-
* └─────────────────────────────────┘
|
|
317
|
-
*/
|
|
318
446
|
function printScoreCard(fileName, scores) {
|
|
319
447
|
const w = Math.min(getTermWidth() - 4, 50);
|
|
320
|
-
const iw = w - 2;
|
|
448
|
+
const iw = w - 2;
|
|
321
449
|
const title = `Code Review · ${fileName}`;
|
|
322
450
|
console.log('');
|
|
323
|
-
console.log(exports.theme.
|
|
324
|
-
console.log(exports.theme.brand(` │ `) + exports.theme.white(title) + ' '.repeat(Math.max(0, iw - title.length - 2)) + exports.theme.brand(`│`));
|
|
325
|
-
console.log(exports.theme.brand(` ├${'─'.repeat(iw)}┤`));
|
|
451
|
+
console.log(exports.theme.dim(` ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄`));
|
|
326
452
|
for (const { label, score, max = 10 } of scores) {
|
|
327
453
|
const barWidth = 10;
|
|
328
454
|
const filled = Math.round((score / max) * barWidth);
|
|
329
455
|
const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
const content = `${label}${' '.repeat(Math.max(1, pad))}${bar}${scoreStr}`;
|
|
334
|
-
const rowPad = iw - content.length - 2;
|
|
335
|
-
console.log(exports.theme.brand(` │ `) + exports.theme.dim(label) + ' '.repeat(Math.max(1, pad)) + barColor(bar) + exports.theme.dim(scoreStr) + ' '.repeat(Math.max(0, rowPad)) + exports.theme.brand(`│`));
|
|
456
|
+
const barColor = score >= 8 ? exports.theme.brand : score >= 5 ? exports.theme.warning : exports.theme.error;
|
|
457
|
+
const dots = '·'.repeat(Math.max(1, 16 - label.length));
|
|
458
|
+
console.log(` ${exports.theme.dim(label)} ${exports.theme.dim(dots)} ${exports.theme.white(String(score))} ${barColor(bar)}`);
|
|
336
459
|
}
|
|
337
|
-
console.log(exports.theme.
|
|
460
|
+
console.log(exports.theme.dim(` ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄`));
|
|
338
461
|
console.log('');
|
|
339
462
|
}
|
|
340
463
|
// ============================================================================
|
|
341
|
-
//
|
|
464
|
+
// COMMIT BOX (coworker commit)
|
|
342
465
|
// ============================================================================
|
|
343
|
-
/**
|
|
344
|
-
* ╭─ Suggested Commit ──────────────────╮
|
|
345
|
-
* │ ✨ feat: add user auth middleware │
|
|
346
|
-
* │ │
|
|
347
|
-
* │ Use this commit message? (y/n) › │
|
|
348
|
-
* ╰─────────────────────────────────────╯
|
|
349
|
-
*/
|
|
350
466
|
function printCommitBox(commitMessage) {
|
|
351
467
|
const w = Math.min(getTermWidth() - 2, 55);
|
|
352
|
-
const label = '
|
|
468
|
+
const label = 'suggested commit';
|
|
353
469
|
const headerPad = w - 6 - label.length;
|
|
354
|
-
const msgLine = `✨ ${commitMessage}`;
|
|
355
|
-
const promptLine = 'Use this commit message? (y/n) ›';
|
|
356
470
|
console.log('');
|
|
357
471
|
console.log(exports.theme.brand(` ╭─ ${label} ${'─'.repeat(Math.max(0, headerPad))}╮`));
|
|
358
|
-
console.log(exports.theme.brand(` │ `) + exports.theme.success(msgLine) + ' '.repeat(Math.max(0, w - 5 - msgLine.length)) + exports.theme.brand(`│`));
|
|
359
472
|
console.log(exports.theme.brand(` │`) + ' '.repeat(w - 2) + exports.theme.brand(`│`));
|
|
360
|
-
|
|
473
|
+
// Wrap message to fit box
|
|
474
|
+
const maxMsgWidth = w - 6;
|
|
475
|
+
const msgLines = commitMessage.match(new RegExp(`.{1,${maxMsgWidth}}`, 'g')) || [commitMessage];
|
|
476
|
+
for (const msgLine of msgLines) {
|
|
477
|
+
const pad = w - 5 - msgLine.length;
|
|
478
|
+
console.log(exports.theme.brand(` │ `) + exports.theme.success(msgLine) + ' '.repeat(Math.max(0, pad)) + exports.theme.brand(`│`));
|
|
479
|
+
}
|
|
480
|
+
console.log(exports.theme.brand(` │`) + ' '.repeat(w - 2) + exports.theme.brand(`│`));
|
|
361
481
|
console.log(exports.theme.brand(` ╰${'─'.repeat(w - 2)}╯`));
|
|
362
482
|
console.log('');
|
|
363
483
|
}
|
|
364
484
|
// ============================================================================
|
|
365
|
-
//
|
|
485
|
+
// DIFF PREVIEW (coworker edit)
|
|
366
486
|
// ============================================================================
|
|
367
|
-
/**
|
|
368
|
-
* ── Changes to src/api/sylix.ts ──────────
|
|
369
|
-
* - const url = "old-endpoint" ← red
|
|
370
|
-
* + const url = "new-endpoint" ← green
|
|
371
|
-
* ─────────────────────────────────────────
|
|
372
|
-
* Apply these changes? (y/n) ›
|
|
373
|
-
*/
|
|
374
487
|
function printDiff(fileName, changes) {
|
|
375
488
|
const w = Math.min(getTermWidth() - 4, 60);
|
|
376
489
|
const header = `Changes to ${fileName}`;
|
|
@@ -389,20 +502,21 @@ function printDiff(fileName, changes) {
|
|
|
389
502
|
}
|
|
390
503
|
}
|
|
391
504
|
console.log(exports.theme.dim(` ${'─'.repeat(w)}`));
|
|
392
|
-
console.log(exports.theme.dim(` Apply these changes? (y/n) ›`));
|
|
393
505
|
console.log('');
|
|
394
506
|
}
|
|
395
507
|
// ============================================================================
|
|
396
|
-
// INTERACTIVE HELP
|
|
508
|
+
// INTERACTIVE HELP — Updated with new commands
|
|
397
509
|
// ============================================================================
|
|
398
510
|
function printHelp() {
|
|
399
511
|
console.log(`
|
|
400
512
|
${exports.theme.brand(' Slash Commands:')}
|
|
401
|
-
${exports.theme.code('/help')}
|
|
402
|
-
${exports.theme.code('/clear')}
|
|
403
|
-
${exports.theme.code('/
|
|
404
|
-
${exports.theme.code('/
|
|
405
|
-
${exports.theme.code('/
|
|
513
|
+
${exports.theme.code('/help')} ${exports.theme.dim('Show this help')}
|
|
514
|
+
${exports.theme.code('/clear')} ${exports.theme.dim('Clear the screen')}
|
|
515
|
+
${exports.theme.code('/review')} ${exports.theme.dim('Code review current branch')}
|
|
516
|
+
${exports.theme.code('/commit')} ${exports.theme.dim('Generate commit message from diff')}
|
|
517
|
+
${exports.theme.code('/status')} ${exports.theme.dim('Show auth & session status')}
|
|
518
|
+
${exports.theme.code('/compact')} ${exports.theme.dim('Show context window usage')}
|
|
519
|
+
${exports.theme.code('/exit')} ${exports.theme.dim('Exit CoWorker')}
|
|
406
520
|
`);
|
|
407
521
|
}
|
|
408
522
|
function printCompact(msgCount, tokenEst, sessionId) {
|
|
@@ -412,11 +526,41 @@ function printCompact(msgCount, tokenEst, sessionId) {
|
|
|
412
526
|
const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
|
|
413
527
|
const color = usage > 80 ? exports.theme.error : usage > 50 ? exports.theme.warning : exports.theme.success;
|
|
414
528
|
console.log('');
|
|
415
|
-
console.log(exports.theme.dim(` Messages: ${msgCount} │ Context: `) +
|
|
529
|
+
console.log(exports.theme.dim(` Messages: ${msgCount} │ Context: `) +
|
|
530
|
+
color(`[${bar}] ${usage}%`) +
|
|
531
|
+
exports.theme.dim(` │ Session: ${sessionId.substring(0, 8)}`));
|
|
416
532
|
console.log('');
|
|
417
533
|
}
|
|
534
|
+
const BUILTIN_COMMANDS = [
|
|
535
|
+
{ name: '/help', desc: 'show all commands' },
|
|
536
|
+
{ name: '/clear', desc: 'clear the screen' },
|
|
537
|
+
{ name: '/review', desc: 'review current branch' },
|
|
538
|
+
{ name: '/commit', desc: 'generate commit message' },
|
|
539
|
+
{ name: '/status', desc: 'show auth & session' },
|
|
540
|
+
{ name: '/compact', desc: 'context window usage' },
|
|
541
|
+
{ name: '/exit', desc: 'end session' },
|
|
542
|
+
];
|
|
543
|
+
/**
|
|
544
|
+
* Returns matching slash commands for a partial input.
|
|
545
|
+
*/
|
|
546
|
+
function getSlashCompletions(partial) {
|
|
547
|
+
if (!partial.startsWith('/'))
|
|
548
|
+
return [];
|
|
549
|
+
return BUILTIN_COMMANDS.filter(cmd => cmd.name.startsWith(partial));
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Renders the slash completion menu below the input.
|
|
553
|
+
*/
|
|
554
|
+
function renderSlashMenu(matches) {
|
|
555
|
+
const lines = [];
|
|
556
|
+
for (let i = 0; i < matches.length; i++) {
|
|
557
|
+
const prefix = i === matches.length - 1 ? '└' : '├';
|
|
558
|
+
lines.push(` ${exports.theme.dim(prefix)} ${exports.theme.code(matches[i].name.padEnd(12))} ${exports.theme.dim(matches[i].desc)}`);
|
|
559
|
+
}
|
|
560
|
+
return lines.join('\n');
|
|
561
|
+
}
|
|
418
562
|
// ============================================================================
|
|
419
|
-
// GENERIC
|
|
563
|
+
// GENERIC BOX
|
|
420
564
|
// ============================================================================
|
|
421
565
|
function printBox(label, lines) {
|
|
422
566
|
const w = Math.min(getTermWidth() - 2, 70);
|
|
@@ -429,7 +573,7 @@ function printBox(label, lines) {
|
|
|
429
573
|
console.log(exports.theme.brand(` ╰${'─'.repeat(w - 3)}╯`));
|
|
430
574
|
}
|
|
431
575
|
// ============================================================================
|
|
432
|
-
//
|
|
576
|
+
// TIMER
|
|
433
577
|
// ============================================================================
|
|
434
578
|
class Timer {
|
|
435
579
|
constructor() {
|