@shenlee/devcrew 0.1.4 → 0.1.5

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
@@ -35,7 +35,7 @@ Restart Codex, open the plugin directory, choose the DevCrew marketplace, and in
35
35
  The plugin starts the DevCrew MCP server with:
36
36
 
37
37
  ```bash
38
- npm exec --silent --yes --package=@shenlee/devcrew@0.1.4 -- node -e "<DevCrew CLI wrapper>" -- serve --stdio
38
+ npm exec --silent --yes --package=@shenlee/devcrew@0.1.5 -- node -e "<DevCrew CLI wrapper>" -- serve --stdio
39
39
  ```
40
40
 
41
41
  The plugin locks the MCP server to the published npm package version, so users do not need to clone the source or build TypeScript at install time. You only need Node.js and network access the first time Codex starts the MCP server.
package/README.zh-CN.md CHANGED
@@ -35,7 +35,7 @@ codex plugin marketplace add lishen802/devcrew
35
35
  插件会用下面的命令启动 DevCrew MCP 服务:
36
36
 
37
37
  ```bash
38
- npm exec --silent --yes --package=@shenlee/devcrew@0.1.4 -- node -e "<DevCrew CLI wrapper>" -- serve --stdio
38
+ npm exec --silent --yes --package=@shenlee/devcrew@0.1.5 -- node -e "<DevCrew CLI wrapper>" -- serve --stdio
39
39
  ```
40
40
 
41
41
  插件会锁定到已发布的 npm 包版本,因此用户不需要克隆源码,也不需要在安装时编译 TypeScript;只需要本机有 Node.js,并且 Codex 第一次启动 MCP 服务时可以访问网络。
@@ -1,3 +1,5 @@
1
+ import { accessSync, constants, realpathSync } from "node:fs";
2
+ import { delimiter, join, sep } from "node:path";
1
3
  import { DEVCREW_NPM_PACKAGE, ROLE_SECTIONS } from "../../core/src/index.js";
