@sylix/coworker 1.2.6 → 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 (82) hide show
  1. package/dist/cli/index.d.ts.map +1 -1
  2. package/dist/cli/index.js +181 -106
  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 -2
  37. package/dist/core/CoWorkerAgent.d.ts.map +1 -1
  38. package/dist/core/CoWorkerAgent.js +179 -36
  39. package/dist/core/CoWorkerAgent.js.map +1 -1
  40. package/dist/review/CodeReviewEngine.d.ts +78 -0
  41. package/dist/review/CodeReviewEngine.d.ts.map +1 -0
  42. package/dist/review/CodeReviewEngine.js +449 -0
  43. package/dist/review/CodeReviewEngine.js.map +1 -0
  44. package/dist/session/SessionManager.js +10 -5
  45. package/dist/session/SessionManager.js.map +1 -1
  46. package/dist/skills/HookAndSkillManager.d.ts +29 -5
  47. package/dist/skills/HookAndSkillManager.d.ts.map +1 -1
  48. package/dist/skills/HookAndSkillManager.js +167 -12
  49. package/dist/skills/HookAndSkillManager.js.map +1 -1
  50. package/dist/todos/TodoManager.d.ts +19 -0
  51. package/dist/todos/TodoManager.d.ts.map +1 -0
  52. package/dist/todos/TodoManager.js +102 -0
  53. package/dist/todos/TodoManager.js.map +1 -0
  54. package/dist/tools/NativeTools.d.ts +98 -0
  55. package/dist/tools/NativeTools.d.ts.map +1 -1
  56. package/dist/tools/NativeTools.js +571 -0
  57. package/dist/tools/NativeTools.js.map +1 -1
  58. package/dist/tools/Schemas.d.ts +1199 -102
  59. package/dist/tools/Schemas.d.ts.map +1 -1
  60. package/dist/tools/Schemas.js +358 -40
  61. package/dist/tools/Schemas.js.map +1 -1
  62. package/dist/utils/conversations.d.ts +14 -0
  63. package/dist/utils/conversations.d.ts.map +1 -0
  64. package/dist/utils/conversations.js +100 -0
  65. package/dist/utils/conversations.js.map +1 -0
  66. package/dist/utils/inputbar.d.ts +87 -0
  67. package/dist/utils/inputbar.d.ts.map +1 -0
  68. package/dist/utils/inputbar.js +263 -0
  69. package/dist/utils/inputbar.js.map +1 -0
  70. package/dist/utils/output.d.ts +73 -48
  71. package/dist/utils/output.d.ts.map +1 -1
  72. package/dist/utils/output.js +349 -158
  73. package/dist/utils/output.js.map +1 -1
  74. package/dist/utils/palette.d.ts +25 -0
  75. package/dist/utils/palette.d.ts.map +1 -0
  76. package/dist/utils/palette.js +92 -0
  77. package/dist/utils/palette.js.map +1 -0
  78. package/dist/utils/welcome.d.ts +2 -0
  79. package/dist/utils/welcome.d.ts.map +1 -0
  80. package/dist/utils/welcome.js +130 -0
  81. package/dist/utils/welcome.js.map +1 -0
  82. package/package.json +1 -1
@@ -33,14 +33,14 @@ 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.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
- exports.boxTop = boxTop;
41
- exports.boxBottom = boxBottom;
40
+ exports.showSessionInfo = showSessionInfo;
42
41
  exports.printResponseHeader = printResponseHeader;
43
42
  exports.printResponseFooter = printResponseFooter;
43
+ exports.streamResponseChunk = streamResponseChunk;
44
44
  exports.printUserPrompt = printUserPrompt;
45
45
  exports.printError = printError;
46
46
  exports.printSuccess = printSuccess;
@@ -52,18 +52,21 @@ exports.printToolError = printToolError;
52
52
  exports.printScoreCard = printScoreCard;
53
53
  exports.printCommitBox = printCommitBox;
54
54
  exports.printDiff = printDiff;
55
+ exports.checkWorkspaceTrust = checkWorkspaceTrust;
55
56
  exports.printHelp = printHelp;
56
57
  exports.printCompact = printCompact;
57
58
  exports.printBox = printBox;
59
+ exports.renderMarkdown = renderMarkdown;
58
60
  const chalk = __importStar(require("chalk"));
