@sylix/coworker 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/cli/index.d.ts.map +1 -1
  2. package/dist/cli/index.js +184 -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 -5
  37. package/dist/core/CoWorkerAgent.d.ts.map +1 -1
  38. package/dist/core/CoWorkerAgent.js +171 -52
  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.d.ts +7 -2
  43. package/dist/skills/HookAndSkillManager.d.ts.map +1 -1
  44. package/dist/skills/HookAndSkillManager.js +35 -8
  45. package/dist/skills/HookAndSkillManager.js.map +1 -1
  46. package/dist/skills/defaults/brainstorming.md +43 -0
  47. package/dist/skills/defaults/commit.md +37 -0
  48. package/dist/skills/defaults/executing-plans.md +41 -0
  49. package/dist/skills/defaults/finishing-a-development-branch.md +35 -0
  50. package/dist/skills/defaults/receiving-code-review.md +40 -0
  51. package/dist/skills/defaults/requesting-code-review.md +43 -0
  52. package/dist/skills/defaults/review.md +50 -0
  53. package/dist/skills/defaults/subagent-driven-development.md +55 -0
  54. package/dist/skills/defaults/systematic-debugging.md +93 -0
  55. package/dist/skills/defaults/test-driven-development.md +97 -0
  56. package/dist/skills/defaults/using-git-worktrees.md +36 -0
  57. package/dist/skills/defaults/verification-before-completion.md +47 -0
  58. package/dist/skills/defaults/writing-plans.md +84 -0
  59. package/dist/skills/defaults/writing-skills.md +46 -0
  60. package/dist/tools/NativeTools.d.ts +0 -4
  61. package/dist/tools/NativeTools.d.ts.map +1 -1
  62. package/dist/tools/NativeTools.js +50 -10
  63. package/dist/tools/NativeTools.js.map +1 -1
  64. package/dist/utils/conversations.d.ts +14 -0
  65. package/dist/utils/conversations.d.ts.map +1 -0
  66. package/dist/utils/conversations.js +100 -0
  67. package/dist/utils/conversations.js.map +1 -0
  68. package/dist/utils/inputbar.d.ts +87 -0
  69. package/dist/utils/inputbar.d.ts.map +1 -0
  70. package/dist/utils/inputbar.js +263 -0
  71. package/dist/utils/inputbar.js.map +1 -0
  72. package/dist/utils/output.d.ts +48 -42
  73. package/dist/utils/output.d.ts.map +1 -1
  74. package/dist/utils/output.js +245 -186
  75. package/dist/utils/output.js.map +1 -1
  76. package/dist/utils/palette.d.ts +25 -0
  77. package/dist/utils/palette.d.ts.map +1 -0
  78. package/dist/utils/palette.js +92 -0
  79. package/dist/utils/palette.js.map +1 -0
  80. package/dist/utils/welcome.d.ts +2 -0
  81. package/dist/utils/welcome.d.ts.map +1 -0
  82. package/dist/utils/welcome.js +130 -0
  83. package/dist/utils/welcome.js.map +1 -0
  84. package/package.json +2 -2
@@ -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":";AAgSA,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
+ catch { }
77
78
  }
78
79
  // ============================================================================
