@sylix/coworker 1.3.0 → 1.3.1

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.
Files changed (64) hide show
  1. package/dist/cli/index.d.ts.map +1 -1
  2. package/dist/cli/index.js +145 -292
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/commands/slash/advanced.d.ts +3 -0
  5. package/dist/commands/slash/advanced.d.ts.map +1 -0
  6. package/dist/commands/slash/advanced.js +225 -0
  7. package/dist/commands/slash/advanced.js.map +1 -0
  8. package/dist/commands/slash/config.d.ts +3 -0
  9. package/dist/commands/slash/config.d.ts.map +1 -0
  10. package/dist/commands/slash/config.js +161 -0
  11. package/dist/commands/slash/config.js.map +1 -0
  12. package/dist/commands/slash/context.d.ts +3 -0
  13. package/dist/commands/slash/context.d.ts.map +1 -0
  14. package/dist/commands/slash/context.js +127 -0
  15. package/dist/commands/slash/context.js.map +1 -0
  16. package/dist/commands/slash/core.d.ts +3 -0
  17. package/dist/commands/slash/core.d.ts.map +1 -0
  18. package/dist/commands/slash/core.js +112 -0
  19. package/dist/commands/slash/core.js.map +1 -0
  20. package/dist/commands/slash/developer.d.ts +3 -0
  21. package/dist/commands/slash/developer.d.ts.map +1 -0
  22. package/dist/commands/slash/developer.js +174 -0
  23. package/dist/commands/slash/developer.js.map +1 -0
  24. package/dist/commands/slash/files.d.ts +3 -0
  25. package/dist/commands/slash/files.d.ts.map +1 -0
  26. package/dist/commands/slash/files.js +216 -0
  27. package/dist/commands/slash/files.js.map +1 -0
  28. package/dist/commands/slash/registry.d.ts +36 -0
  29. package/dist/commands/slash/registry.d.ts.map +1 -0
  30. package/dist/commands/slash/registry.js +69 -0
  31. package/dist/commands/slash/registry.js.map +1 -0
  32. package/dist/commands/slash/session.d.ts +3 -0
  33. package/dist/commands/slash/session.d.ts.map +1 -0
  34. package/dist/commands/slash/session.js +144 -0
  35. package/dist/commands/slash/session.js.map +1 -0
  36. package/dist/core/CoWorkerAgent.d.ts +8 -3
  37. package/dist/core/CoWorkerAgent.d.ts.map +1 -1
  38. package/dist/core/CoWorkerAgent.js +99 -32
  39. package/dist/core/CoWorkerAgent.js.map +1 -1
  40. package/dist/session/SessionManager.js +10 -5
  41. package/dist/session/SessionManager.js.map +1 -1
  42. package/dist/skills/HookAndSkillManager.js +2 -1
  43. package/dist/skills/HookAndSkillManager.js.map +1 -1
  44. package/dist/utils/conversations.d.ts +14 -0
  45. package/dist/utils/conversations.d.ts.map +1 -0
  46. package/dist/utils/conversations.js +100 -0
  47. package/dist/utils/conversations.js.map +1 -0
  48. package/dist/utils/inputbar.d.ts +87 -0
  49. package/dist/utils/inputbar.d.ts.map +1 -0
  50. package/dist/utils/inputbar.js +263 -0
  51. package/dist/utils/inputbar.js.map +1 -0
  52. package/dist/utils/output.d.ts +48 -42
  53. package/dist/utils/output.d.ts.map +1 -1
  54. package/dist/utils/output.js +233 -186
  55. package/dist/utils/output.js.map +1 -1
  56. package/dist/utils/palette.d.ts +25 -0
  57. package/dist/utils/palette.d.ts.map +1 -0
  58. package/dist/utils/palette.js +92 -0
  59. package/dist/utils/palette.js.map +1 -0
  60. package/dist/utils/welcome.d.ts +2 -0
  61. package/dist/utils/welcome.d.ts.map +1 -0
  62. package/dist/utils/welcome.js +130 -0
  63. package/dist/utils/welcome.js.map +1 -0
  64. package/package.json +1 -1