59
61
  /**
60
62
  * ═══════════════════════════════════════════════════════════════════
61
- * CoWorker by Sylix — Premium Output System
63
+ * CoWorker v1.3.0 by Sylix — Premium Output System
62
64
  * ═══════════════════════════════════════════════════════════════════
63
65
  *
64
- * Claude Code meets hacker terminal meets luxury product.
65
- * Handles all terminal rendering: animations, themes, boxes, streaming.
66
+ * Premium terminal rendering with true streaming support.
67
+ * All response text renders inside ╷│╵ borders as it arrives.
66
68
  */
69
+ exports.CW_VERSION = '1.3.1';
67
70
  // ============================================================================
68
71
  // THEME SYSTEM
69
72
  // ============================================================================
@@ -80,15 +83,17 @@ function hex(color) {
80
83
  }
81
84
  }
82
85
  exports.theme = {
83
- brand: hex('#00D4FF'), // Electric cyan — Sylix brand
84
- accent: hex('#7C3AED'), // Deep purple — highlights
85
- success: hex('#10B981'), // Emerald green
86
- error: hex('#EF4444'), // Clean red
87
- warning: hex('#F59E0B'), // Amber
88
- ai: hex('#A78BFA'), // Soft purple — AI responses
89
- dim: hex('#6B7280'), // Gray — secondary info
90
- white: hex('#F9FAFB'), // Off white
91
- code: hex('#FCD34D'), // Yellow — code and commands
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'),
92
97
  };
93
98
  // ============================================================================
94
99
  // TERMINAL UTILS