79
- // /REVIEW COMMANDCode 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
- }
245
- }
246
- // ============================================================================
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,104 @@ 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
+ // ── Handle dynamic skills (Superpowers) ──
161
+ const skill = agent.hooks.loadedSkills.get(cmdName);
162
+ if (skill) {
163
+ agent.hooks.activateSkill(cmdName);
164
+ console.log(`\n ${output_1.theme.brand('◈')} ${output_1.theme.brand(`Activating ${skill.name}`)} superpower...`);
165
+ console.log(` ${output_1.theme.dim(skill.description)}`);
166
+ console.log('');
167
+ // Trigger an immediate agent response for the skill
168
+ const initialText = `I have activated the ${skill.name} superpower. How should we proceed?`;
169
+ console.log(` ${output_1.theme.dim('you')} ${output_1.theme.brand('\u203a')} ${output_1.theme.white(`[Skill Activation: ${skill.name}]`)}`);
170
+ console.log('');
171
+ const thinking = new output_1.ThinkingAnimation();
172
+ thinking.start();
173
+ const renderer = new output_1.StreamRenderer();
174
+ let hasContent = false;
175
+ try {
176
+ for await (const chunk of agent.chat(`I just activated the ${skill.name} skill. Please start the workflow.`)) {
177
+ if (!hasContent) {
178
+ thinking.stop();
179
+ hasContent = true;
180
+ }
181
+ renderer.write(chunk);
182
+ }
183
+ }
184
+ catch (err) {
185
+ thinking.stop();
186
+ (0, output_1.printError)(`Skill execution failed`, err.message);
187
+ }
188
+ if (!hasContent)
189
+ thinking.stop();
190
+ renderer.finish();
191
+ inputBar.reset();
192
+ continue;
193
+ }
194
+ // Unknown command — suggest closest
195
+ const suggestions = (0, registry_1.filterCommands)(cmdName);
196
+ if (suggestions.length > 0) {
197
+ console.log(output_1.theme.dim(` Unknown command. Did you mean:`));
198
+ for (const s of suggestions.slice(0, 3)) {
199
+ console.log(` ${output_1.theme.brand(`/${s.name}`)} ${output_1.theme.dim(s.description)}`);
200
+ }
201
+ console.log('');
202
+ }
203
+ else {
204
+ console.log(output_1.theme.dim(` Unknown command. Type / for all commands.`));
205
+ console.log('');
206
+ }
322
207
  continue;
323
208
  }
324
- // ── Show user message ──
325
- (0, output_1.printUserPrompt)(input);
326
- // ── Thinking animation ──
209
+ // ── Regular chat message ──
210
+ // Clear the input frame drawn by ask() (bottom border + status)
211
+ process.stdout.write('\x1b[1A\x1b[2K'); // clear status line
212
+ process.stdout.write('\x1b[1A\x1b[2K'); // clear bottom border
213
+ process.stdout.write('\x1b[1A\x1b[2K'); // clear input line
214
+ process.stdout.write('\x1b[1A\x1b[2K'); // clear top border
215
+ // Show user message above
216
+ console.log('');
217
+ console.log(` ${output_1.theme.dim('you')} ${output_1.theme.brand('\u203a')} ${input}`);
218
+ console.log('');
219
+ // Thinking animation
327
220
  const thinking = new output_1.ThinkingAnimation();
328
221
  thinking.start();
329
- const startTime = Date.now();
330
- let responseText = '';
331
- let headerPrinted = false;
222
+ // Stream the AI response
223
+ const renderer = new output_1.StreamRenderer();
224
+ let hasContent = false;
332
225
  try {
333
226
  for await (const chunk of agent.chat(input)) {
334
- // First chunk → stop thinking, open response box
335
- if (!headerPrinted) {
227
+ if (!hasContent) {
336
228
  thinking.stop();
337
- (0, output_1.printResponseHeader)();
338
- headerPrinted = true;
229
+ hasContent = true;
339
230
  }
340
- // Stream into the response box
341
- (0, output_1.streamResponseChunk)(chunk);
342
- responseText += chunk;
231
+ renderer.write(chunk);
343
232
  }
344
233
  }
345
234
  catch (err) {
@@ -347,19 +236,16 @@ async function runInteractiveChat(agent, opts) {
347
236
  (0, output_1.printError)(`Error: ${err.message}`, undefined, 'Check your network connection or try again');
348
237
  continue;
349
238
  }
350
- if (!headerPrinted) {
239
+ if (!hasContent)
351
240
  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('');
241
+ renderer.finish();
242
+ // Reset for next input (no "done" message — the ╵ footer is sufficient)
243
+ inputBar.reset();
244
+ // Update conversation message count
245
+ (0, conversations_1.updateConversation)(agent.sessions.currentSessionId, {
246
+ messageCount: agent.sessions.state.messages.length,
247
+ });
360
248
  }
361
- rl.close();
362
- process.exit(0);
363
249
  }
364
250
  // ============================================================================
365
251
  // ONE-SHOT MODE
@@ -371,19 +257,19 @@ async function runOneShot(agent, prompt, format) {
371
257
  process.stdout.write(chunk);
372
258
  outputText += chunk;
373
259
  }
374
- if (format === 'json') {
260
+ if (format === 'json')
375
261
  console.log(JSON.stringify({ response: outputText }));
376
- }
377
262
  process.exit(0);
378
263
  }
379
264
  // ============================================================================
