bacluc-opencode-completion-check-command 0.4.1 → 0.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bacluc-opencode-completion-check-command",
3
- "version": "v0.4.1",
3
+ "version": "v0.5.0",
4
4
  "description": "",
5
5
  "keywords": [
6
6
  "opencode",
@@ -1,6 +1,7 @@
1
- import type { Hooks, Plugin, PluginInput } from '@opencode-ai/plugin'
1
+ import type { Hooks, Plugin } from '@opencode-ai/plugin'
2
2
  import type { Event } from '@opencode-ai/sdk'
3
3
  import { promises as fs } from 'fs'
4
+ import { exec } from 'child_process'
4
5
 
5
6
  export const DEFAULT_MAX_RETRIES = 10
6
7
 
@@ -78,21 +79,34 @@ export interface CommandResult {
78
79
  stderr: string
79
80
  }
80
81
 
81
- export async function executeCommand($: PluginInput['$'], command: string, cwd: string): Promise<CommandResult> {
82
- try {
83
- const result = await $`${command}`.nothrow().quiet().cwd(cwd)
84
- return {
85
- exitCode: result.exitCode,
86
- stdout: result.text(),
87
- stderr: result.stderr.toString(),
88
- }
89
- } catch (error) {
90
- return {
91
- exitCode: 1,
92
- stdout: '',
93
- stderr: error instanceof Error ? error.message : String(error),
94
- }
95
- }
82
+ /**
83
+ * Runs the completion check command in a real system shell (`/bin/sh -c`).
84
+ *
85
+ * The previous implementation used opencode's built-in Bun shell via
86
+ * `` $`${command}` ``. Bun interpolates the whole command string as a single
87
+ * quoted argument and resolves binaries against its own restricted PATH, which
88
+ * breaks commands such as `docker compose run ...` with errors like
89
+ * "Bun: command not found: docker" even though `docker` is on the user's PATH.
90
+ *
91
+ * Using `child_process.exec` runs the command through the real `/bin/sh`, so
92
+ * the command line is parsed normally and binaries are resolved against the
93
+ * inherited PATH exactly like in a normal terminal (including `/sbin`,
94
+ * `/usr/sbin`, etc.).
95
+ */
96
+ export async function executeCommand(command: string, cwd: string): Promise<CommandResult> {
97
+ return new Promise((resolve) => {
98
+ exec(command, { cwd, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
99
+ let exitCode = 0
100
+ if (error) {
101
+ exitCode = typeof error.code === 'number' ? error.code : 1
102
+ }
103
+ resolve({
104
+ exitCode,
105
+ stdout: stdout.toString(),
106
+ stderr: stderr.toString(),
107
+ })
108
+ })
109
+ })
96
110
  }
97
111
 
98
112
  export function buildFailureMessage(result: CommandResult): string {
@@ -123,7 +137,7 @@ export async function readDefaultCommandFromAgentsMd(directory: string): Promise
123
137
  }
124
138
 
125
139
  export const CompletionCheckCommandPlugin: Plugin = async (input, options) => {
126
- const { client, $ } = input
140
+ const { client } = input
127
141
  const maxRetries = typeof options?.maxRetries === 'number' ? options.maxRetries : DEFAULT_MAX_RETRIES
128
142
  const store = new CompletionCheckStore(maxRetries)
129
143
 
@@ -225,7 +239,7 @@ export const CompletionCheckCommandPlugin: Plugin = async (input, options) => {
225
239
  processing.add(sessionID)
226
240
 
227
241
  try {
228
- const result = await executeCommand($, command, input.directory)
242
+ const result = await executeCommand(command, input.directory)
229
243
 
230
244
  if (result.exitCode === 0) {
231
245
  store.delete(sessionID)