@@ -96,9 +101,6 @@ exports.theme = {
96
101
  function getTermWidth() {
97
102
  return process.stdout.columns || 80;
98
103
  }
99
- function isNarrow() {
100
- return getTermWidth() < 60;
101
- }
102
104
  function sleep(ms) {
103
105
  return new Promise(r => setTimeout(r, ms));
104
106
  }
@@ -111,7 +113,7 @@ function showCursor() {
111
113
  process.stdout.write('\x1b[?25h');
112
114
  }
113
115
  // ============================================================================
114
- // STARTUP BANNER — Char-by-char typewriter
116
+ // ANIMATED BANNER
115
117
  // ============================================================================
116
118
  const LOGO_LINES = [
117
119
  ' ██████╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗███████╗██████╗ ',
@@ -121,60 +123,64 @@ const LOGO_LINES = [
121
123
  '╚██████╗╚██████╔╝╚███╔███╔╝╚██████╔╝██║ ██║██║ ██╗███████╗██║ ██║',
122
124
  ' ╚═════╝ ╚═════╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝',
123
125
  ];
124
- /**
125
- * Full animated startup banner.
126
- * Step 1: Char-by-char typewriter ASCII logo
127
- * Step 2: Tagline fade-in word by word
128
- * Step 3: Animated thin line draw
129
- * Step 4: User/model info in dim text
130
- */
131
126
  async function showBanner(version, userEmail, model = 'helix-1.2') {
132
- if (IS_PIPE)
127
+ if (IS_PIPE || process.env.CW_NO_BANNER === '1')
133
128
  return;
134
129
  hideCursor();
135
130
  console.log('');
136
- // Step 1: Typewriter ASCII art — char by char (batched 4 chars at 2ms for speed)
137
- for (const line of LOGO_LINES) {
138
- const chars = [...line];
139
- let buffer = '';
140
- for (let i = 0; i < chars.length; i++) {
141
- buffer += chars[i];
142
- if (i % 4 === 3 || i === chars.length - 1) {
143
- process.stdout.write(exports.theme.brand(buffer));
144
- buffer = '';
145
- await sleep(2);
131
+ try {
132
+ // Step 1: Logo — line by line, each types across
133
+ for (const line of LOGO_LINES) {
134
+ const chars = [...line];
135
+ let buffer = '';
136
+ for (let i = 0; i < chars.length; i++) {
137
+ buffer += chars[i];
138
+ if (i % 6 === 5 || i === chars.length - 1) {
139
+ process.stdout.write(exports.theme.brand(buffer));
140
+ buffer = '';
141
+ await sleep(1);
142
+ }
146
143
  }
144
+ process.stdout.write('\n');
145
+ await sleep(60);
147
146
  }
148
- process.stdout.write('\n');
149
- }
150
- // Step 2: Fade in tagline one word at a time
151
- console.log('');
152
- const taglineWords = [' by', 'Sylix', ' ·', ' Your', 'AI', 'Coding', 'Partner'];
153
- for (const word of taglineWords) {
154
- process.stdout.write(exports.theme.dim(word + ' '));
155
- await sleep(80);
147
+ // Step 2: Tagline — word by word with semantic colors
148
+ console.log('');
149
+ const taglineWords = [
150
+ { text: 'by', color: exports.theme.dim },
151
+ { text: 'Sylix', color: exports.theme.brand },
152
+ { text: '·', color: exports.theme.dim },
153
+ { text: 'Your', color: exports.theme.white },
154
+ { text: 'AI', color: exports.theme.purple },
155
+ { text: 'Coding', color: exports.theme.white },
156
+ { text: 'Partner', color: exports.theme.white },
157
+ ];
158
+ process.stdout.write(' ');
159
+ for (const word of taglineWords) {
160
+ process.stdout.write(word.color(word.text) + ' ');
161
+ await sleep(100);
162
+ }
163
+ console.log('');
164
+ // Step 3: Divider draws left to right
165
+ console.log('');
166
+ const lineWidth = Math.min(getTermWidth() - 4, 60);
167
+ process.stdout.write(' ');
168
+ for (let i = 0; i < lineWidth; i++) {
169
+ process.stdout.write(exports.theme.dim('━'));
170
+ if (i % 3 === 0)
171
+ await sleep(3);
172
+ }
173
+ console.log('');
174
+ // Step 4: Info line
175
+ console.log('');
176
+ const userStr = userEmail ? `Signed in as ${userEmail}` : 'Not signed in';
177
+ console.log(` ${exports.theme.brand('◆')} ${exports.theme.dim(`${userStr} · Model: ${model} · v${version}`)}`);
178
+ console.log('');
156
179
  }
157
- console.log('');
158
- // Step 3: Animated thin line draw across terminal
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);
180
+ finally {
181
+ showCursor();
166
182
  }
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
183
  }
175
- /**
176
- * Static banner for non-TTY / piped / --no-banner mode.
177
- */
178
184
  function showStaticBanner(version) {
179
185
  console.log('');
180
186
  for (const line of LOGO_LINES) {
@@ -182,33 +188,51 @@ function showStaticBanner(version) {
182
188
  }
183
189
  console.log(exports.theme.dim(`\n by Sylix · v${version}\n`));
184
190
  }
191
+ async function showSessionInfo(cwd, sessionId) {
192
+ if (IS_PIPE) {
193
+ console.log(` cwd: ${cwd}`);
194
+ console.log(` session: ${sessionId.substring(0, 8)}...`);
195
+ console.log(` tips: /help for commands, Ctrl+C to exit\n`);
196
+ return;
197
+ }
198
+ const lines = [
199
+ exports.theme.dim(` cwd: ${cwd}`),
200
+ exports.theme.dim(` session: ${sessionId.substring(0, 8)}...`),
201
+ exports.theme.dim(` tips: /help for commands, Ctrl+C to exit`),
202
+ ];
203
+ for (const line of lines) {
204
+ console.log(line);
205
+ await sleep(50);
206
+ }
207
+ console.log('');
208
+ }
185
209
  // ============================================================================
186
- // THINKING / LOADING ANIMATION — Pulsing dots
210
+ // THINKING ANIMATION — Rotating messages with pulsing dots
187
211
  // ============================================================================
188
- const LOADING_MESSAGES = [
189
- '◆ Sylix brain activated...',
190
- '◆ Reading your codebase...',
191
- ' Thinking really hard...',
192
- '◆ Consulting the AI oracle...',
193
- ' Crafting something brilliant...',
194
- '◆ CoWorker is on it...',
212
+ const THINKING_MESSAGES = [
213
+ 'working',
214
+ 'thinking',
215
+ 'reading codebase',
216
+ 'analyzing',
217
+ 'on it',
195
218
  ];
196
219
  class ThinkingAnimation {
197
- constructor() {
220
+ constructor(customMessage) {
198
221
  this.interval = null;
199
222
  this.frame = 0;
200
- this.message = LOADING_MESSAGES[Math.floor(Math.random() * LOADING_MESSAGES.length)];
223
+ this.message = customMessage || THINKING_MESSAGES[Math.floor(Math.random() * THINKING_MESSAGES.length)];
201
224
  }
202
225
  start() {
203
226
  if (IS_PIPE)
204
227
  return;
205
228
  hideCursor();
206
- const dots = ['◉ ○ ○', '○ ◉ ○', '○ ○ ◉'];
207
229
  this.interval = setInterval(() => {
208
- const dot = dots[this.frame % dots.length];
209
- process.stdout.write(`\r ${exports.theme.brand(dot)} ${exports.theme.dim(this.message)}` + ' '.repeat(10));
230
+ const dotCount = (this.frame % 4);
231
+ const dots = '.'.repeat(dotCount);
232
+ const pad = ' '.repeat(3 - dotCount);
233
+ process.stdout.write(`\r ${exports.theme.brand('◆')} ${exports.theme.dim(this.message + dots)}${pad}` + ' '.repeat(10));
210
234
  this.frame++;
211
- }, 300);
235
+ }, 400);
212
236
  }
213
237
  stop() {
214
238
  if (this.interval) {
@@ -221,42 +245,126 @@ class ThinkingAnimation {
221
245
  }
222
246
  exports.ThinkingAnimation = ThinkingAnimation;
223
247
  // ============================================================================
224
- // BOXED AI RESPONSE RENDERING
248
+ // STREAMING RESPONSE RENDERER
225
249
  // ============================================================================
226
- function boxTop(label = 'CoWorker') {
227
- const w = Math.min(getTermWidth() - 2, 70);
228
- if (isNarrow())
229
- return exports.theme.brand(`─ ${label} ─`);
230
- const inner = w - 6 - label.length;
231
- return exports.theme.brand(` ╭─ ${label} ${'─'.repeat(Math.max(0, inner))}╮`);
232
- }
233
- function boxBottom() {
234
- const w = Math.min(getTermWidth() - 2, 70);
235
- if (isNarrow())
236
- return exports.theme.brand('─'.repeat(20));
237
- return exports.theme.brand(` ╰${'─'.repeat(w - 4)}╯`);
250
+ /**
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 │ "
268
+ */
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;
286
+ }
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;
293
+ }
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));
304
+ }
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
+ }
311
+ }
312
+ }
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('');
325
+ }
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;
333
+ }
238
334
  }
