@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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAgbA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAiKvD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AA0PA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkKvD"}
package/dist/cli/index.js CHANGED
@@ -40,18 +40,20 @@ const AuthManager_1 = require("../config/AuthManager");
40
40
  const child_process_1 = require("child_process");
41
41
  const http = __importStar(require("http"));
42
42
  const ws_1 = require("ws");
43
- const readline = __importStar(require("readline"));
44
43
  const fs = __importStar(require("fs"));
45
44
  const path = __importStar(require("path"));
46
45
  const os = __importStar(require("os"));
47
46
  const output_1 = require("../utils/output");
47
+ const welcome_1 = require("../utils/welcome");
48
+ const palette_1 = require("../utils/palette");
49
+ const inputbar_1 = require("../utils/inputbar");
50
+ const registry_1 = require("../commands/slash/registry");
51
+ const conversations_1 = require("../utils/conversations");
48
52
  /**
49
- * CoWorker CLI v1.3.0 — The Polish Update
50
- * Premium interactive REPL with animated UI, /review, /commit,
51
- * history, slash command autocomplete, markdown-aware rendering.
53
+ * CoWorker CLI v1.3.0 — Premium Input Bar + Full Slash Commands
52
54
  */
53
55
  // ============================================================================
54
- // COMMAND HISTORY — Persists to ~/.coworker/history.json
56
+ // COMMAND HISTORY
55
57
  // ============================================================================
56
58
  const HISTORY_PATH = path.join(os.homedir(), '.coworker', 'history.json');
57
59
  const MAX_HISTORY = 100;
@@ -62,224 +64,66 @@ function loadHistory() {
62
64
  return Array.isArray(data) ? data.slice(-MAX_HISTORY) : [];
63
65
  }
64
66
  }
65
- catch { /* ignore */ }
67
+ catch { }
66
68
  return [];
67
69
  }
