remote-opencode 1.0.10 β†’ 1.1.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.
package/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  > Control your AI coding assistant from anywhere β€” your phone, tablet, or another computer.
4
4
 
5
+ ![npm](https://img.shields.io/npm/dt/remote-opencode) πŸ“¦ Used by developers worldwide β€” **800+ weekly downloads** on npm
6
+
7
+
5
8
  <div align="center">
6
9
  <img width="1024" alt="Gemini_Generated_Image_47d5gq47d5gq47d5" src="https://github.com/user-attachments/assets/1defa11d-6195-4a9c-956b-4f87470f6393" />
7
10
  </div>
@@ -561,10 +564,4 @@ MIT
561
564
 
562
565
  ## Contributing
563
566
 
564
- Contributions are welcome! Please feel free to submit a Pull Request.
565
-
566
- 1. Fork the repository
567
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
568
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
569
- 4. Push to the branch (`git push origin feature/amazing-feature`)
570
- 5. Open a Pull Request
567
+ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) before submitting a Pull Request.
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { parseSSEEvent, extractTextFromPart, accumulateText, formatOutput, stripAnsi } from '../utils/messageFormatter.js';
2
+ import { parseSSEEvent, extractTextFromPart, accumulateText, formatOutput, stripAnsi, buildContextHeader } from '../utils/messageFormatter.js';
3
3
  describe('messageFormatter', () => {
4
4
  describe('stripAnsi', () => {
5
5
  it('should remove ANSI escape codes', () => {
@@ -63,6 +63,20 @@ describe('messageFormatter', () => {
63
63
  expect(accumulateText('', 'Hello')).toBe('Hello');
64
64
  });
65
65
  });
66
+ describe('buildContextHeader', () => {
67
+ it('should format branch name and model name', () => {
68
+ const result = buildContextHeader('feature/dark-mode', 'claude-sonnet-4-20250514');
69
+ expect(result).toBe('🌿 `feature/dark-mode` Β· πŸ€– `claude-sonnet-4-20250514`');
70
+ });
71
+ it('should handle default model', () => {
72
+ const result = buildContextHeader('main', 'default');
73
+ expect(result).toBe('🌿 `main` Β· πŸ€– `default`');
74
+ });
75
+ it('should handle auto-generated branch names', () => {
76
+ const result = buildContextHeader('auto/abc12345-1738600000000', 'default');
77
+ expect(result).toBe('🌿 `auto/abc12345-1738600000000` Β· πŸ€– `default`');
78
+ });
79
+ });
66
80
  describe('formatOutput (existing functionality)', () => {
67
81
  it('should work for OpenCode JSON output with newlines preserved', () => {
68
82
  const buffer = JSON.stringify({ type: 'text', part: { text: 'Hello' } }) + '\n' +
@@ -4,7 +4,7 @@ import * as sessionManager from './sessionManager.js';
4
4
  import * as serveManager from './serveManager.js';
5
5
  import * as worktreeManager from './worktreeManager.js';
6
6
  import { SSEClient } from './sseClient.js';
7
- import { formatOutput } from '../utils/messageFormatter.js';
7
+ import { formatOutput, buildContextHeader } from '../utils/messageFormatter.js';
8
8
  import { processNextInQueue } from './queueManager.js';
9
9
  export async function runPrompt(channel, threadId, prompt, parentChannelId) {
10
10
  const projectPath = dataStore.getChannelProjectPath(parentChannelId);
@@ -52,7 +52,9 @@ export async function runPrompt(channel, threadId, prompt, parentChannelId) {
52
52
  }
53
53
  const effectivePath = worktreeMapping?.worktreePath ?? projectPath;
54
54
  const preferredModel = dataStore.getChannelModel(parentChannelId);
55
- const modelDisplay = preferredModel ? `\`${preferredModel}\`` : 'default';
55
+ const modelDisplay = preferredModel ? `${preferredModel}` : 'default';
56
+ const branchName = worktreeMapping?.branchName ?? await worktreeManager.getCurrentBranch(effectivePath) ?? 'main';
57
+ const contextHeader = buildContextHeader(branchName, modelDisplay);
56
58
  const buttons = new ActionRowBuilder()
57
59
  .addComponents(new ButtonBuilder()
58
60
  .setCustomId(`interrupt_${threadId}`)
@@ -61,7 +63,7 @@ export async function runPrompt(channel, threadId, prompt, parentChannelId) {
61
63
  let streamMessage;
62
64
  try {
63
65
  streamMessage = await channel.send({
64
- content: `πŸ“Œ **Prompt**: ${prompt}\n\nπŸš€ Starting OpenCode server... (Model: ${modelDisplay})`,
66
+ content: `${contextHeader}\nπŸ“Œ **Prompt**: ${prompt}\n\nπŸš€ Starting OpenCode server...`,
65
67
  components: [buttons]
66
68
  });
67
69
  }
@@ -84,7 +86,7 @@ export async function runPrompt(channel, threadId, prompt, parentChannelId) {
84
86
  };
85
87
  try {
86
88
  port = await serveManager.spawnServe(effectivePath, preferredModel);
87
- await updateStreamMessage(`πŸ“Œ **Prompt**: ${prompt}\n\n⏳ Waiting for OpenCode server... (Model: ${modelDisplay})`, [buttons]);
89
+ await updateStreamMessage(`${contextHeader}\nπŸ“Œ **Prompt**: ${prompt}\n\n⏳ Waiting for OpenCode server...`, [buttons]);
88
90
  await serveManager.waitForReady(port, 30000, effectivePath, preferredModel);
89
91
  const settings = dataStore.getQueueSettings(threadId);
90
92
  // If fresh context is enabled, we always clear the session before starting
@@ -127,7 +129,7 @@ export async function runPrompt(channel, threadId, prompt, parentChannelId) {
127
129
  .setLabel('⏸️ Interrupt')
128
130
  .setStyle(ButtonStyle.Secondary)
129
131
  .setDisabled(true));
130
- await updateStreamMessage(`πŸ“Œ **Prompt**: ${prompt}\n\n\`\`\`\n${formatted}\n\`\`\``, [disabledButtons]);
132
+ await updateStreamMessage(`${contextHeader}\nπŸ“Œ **Prompt**: ${prompt}\n\n\`\`\`\n${formatted}\n\`\`\``, [disabledButtons]);
131
133
  await channel.send({ content: 'βœ… Done' });
132
134
  sseClient.disconnect();
133
135
  sessionManager.clearSseClient(threadId);
@@ -146,7 +148,7 @@ export async function runPrompt(channel, threadId, prompt, parentChannelId) {
146
148
  }
147
149
  (async () => {
148
150
  try {
149
- await updateStreamMessage(`πŸ“Œ **Prompt**: ${prompt}\n\n❌ Connection error: ${error.message}`, []);
151
+ await updateStreamMessage(`${contextHeader}\nπŸ“Œ **Prompt**: ${prompt}\n\n❌ Connection error: ${error.message}`, []);
150
152
  sseClient.disconnect();
151
153
  sessionManager.clearSseClient(threadId);
152
154
  const settings = dataStore.getQueueSettings(threadId);
@@ -170,13 +172,13 @@ export async function runPrompt(channel, threadId, prompt, parentChannelId) {
170
172
  const newContent = formatted || 'Processing...';
171
173
  if (newContent !== lastContent || tick % 2 === 0) {
172
174
  lastContent = newContent;
173
- await updateStreamMessage(`πŸ“Œ **Prompt**: ${prompt}\n\n${spinnerChar} **Running...**\n\`\`\`\n${newContent}\n\`\`\``, [buttons]);
175
+ await updateStreamMessage(`${contextHeader}\nπŸ“Œ **Prompt**: ${prompt}\n\n${spinnerChar} **Running...**\n\`\`\`\n${newContent}\n\`\`\``, [buttons]);
174
176
  }
175
177
  }
176
178
  catch {
177
179
  }
178
180
  }, 1000);
179
- await updateStreamMessage(`πŸ“Œ **Prompt**: ${prompt}\n\nπŸ“ Sending prompt...`, [buttons]);
181
+ await updateStreamMessage(`${contextHeader}\nπŸ“Œ **Prompt**: ${prompt}\n\nπŸ“ Sending prompt...`, [buttons]);
180
182
  await sessionManager.sendPrompt(port, sessionId, prompt, preferredModel);
181
183
  }
182
184
  catch (error) {
@@ -184,7 +186,7 @@ export async function runPrompt(channel, threadId, prompt, parentChannelId) {
184
186
  clearInterval(updateInterval);
185
187
  }
186
188
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
187
- await updateStreamMessage(`πŸ“Œ **Prompt**: ${prompt}\n\n❌ OpenCode execution failed: ${errorMessage}`, []);
189
+ await updateStreamMessage(`${contextHeader}\nπŸ“Œ **Prompt**: ${prompt}\n\n❌ OpenCode execution failed: ${errorMessage}`, []);
188
190
  const client = sessionManager.getSseClient(threadId);
189
191
  if (client) {
190
192
  client.disconnect();
@@ -67,6 +67,15 @@ export async function removeWorktree(worktreePath, deleteBranch) {
67
67
  throw new Error(`Failed to remove worktree: ${error.message}`);
68
68
  }
69
69
  }
70
+ export async function getCurrentBranch(cwd) {
71
+ try {
72
+ const { stdout } = await execAsync('git rev-parse --abbrev-ref HEAD', { cwd });
73
+ return stdout.trim() || null;
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
70
79
  export function worktreeExists(worktreePath) {
71
80
  return existsSync(worktreePath);
72
81
  }
@@ -54,6 +54,9 @@ export function parseOpenCodeOutput(buffer) {
54
54
  }
55
55
  return result;
56
56
  }
57
+ export function buildContextHeader(branchName, modelName) {
58
+ return `🌿 \`${branchName}\` Β· πŸ€– \`${modelName}\``;
59
+ }
57
60
  export function formatOutput(buffer, maxLength = 1900) {
58
61
  const parsed = parseOpenCodeOutput(buffer);
59
62
  if (!parsed.trim()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-opencode",
3
- "version": "1.0.10",
3
+ "version": "1.1.1",
4
4
  "description": "Discord bot for remote OpenCode CLI access",
5
5
  "main": "dist/src/index.js",
6
6
  "bin": {
@@ -42,6 +42,7 @@
42
42
  "eventsource": "^4.1.0",
43
43
  "node-pty": "^1.1.0",
44
44
  "open": "^10.1.0",
45
+ "opencode-antigravity-auth": "^1.4.6",
45
46
  "picocolors": "^1.1.1",
46
47
  "update-notifier": "^7.3.1"
47
48
  },