335
+ exports.StreamRenderer = StreamRenderer;
336
+ // ============================================================================
337
+ // LEGACY COMPAT — printResponseHeader/Footer for tool outputs
338
+ // ============================================================================
239
339
  function printResponseHeader() {
240
- console.log(boxTop('CoWorker'));
241
- console.log(exports.theme.brand(' │'));
340
+ console.log(exports.theme.border(''));
341
+ process.stdout.write(exports.theme.border(' │') + ' ');
242
342
  }
243
343
  function printResponseFooter(elapsed, tokenEstimate) {
244
- console.log(exports.theme.brand(' │'));
245
- console.log(boxBottom());
246
- console.log('');
247
344
  const elapsedStr = elapsed < 1000 ? `${elapsed}ms` : `${(elapsed / 1000).toFixed(1)}s`;
248
- const tokenStr = tokenEstimate ? ` · tokens: ~${tokenEstimate}` : '';
249
- console.log(exports.theme.dim(` Done · sylix.ai · ${elapsedStr}${tokenStr}`));
345
+ const tokenStr = tokenEstimate ? ` · ${tokenEstimate} tokens` : '';
346
+ console.log(exports.theme.border(` ╵`) + exports.theme.dim(` ✦ ${elapsedStr}${tokenStr}`));
347
+ }
348
+ function streamResponseChunk(chunk) {
349
+ const parts = chunk.split('\n');
350
+ for (let i = 0; i < parts.length; i++) {
351
+ if (i > 0) {
352
+ process.stdout.write('\n' + exports.theme.border(' │') + ' ');
353
+ }
354
+ process.stdout.write(exports.theme.ai(parts[i]));
355
+ }
250
356
  }
251
357
  // ============================================================================
252
- // PROMPT RENDERING
358
+ // PROMPT RENDERING — Only called ONCE after user hits Enter
253
359
  // ============================================================================