68
70
  function saveHistory(history) {
69
71
  try {
70
72
  const dir = path.dirname(HISTORY_PATH);
71
- if (!fs.existsSync(dir)) {
73
+ if (!fs.existsSync(dir))
72
74
  fs.mkdirSync(dir, { recursive: true });
73
- }
74
75
  fs.writeFileSync(HISTORY_PATH, JSON.stringify(history.slice(-MAX_HISTORY), null, 2));
75
76
  }
76
- catch { /* ignore */ }
77
- }
78
- // ============================================================================
79
- // /REVIEW COMMAND — Code review with score cards
80
- // ============================================================================
81
- async function handleReviewCommand(agent, arg) {
82
- // If arg is a file path, review that file; otherwise review the branch
83
- const thinking = new output_1.ThinkingAnimation('reviewing');
84
- thinking.start();
85
- try {
86
- if (arg) {
87
- // Review a specific file
88
- const filePath = path.resolve(arg);
89
- if (!fs.existsSync(filePath)) {
90
- thinking.stop();
91
- (0, output_1.printError)('File not found', filePath);
92
- return;
93
- }
94
- const content = fs.readFileSync(filePath, 'utf8');
95
- const prompt = `Do a thorough code review of this file. Score quality, security, performance, readability each out of 10. List bugs, improvements, and critical issues.\n\nFile: ${arg}\n\`\`\`\n${content.substring(0, 8000)}\n\`\`\``;
96
- const startTime = Date.now();
97
- let responseText = '';
98
- let headerPrinted = false;
99
- for await (const chunk of agent.chat(prompt)) {
100
- if (!headerPrinted) {
101
- thinking.stop();
102
- (0, output_1.printResponseHeader)();
103
- headerPrinted = true;
104
- }
105
- (0, output_1.streamResponseChunk)(chunk);
106
- responseText += chunk;
107
- }
108
- if (!headerPrinted)
109
- thinking.stop();
110
- const elapsed = Date.now() - startTime;
111
- if (headerPrinted) {
112
- console.log('');
113
- (0, output_1.printResponseFooter)(elapsed, Math.ceil(responseText.length / 4));
114
- }
115
- // Try to parse scores from response
116
- const scores = parseScores(responseText);
117
- if (scores.length > 0) {
118
- (0, output_1.printScoreCard)(arg, scores);
119
- }
120
- }
121
- else {
122
- // Review the entire branch using CodeReviewEngine
123
- thinking.stop();
124
- const { CodeReviewEngine } = await Promise.resolve().then(() => __importStar(require('../review/CodeReviewEngine')));
125
- const engine = new CodeReviewEngine();
126
- const result = await engine.review();
127
- console.log(CodeReviewEngine.formatForTerminal(result));
128
- }
129
- }
130
- catch (err) {
131
- thinking.stop();
132
- (0, output_1.printError)('Review failed', err.message);
133
- }
134
- }
135
- function parseScores(text) {
136
- const scores = [];
137
- const patterns = [
138
- { label: 'quality', regex: /quality[:\s]*(\d+)/i },
139
- { label: 'security', regex: /security[:\s]*(\d+)/i },
140
- { label: 'performance', regex: /performance[:\s]*(\d+)/i },
141
- { label: 'readability', regex: /readability[:\s]*(\d+)/i },
142
- ];
143
- for (const { label, regex } of patterns) {
144
- const match = text.match(regex);
145
- if (match) {
146
- scores.push({ label, score: Math.min(10, parseInt(match[1], 10)) });
147
- }
148
- }
149
- return scores;
150
- }
151
- // ============================================================================
152
- // /COMMIT COMMAND — AI-generated commit messages
153
- // ============================================================================
154
- async function handleCommitCommand(agent) {
155
- const thinking = new output_1.ThinkingAnimation('analyzing changes');
156
- thinking.start();
157
- try {
158
- // Get the diff
159
- let diff = '';
160
- try {
161
- diff = (0, child_process_1.execSync)('git diff --staged', { encoding: 'utf8', maxBuffer: 1024 * 1024 * 5 }).trim();
162
- }
163
- catch { /* no staged changes */ }
164
- if (!diff) {
165
- try {
166
- diff = (0, child_process_1.execSync)('git diff HEAD', { encoding: 'utf8', maxBuffer: 1024 * 1024 * 5 }).trim();
167
- }
168
- catch { /* no changes */ }
169
- }
170
- if (!diff) {
171
- thinking.stop();
172
- (0, output_1.printError)('No changes to commit', 'Stage files with git add, or make changes first.');
173
- return;
174
- }
175
- // Send to AI for commit message generation
176
- const prompt = `Generate a conventional commit message for this diff. Format: type(scope): description\nTypes: feat/fix/refactor/docs/style/test/chore. Be concise, one line only.\n\nDiff:\n${diff.substring(0, 6000)}`;
177
- let commitMessage = '';
178
- for await (const chunk of agent.chat(prompt)) {
179
- if (!commitMessage)
180
- thinking.stop();
181
- commitMessage += chunk;
182
- }
183
- // Clean up the message — extract the actual commit line
184
- commitMessage = commitMessage
185
- .split('\n')
186
- .find(line => /^(feat|fix|refactor|docs|style|test|chore|build|ci|perf)\(/.test(line.trim()))
187
- || commitMessage.split('\n')[0];
188
- commitMessage = commitMessage.replace(/^[`'"]+|[`'"]+$/g, '').trim();
189
- // Show commit box
190
- (0, output_1.printCommitBox)(commitMessage);
191
- // Ask user to confirm
192
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
193
- const answer = await new Promise((resolve) => {
194
- rl.question(` ${output_1.theme.dim('use this commit?')} ${output_1.theme.brand('›')} `, (a) => {
195
- resolve(a.trim().toLowerCase());
196
- rl.close();
197
- });
198
- });
199
- if (answer === 'y' || answer === 'yes') {
200
- try {
201
- // Stage all if nothing was staged
202
- try {
203
- const stagedCheck = (0, child_process_1.execSync)('git diff --staged --name-only', { encoding: 'utf8' }).trim();
204
- if (!stagedCheck) {
205
- (0, child_process_1.execSync)('git add -A', { encoding: 'utf8' });
206
- }
207
- }
208
- catch { /* ignore */ }
209
- (0, child_process_1.execSync)(`git commit -m "${commitMessage.replace(/"/g, '\\"')}"`, { encoding: 'utf8' });
210
- (0, output_1.printSuccess)('Committed successfully');
211
- }
212
- catch (e) {
213
- (0, output_1.printError)('Commit failed', e.stderr?.toString() || e.message);
214
- }
215
- }
216
- else if (answer === 'n' || answer === 'no') {
217
- const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
218
- const customMsg = await new Promise((resolve) => {
219
- rl2.question(` ${output_1.theme.dim('edit message?')} ${output_1.theme.brand('›')} `, (a) => {
220
- resolve(a.trim());
221
- rl2.close();
222
- });
223
- });
224
- if (customMsg) {
225
- try {
226
- try {
227
- const stagedCheck = (0, child_process_1.execSync)('git diff --staged --name-only', { encoding: 'utf8' }).trim();
228
- if (!stagedCheck)
229
- (0, child_process_1.execSync)('git add -A', { encoding: 'utf8' });
230
- }
231
- catch { /* */ }
232
- (0, child_process_1.execSync)(`git commit -m "${customMsg.replace(/"/g, '\\"')}"`, { encoding: 'utf8' });
233
- (0, output_1.printSuccess)('Committed successfully');
234
- }
235
- catch (e) {
236
- (0, output_1.printError)('Commit failed', e.stderr?.toString() || e.message);
237
- }
238
- }
239
- }
240
- }
241
- catch (err) {
242
- thinking.stop();
243
- (0, output_1.printError)('Commit command failed', err.message);
244
- }
77
+ catch { }
245
78
  }
246
79
  // ============================================================================
247
- // INTERACTIVE CHAT REPL — Premium v1.3.0
80
+ // INTERACTIVE CHAT REPL — Premium framed input bar
248
81
  // ============================================================================
249
82
  async function runInteractiveChat(agent, opts) {
250
83
  const history = loadHistory();
251
- const rl = readline.createInterface({
252
- input: process.stdin,
253
- output: process.stdout,
254
- terminal: true,
255
- history: history,
256
- historySize: MAX_HISTORY,
257
- });
258
- // Graceful exit
84
+ const sessionStartTime = Date.now();
85
+ let firstUserMessage = '';
86
+ const inputBar = new inputbar_1.InputBarManager();
87
+ // Set global command context
88
+ (0, registry_1.setCommandContext)({ agent, startTime: sessionStartTime });
259
89
  process.on('SIGINT', () => {
260
90
  saveHistory(history);
91
+ if (firstUserMessage) {
92
+ (0, conversations_1.updateConversation)(agent.sessions.currentSessionId, {
93
+ messageCount: agent.sessions.state.messages.length,
94
+ lastActive: new Date().toISOString(),
95
+ });
96
+ }
97
+ inputBar.destroy();
261
98
  (0, output_1.printExit)();
262
- rl.close();
263
99
  process.exit(0);
264
100
  });
265
- // Animated startup banner
266
- if (!opts.skipBanner) {
101
+ // Register conversation
102
+ (0, conversations_1.addConversation)({
103
+ id: agent.sessions.currentSessionId,
104
+ name: 'new session',
105
+ startedAt: new Date().toISOString(),
106
+ lastActive: new Date().toISOString(),
107
+ cwd: process.cwd(),
108
+ messageCount: 0,
109
+ });
110
+ // Show welcome screen or banner
111
+ if (!opts.skipBanner && !opts.skipWelcome) {
112
+ const token = await AuthManager_1.AuthManager.getToken();
113
+ const authData = token ? await AuthManager_1.AuthManager.loadAuthPublic() : null;
114
+ (0, welcome_1.showWelcomeScreen)(authData?.user?.email, opts.model, process.cwd());
115
+ }
116
+ else if (!opts.skipBanner) {
267
117
  const token = await AuthManager_1.AuthManager.getToken();
268
118
  const authData = token ? await AuthManager_1.AuthManager.loadAuthPublic() : null;
269
119
  await (0, output_1.showBanner)(output_1.CW_VERSION, authData?.user?.email, opts.model);
120
+ await (0, output_1.showSessionInfo)(process.cwd(), agent.sessions.currentSessionId);
270
121
  }
271
- // Session info with delay
272
- await (0, output_1.showSessionInfo)(process.cwd(), agent.sessions.currentSessionId);
273
- const askPrompt = () => {
274
- return new Promise((resolve) => {
275
- rl.question(` ${output_1.theme.dim('you')} ${output_1.theme.brand('›')} `, (answer) => {
276
- resolve(answer.trim());
277
- });
278
- });
279
- };
280
122
  // Main REPL loop
281
123
  while (true) {
282
- const input = await askPrompt();
124
+ // ── Draw framed input bar and get user input ──
125
+ (0, inputbar_1.drawTopBorder)();
126
+ const input = await inputBar.ask(history);
283
127
  if (!input)
284
128
  continue;
285
129
  // Save to history
@@ -287,59 +131,64 @@ async function runInteractiveChat(agent, opts) {
287
131
  if (history.length > MAX_HISTORY)
288
132
  history.shift();
289
133
  saveHistory(history);
290
- // ── Slash commands ──
291
- if (input === '/exit' || input === '/quit') {
292
- saveHistory(history);
293
- (0, output_1.printExit)();
294
- break;
295
- }
296
- if (input === '/clear') {
297
- console.clear();
298
- (0, output_1.showStaticBanner)(output_1.CW_VERSION);
299
- continue;
300
- }
301
- if (input === '/status') {
302
- await AuthManager_1.AuthManager.status();
303
- continue;
304
- }
305
- if (input === '/help') {
306
- (0, output_1.printHelp)();
307
- continue;
308
- }
309
- if (input === '/compact') {
310
- const msgCount = agent.sessions.state.messages.length;
311
- const totalChars = agent.sessions.state.messages.reduce((a, m) => a + (m.content?.length || 0), 0);
312
- (0, output_1.printCompact)(msgCount, Math.ceil(totalChars / 4), agent.sessions.currentSessionId);
313
- continue;
134
+ // Track first user message for conversation naming
135
+ if (!firstUserMessage) {
136
+ firstUserMessage = input;
137
+ (0, conversations_1.updateConversation)(agent.sessions.currentSessionId, {
138
+ name: (0, conversations_1.autoNameConversation)(input),
139
+ });
314
140
  }
315
- if (input === '/review' || input.startsWith('/review ')) {
316
- const arg = input.replace('/review', '').trim() || undefined;
317
- await handleReviewCommand(agent, arg);
141
+ // ── "/" alone show palette ──
142
+ if (input === '/') {
143
+ (0, palette_1.printStaticPalette)();
318
144
  continue;
319
145
  }
320
- if (input === '/commit') {
321
- await handleCommitCommand(agent);
146
+ // ── Slash commands → route through registry ──
147
+ if (input.startsWith('/')) {
148
+ const cmdName = input.slice(1).split(' ')[0];
149
+ const cmdArgs = input.slice(1 + cmdName.length).trim() || undefined;
150
+ const cmd = (0, registry_1.resolveCommand)(input);
151
+ if (cmd) {
152
+ try {
153
+ await cmd.handler(agent, cmdArgs);
154
+ }
155
+ catch (err) {
156
+ (0, output_1.printError)(`/${cmd.name} failed`, err.message);
157
+ }
158
+ continue;
159
+ }
160
+ // Unknown command — suggest closest
161
+ const suggestions = (0, registry_1.filterCommands)(cmdName);
162
+ if (suggestions.length > 0) {
163
+ console.log(output_1.theme.dim(` Unknown command. Did you mean:`));
164
+ for (const s of suggestions.slice(0, 3)) {
165
+ console.log(` ${output_1.theme.brand(`/${s.name}`)} ${output_1.theme.dim(s.description)}`);
166
+ }
167
+ console.log('');
168
+ }
169
+ else {
170
+ console.log(output_1.theme.dim(` Unknown command. Type / for all commands.`));
171
+ console.log('');
172
+ }
322
173
  continue;
323
174
  }
324
- // ── Show user message ──
325
- (0, output_1.printUserPrompt)(input);
326
- // ── Thinking animation ──
175
+ // ── Regular chat message ──
176
+ // Update status to "sending"
177
+ inputBar.showSending(input);
178
+ // Thinking animation
327
179
  const thinking = new output_1.ThinkingAnimation();
328
180
  thinking.start();
329
- const startTime = Date.now();
330
- let responseText = '';
331
- let headerPrinted = false;
181
+ // Stream the AI response
182
+ const renderer = new output_1.StreamRenderer();
183
+ let hasContent = false;
332
184
  try {
333
185
  for await (const chunk of agent.chat(input)) {
334
- // First chunk → stop thinking, open response box
335
- if (!headerPrinted) {
186
+ if (!hasContent) {
336
187
  thinking.stop();
337
- (0, output_1.printResponseHeader)();
338
- headerPrinted = true;
188
+ inputBar.showReceiving();
189
+ hasContent = true;
339
190
  }
340
- // Stream into the response box
341
- (0, output_1.streamResponseChunk)(chunk);
342
- responseText += chunk;
191
+ renderer.write(chunk);
343
192
  }
344
193
  }
345
194
  catch (err) {
@@ -347,19 +196,17 @@ async function runInteractiveChat(agent, opts) {
347
196
  (0, output_1.printError)(`Error: ${err.message}`, undefined, 'Check your network connection or try again');
348
197
  continue;
349
198
  }
350
- if (!headerPrinted) {
199
+ if (!hasContent)
351
200
  thinking.stop();
352
- }
353
- const elapsed = Date.now() - startTime;
354
- const tokenEst = Math.ceil(responseText.length / 4);
355
- if (headerPrinted) {
356
- console.log(''); // newline after streamed content
357
- (0, output_1.printResponseFooter)(elapsed, tokenEst);
358
- }
359
- console.log('');
201
+ renderer.finish();
202
+ // Show success briefly, then reset
203
+ inputBar.showSuccess();
204
+ inputBar.reset();
205
+ // Update conversation message count
206
+ (0, conversations_1.updateConversation)(agent.sessions.currentSessionId, {
207
+ messageCount: agent.sessions.state.messages.length,
208
+ });
360
209
  }
361
- rl.close();
362
- process.exit(0);
363
210
  }
364
211
  // ============================================================================
365
212
  // ONE-SHOT MODE
@@ -371,19 +218,19 @@ async function runOneShot(agent, prompt, format) {
371
218
  process.stdout.write(chunk);
372
219
  outputText += chunk;
373
220
  }
374
- if (format === 'json') {
221
+ if (format === 'json')
375
222
  console.log(JSON.stringify({ response: outputText }));
376
- }
377
223
  process.exit(0);
378
224
  }
379
225
  // ============================================================================
380
- // HEADLESS / REMOTE CONTROL SERVER
226
+ // HEADLESS SERVER
381
227
  // ============================================================================
382
228
  function startHeadlessServer(port) {
383
229
  const server = http.createServer();
384
230
  const wss = new ws_1.WebSocketServer({ server });
385
231
  wss.on('connection', (ws) => {
386
- console.log('[Remote-Control] IDE Connected via WebSocket');
232
+ if (process.env.CW_VERBOSE)
233
+ console.log('[Remote-Control] IDE Connected');
387
234
  const remoteAgent = new CoWorkerAgent_1.CoWorkerAgent({ model: 'helix-1.2', role: 'General' });
388
235
  ws.on('message', async (data) => {
389
236
  const { prompt, sessionId } = JSON.parse(data.toString());
@@ -395,12 +242,10 @@ function startHeadlessServer(port) {
395
242
  ws.send(JSON.stringify({ type: 'done' }));
396
243
  });
397
244
  });
398
- server.listen(port, () => {
399
- console.log(`[Remote-Control] Listening on port ${port}...`);
400
- });
245
+ server.listen(port, () => console.log(`[Remote-Control] Listening on port ${port}...`));
401
246
  }
402
247
  // ============================================================================
403
- // CLI MAIN — Manual arg parsing
248
+ // CLI MAIN
404
249
  // ============================================================================
405
250
  async function bootstrapCoWorker() {
406
251
  const args = process.argv.slice(2);
@@ -437,6 +282,8 @@ async function bootstrapCoWorker() {
437
282
  console.log(` ${output_1.theme.code('status')} ${output_1.theme.dim('Show auth & account status')}`);
438
283
  console.log(` ${output_1.theme.code('review [file]')} ${output_1.theme.dim('Code review a file or current branch')}`);
439
284
  console.log(` ${output_1.theme.code('commit')} ${output_1.theme.dim('AI-generated conventional commit')}`);
285
+ console.log(` ${output_1.theme.code('doctor')} ${output_1.theme.dim('Diagnose installation')}`);
286
+ console.log(` ${output_1.theme.code('init')} ${output_1.theme.dim('Create .coworker.md')}`);
440
287
  console.log(` ${output_1.theme.code('update')} ${output_1.theme.dim('Auto-update the CoWorker CLI')}\n`);
441
288
  console.log(` ${output_1.theme.brand('Options:')}`);
442
289
  console.log(` ${output_1.theme.code('-p, --print')} ${output_1.theme.dim('One-shot mode (process and exit)')}`);
@@ -446,9 +293,15 @@ async function bootstrapCoWorker() {
446
293
  console.log(` ${output_1.theme.code('--model <name>')} ${output_1.theme.dim('Override model (default: helix-1.2)')}`);
447
294
  console.log(` ${output_1.theme.code('--dangerously-skip-permissions')} ${output_1.theme.dim('Bypass all security policies')}`);
448
295
  console.log(` ${output_1.theme.code('--remote-control <port>')} ${output_1.theme.dim('Start WebSocket server for IDE')}`);
449
- console.log(` ${output_1.theme.code('--no-banner')} ${output_1.theme.dim('Skip the animated startup banner')}`);
296
+ console.log(` ${output_1.theme.code('--no-banner')} ${output_1.theme.dim('Skip the startup screen')}`);
450
297
  console.log(` ${output_1.theme.code('-V, --version')} ${output_1.theme.dim('Output version number')}`);
451
298
  console.log(` ${output_1.theme.code('-h, --help')} ${output_1.theme.dim('Display help')}\n`);
299
+ console.log(` ${output_1.theme.brand('Slash Commands (in REPL):')}`);
300
+ for (const cmd of registry_1.COMMANDS.slice(0, 10)) {
301
+ const pad = ' '.repeat(Math.max(1, 20 - cmd.name.length));
302
+ console.log(` ${output_1.theme.code(`/${cmd.name}`)}${pad}${output_1.theme.dim(cmd.description)}`);
303
+ }
304
+ console.log(` ${output_1.theme.dim(`... and ${registry_1.COMMANDS.length - 10} more. Type / in REPL for full list.`)}\n`);
452
305
  return;
453
306
  }
454
307
  // ── Parse flags ──
@@ -511,9 +364,16 @@ async function bootstrapCoWorker() {
511
364
  return;
512
365
  }
513
366
  }
367
+ // ── Workspace Trust Check ──
368
+ const cwd = process.cwd();
369
+ const trusted = await (0, output_1.checkWorkspaceTrust)(cwd);
370
+ if (!trusted) {
371
+ console.log(output_1.theme.dim('\n Exiting — workspace not trusted.\n'));
372
+ process.exit(0);
373
+ }
514
374
  // ── Worktree isolation ──
515
375
  if (worktree) {
516
- console.log(output_1.theme.dim(' [Isolating Agent...] Creating Git Worktree'));
376
+ console.log(output_1.theme.dim(' Creating Git Worktree...'));
517
377
  try {
518
378
  (0, child_process_1.spawnSync)('git', ['worktree', 'add', '../coworker-task', '-b', `coworker/temp-${Date.now()}`]);
519
379
  process.chdir('../coworker-task');
@@ -531,22 +391,19 @@ async function bootstrapCoWorker() {
531
391
  }
532
392
  // ── Boot agent ──
533
393
  const agent = new CoWorkerAgent_1.CoWorkerAgent({ model, role: 'General' });
534
- if (resumeSession) {
394
+ if (resumeSession)
535
395
  agent.sessions.currentSessionId = resumeSession;
536
- }
537
396
  await agent.boot();
538
397
  if (skipPermissions) {
539
398
  console.log(output_1.theme.error(' ⚠️ WARNING: All permission checks are DISABLED.'));
540
399
  agent.permissions = { intercept: async () => true };
541
400
  }
542
- // ── Handle direct subcommands: coworker review / coworker commit ──
543
- if (command === 'review') {
544
- const fileArg = args[1];
545
- await handleReviewCommand(agent, fileArg);
546
- process.exit(0);
547
- }
548
- if (command === 'commit') {
549
- await handleCommitCommand(agent);
401
+ // ── Direct subcommands ──
402
+ if (command === 'review' || command === 'commit' || command === 'doctor' || command === 'init') {
403
+ (0, registry_1.setCommandContext)({ agent, startTime: Date.now() });
404
+ const cmd = (0, registry_1.resolveCommand)(`/${command}`);
405
+ if (cmd)
406
+ await cmd.handler(agent, args[1]);
550
407
  process.exit(0);
551
408
  }
552
409
  // ── Execute ──
@@ -555,43 +412,39 @@ async function bootstrapCoWorker() {
555
412
  await runOneShot(agent, initialPrompt, outputFormat);
556
413
  }
557
414
  else if (initialPrompt) {
558
- // Run prompt inline, then drop into REPL
415
+ // Show welcome, run initial prompt, then drop into REPL
559
416
  if (!noBanner) {
560
417
  const token = await AuthManager_1.AuthManager.getToken();
561
418
  const authData = token ? await AuthManager_1.AuthManager.loadAuthPublic() : null;
562
- await (0, output_1.showBanner)(output_1.CW_VERSION, authData?.user?.email, model);
419
+ (0, welcome_1.showWelcomeScreen)(authData?.user?.email, model, process.cwd());
563
420
  }
564
- (0, output_1.printUserPrompt)(initialPrompt);
421
+ // Run the initial prompt with framed display
422
+ (0, inputbar_1.drawTopBorder)();
423
+ console.log(output_1.theme.brand(' ◆ ') + output_1.theme.white(initialPrompt));
424
+ (0, inputbar_1.drawBottomBorder)();
425
+ (0, inputbar_1.drawStatusLine)('sending');
565
426
  const thinking = new output_1.ThinkingAnimation();
566
427
  thinking.start();
567
- const startTime = Date.now();
568
- let responseText = '';
569
- let headerPrinted = false;
428
+ const renderer = new output_1.StreamRenderer();
429
+ let hasContent = false;
570
430
  for await (const chunk of agent.chat(initialPrompt)) {
571
- if (!headerPrinted) {
431
+ if (!hasContent) {
572
432
  thinking.stop();
573
- (0, output_1.printResponseHeader)();
574
- headerPrinted = true;
433
+ hasContent = true;
575
434
  }
576
- (0, output_1.streamResponseChunk)(chunk);
577
- responseText += chunk;
435
+ renderer.write(chunk);
578
436
  }
579
- if (!headerPrinted)
437
+ if (!hasContent)
580
438
  thinking.stop();
581
- const elapsed = Date.now() - startTime;
582
- if (headerPrinted) {
583
- console.log('');
584
- (0, output_1.printResponseFooter)(elapsed, Math.ceil(responseText.length / 4));
585
- }
439
+ renderer.finish();
586
440
  console.log('');
587
- await runInteractiveChat(agent, { model, skipBanner: true });
441
+ await runInteractiveChat(agent, { model, skipBanner: true, skipWelcome: true });
588
442
  }
589
443
  else {
590
- // DEFAULT: No args → interactive chat REPL
444
+ // DEFAULT: No args → interactive REPL
591
445
  await runInteractiveChat(agent, { model, skipBanner: noBanner });
592
446
  }
593
447
  }
594
- // Self-execute
595
448
  if (require.main === module) {
596
449
  bootstrapCoWorker();
597
450
  }