@sylix/coworker 1.3.0 → 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.
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +184 -292
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/slash/advanced.d.ts +3 -0
- package/dist/commands/slash/advanced.d.ts.map +1 -0
- package/dist/commands/slash/advanced.js +225 -0
- package/dist/commands/slash/advanced.js.map +1 -0
- package/dist/commands/slash/config.d.ts +3 -0
- package/dist/commands/slash/config.d.ts.map +1 -0
- package/dist/commands/slash/config.js +161 -0
- package/dist/commands/slash/config.js.map +1 -0
- package/dist/commands/slash/context.d.ts +3 -0
- package/dist/commands/slash/context.d.ts.map +1 -0
- package/dist/commands/slash/context.js +127 -0
- package/dist/commands/slash/context.js.map +1 -0
- package/dist/commands/slash/core.d.ts +3 -0
- package/dist/commands/slash/core.d.ts.map +1 -0
- package/dist/commands/slash/core.js +112 -0
- package/dist/commands/slash/core.js.map +1 -0
- package/dist/commands/slash/developer.d.ts +3 -0
- package/dist/commands/slash/developer.d.ts.map +1 -0
- package/dist/commands/slash/developer.js +174 -0
- package/dist/commands/slash/developer.js.map +1 -0
- package/dist/commands/slash/files.d.ts +3 -0
- package/dist/commands/slash/files.d.ts.map +1 -0
- package/dist/commands/slash/files.js +216 -0
- package/dist/commands/slash/files.js.map +1 -0
- package/dist/commands/slash/registry.d.ts +36 -0
- package/dist/commands/slash/registry.d.ts.map +1 -0
- package/dist/commands/slash/registry.js +69 -0
- package/dist/commands/slash/registry.js.map +1 -0
- package/dist/commands/slash/session.d.ts +3 -0
- package/dist/commands/slash/session.d.ts.map +1 -0
- package/dist/commands/slash/session.js +144 -0
- package/dist/commands/slash/session.js.map +1 -0
- package/dist/core/CoWorkerAgent.d.ts +8 -5
- package/dist/core/CoWorkerAgent.d.ts.map +1 -1
- package/dist/core/CoWorkerAgent.js +171 -52
- package/dist/core/CoWorkerAgent.js.map +1 -1
- package/dist/session/SessionManager.js +10 -5
- package/dist/session/SessionManager.js.map +1 -1
- package/dist/skills/HookAndSkillManager.d.ts +7 -2
- package/dist/skills/HookAndSkillManager.d.ts.map +1 -1
- package/dist/skills/HookAndSkillManager.js +35 -8
- package/dist/skills/HookAndSkillManager.js.map +1 -1
- package/dist/skills/defaults/brainstorming.md +43 -0
- package/dist/skills/defaults/commit.md +37 -0
- package/dist/skills/defaults/executing-plans.md +41 -0
- package/dist/skills/defaults/finishing-a-development-branch.md +35 -0
- package/dist/skills/defaults/receiving-code-review.md +40 -0
- package/dist/skills/defaults/requesting-code-review.md +43 -0
- package/dist/skills/defaults/review.md +50 -0
- package/dist/skills/defaults/subagent-driven-development.md +55 -0
- package/dist/skills/defaults/systematic-debugging.md +93 -0
- package/dist/skills/defaults/test-driven-development.md +97 -0
- package/dist/skills/defaults/using-git-worktrees.md +36 -0
- package/dist/skills/defaults/verification-before-completion.md +47 -0
- package/dist/skills/defaults/writing-plans.md +84 -0
- package/dist/skills/defaults/writing-skills.md +46 -0
- package/dist/tools/NativeTools.d.ts +0 -4
- package/dist/tools/NativeTools.d.ts.map +1 -1
- package/dist/tools/NativeTools.js +50 -10
- package/dist/tools/NativeTools.js.map +1 -1
- package/dist/utils/conversations.d.ts +14 -0
- package/dist/utils/conversations.d.ts.map +1 -0
- package/dist/utils/conversations.js +100 -0
- package/dist/utils/conversations.js.map +1 -0
- package/dist/utils/inputbar.d.ts +87 -0
- package/dist/utils/inputbar.d.ts.map +1 -0
- package/dist/utils/inputbar.js +263 -0
- package/dist/utils/inputbar.js.map +1 -0
- package/dist/utils/output.d.ts +48 -42
- package/dist/utils/output.d.ts.map +1 -1
- package/dist/utils/output.js +245 -186
- package/dist/utils/output.js.map +1 -1
- package/dist/utils/palette.d.ts +25 -0
- package/dist/utils/palette.d.ts.map +1 -0
- package/dist/utils/palette.js +92 -0
- package/dist/utils/palette.js.map +1 -0
- package/dist/utils/welcome.d.ts +2 -0
- package/dist/utils/welcome.d.ts.map +1 -0
- package/dist/utils/welcome.js +130 -0
- package/dist/utils/welcome.js.map +1 -0
- package/package.json +2 -2
package/dist/utils/output.js
CHANGED
|
@@ -33,14 +33,12 @@ 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 = exports.CW_VERSION = void 0;
|
|
36
|
+
exports.Timer = exports.StreamRenderer = 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
40
|
exports.showSessionInfo = showSessionInfo;
|
|
41
|
-
exports.renderMarkdown = renderMarkdown;
|
|
42
41
|
exports.printResponseHeader = printResponseHeader;
|
|
43
|
-
exports.printResponseLine = printResponseLine;
|
|
44
42
|
exports.printResponseFooter = printResponseFooter;
|
|
45
43
|
exports.streamResponseChunk = streamResponseChunk;
|
|
46
44
|
exports.printUserPrompt = printUserPrompt;
|
|
@@ -54,24 +52,21 @@ exports.printToolError = printToolError;
|
|
|
54
52
|
exports.printScoreCard = printScoreCard;
|
|
55
53
|
exports.printCommitBox = printCommitBox;
|
|
56
54
|
exports.printDiff = printDiff;
|
|
55
|
+
exports.checkWorkspaceTrust = checkWorkspaceTrust;
|
|
57
56
|
exports.printHelp = printHelp;
|
|
58
57
|
exports.printCompact = printCompact;
|
|
59
|
-
exports.getSlashCompletions = getSlashCompletions;
|
|
60
|
-
exports.renderSlashMenu = renderSlashMenu;
|
|
61
58
|
exports.printBox = printBox;
|
|
59
|
+
exports.renderMarkdown = renderMarkdown;
|
|
62
60
|
const chalk = __importStar(require("chalk"));
|
|
63
61
|
/**
|
|
64
62
|
* ═══════════════════════════════════════════════════════════════════
|
|
65
63
|
* CoWorker v1.3.0 by Sylix — Premium Output System
|
|
66
64
|
* ═══════════════════════════════════════════════════════════════════
|
|
67
65
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* - Thinking animation with rotating messages
|
|
71
|
-
* - Markdown-aware response rendering (code blocks, bold, inline code)
|
|
72
|
-
* - Score cards, commit boxes, diff previews
|
|
66
|
+
* Premium terminal rendering with true streaming support.
|
|
67
|
+
* All response text renders inside ╷│╵ borders as it arrives.
|
|
73
68
|
*/
|
|
74
|
-
exports.CW_VERSION = '1.
|
|
69
|
+
exports.CW_VERSION = '1.4.0';
|
|
75
70
|
// ============================================================================
|
|
76
71
|
// THEME SYSTEM
|
|
77
72
|
// ============================================================================
|
|
@@ -88,16 +83,17 @@ function hex(color) {
|
|
|
88
83
|
}
|
|
89
84
|
}
|
|
90
85
|
exports.theme = {
|
|
91
|
-
brand: hex('#00D4FF'),
|
|
92
|
-
accent: hex('#7C3AED'),
|
|
93
|
-
success: hex('#10B981'),
|
|
94
|
-
error: hex('#EF4444'),
|
|
95
|
-
warning: hex('#F59E0B'),
|
|
96
|
-
ai: hex('#A78BFA'),
|
|
97
|
-
dim: hex('#6B7280'),
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
86
|
+
brand: hex('#00D4FF'),
|
|
87
|
+
accent: hex('#7C3AED'),
|
|
88
|
+
success: hex('#10B981'),
|
|
89
|
+
error: hex('#EF4444'),
|
|
90
|
+
warning: hex('#F59E0B'),
|
|
91
|
+
ai: hex('#A78BFA'),
|
|
92
|
+
dim: hex('#6B7280'),
|
|
93
|
+
border: hex('#4B5563'),
|
|
94
|
+
white: hex('#F9FAFB'),
|
|
95
|
+
code: hex('#FCD34D'),
|
|
96
|
+
purple: hex('#A78BFA'),
|
|
101
97
|
};
|
|
102
98
|
// ============================================================================
|
|
103
99
|
// TERMINAL UTILS
|
|
@@ -105,9 +101,6 @@ exports.theme = {
|
|
|
105
101
|
function getTermWidth() {
|
|
106
102
|
return process.stdout.columns || 80;
|
|
107
103
|
}
|
|
108
|
-
function isNarrow() {
|
|
109
|
-
return getTermWidth() < 60;
|
|
110
|
-
}
|
|
111
104
|
function sleep(ms) {
|
|
112
105
|
return new Promise(r => setTimeout(r, ms));
|
|
113
106
|
}
|
|
@@ -120,7 +113,7 @@ function showCursor() {
|
|
|
120
113
|
process.stdout.write('\x1b[?25h');
|
|
121
114
|
}
|
|
122
115
|
// ============================================================================
|
|
123
|
-
//
|
|
116
|
+
// ANIMATED BANNER
|
|
124
117
|
// ============================================================================
|
|
125
118
|
const LOGO_LINES = [
|
|
126
119
|
' ██████╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗███████╗██████╗ ',
|
|
@@ -130,29 +123,18 @@ const LOGO_LINES = [
|
|
|
130
123
|
'╚██████╗╚██████╔╝╚███╔███╔╝╚██████╔╝██║ ██║██║ ██╗███████╗██║ ██║',
|
|
131
124
|
' ╚═════╝ ╚═════╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝',
|
|
132
125
|
];
|
|
133
|
-
/**
|
|
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
|
|
142
|
-
*/
|
|
143
126
|
async function showBanner(version, userEmail, model = 'helix-1.2') {
|
|
144
127
|
if (IS_PIPE || process.env.CW_NO_BANNER === '1')
|
|
145
128
|
return;
|
|
146
129
|
hideCursor();
|
|
147
130
|
console.log('');
|
|
148
131
|
try {
|
|
149
|
-
// Step 1: Logo — line by line, each types across
|
|
132
|
+
// Step 1: Logo — line by line, each types across
|
|
150
133
|
for (const line of LOGO_LINES) {
|
|
151
134
|
const chars = [...line];
|
|
152
135
|
let buffer = '';
|
|
153
136
|
for (let i = 0; i < chars.length; i++) {
|
|
154
137
|
buffer += chars[i];
|
|
155
|
-
// Batch 6 chars at a time for speed
|
|
156
138
|
if (i % 6 === 5 || i === chars.length - 1) {
|
|
157
139
|
process.stdout.write(exports.theme.brand(buffer));
|
|
158
140
|
buffer = '';
|
|
@@ -160,9 +142,9 @@ async function showBanner(version, userEmail, model = 'helix-1.2') {
|
|
|
160
142
|
}
|
|
161
143
|
}
|
|
162
144
|
process.stdout.write('\n');
|
|
163
|
-
await sleep(60);
|
|
145
|
+
await sleep(60);
|
|
164
146
|
}
|
|
165
|
-
// Step 2: Tagline — word by word
|
|
147
|
+
// Step 2: Tagline — word by word with semantic colors
|
|
166
148
|
console.log('');
|
|
167
149
|
const taglineWords = [
|
|
168
150
|
{ text: 'by', color: exports.theme.dim },
|
|
@@ -179,7 +161,7 @@ async function showBanner(version, userEmail, model = 'helix-1.2') {
|
|
|
179
161
|
await sleep(100);
|
|
180
162
|
}
|
|
181
163
|
console.log('');
|
|
182
|
-
// Step 3: Divider
|
|
164
|
+
// Step 3: Divider draws left to right
|
|
183
165
|
console.log('');
|
|
184
166
|
const lineWidth = Math.min(getTermWidth() - 4, 60);
|
|
185
167
|
process.stdout.write(' ');
|
|
@@ -189,7 +171,7 @@ async function showBanner(version, userEmail, model = 'helix-1.2') {
|
|
|
189
171
|
await sleep(3);
|
|
190
172
|
}
|
|
191
173
|
console.log('');
|
|
192
|
-
// Step 4: Info line
|
|
174
|
+
// Step 4: Info line
|
|
193
175
|
console.log('');
|
|
194
176
|
const userStr = userEmail ? `Signed in as ${userEmail}` : 'Not signed in';
|
|
195
177
|
console.log(` ${exports.theme.brand('◆')} ${exports.theme.dim(`${userStr} · Model: ${model} · v${version}`)}`);
|
|
@@ -199,9 +181,6 @@ async function showBanner(version, userEmail, model = 'helix-1.2') {
|
|
|
199
181
|
showCursor();
|
|
200
182
|
}
|
|
201
183
|
}
|
|
202
|
-
/**
|
|
203
|
-
* Static banner for non-TTY / piped / quick modes.
|
|
204
|
-
*/
|
|
205
184
|
function showStaticBanner(version) {
|
|
206
185
|
console.log('');
|
|
207
186
|
for (const line of LOGO_LINES) {
|
|
@@ -209,9 +188,6 @@ function showStaticBanner(version) {
|
|
|
209
188
|
}
|
|
210
189
|
console.log(exports.theme.dim(`\n by Sylix · v${version}\n`));
|
|
211
190
|
}
|
|
212
|
-
/**
|
|
213
|
-
* Session info lines — appear with delay between each.
|
|
214
|
-
*/
|
|
215
191
|
async function showSessionInfo(cwd, sessionId) {
|
|
216
192
|
if (IS_PIPE) {
|
|
217
193
|
console.log(` cwd: ${cwd}`);
|
|
@@ -231,7 +207,7 @@ async function showSessionInfo(cwd, sessionId) {
|
|
|
231
207
|
console.log('');
|
|
232
208
|
}
|
|
233
209
|
// ============================================================================
|
|
234
|
-
//
|
|
210
|
+
// THINKING ANIMATION — Rotating messages with pulsing dots
|
|
235
211
|
// ============================================================================
|
|
236
212
|
const THINKING_MESSAGES = [
|
|
237
213
|
'working',
|
|
@@ -239,8 +215,6 @@ const THINKING_MESSAGES = [
|
|
|
239
215
|
'reading codebase',
|
|
240
216
|
'analyzing',
|
|
241
217
|
'on it',
|
|
242
|
-
'crafting something',
|
|
243
|
-
'processing',
|
|
244
218
|
];
|
|
245
219
|
class ThinkingAnimation {
|
|
246
220
|
constructor(customMessage) {
|
|
@@ -253,11 +227,10 @@ class ThinkingAnimation {
|
|
|
253
227
|
return;
|
|
254
228
|
hideCursor();
|
|
255
229
|
this.interval = setInterval(() => {
|
|
256
|
-
// Pulsing dots: working· working·· working··· working
|
|
257
230
|
const dotCount = (this.frame % 4);
|
|
258
231
|
const dots = '.'.repeat(dotCount);
|
|
259
|
-
const pad = '
|
|
260
|
-
process.stdout.write(`\r ${exports.theme.brand('◆')} ${exports.theme.dim(this.message + dots)}${
|
|
232
|
+
const pad = ' '.repeat(3 - dotCount);
|
|
233
|
+
process.stdout.write(`\r ${exports.theme.brand('◆')} ${exports.theme.dim(this.message + dots)}${pad}` + ' '.repeat(10));
|
|
261
234
|
this.frame++;
|
|
262
235
|
}, 400);
|
|
263
236
|
}
|
|
@@ -266,130 +239,141 @@ class ThinkingAnimation {
|
|
|
266
239
|
clearInterval(this.interval);
|
|
267
240
|
this.interval = null;
|
|
268
241
|
}
|
|
269
|
-
// Clear the line
|
|
270
242
|
process.stdout.write('\r' + ' '.repeat(getTermWidth()) + '\r');
|
|
271
243
|
showCursor();
|
|
272
244
|
}
|
|
273
245
|
}
|
|
274
246
|
exports.ThinkingAnimation = ThinkingAnimation;
|
|
275
247
|
// ============================================================================
|
|
276
|
-
//
|
|
248
|
+
// STREAMING RESPONSE RENDERER
|
|
277
249
|
// ============================================================================
|
|
278
250
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
251
|
+
* Manages the response box state during streaming.
|
|
252
|
+
*
|
|
253
|
+
* Correct output:
|
|
254
|
+
* ╷
|
|
255
|
+
* │ Hi! I'm CoWorker, your AI coding assistant.
|
|
256
|
+
* │
|
|
257
|
+
* │ I can help you with:
|
|
258
|
+
* │ · Writing and debugging code
|
|
259
|
+
* │
|
|
260
|
+
* ╵ ✦ 1.2s · 34 tokens
|
|
261
|
+
*
|
|
262
|
+
* Rules:
|
|
263
|
+
* - ALL text inside ╷│╵ border
|
|
264
|
+
* - Border chars (╷ │ ╵) in dim gray #4B5563
|
|
265
|
+
* - Response text in purple #A78BFA
|
|
266
|
+
* - Code blocks switch to yellow #FCD34D
|
|
267
|
+
* - On every \n → print "\n │ "
|
|
281
268
|
*/
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
269
|
+
class StreamRenderer {
|
|
270
|
+
constructor() {
|
|
271
|
+
this.started = false;
|
|
272
|
+
this.inCodeBlock = false;
|
|
273
|
+
this.charCount = 0;
|
|
274
|
+
this.startTime = Date.now();
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Call this for EACH chunk as it arrives from the SSE stream.
|
|
278
|
+
* Renders immediately — no buffering.
|
|
279
|
+
*/
|
|
280
|
+
write(chunk) {
|
|
281
|
+
if (!this.started) {
|
|
282
|
+
// Open the response border
|
|
283
|
+
console.log(exports.theme.border(' ╷'));
|
|
284
|
+
process.stdout.write(exports.theme.border(' │') + ' ');
|
|
285
|
+
this.started = true;
|
|
295
286
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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('│')}`);
|
|
287
|
+
let i = 0;
|
|
288
|
+
while (i < chunk.length) {
|
|
289
|
+
const char = chunk[i];
|
|
290
|
+
this.charCount++;
|
|
291
|
+
if (char === '\n') {
|
|
292
|
+
// Start a new bordered line
|
|
293
|
+
process.stdout.write('\n' + exports.theme.border(' │') + ' ');
|
|
294
|
+
i++;
|
|
295
|
+
continue;
|
|
307
296
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
297
|
+
// ANSI Escape Code Detection
|
|
298
|
+
if (char === '\x1b' && chunk[i + 1] === '[') {
|
|
299
|
+
let esc = '\x1b[';
|
|
300
|
+
let j = i + 2;
|
|
301
|
+
while (j < chunk.length && !/[a-zA-Z]/.test(chunk[j])) {
|
|
302
|
+
esc += chunk[j];
|
|
303
|
+
j++;
|
|
304
|
+
}
|
|
305
|
+
if (j < chunk.length) {
|
|
306
|
+
esc += chunk[j];
|
|
307
|
+
process.stdout.write(esc);
|
|
308
|
+
i = j + 1;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Choose color based on whether we're in a code block
|
|
313
|
+
const color = this.inCodeBlock ? exports.theme.code : exports.theme.ai;
|
|
314
|
+
process.stdout.write(color(char));
|
|
315
|
+
i++;
|
|
324
316
|
}
|
|
325
|
-
//
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
317
|
+
// Check for code block toggles in the chunk
|
|
318
|
+
const backtickMatches = chunk.match(/```/g);
|
|
319
|
+
if (backtickMatches) {
|
|
320
|
+
for (const _match of backtickMatches) {
|
|
321
|
+
this.inCodeBlock = !this.inCodeBlock;
|
|
322
|
+
}
|
|
330
323
|
}
|
|
331
|
-
// Regular text with inline formatting
|
|
332
|
-
rendered.push(renderInline(line));
|
|
333
324
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
}
|
|
325
|
+
/**
|
|
326
|
+
* Close the response box and print footer.
|
|
327
|
+
*/
|
|
328
|
+
finish() {
|
|
329
|
+
if (!this.started)
|
|
330
|
+
return;
|
|
331
|
+
const elapsed = Date.now() - this.startTime;
|
|
332
|
+
const elapsedStr = elapsed < 1000 ? `${elapsed}ms` : `${(elapsed / 1000).toFixed(1)}s`;
|
|
333
|
+
const tokenEst = Math.ceil(this.charCount / 4);
|
|
334
|
+
console.log(''); // end the last │ line
|
|
335
|
+
console.log(exports.theme.border(` ╵`) + exports.theme.dim(` ✦ ${elapsedStr} · ${tokenEst} tokens`));
|
|
336
|
+
console.log('');
|
|
337
|
+
}
|
|
338
|
+
/** Returns the estimated token count. */
|
|
339
|
+
getTokenEstimate() {
|
|
340
|
+
return Math.ceil(this.charCount / 4);
|
|
363
341
|
}
|
|
364
|
-
|
|
365
|
-
|
|
342
|
+
/** Returns elapsed ms. */
|
|
343
|
+
getElapsed() {
|
|
344
|
+
return Date.now() - this.startTime;
|
|
366
345
|
}
|
|
367
346
|
}
|
|
347
|
+
exports.StreamRenderer = StreamRenderer;
|
|
348
|
+
// ============================================================================
|
|
349
|
+
// LEGACY COMPAT — printResponseHeader/Footer for tool outputs
|
|
350
|
+
// ============================================================================
|
|
351
|
+
function printResponseHeader() {
|
|
352
|
+
console.log(exports.theme.border(' ╷'));
|
|
353
|
+
process.stdout.write(exports.theme.border(' │') + ' ');
|
|
354
|
+
}
|
|
368
355
|
function printResponseFooter(elapsed, tokenEstimate) {
|
|
369
356
|
const elapsedStr = elapsed < 1000 ? `${elapsed}ms` : `${(elapsed / 1000).toFixed(1)}s`;
|
|
370
357
|
const tokenStr = tokenEstimate ? ` · ${tokenEstimate} tokens` : '';
|
|
371
|
-
console.log(exports.theme.dim(`
|
|
358
|
+
console.log(exports.theme.border(` ╵`) + exports.theme.dim(` ✦ ${elapsedStr}${tokenStr}`));
|
|
372
359
|
}
|
|
373
|
-
/**
|
|
374
|
-
* Streams AI response text into the response box with markdown rendering.
|
|
375
|
-
* Call printResponseHeader() first, then stream chunks, then printResponseFooter().
|
|
376
|
-
*/
|
|
377
360
|
function streamResponseChunk(chunk) {
|
|
378
|
-
// Split by newlines and render each part within the box
|
|
379
361
|
const parts = chunk.split('\n');
|
|
380
362
|
for (let i = 0; i < parts.length; i++) {
|
|
381
363
|
if (i > 0) {
|
|
382
|
-
|
|
383
|
-
process.stdout.write(`\n${exports.theme.dim(' │')} `);
|
|
364
|
+
process.stdout.write('\n' + exports.theme.border(' │') + ' ');
|
|
384
365
|
}
|
|
385
366
|
process.stdout.write(exports.theme.ai(parts[i]));
|
|
386
367
|
}
|
|
387
368
|
}
|
|
388
369
|
// ============================================================================
|
|
389
|
-
// PROMPT RENDERING
|
|
370
|
+
// PROMPT RENDERING — Only called ONCE after user hits Enter
|
|
390
371
|
// ============================================================================
|
|
391
372
|
function printUserPrompt(input) {
|
|
392
|
-
|
|
373
|
+
// NOTE: Do NOT call this if readline already echoed the prompt.
|
|
374
|
+
// In the REPL, readline shows "you › input" automatically.
|
|
375
|
+
// This is ONLY for re-printing in non-readline contexts.
|
|
376
|
+
console.log(` ${exports.theme.dim('you')} ${exports.theme.brand('›')} ${input}`);
|
|
393
377
|
}
|
|
394
378
|
// ============================================================================
|
|
395
379
|
// ERROR / SUCCESS / WARNING
|
|
@@ -418,7 +402,7 @@ function printWarning(message) {
|
|
|
418
402
|
console.log(exports.theme.warning(` ⚠ ${message}`));
|
|
419
403
|
}
|
|
420
404
|
// ============================================================================
|
|
421
|
-
// EXIT
|
|
405
|
+
// EXIT
|
|
422
406
|
// ============================================================================
|
|
423
407
|
function printExit() {
|
|
424
408
|
if (IS_PIPE)
|
|
@@ -441,12 +425,9 @@ function printToolError(toolName, error) {
|
|
|
441
425
|
process.stdout.write(`\r ${exports.theme.error('✗')} ${exports.theme.code(toolName)} ${exports.theme.dim(error.substring(0, 60))}` + ' '.repeat(10) + '\n');
|
|
442
426
|
}
|
|
443
427
|
// ============================================================================
|
|
444
|
-
// SCORE CARD
|
|
428
|
+
// SCORE CARD
|
|
445
429
|
// ============================================================================
|
|
446
430
|
function printScoreCard(fileName, scores) {
|
|
447
|
-
const w = Math.min(getTermWidth() - 4, 50);
|
|
448
|
-
const iw = w - 2;
|
|
449
|
-
const title = `Code Review · ${fileName}`;
|
|
450
431
|
console.log('');
|
|
451
432
|
console.log(exports.theme.dim(` ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄`));
|
|
452
433
|
for (const { label, score, max = 10 } of scores) {
|
|
@@ -461,7 +442,7 @@ function printScoreCard(fileName, scores) {
|
|
|
461
442
|
console.log('');
|
|
462
443
|
}
|
|
463
444
|
// ============================================================================
|
|
464
|
-
// COMMIT BOX
|
|
445
|
+
// COMMIT BOX
|
|
465
446
|
// ============================================================================
|
|
466
447
|
function printCommitBox(commitMessage) {
|
|
467
448
|
const w = Math.min(getTermWidth() - 2, 55);
|
|
@@ -470,7 +451,6 @@ function printCommitBox(commitMessage) {
|
|
|
470
451
|
console.log('');
|
|
471
452
|
console.log(exports.theme.brand(` ╭─ ${label} ${'─'.repeat(Math.max(0, headerPad))}╮`));
|
|
472
453
|
console.log(exports.theme.brand(` │`) + ' '.repeat(w - 2) + exports.theme.brand(`│`));
|
|
473
|
-
// Wrap message to fit box
|
|
474
454
|
const maxMsgWidth = w - 6;
|
|
475
455
|
const msgLines = commitMessage.match(new RegExp(`.{1,${maxMsgWidth}}`, 'g')) || [commitMessage];
|
|
476
456
|
for (const msgLine of msgLines) {
|
|
@@ -482,7 +462,7 @@ function printCommitBox(commitMessage) {
|
|
|
482
462
|
console.log('');
|
|
483
463
|
}
|
|
484
464
|
// ============================================================================
|
|
485
|
-
// DIFF PREVIEW
|
|
465
|
+
// DIFF PREVIEW
|
|
486
466
|
// ============================================================================
|
|
487
467
|
function printDiff(fileName, changes) {
|
|
488
468
|
const w = Math.min(getTermWidth() - 4, 60);
|
|
@@ -505,7 +485,80 @@ function printDiff(fileName, changes) {
|
|
|
505
485
|
console.log('');
|
|
506
486
|
}
|
|
507
487
|
// ============================================================================
|
|
508
|
-
//
|
|
488
|
+
// TRUST PROMPT — Branded CoWorker workspace verification
|
|
489
|
+
// ============================================================================
|
|
490
|
+
const fs = __importStar(require("fs"));
|
|
491
|
+
const path = __importStar(require("path"));
|
|
492
|
+
const os = __importStar(require("os"));
|
|
493
|
+
const readline = __importStar(require("readline"));
|
|
494
|
+
const TRUSTED_PATH = path.join(os.homedir(), '.coworker', 'trusted.json');
|
|
495
|
+
function loadTrustedPaths() {
|
|
496
|
+
try {
|
|
497
|
+
if (fs.existsSync(TRUSTED_PATH)) {
|
|
498
|
+
const data = JSON.parse(fs.readFileSync(TRUSTED_PATH, 'utf8'));
|
|
499
|
+
return Array.isArray(data?.trustedPaths) ? data.trustedPaths : [];
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
catch { /* ignore */ }
|
|
503
|
+
return [];
|
|
504
|
+
}
|
|
505
|
+
function saveTrustedPaths(paths) {
|
|
506
|
+
try {
|
|
507
|
+
const dir = path.dirname(TRUSTED_PATH);
|
|
508
|
+
if (!fs.existsSync(dir)) {
|
|
509
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
510
|
+
}
|
|
511
|
+
fs.writeFileSync(TRUSTED_PATH, JSON.stringify({ trustedPaths: paths }, null, 2));
|
|
512
|
+
}
|
|
513
|
+
catch { /* ignore */ }
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Shows the CoWorker branded workspace trust prompt.
|
|
517
|
+
* Returns true if user trusts, false if they want to exit.
|
|
518
|
+
* Skips silently if the directory is already trusted.
|
|
519
|
+
*/
|
|
520
|
+
async function checkWorkspaceTrust(cwd) {
|
|
521
|
+
if (IS_PIPE)
|
|
522
|
+
return true; // Non-interactive mode — skip
|
|
523
|
+
const trustedPaths = loadTrustedPaths();
|
|
524
|
+
const normalizedCwd = cwd.replace(/\\/g, '/').toLowerCase();
|
|
525
|
+
// Check if already trusted
|
|
526
|
+
const isTrusted = trustedPaths.some(p => {
|
|
527
|
+
const normalized = p.replace(/\\/g, '/').toLowerCase();
|
|
528
|
+
return normalizedCwd.startsWith(normalized) || normalizedCwd === normalized;
|
|
529
|
+
});
|
|
530
|
+
if (isTrusted)
|
|
531
|
+
return true;
|
|
532
|
+
// Show the branded trust prompt
|
|
533
|
+
const lineWidth = Math.min(getTermWidth() - 4, 50);
|
|
534
|
+
console.log('');
|
|
535
|
+
console.log(exports.theme.dim(` ${'─'.repeat(lineWidth)}`));
|
|
536
|
+
console.log(` ${exports.theme.brand('◆')} ${exports.theme.brand('Workspace:')} ${exports.theme.white(cwd)}`);
|
|
537
|
+
console.log(exports.theme.dim(` ${'─'.repeat(lineWidth)}`));
|
|
538
|
+
console.log('');
|
|
539
|
+
console.log(exports.theme.dim(' CoWorker can read, edit, and run files here.'));
|
|
540
|
+
console.log(exports.theme.dim(' Only use in folders you own or trust.'));
|
|
541
|
+
console.log('');
|
|
542
|
+
console.log(` ${exports.theme.brand('›')} ${exports.theme.white('1. Got it, let\'s go')}`);
|
|
543
|
+
console.log(` ${exports.theme.dim('2. Exit')}`);
|
|
544
|
+
console.log('');
|
|
545
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
546
|
+
const answer = await new Promise((resolve) => {
|
|
547
|
+
rl.question(exports.theme.dim(' Enter to confirm · Esc to cancel › '), (a) => {
|
|
548
|
+
resolve(a.trim());
|
|
549
|
+
rl.close();
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
if (answer === '2' || answer.toLowerCase() === 'exit') {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
// Trust this directory
|
|
556
|
+
trustedPaths.push(cwd);
|
|
557
|
+
saveTrustedPaths(trustedPaths);
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
// ============================================================================
|
|
561
|
+
// HELP + COMPACT
|
|
509
562
|
// ============================================================================
|
|
510
563
|
function printHelp() {
|
|
511
564
|
console.log(`
|
|
@@ -531,36 +584,8 @@ function printCompact(msgCount, tokenEst, sessionId) {
|
|
|
531
584
|
exports.theme.dim(` │ Session: ${sessionId.substring(0, 8)}`));
|
|
532
585
|
console.log('');
|
|
533
586
|
}
|
|
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
|
-
}
|
|
562
587
|
// ============================================================================
|
|
563
|
-
// GENERIC BOX
|
|
588
|
+
// GENERIC BOX + TIMER
|
|
564
589
|
// ============================================================================
|
|
565
590
|
function printBox(label, lines) {
|
|
566
591
|
const w = Math.min(getTermWidth() - 2, 70);
|
|
@@ -572,9 +597,6 @@ function printBox(label, lines) {
|
|
|
572
597
|
}
|
|
573
598
|
console.log(exports.theme.brand(` ╰${'─'.repeat(w - 3)}╯`));
|
|
574
599
|
}
|
|
575
|
-
// ============================================================================
|
|
576
|
-
// TIMER
|
|
577
|
-
// ============================================================================
|
|
578
600
|
class Timer {
|
|
579
601
|
constructor() {
|
|
580
602
|
this.start = Date.now();
|
|
@@ -588,4 +610,41 @@ class Timer {
|
|
|
588
610
|
}
|
|
589
611
|
}
|
|
590
612
|
exports.Timer = Timer;
|
|
613
|
+
// ============================================================================
|
|
614
|
+
// RENDER MARKDOWN (for buffered final rendering)
|
|
615
|
+
// ============================================================================
|
|
616
|
+
function renderMarkdown(text) {
|
|
617
|
+
const lines = text.split('\n');
|
|
618
|
+
const rendered = [];
|
|
619
|
+
let inCodeBlock = false;
|
|
620
|
+
for (const line of lines) {
|
|
621
|
+
if (line.trim().startsWith('```') && !inCodeBlock) {
|
|
622
|
+
inCodeBlock = true;
|
|
623
|
+
const lang = line.trim().replace('```', '').trim() || 'code';
|
|
624
|
+
rendered.push(` ${exports.theme.dim(`┌─ ${lang} ──`)}`);
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (line.trim() === '```' && inCodeBlock) {
|
|
628
|
+
inCodeBlock = false;
|
|
629
|
+
rendered.push(` ${exports.theme.dim('└──')}`);
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
if (inCodeBlock) {
|
|
633
|
+
rendered.push(` ${exports.theme.dim('│')} ${exports.theme.code(line)}`);
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
if (line.match(/^#{1,3}\s+/)) {
|
|
637
|
+
rendered.push(exports.theme.brand(line.replace(/^#{1,3}\s+/, '')));
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
if (line.match(/^\s*[-*]\s+/)) {
|
|
641
|
+
rendered.push(`${exports.theme.dim('·')} ${line.replace(/^\s*[-*]\s+/, '')}`);
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
let result = line.replace(/\*\*(.+?)\*\*/g, (_, c) => exports.theme.white(c));
|
|
645
|
+
result = result.replace(/`(.+?)`/g, (_, c) => exports.theme.code(c));
|
|
646
|
+
rendered.push(result);
|
|
647
|
+
}
|
|
648
|
+
return rendered.join('\n');
|
|
649
|
+
}
|
|
591
650
|
//# sourceMappingURL=output.js.map
|