254
360
  function printUserPrompt(input) {
255
- console.log(` ${exports.theme.white('You')} ${exports.theme.dim('›')} ${input}`);
256
- console.log('');
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}`);
257
365
  }
258
366
  // ============================================================================
259
- // ERROR / SUCCESS / WARNING STYLING
367
+ // ERROR / SUCCESS / WARNING
260
368
  // ============================================================================
261
369
  function printError(title, details, hint) {
262
370
  console.log('');
@@ -282,7 +390,7 @@ function printWarning(message) {
282
390
  console.log(exports.theme.warning(` ⚠ ${message}`));
283
391
  }
284
392
  // ============================================================================
285
- // EXIT MESSAGE
393
+ // EXIT
286
394
  // ============================================================================
287
395
  function printExit() {
288
396
  if (IS_PIPE)
@@ -305,72 +413,45 @@ function printToolError(toolName, error) {
305
413
  process.stdout.write(`\r ${exports.theme.error('✗')} ${exports.theme.code(toolName)} ${exports.theme.dim(error.substring(0, 60))}` + ' '.repeat(10) + '\n');
306
414
  }
307
415
  // ============================================================================
308
- // COMMAND-SPECIFIC STYLING — Score Card (coworker review)
416
+ // SCORE CARD
309
417
  // ============================================================================
310
- /**
311
- * ┌─────────────────────────────────┐
312
- * │ Code Review · src/index.ts │
313
- * ├─────────────────────────────────┤
314
- * │ Quality ████████░░ 8/10 │
315
- * │ Security ██████████ 10/10 │
316
- * └─────────────────────────────────┘
317
- */
318
418
  function printScoreCard(fileName, scores) {
319
- const w = Math.min(getTermWidth() - 4, 50);
320
- const iw = w - 2; // inner width
321
- const title = `Code Review · ${fileName}`;
322
419
  console.log('');
323
- console.log(exports.theme.brand(` ┌${'─'.repeat(iw)}┐`));
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)}┤`));
420
+ console.log(exports.theme.dim(` ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄`));
326
421
  for (const { label, score, max = 10 } of scores) {
327
422
  const barWidth = 10;
328
423
  const filled = Math.round((score / max) * barWidth);
329
424
  const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
330
- const scoreStr = ` ${score}/${max}`;
331
- const barColor = score >= 8 ? exports.theme.success : score >= 5 ? exports.theme.warning : exports.theme.error;
332
- const pad = 15 - label.length;
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(`│`));
425
+ const barColor = score >= 8 ? exports.theme.brand : score >= 5 ? exports.theme.warning : exports.theme.error;
426
+ const dots = '·'.repeat(Math.max(1, 16 - label.length));
427
+ console.log(` ${exports.theme.dim(label)} ${exports.theme.dim(dots)} ${exports.theme.white(String(score))} ${barColor(bar)}`);
336
428
  }
337
- console.log(exports.theme.brand(` └${'─'.repeat(iw)}┘`));
429
+ console.log(exports.theme.dim(` ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄`));
338
430
  console.log('');
339
431
  }
340
432
  // ============================================================================
341
- // COMMAND-SPECIFIC STYLING — Commit Box (coworker commit)
433
+ // COMMIT BOX
342
434
  // ============================================================================
343
- /**
344
- * ╭─ Suggested Commit ──────────────────╮
345
- * │ ✨ feat: add user auth middleware │
346
- * │ │
347
- * │ Use this commit message? (y/n) › │
348
- * ╰─────────────────────────────────────╯
349
- */
350
435
  function printCommitBox(commitMessage) {
351
436
  const w = Math.min(getTermWidth() - 2, 55);
352
- const label = 'Suggested Commit';
437
+ const label = 'suggested commit';
353
438
  const headerPad = w - 6 - label.length;
354
- const msgLine = `✨ ${commitMessage}`;
355
- const promptLine = 'Use this commit message? (y/n) ›';
356
439
  console.log('');
357
440
  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
441
  console.log(exports.theme.brand(` │`) + ' '.repeat(w - 2) + exports.theme.brand(`│`));
360
- console.log(exports.theme.brand(` │ `) + exports.theme.dim(promptLine) + ' '.repeat(Math.max(0, w - 5 - promptLine.length)) + exports.theme.brand(`│`));
442
+ const maxMsgWidth = w - 6;
443
+ const msgLines = commitMessage.match(new RegExp(`.{1,${maxMsgWidth}}`, 'g')) || [commitMessage];
444
+ for (const msgLine of msgLines) {
445
+ const pad = w - 5 - msgLine.length;
446
+ console.log(exports.theme.brand(` │ `) + exports.theme.success(msgLine) + ' '.repeat(Math.max(0, pad)) + exports.theme.brand(`│`));
447
+ }
448
+ console.log(exports.theme.brand(` │`) + ' '.repeat(w - 2) + exports.theme.brand(`│`));
361
449
  console.log(exports.theme.brand(` ╰${'─'.repeat(w - 2)}╯`));
362
450
  console.log('');
363
451
  }