@@ -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
- * 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
+ * Premium terminal rendering with true streaming support.
67
+ * All response text renders inside ╷│╵ borders as it arrives.
73
68
  */
74
- exports.CW_VERSION = '1.3.0';
69
+ exports.CW_VERSION = '1.3.1';
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'), // Electric cyan — Sylix brand
92
- accent: hex('#7C3AED'), // Deep purple — highlights
93
- success: hex('#10B981'), // Emerald green
94
- error: hex('#EF4444'), // Clean red
95
- warning: hex('#F59E0B'), // Amber
96
- ai: hex('#A78BFA'), // Soft purple — AI responses
97
- dim: hex('#6B7280'), // Gray — secondary info
98
- white: hex('#F9FAFB'), // Off white
99
- code: hex('#FCD34D'), // Yellow — code and commands
100
- purple: hex('#A78BFA'), // Violet for tagline
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
- // PART 1: ANIMATED BANNER — Line-by-line slide-in, word-by-word tagline
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 rapidly
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); // 60ms between lines
145
+ await sleep(60);
164
146
  }
165
- // Step 2: Tagline — word by word, each with its own color
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 draws left to right, char by char
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 — fades in all at once
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
- // PART 2: THINKING ANIMATION — Rotating messages with pulsing dots
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 = '.'.repeat(3 - dotCount);
260
- process.stdout.write(`\r ${exports.theme.brand('◆')} ${exports.theme.dim(this.message + dots)}${exports.theme.dim(pad)}` + ' '.repeat(10));
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,129 @@ 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
- // PART 3: MARKDOWN-AWARE RESPONSE RENDERING
248
+ // STREAMING RESPONSE RENDERER
277
249
  // ============================================================================
278
250
  /**
279
- * Renders a block of AI response text with markdown awareness.
280
- * Handles: code blocks, **bold**, `inline code`, ## headers, - lists
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
- 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;
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
- // 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('│')}`);
287
+ for (const char of chunk) {
288
+ this.charCount++;
289
+ if (char === '\n') {
290
+ // Start a new bordered line
291
+ process.stdout.write('\n' + exports.theme.border(' │') + ' ');
292
+ continue;
307
293
  }
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;
294
+ // Detect code block toggle
295
+ // We track ``` by checking accumulated chars, but simplified:
296
+ // the chunk itself may contain ```. We'll track via state.
297
+ if (char === '`') {
298
+ // Just render it — code block detection is per-line in the final text
299
+ // For streaming, we use simple color: ai color for prose, code color inside ```
300
+ }
301
+ // Choose color based on whether we're in a code block
302
+ const color = this.inCodeBlock ? exports.theme.code : exports.theme.ai;
303
+ process.stdout.write(color(char));
324
304
  }
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;
305
+ // Check for code block toggles in the chunk
306
+ const backtickMatches = chunk.match(/```/g);
307
+ if (backtickMatches) {
308
+ for (const _match of backtickMatches) {
309
+ this.inCodeBlock = !this.inCodeBlock;
310
+ }
330
311
  }
331
- // Regular text with inline formatting
332
- rendered.push(renderInline(line));
333
312
  }
334
- return rendered.join('\n');
335
- }
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;
345
- }
346
- // ── Response box rendering ──
347
- function printResponseHeader() {
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
- }
313
+ /**
314
+ * Close the response box and print footer.
315
+ */
316
+ finish() {
317
+ if (!this.started)
318
+ return;
319
+ const elapsed = Date.now() - this.startTime;
320
+ const elapsedStr = elapsed < 1000 ? `${elapsed}ms` : `${(elapsed / 1000).toFixed(1)}s`;
321
+ const tokenEst = Math.ceil(this.charCount / 4);
322
+ console.log(''); // end the last │ line
323
+ console.log(exports.theme.border(` ╵`) + exports.theme.dim(` ✦ ${elapsedStr} · ${tokenEst} tokens`));
324
+ console.log('');
363
325
  }
