opencode-pilot 0.24.6 → 0.24.7

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.
@@ -1,8 +1,8 @@
1
1
  class OpencodePilot < Formula
2
2
  desc "Automation daemon for OpenCode - polls GitHub/Linear issues and spawns sessions"
3
3
  homepage "https://github.com/athal7/opencode-pilot"
4
- url "https://github.com/athal7/opencode-pilot/archive/refs/tags/v0.24.5.tar.gz"
5
- sha256 "6a191feaa9f7ec7421272a0c9533ebbd734a45483ca7116135b62e4ba11e780d"
4
+ url "https://github.com/athal7/opencode-pilot/archive/refs/tags/v0.24.6.tar.gz"
5
+ sha256 "60f487bfbe6209da3687d854b848b920791dde739046fd72bf75594d6e5e6163"
6
6
  license "MIT"
7
7
 
8
8
  depends_on "node"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-pilot",
3
- "version": "0.24.6",
3
+ "version": "0.24.7",
4
4
  "type": "module",
5
5
  "main": "plugin/index.js",
6
6
  "description": "Automation daemon for OpenCode - polls for work and spawns sessions",
@@ -673,14 +673,11 @@ export async function findReusableSession(serverUrl, directory, options = {}) {
673
673
  /**
674
674
  * Create a session via the OpenCode HTTP API
675
675
  *
676
- * This is a workaround for the known issue where `opencode run --attach`
677
- * doesn't support a --dir flag, causing sessions to run in the wrong directory
678
- * when attached to a global server.
679
- *
680
676
  * @param {string} serverUrl - Server URL (e.g., "http://localhost:4096")
681
- * @param {string} directory - Working directory for the session
677
+ * @param {string} directory - Working directory for file operations (may be a worktree)
682
678
  * @param {string} prompt - The prompt/message to send
683
679
  * @param {object} [options] - Options
680
+ * @param {string} [options.projectDirectory] - Project directory for session scoping (defaults to directory)
684
681
  * @param {string} [options.title] - Session title
685
682
  * @param {string} [options.agent] - Agent to use
686
683
  * @param {string} [options.model] - Model to use
@@ -690,13 +687,16 @@ export async function findReusableSession(serverUrl, directory, options = {}) {
690
687
  export async function createSessionViaApi(serverUrl, directory, prompt, options = {}) {
691
688
  const fetchFn = options.fetch || fetch;
692
689
  const headerTimeout = options.headerTimeout || HEADER_TIMEOUT_MS;
690
+ // Use project directory for session creation (determines projectID in OpenCode).
691
+ // The working directory (which may be a worktree) is used for messages/commands.
692
+ const projectDir = options.projectDirectory || directory;
693
693
 
694
694
  let session = null;
695
695
 
696
696
  try {
697
- // Step 1: Create a new session with the directory parameter
697
+ // Step 1: Create session scoped to the project directory
698
698
  const sessionUrl = new URL('/session', serverUrl);
699
- sessionUrl.searchParams.set('directory', directory);
699
+ sessionUrl.searchParams.set('directory', projectDir);
700
700
 
701
701
  const createResponse = await fetchFn(sessionUrl.toString(), {
702
702
  method: 'POST',
@@ -715,7 +715,7 @@ export async function createSessionViaApi(serverUrl, directory, prompt, options
715
715
  // Step 2: Update session title if provided
716
716
  if (options.title) {
717
717
  const updateUrl = new URL(`/session/${session.id}`, serverUrl);
718
- updateUrl.searchParams.set('directory', directory);
718
+ updateUrl.searchParams.set('directory', projectDir);
719
719
  await fetchFn(updateUrl.toString(), {
720
720
  method: 'PATCH',
721
721
  headers: { 'Content-Type': 'application/json' },
@@ -842,7 +842,7 @@ export async function createSessionViaApi(serverUrl, directory, prompt, options
842
842
  * @param {object} [options] - Execution options
843
843
  * @returns {Promise<object>} Result with command, success, sessionId, etc.
844
844
  */
845
- async function executeInDirectory(serverUrl, cwd, item, config, options = {}) {
845
+ async function executeInDirectory(serverUrl, cwd, item, config, options = {}, projectDirectory = null) {
846
846
  // Build prompt from template
847
847
  const prompt = buildPromptFromTemplate(config.prompt || "default", item);
848
848
 
@@ -923,6 +923,7 @@ async function executeInDirectory(serverUrl, cwd, item, config, options = {}) {
923
923
  }
924
924
 
925
925
  const result = await createSessionViaApi(serverUrl, cwd, prompt, {
926
+ projectDirectory: projectDirectory || cwd,
926
927
  title: sessionTitle,
927
928
  agent: config.agent,
928
929
  model: config.model,
@@ -986,7 +987,7 @@ export async function executeAction(item, config, options = {}) {
986
987
  if (config.existing_directory) {
987
988
  debug(`executeAction: using existing_directory=${config.existing_directory}`);
988
989
  const cwd = expandPath(config.existing_directory);
989
- return await executeInDirectory(serverUrl, cwd, item, config, options);
990
+ return await executeInDirectory(serverUrl, cwd, item, config, options, baseCwd);
990
991
  }
991
992
 
992
993
  // Resolve worktree directory if configured
@@ -1038,5 +1039,5 @@ export async function executeAction(item, config, options = {}) {
1038
1039
 
1039
1040
  debug(`executeAction: using cwd=${cwd}`);
1040
1041
 
1041
- return await executeInDirectory(serverUrl, cwd, item, config, options);
1042
+ return await executeInDirectory(serverUrl, cwd, item, config, options, baseCwd);
1042
1043
  }
@@ -564,7 +564,9 @@ describe("integration: worktree creation with worktree_name", () => {
564
564
  assert.ok(worktreeCreateCalled, "Should create worktree when worktree_name is configured");
565
565
  assert.strictEqual(createdWorktreeName, "pr-42", "Should expand worktree_name template");
566
566
  assert.ok(sessionCreated, "Should create session");
567
- assert.strictEqual(sessionDirectory, "/worktree/pr-42", "Session should be in worktree directory");
567
+ // Session creation uses the project directory (for correct projectID scoping)
568
+ // The worktree path is used for messages/commands (file operations)
569
+ assert.strictEqual(sessionDirectory, "/proj", "Session should be scoped to project directory");
568
570
  });
569
571
 
570
572
  it("reuses stored directory when reprocessing same item", async () => {
@@ -616,8 +618,8 @@ describe("integration: worktree creation with worktree_name", () => {
616
618
  assert.ok(result.success, "Action should succeed");
617
619
  // Should NOT create a new worktree since we have existing_directory
618
620
  assert.strictEqual(worktreeCreateCalled, false, "Should NOT create new worktree when existing_directory provided");
619
- // Session should be created in the existing directory
620
- assert.strictEqual(sessionDirectory, existingWorktreeDir, "Session should use existing directory");
621
+ // Session creation uses the project directory (for correct projectID scoping)
622
+ assert.strictEqual(sessionDirectory, "/proj", "Session should be scoped to project directory");
621
623
  });
622
624
  });
623
625
 
@@ -981,11 +981,12 @@ Check for bugs and security issues.`;
981
981
  // Should NOT call worktree endpoints when existing_directory is provided
982
982
  assert.strictEqual(worktreeListCalled, false, 'Should NOT list worktrees');
983
983
  assert.strictEqual(worktreeCreateCalled, false, 'Should NOT create worktree');
984
- // Should use the existing directory
985
- assert.strictEqual(sessionDirectory, '/data/worktree/calm-wizard',
986
- 'Should use existing_directory for session');
984
+ // Session creation uses project directory (for correct projectID scoping)
985
+ assert.strictEqual(sessionDirectory, '/data/proj',
986
+ 'Session creation should use project directory, not worktree');
987
+ // Result directory is the worktree (where file operations happen)
987
988
  assert.strictEqual(result.directory, '/data/worktree/calm-wizard',
988
- 'Result should include directory');
989
+ 'Result should include worktree directory');
989
990
  });
990
991
  });
991
992
 
@@ -1041,6 +1042,52 @@ Check for bugs and security issues.`;
1041
1042
  assert.ok(messageUrl.includes('%2Fpath%2Fto%2Fproject'), 'Message URL should include encoded directory path');
1042
1043
  });
1043
1044
 
1045
+ test('uses projectDirectory for session creation, working directory for messages', async () => {
1046
+ const { createSessionViaApi } = await import('../../service/actions.js');
1047
+
1048
+ const mockSessionId = 'ses_test_proj';
1049
+ let createUrl = null;
1050
+ let messageUrl = null;
1051
+
1052
+ const mockFetch = async (url, opts) => {
1053
+ const urlObj = new URL(url);
1054
+
1055
+ if (urlObj.pathname === '/session' && opts?.method === 'POST') {
1056
+ createUrl = url;
1057
+ return { ok: true, json: async () => ({ id: mockSessionId }) };
1058
+ }
1059
+
1060
+ if (urlObj.pathname.includes('/message') && opts?.method === 'POST') {
1061
+ messageUrl = url;
1062
+ return { ok: true, json: async () => ({ success: true }) };
1063
+ }
1064
+
1065
+ return { ok: false, text: async () => 'Not found' };
1066
+ };
1067
+
1068
+ await createSessionViaApi(
1069
+ 'http://localhost:4096',
1070
+ '/home/user/.local/share/opencode/worktree/abc123/pr-415',
1071
+ 'Fix the bug',
1072
+ {
1073
+ fetch: mockFetch,
1074
+ projectDirectory: '/home/user/code/odin',
1075
+ }
1076
+ );
1077
+
1078
+ // Session creation should use the project directory (for correct projectID scoping)
1079
+ assert.ok(createUrl.includes('%2Fhome%2Fuser%2Fcode%2Fodin'),
1080
+ 'Session creation should use projectDirectory');
1081
+ assert.ok(!createUrl.includes('worktree'),
1082
+ 'Session creation should NOT use the worktree path');
1083
+
1084
+ // Message should use the working directory (for file operations in the worktree)
1085
+ assert.ok(messageUrl.includes('worktree'),
1086
+ 'Message should use the worktree working directory');
1087
+ assert.ok(messageUrl.includes('pr-415'),
1088
+ 'Message should use the worktree working directory');
1089
+ });
1090
+
1044
1091
  test('handles session creation failure', async () => {
1045
1092
  const { createSessionViaApi } = await import('../../service/actions.js');
1046
1093