364
452
  // ============================================================================
365
- // COMMAND-SPECIFIC STYLING — Diff Preview (coworker edit)
453
+ // DIFF PREVIEW
366
454
  // ============================================================================
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
455
  function printDiff(fileName, changes) {
375
456
  const w = Math.min(getTermWidth() - 4, 60);
376
457
  const header = `Changes to ${fileName}`;
@@ -389,20 +470,94 @@ function printDiff(fileName, changes) {
389
470
  }
390
471
  }
391
472
  console.log(exports.theme.dim(` ${'─'.repeat(w)}`));
392
- console.log(exports.theme.dim(` Apply these changes? (y/n) ›`));
393
473
  console.log('');
394
474
  }
395
475
  // ============================================================================
396
- // INTERACTIVE HELP
476
+ // TRUST PROMPT — Branded 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
397
550
  // ============================================================================
398
551
  function printHelp() {
399
552
  console.log(`
400
553
  ${exports.theme.brand(' Slash Commands:')}
401
- ${exports.theme.code('/help')} ${exports.theme.dim('Show this help')}
402
- ${exports.theme.code('/clear')} ${exports.theme.dim('Clear the screen')}
403
- ${exports.theme.code('/status')} ${exports.theme.dim('Show auth & session status')}
404
- ${exports.theme.code('/compact')} ${exports.theme.dim('Show context window usage')}
405
- ${exports.theme.code('/exit')} ${exports.theme.dim('Exit CoWorker')}
554
+ ${exports.theme.code('/help')} ${exports.theme.dim('Show this help')}
555
+ ${exports.theme.code('/clear')} ${exports.theme.dim('Clear the screen')}
556
+ ${exports.theme.code('/review')} ${exports.theme.dim('Code review current branch')}
557
+ ${exports.theme.code('/commit')} ${exports.theme.dim('Generate commit message from diff')}
558
+ ${exports.theme.code('/status')} ${exports.theme.dim('Show auth & session status')}
559
+ ${exports.theme.code('/compact')} ${exports.theme.dim('Show context window usage')}
560
+ ${exports.theme.code('/exit')} ${exports.theme.dim('Exit CoWorker')}
406
561
  `);
407
562
  }
408
563
  function printCompact(msgCount, tokenEst, sessionId) {
@@ -412,11 +567,13 @@ function printCompact(msgCount, tokenEst, sessionId) {
412
567
  const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
413
568
  const color = usage > 80 ? exports.theme.error : usage > 50 ? exports.theme.warning : exports.theme.success;
414
569
  console.log('');
415
- console.log(exports.theme.dim(` Messages: ${msgCount} │ Context: `) + color(`[${bar}] ${usage}%`) + exports.theme.dim(` │ Session: ${sessionId.substring(0, 8)}`));
570
+ console.log(exports.theme.dim(` Messages: ${msgCount} │ Context: `) +
571
+ color(`[${bar}] ${usage}%`) +
572
+ exports.theme.dim(` │ Session: ${sessionId.substring(0, 8)}`));
416
573
  console.log('');
417
574
  }
418
575
  // ============================================================================
419
- // GENERIC STYLED BOX (reusable for any content)
576
+ // GENERIC BOX + TIMER
420
577
  // ============================================================================
421
578
  function printBox(label, lines) {
422
579
  const w = Math.min(getTermWidth() - 2, 70);
@@ -428,9 +585,6 @@ function printBox(label, lines) {
428
585
  }
429
586
  console.log(exports.theme.brand(` ╰${'─'.repeat(w - 3)}╯`));
430
587
  }
431
- // ============================================================================
432
- // ELAPSED TIME UTILITY
433
- // ============================================================================
434
588
  class Timer {
435
589
  constructor() {
436
590
  this.start = Date.now();
@@ -444,4 +598,41 @@ class Timer {
444
598
  }
445
599
  }
446
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
+ }
447
638
  //# sourceMappingURL=output.js.map