364
- if (currentLine) {
365
- console.log(`${exports.theme.dim(' │')} ${exports.theme.ai(currentLine)}`);
326
+ /** Returns the estimated token count. */
327
+ getTokenEstimate() {
328
+ return Math.ceil(this.charCount / 4);
329
+ }
330
+ /** Returns elapsed ms. */
331
+ getElapsed() {
332
+ return Date.now() - this.startTime;
366
333
  }
367
334
  }
335
+ exports.StreamRenderer = StreamRenderer;
336
+ // ============================================================================
337
+ // LEGACY COMPAT — printResponseHeader/Footer for tool outputs
338
+ // ============================================================================
339
+ function printResponseHeader() {
340
+ console.log(exports.theme.border(' ╷'));
341
+ process.stdout.write(exports.theme.border(' │') + ' ');
342
+ }
368
343
  function printResponseFooter(elapsed, tokenEstimate) {
369
344
  const elapsedStr = elapsed < 1000 ? `${elapsed}ms` : `${(elapsed / 1000).toFixed(1)}s`;
370
345
  const tokenStr = tokenEstimate ? ` · ${tokenEstimate} tokens` : '';
371
- console.log(exports.theme.dim(` ✦ ${elapsedStr}${tokenStr}`));
346
+ console.log(exports.theme.border(` ╵`) + exports.theme.dim(` ✦ ${elapsedStr}${tokenStr}`));
372
347
  }
373
- /**
374
- * Streams AI response text into the response box with markdown rendering.
375
- * Call printResponseHeader() first, then stream chunks, then printResponseFooter().
376
- */
377
348
  function streamResponseChunk(chunk) {
378
- // Split by newlines and render each part within the box
379
349
  const parts = chunk.split('\n');
380
350
  for (let i = 0; i < parts.length; i++) {
381
351
  if (i > 0) {
382
- // Newline within the chunk — start a new box line
383
- process.stdout.write(`\n${exports.theme.dim(' │')} `);
352
+ process.stdout.write('\n' + exports.theme.border(' │') + ' ');
384
353
  }
385
354
  process.stdout.write(exports.theme.ai(parts[i]));
386
355
  }
387
356
  }
388
357
  // ============================================================================
389
- // PROMPT RENDERING
358
+ // PROMPT RENDERING — Only called ONCE after user hits Enter
390
359
  // ============================================================================
391
360
  function printUserPrompt(input) {
392
- console.log(`\n ${exports.theme.dim('you')} ${exports.theme.brand('›')} ${input}\n`);
361
+ // NOTE: Do NOT call this if readline already echoed the prompt.
362
+ // In the REPL, readline shows "you › input" automatically.
363
+ // This is ONLY for re-printing in non-readline contexts.
364
+ console.log(` ${exports.theme.dim('you')} ${exports.theme.brand('›')} ${input}`);
393
365
  }
394
366
  // ============================================================================
395
367
  // ERROR / SUCCESS / WARNING
@@ -418,7 +390,7 @@ function printWarning(message) {
418
390
  console.log(exports.theme.warning(` ⚠ ${message}`));
419
391
  }
420
392
  // ============================================================================
421
- // EXIT MESSAGE
393
+ // EXIT
422
394
  // ============================================================================