2
4
  export function resolveBackendName(input) {
3
5
  if (input.configuredBackend && input.configuredBackend !== "host-preferred") {
@@ -349,10 +351,48 @@ function claudeOptionsForRole(input) {
349
351
  const allowedTools = input.role === "implementer" ? ["Read", "Grep", "Glob", "Edit", "Write"] : ["Read", "Grep", "Glob"];
350
352
  return buildClaudeOptions(input.cwd, "dontAsk", allowedTools);
351
353
  }
354
+ function resolveHostCodexExecutable() {
355
+ const explicitPath = process.env.DEVCREW_CODEX_PATH?.trim();
356
+ if (explicitPath) {
357
+ return explicitPath;
358
+ }
359
+ const pathValue = process.env.PATH ?? Object.entries(process.env)
360
+ .find(([key]) => key.toLowerCase() === "path")?.[1];
361
+ if (!pathValue) {
362
+ return undefined;
363
+ }
364
+ const executableNames = process.platform === "win32"
365
+ ? ["codex.exe", "codex.cmd", "codex.bat", "codex"]
366
+ : ["codex"];
367
+ for (const entry of pathValue.split(delimiter)) {
368
+ const directory = entry.trim().replace(/^"|"$/g, "");
369
+ if (!directory) {
370
+ continue;
371
+ }
372
+ for (const executableName of executableNames) {
373
+ const candidate = join(directory, executableName);
374
+ try {
375
+ accessSync(candidate, process.platform === "win32" ? constants.F_OK : constants.X_OK);
376
+ const normalizedCandidate = candidate.split(sep).join("/");
377
+ const normalizedRealPath = realpathSync(candidate).split(sep).join("/");
378
+ if (normalizedCandidate.includes("/node_modules/.bin/")
379
+ || normalizedRealPath.includes("/node_modules/@openai/codex/")) {
380
+ continue;
381
+ }
382
+ return candidate;
383
+ }
384
+ catch {
385
+ // Keep searching PATH; the SDK's packaged runtime remains the fallback.
386
+ }
387
+ }
388
+ }
389
+ return undefined;
390
+ }
352
391
  async function runWithCodex(input, prompt, loadModule) {
353
392
  const mod = await loadModule(HOST_SDK_PACKAGES.codex);
354
393
  const CodexClass = mod.Codex;
355
- const codex = new CodexClass();
394
+ const codexPathOverride = resolveHostCodexExecutable();
395
+ const codex = new CodexClass(codexPathOverride ? { codexPathOverride } : undefined);
356
396
  const thread = codex.startThread(codexOptionsForRole(input));
357
397
  const turn = await thread.run(prompt);
358
398
  return extractCodexText(turn);
@@ -1,2 +1,2 @@
1
- export const DEVCREW_VERSION = "0.1.4";
1
+ export const DEVCREW_VERSION = "0.1.5";
2
2
  export const DEVCREW_NPM_PACKAGE = "@shenlee/devcrew";
@@ -41,7 +41,7 @@ For local plugin testing:
41
41
  claude --plugin-dir plugins/devcrew-claude
42
42
  ```
43
43
 
44
- Then ask Claude Code to use DevCrew for a feature or product workflow. The generated `.mcp.json` starts the version-locked npm package with an `npm exec --package=@shenlee/devcrew@0.1.4` wrapper.
44
+ Then ask Claude Code to use DevCrew for a feature or product workflow. The generated `.mcp.json` starts the version-locked npm package with an `npm exec --package=@shenlee/devcrew@0.1.5` wrapper.
45
45
 
46
46
  The default apply policy is `interactive-host`: Claude Code performs implementation and testing with its native controls. Explicit `headless-restricted` and `headless-unattended` policies are independent DevCrew SDK policies and do not inherit the current Claude Code approval session.
47
47
 
package/docs/codex.md CHANGED
@@ -13,7 +13,7 @@ Restart Codex, open the plugin directory, select the DevCrew marketplace, and in
13
13
  The plugin launches the MCP server with:
14
14
 
15
15
  ```bash
16
- npm exec --silent --yes --package=@shenlee/devcrew@0.1.4 -- node -e "<DevCrew CLI wrapper>" -- serve --stdio
16
+ npm exec --silent --yes --package=@shenlee/devcrew@0.1.5 -- node -e "<DevCrew CLI wrapper>" -- serve --stdio
17
17
  ```
18
18
 
19
19
  Use this path when you want to use DevCrew without cloning the repository first. The version is locked to the published npm package that matches the plugin manifest.
@@ -83,7 +83,7 @@ npm install -g @shenlee/devcrew --include=optional
83
83
 
84
84
  ## Marketplace smoke test
85
85
 
86
- After publishing `@shenlee/devcrew@0.1.4`, run the real marketplace smoke test. Do not treat this as a pre-publication check because the plugin is locked to that npm version:
86
+ After publishing `@shenlee/devcrew@0.1.5`, run the real marketplace smoke test. Do not treat this as a pre-publication check because the plugin is locked to that npm version:
87
87
 
88
88
  ```bash
89
89
  npm run smoke:codex-plugin
@@ -37,7 +37,7 @@ devcrew serve --stdio
37
37
  ```
38
38
 
39
39
  Normally the generated plugin starts this command for the host agent.
40
- Codex and Claude plugin bundles use a version-locked `npm exec --package=@shenlee/devcrew@0.1.4` wrapper so the MCP service is locked to the published package version.
40
+ Codex and Claude plugin bundles use a version-locked `npm exec --package=@shenlee/devcrew@0.1.5` wrapper so the MCP service is locked to the published package version.
41
41
 
42
42
  ## 4. Run A Workflow
43
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shenlee/devcrew",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Multi-host agent workflow service for Codex and Claude Code.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -55,6 +55,6 @@
55
55
  },
56
56
  "optionalDependencies": {
57
57
  "@anthropic-ai/claude-agent-sdk": "0.3.163",
58
- "@openai/codex-sdk": "0.137.0"
58
+ "@openai/codex-sdk": "0.144.5"
59
59
  }
60
60
  }
@@ -1,3 +1,6 @@
1
+ import { accessSync, constants, realpathSync } from "node:fs";
2
+ import { delimiter, join, sep } from "node:path";
3
+
1
4
  import { DEVCREW_NPM_PACKAGE, ROLE_SECTIONS } from "../../core/src/index.js";
2
5
  import type {
3
6
  ArtifactName,
@@ -338,7 +341,7 @@ function titleForPhase(phase: Phase): string {
338
341
  // --- Host SDK contracts -----------------------------------------------------
339
342
  // These types pin the published surface of the optional host SDKs so the
340
343
  // adapter logic is type-checked even though the packages are not installed.
341
- // Verified against @openai/codex-sdk 0.136.0 (sdk/typescript/src/threadOptions.ts
344
+ // Verified against @openai/codex-sdk 0.144.5 (sdk/typescript/src/threadOptions.ts
342
345
  // and thread.ts) and @anthropic-ai/claude-agent-sdk (Agent SDK TypeScript
343
346
  // reference). Update these when the upstream contracts change.
344
347
 
@@ -368,7 +371,11 @@ interface CodexClient {
368
371
  startThread: (options?: CodexThreadOptions) => CodexThread;
369
372
  }
370
373
 
371
- type CodexConstructor = new () => CodexClient;
374
+ interface CodexClientOptions {
375
+ codexPathOverride?: string;
376
+ }
377
+
378
+ type CodexConstructor = new (options?: CodexClientOptions) => CodexClient;
372
379
 
373
380
  export type ClaudePermissionMode = "default" | "acceptEdits" | "bypassPermissions" | "dontAsk" | "plan";
374
381
 
@@ -535,10 +542,52 @@ function claudeOptionsForRole(input: RoleRunInput): ClaudeQueryOptions {
535
542
  return buildClaudeOptions(input.cwd, "dontAsk", allowedTools);
536
543
  }
537
544
 
545
+ function resolveHostCodexExecutable(): string | undefined {
546
+ const explicitPath = process.env.DEVCREW_CODEX_PATH?.trim();
547
+ if (explicitPath) {
548
+ return explicitPath;
549
+ }
550
+
551
+ const pathValue = process.env.PATH ?? Object.entries(process.env)
552
+ .find(([key]) => key.toLowerCase() === "path")?.[1];
553
+ if (!pathValue) {
554
+ return undefined;
555
+ }
556
+
557
+ const executableNames = process.platform === "win32"
558
+ ? ["codex.exe", "codex.cmd", "codex.bat", "codex"]
559
+ : ["codex"];
560
+ for (const entry of pathValue.split(delimiter)) {
561
+ const directory = entry.trim().replace(/^"|"$/g, "");
562
+ if (!directory) {
563
+ continue;
564
+ }
565
+ for (const executableName of executableNames) {
566
+ const candidate = join(directory, executableName);
567
+ try {
568
+ accessSync(candidate, process.platform === "win32" ? constants.F_OK : constants.X_OK);
569
+ const normalizedCandidate = candidate.split(sep).join("/");
570
+ const normalizedRealPath = realpathSync(candidate).split(sep).join("/");
571
+ if (
572
+ normalizedCandidate.includes("/node_modules/.bin/")
573
+ || normalizedRealPath.includes("/node_modules/@openai/codex/")
574
+ ) {
575
+ continue;
576
+ }
577
+ return candidate;
578
+ } catch {
579
+ // Keep searching PATH; the SDK's packaged runtime remains the fallback.
580
+ }
581
+ }
582
+ }
583
+ return undefined;
584
+ }
585
+
538
586
  async function runWithCodex(input: RoleRunInput, prompt: string, loadModule: ModuleLoader): Promise<string> {
539
587
  const mod = await loadModule(HOST_SDK_PACKAGES.codex);
540
588
  const CodexClass = mod.Codex as CodexConstructor;
541
- const codex = new CodexClass();
589
+ const codexPathOverride = resolveHostCodexExecutable();
590
+ const codex = new CodexClass(codexPathOverride ? { codexPathOverride } : undefined);
542
591
  const thread = codex.startThread(codexOptionsForRole(input));
543
592
  const turn = await thread.run(prompt);
544
593
  return extractCodexText(turn);
@@ -1,2 +1,2 @@
1
- export const DEVCREW_VERSION = "0.1.4";
1
+ export const DEVCREW_VERSION = "0.1.5";
2
2
  export const DEVCREW_NPM_PACKAGE = "@shenlee/devcrew";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devcrew",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "DevCrew gated multi-role workflow service for Codex.",
5
5
  "author": {
6
6
  "name": "DevCrew Contributors",
@@ -6,7 +6,7 @@
6
6
  "exec",
7
7
  "--silent",
8
8
  "--yes",
9
- "--package=@shenlee/devcrew@0.1.4",
9
+ "--package=@shenlee/devcrew@0.1.5",
10
10
  "--",
11
11
  "node",
12
12
  "-e",
@@ -298,7 +298,7 @@ async function main() {
298
298
  await client.request("initialize", {
299
299
  protocolVersion: "2025-03-26",
300
300
  capabilities: {},
301
- clientInfo: { name: "devcrew-codex-plugin-smoke", version: "0.1.4" },
301
+ clientInfo: { name: "devcrew-codex-plugin-smoke", version: "0.1.5" },
302
302
  });
303
303
  client.notify("notifications/initialized", {});
304
304
  const tools = await client.request("tools/list", {});