380
- // HEADLESS / REMOTE CONTROL SERVER
265
+ // HEADLESS SERVER
381
266
  // ============================================================================
382
267
  function startHeadlessServer(port) {
383
268
  const server = http.createServer();
384
269
  const wss = new ws_1.WebSocketServer({ server });
385
270
  wss.on('connection', (ws) => {
386
- console.log('[Remote-Control] IDE Connected via WebSocket');
271
+ if (process.env.CW_VERBOSE)
272
+ console.log('[Remote-Control] IDE Connected');
387
273
  const remoteAgent = new CoWorkerAgent_1.CoWorkerAgent({ model: 'helix-1.2', role: 'General' });
388
274
  ws.on('message', async (data) => {
389
275
  const { prompt, sessionId } = JSON.parse(data.toString());
@@ -395,12 +281,10 @@ function startHeadlessServer(port) {
395
281
  ws.send(JSON.stringify({ type: 'done' }));
396
282
  });
397
283
  });
398
- server.listen(port, () => {
399
- console.log(`[Remote-Control] Listening on port ${port}...`);
400
- });
284
+ server.listen(port, () => console.log(`[Remote-Control] Listening on port ${port}...`));
401
285
  }
402
286
  // ============================================================================
403
- // CLI MAIN — Manual arg parsing
287
+ // CLI MAIN
404
288
  // ============================================================================
405
289
  async function bootstrapCoWorker() {
406
290
  const args = process.argv.slice(2);
@@ -437,6 +321,8 @@ async function bootstrapCoWorker() {
437
321
  console.log(` ${output_1.theme.code('status')} ${output_1.theme.dim('Show auth & account status')}`);
438
322
  console.log(` ${output_1.theme.code('review [file]')} ${output_1.theme.dim('Code review a file or current branch')}`);
439
323
  console.log(` ${output_1.theme.code('commit')} ${output_1.theme.dim('AI-generated conventional commit')}`);
324
+ console.log(` ${output_1.theme.code('doctor')} ${output_1.theme.dim('Diagnose installation')}`);
325
+ console.log(` ${output_1.theme.code('init')} ${output_1.theme.dim('Create .coworker.md')}`);
440
326
  console.log(` ${output_1.theme.code('update')} ${output_1.theme.dim('Auto-update the CoWorker CLI')}\n`);
441
327
  console.log(` ${output_1.theme.brand('Options:')}`);
442
328
  console.log(` ${output_1.theme.code('-p, --print')} ${output_1.theme.dim('One-shot mode (process and exit)')}`);
@@ -446,9 +332,15 @@ async function bootstrapCoWorker() {
446
332
  console.log(` ${output_1.theme.code('--model <name>')} ${output_1.theme.dim('Override model (default: helix-1.2)')}`);
447
333
  console.log(` ${output_1.theme.code('--dangerously-skip-permissions')} ${output_1.theme.dim('Bypass all security policies')}`);
448
334
  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')}`);
335
+ console.log(` ${output_1.theme.code('--no-banner')} ${output_1.theme.dim('Skip the startup screen')}`);
450
336
  console.log(` ${output_1.theme.code('-V, --version')} ${output_1.theme.dim('Output version number')}`);
451
337
  console.log(` ${output_1.theme.code('-h, --help')} ${output_1.theme.dim('Display help')}\n`);
338
+ console.log(` ${output_1.theme.brand('Slash Commands (in REPL):')}`);
339
+ for (const cmd of registry_1.COMMANDS.slice(0, 10)) {
340
+ const pad = ' '.repeat(Math.max(1, 20 - cmd.name.length));
341
+ console.log(` ${output_1.theme.code(`/${cmd.name}`)}${pad}${output_1.theme.dim(cmd.description)}`);
342
+ }
343
+ console.log(` ${output_1.theme.dim(`... and ${registry_1.COMMANDS.length - 10} more. Type / in REPL for full list.`)}\n`);
452
344
  return;
453
345
  }
454
346
  // ── Parse flags ──
@@ -511,9 +403,16 @@ async function bootstrapCoWorker() {
511
403
  return;
512
404
  }
513
405
  }
406
+ // ── Workspace Trust Check ──
407
+ const cwd = process.cwd();
408
+ const trusted = await (0, output_1.checkWorkspaceTrust)(cwd);
409
+ if (!trusted) {
410
+ console.log(output_1.theme.dim('\n Exiting — workspace not trusted.\n'));
411
+ process.exit(0);
412
+ }
514
413
  // ── Worktree isolation ──
515
414
  if (worktree) {
516
- console.log(output_1.theme.dim(' [Isolating Agent...] Creating Git Worktree'));
415
+ console.log(output_1.theme.dim(' Creating Git Worktree...'));
517
416
  try {
518
417
  (0, child_process_1.spawnSync)('git', ['worktree', 'add', '../coworker-task', '-b', `coworker/temp-${Date.now()}`]);
519
418
  process.chdir('../coworker-task');
@@ -531,22 +430,19 @@ async function bootstrapCoWorker() {
531
430
  }
532
431
  // ── Boot agent ──
533
432
  const agent = new CoWorkerAgent_1.CoWorkerAgent({ model, role: 'General' });
534
- if (resumeSession) {
433
+ if (resumeSession)
535
434
  agent.sessions.currentSessionId = resumeSession;
536
- }
537
435
  await agent.boot();
538
436
  if (skipPermissions) {
539
437
  console.log(output_1.theme.error(' ⚠️ WARNING: All permission checks are DISABLED.'));
540
438
  agent.permissions = { intercept: async () => true };
541
439
  }
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);
440
+ // ── Direct subcommands ──
441
+ if (command === 'review' || command === 'commit' || command === 'doctor' || command === 'init') {
442
+ (0, registry_1.setCommandContext)({ agent, startTime: Date.now() });
443
+ const cmd = (0, registry_1.resolveCommand)(`/${command}`);
444
+ if (cmd)
445
+ await cmd.handler(agent, args[1]);
550
446
  process.exit(0);
551
447
  }
552
448
  // ── Execute ──
@@ -555,43 +451,39 @@ async function bootstrapCoWorker() {
555
451
  await runOneShot(agent, initialPrompt, outputFormat);
556
452
  }
557
453
  else if (initialPrompt) {
558
- // Run prompt inline, then drop into REPL
454
+ // Show welcome, run initial prompt, then drop into REPL
559
455
  if (!noBanner) {
560
456
  const token = await AuthManager_1.AuthManager.getToken();
561
457
  const authData = token ? await AuthManager_1.AuthManager.loadAuthPublic() : null;
562
- await (0, output_1.showBanner)(output_1.CW_VERSION, authData?.user?.email, model);
458
+ (0, welcome_1.showWelcomeScreen)(authData?.user?.email, model, process.cwd());
563
459
  }
564
- (0, output_1.printUserPrompt)(initialPrompt);
460
+ // Run the initial prompt with framed display
461
+ (0, inputbar_1.drawTopBorder)();
462
+ console.log(output_1.theme.brand(' ◆ ') + output_1.theme.white(initialPrompt));
463
+ (0, inputbar_1.drawBottomBorder)();
464
+ (0, inputbar_1.drawStatusLine)('sending');
565
465
  const thinking = new output_1.ThinkingAnimation();
566
466
  thinking.start();
567
- const startTime = Date.now();
568
- let responseText = '';
569
- let headerPrinted = false;
467
+ const renderer = new output_1.StreamRenderer();
468
+ let hasContent = false;
570
469
  for await (const chunk of agent.chat(initialPrompt)) {
571
- if (!headerPrinted) {
470
+ if (!hasContent) {
572
471
  thinking.stop();
573
- (0, output_1.printResponseHeader)();
574
- headerPrinted = true;
472
+ hasContent = true;
575
473
  }
576
- (0, output_1.streamResponseChunk)(chunk);
577
- responseText += chunk;
474
+ renderer.write(chunk);
578
475
  }
579
- if (!headerPrinted)
476
+ if (!hasContent)
580
477
  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
- }
478
+ renderer.finish();
586
479
  console.log('');
587
- await runInteractiveChat(agent, { model, skipBanner: true });
480
+ await runInteractiveChat(agent, { model, skipBanner: true, skipWelcome: true });
588
481
  }
589
482
  else {
590
- // DEFAULT: No args → interactive chat REPL
483
+ // DEFAULT: No args → interactive REPL
591
484
  await runInteractiveChat(agent, { model, skipBanner: noBanner });
592
485
  }
593
486
  }
594
- // Self-execute
595
487
  if (require.main === module) {
596
488
  bootstrapCoWorker();
597
489
  }