423
395
  function printExit() {
424
396
  if (IS_PIPE)
@@ -441,12 +413,9 @@ function printToolError(toolName, error) {
441
413
  process.stdout.write(`\r ${exports.theme.error('✗')} ${exports.theme.code(toolName)} ${exports.theme.dim(error.substring(0, 60))}` + ' '.repeat(10) + '\n');
442
414
  }
443
415
  // ============================================================================
444
- // SCORE CARD (coworker review)
416
+ // SCORE CARD
445
417
  // ============================================================================
446
418
  function printScoreCard(fileName, scores) {
447
- const w = Math.min(getTermWidth() - 4, 50);
448
- const iw = w - 2;
449
- const title = `Code Review · ${fileName}`;
450
419
  console.log('');
451
420
  console.log(exports.theme.dim(` ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄`));
452
421
  for (const { label, score, max = 10 } of scores) {
@@ -461,7 +430,7 @@ function printScoreCard(fileName, scores) {
461
430
  console.log('');
462
431
  }
463
432
  // ============================================================================
464
- // COMMIT BOX (coworker commit)
433
+ // COMMIT BOX
465
434
  // ============================================================================
466
435
  function printCommitBox(commitMessage) {
467
436
  const w = Math.min(getTermWidth() - 2, 55);
@@ -470,7 +439,6 @@ function printCommitBox(commitMessage) {
470
439
  console.log('');
471
440
  console.log(exports.theme.brand(` ╭─ ${label} ${'─'.repeat(Math.max(0, headerPad))}╮`));
472
441
  console.log(exports.theme.brand(` │`) + ' '.repeat(w - 2) + exports.theme.brand(`│`));
473
- // Wrap message to fit box
474
442
  const maxMsgWidth = w - 6;
475
443
  const msgLines = commitMessage.match(new RegExp(`.{1,${maxMsgWidth}}`, 'g')) || [commitMessage];
476
444
  for (const msgLine of msgLines) {
@@ -482,7 +450,7 @@ function printCommitBox(commitMessage) {
482
450
  console.log('');
483
451
  }
484
452
  // ============================================================================
485
- // DIFF PREVIEW (coworker edit)
453
+ // DIFF PREVIEW
486
454
  // ============================================================================
487
455
  function printDiff(fileName, changes) {
488
456
  const w = Math.min(getTermWidth() - 4, 60);
@@ -505,7 +473,80 @@ function printDiff(fileName, changes) {
505
473
  console.log('');
506
474
  }
507
475
  // ============================================================================
508
- // INTERACTIVE HELPUpdated with new commands
476
+ // TRUST PROMPTBranded CoWorker workspace verification
477
+ // ============================================================================
478
+ const fs = __importStar(require("fs"));
479
+ const path = __importStar(require("path"));
480
+ const os = __importStar(require("os"));
481
+ const readline = __importStar(require("readline"));
482
+ const TRUSTED_PATH = path.join(os.homedir(), '.coworker', 'trusted.json');
483
+ function loadTrustedPaths() {
484
+ try {
485
+ if (fs.existsSync(TRUSTED_PATH)) {
486
+ const data = JSON.parse(fs.readFileSync(TRUSTED_PATH, 'utf8'));
487
+ return Array.isArray(data?.trustedPaths) ? data.trustedPaths : [];
488
+ }
489
+ }
490
+ catch { /* ignore */ }
491
+ return [];
492
+ }
493
+ function saveTrustedPaths(paths) {
494
+ try {
495
+ const dir = path.dirname(TRUSTED_PATH);
496
+ if (!fs.existsSync(dir)) {
497
+ fs.mkdirSync(dir, { recursive: true });
498
+ }
499
+ fs.writeFileSync(TRUSTED_PATH, JSON.stringify({ trustedPaths: paths }, null, 2));
500
+ }
501
+ catch { /* ignore */ }
502
+ }
503
+ /**
504
+ * Shows the CoWorker branded workspace trust prompt.
505
+ * Returns true if user trusts, false if they want to exit.
506
+ * Skips silently if the directory is already trusted.
507
+ */
508
+ async function checkWorkspaceTrust(cwd) {
509
+ if (IS_PIPE)
510
+ return true; // Non-interactive mode — skip
511
+ const trustedPaths = loadTrustedPaths();
512
+ const normalizedCwd = cwd.replace(/\\/g, '/').toLowerCase();
513
+ // Check if already trusted
514
+ const isTrusted = trustedPaths.some(p => {
515
+ const normalized = p.replace(/\\/g, '/').toLowerCase();
516
+ return normalizedCwd.startsWith(normalized) || normalizedCwd === normalized;
517
+ });
518
+ if (isTrusted)
519
+ return true;
520
+ // Show the branded trust prompt
521
+ const lineWidth = Math.min(getTermWidth() - 4, 50);
522
+ console.log('');
523
+ console.log(exports.theme.dim(` ${'─'.repeat(lineWidth)}`));
524
+ console.log(` ${exports.theme.brand('◆')} ${exports.theme.brand('Workspace:')} ${exports.theme.white(cwd)}`);
525
+ console.log(exports.theme.dim(` ${'─'.repeat(lineWidth)}`));
526
+ console.log('');
527
+ console.log(exports.theme.dim(' CoWorker can read, edit, and run files here.'));
528
+ console.log(exports.theme.dim(' Only use in folders you own or trust.'));
529
+ console.log('');
530
+ console.log(` ${exports.theme.brand('›')} ${exports.theme.white('1. Got it, let\'s go')}`);
531
+ console.log(` ${exports.theme.dim('2. Exit')}`);
532
+ console.log('');
533
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
534
+ const answer = await new Promise((resolve) => {
535
+ rl.question(exports.theme.dim(' Enter to confirm · Esc to cancel › '), (a) => {
536
+ resolve(a.trim());
537
+ rl.close();
538
+ });
539
+ });
540
+ if (answer === '2' || answer.toLowerCase() === 'exit') {
541
+ return false;
542
+ }
543
+ // Trust this directory
544
+ trustedPaths.push(cwd);
545
+ saveTrustedPaths(trustedPaths);
546
+ return true;
547
+ }
548
+ // ============================================================================
549
+ // HELP + COMPACT
509
550
  // ============================================================================
510
551
  function printHelp() {
511
552
  console.log(`
@@ -531,36 +572,8 @@ function printCompact(msgCount, tokenEst, sessionId) {
531
572
  exports.theme.dim(` │ Session: ${sessionId.substring(0, 8)}`));
532
573
  console.log('');
533
574
  }
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
575
  // ============================================================================
563
- // GENERIC BOX
576
+ // GENERIC BOX + TIMER
564
577
  // ============================================================================
565
578
  function printBox(label, lines) {
566
579
  const w = Math.min(getTermWidth() - 2, 70);
@@ -572,9 +585,6 @@ function printBox(label, lines) {
572
585
  }
573
586
  console.log(exports.theme.brand(` ╰${'─'.repeat(w - 3)}╯`));
574
587
  }
575
- // ============================================================================
576
- // TIMER
577
- // ============================================================================
578
588
  class Timer {
579
589
  constructor() {
580
590
  this.start = Date.now();
@@ -588,4 +598,41 @@ class Timer {
588
598
  }
589
599
  }
590
600
  exports.Timer = Timer;
601
+ // ============================================================================
602
+ // RENDER MARKDOWN (for buffered final rendering)
603
+ // ============================================================================
604
+ function renderMarkdown(text) {
605
+ const lines = text.split('\n');
606
+ const rendered = [];
607
+ let inCodeBlock = false;
608
+ for (const line of lines) {
609
+ if (line.trim().startsWith('```') && !inCodeBlock) {
610
+ inCodeBlock = true;
611
+ const lang = line.trim().replace('```', '').trim() || 'code';
612
+ rendered.push(` ${exports.theme.dim(`┌─ ${lang} ──`)}`);
613
+ continue;
614
+ }
615
+ if (line.trim() === '```' && inCodeBlock) {
616
+ inCodeBlock = false;
617
+ rendered.push(` ${exports.theme.dim('└──')}`);
618
+ continue;
619
+ }
620
+ if (inCodeBlock) {
621
+ rendered.push(` ${exports.theme.dim('│')} ${exports.theme.code(line)}`);
622
+ continue;
623
+ }
624
+ if (line.match(/^#{1,3}\s+/)) {
625
+ rendered.push(exports.theme.brand(line.replace(/^#{1,3}\s+/, '')));
626
+ continue;
627
+ }
628
+ if (line.match(/^\s*[-*]\s+/)) {
629
+ rendered.push(`${exports.theme.dim('·')} ${line.replace(/^\s*[-*]\s+/, '')}`);
630
+ continue;
631
+ }
632
+ let result = line.replace(/\*\*(.+?)\*\*/g, (_, c) => exports.theme.white(c));
633
+ result = result.replace(/`(.+?)`/g, (_, c) => exports.theme.code(c));
634
+ rendered.push(result);
635
+ }
636
+ return rendered.join('\n');
637
+ }
591
638
  //# sourceMappingURL=output.js.map