@shepai/cli 1.123.0 → 1.124.0-pr381.1811a4e

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 (147) hide show
  1. package/dist/packages/core/src/infrastructure/services/agents/common/agent-executor-factory.service.js +1 -1
  2. package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.d.ts +28 -8
  3. package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.d.ts.map +1 -1
  4. package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.js +250 -43
  5. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
  6. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +9 -0
  7. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.d.ts +5 -0
  8. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.d.ts.map +1 -1
  9. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.js +82 -54
  10. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts.map +1 -1
  11. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.js +2 -2
  12. package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
  13. package/dist/src/presentation/web/components/common/feature-node/feature-node.js +14 -6
  14. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts +1 -0
  15. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts.map +1 -1
  16. package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.js +16 -0
  17. package/dist/tsconfig.build.tsbuildinfo +1 -1
  18. package/package.json +1 -1
  19. package/web/.next/BUILD_ID +1 -1
  20. package/web/.next/build-manifest.json +2 -2
  21. package/web/.next/fallback-build-manifest.json +2 -2
  22. package/web/.next/prerender-manifest.json +3 -3
  23. package/web/.next/required-server-files.js +3 -3
  24. package/web/.next/required-server-files.json +3 -3
  25. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +22 -22
  26. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  27. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  28. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +28 -28
  29. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  30. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  31. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +28 -28
  32. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  33. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  34. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +20 -20
  35. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  36. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  37. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +22 -22
  38. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  39. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  40. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +28 -28
  41. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  42. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +28 -28
  44. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  45. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  46. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +20 -20
  47. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  48. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  49. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +20 -20
  50. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  51. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  52. package/web/.next/server/app/_global-error.html +2 -2
  53. package/web/.next/server/app/_global-error.rsc +1 -1
  54. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  55. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  56. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  57. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  58. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  59. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  60. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  61. package/web/.next/server/app/settings/page/server-reference-manifest.json +6 -6
  62. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  63. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  64. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  65. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  67. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  68. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  69. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  70. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  71. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  72. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  73. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_f30d0343.js +1 -1
  74. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_f30d0343.js.map +1 -1
  75. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_9f1775bb.js +1 -1
  76. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_9f1775bb.js.map +1 -1
  77. package/web/.next/server/chunks/ssr/[root-of-the-server]__06fef644._.js +1 -1
  78. package/web/.next/server/chunks/ssr/[root-of-the-server]__14664e9b._.js +2 -2
  79. package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js +1 -1
  80. package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js.map +1 -1
  81. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  82. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  83. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +2 -2
  84. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
  85. package/web/.next/server/chunks/ssr/[root-of-the-server]__7cbda1ea._.js +1 -1
  86. package/web/.next/server/chunks/ssr/[root-of-the-server]__7cbda1ea._.js.map +1 -1
  87. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  88. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__8c553503._.js +2 -2
  90. package/web/.next/server/chunks/ssr/[root-of-the-server]__9e8b25ad._.js +1 -1
  91. package/web/.next/server/chunks/ssr/[root-of-the-server]__9e8b25ad._.js.map +1 -1
  92. package/web/.next/server/chunks/ssr/[root-of-the-server]__a7de2711._.js +2 -2
  93. package/web/.next/server/chunks/ssr/[root-of-the-server]__aed1d6f8._.js +1 -1
  94. package/web/.next/server/chunks/ssr/[root-of-the-server]__aed1d6f8._.js.map +1 -1
  95. package/web/.next/server/chunks/ssr/[root-of-the-server]__ce6057f6._.js +1 -1
  96. package/web/.next/server/chunks/ssr/[root-of-the-server]__ce6057f6._.js.map +1 -1
  97. package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
  98. package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
  99. package/web/.next/server/chunks/ssr/_2f6f48b8._.js +1 -1
  100. package/web/.next/server/chunks/ssr/_2f6f48b8._.js.map +1 -1
  101. package/web/.next/server/chunks/ssr/{_960ceb5d._.js → _6f706886._.js} +2 -2
  102. package/web/.next/server/chunks/ssr/{_960ceb5d._.js.map → _6f706886._.js.map} +1 -1
  103. package/web/.next/server/chunks/ssr/_b0053195._.js +3 -0
  104. package/web/.next/server/chunks/ssr/{_fd4c574b._.js.map → _b0053195._.js.map} +1 -1
  105. package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
  106. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
  107. package/web/.next/server/chunks/ssr/_e9703421._.js +1 -1
  108. package/web/.next/server/chunks/ssr/_e9703421._.js.map +1 -1
  109. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  110. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  111. package/web/.next/server/chunks/ssr/src_presentation_web_04a55deb._.js +3 -0
  112. package/web/.next/server/chunks/ssr/{src_presentation_web_db779ef4._.js.map → src_presentation_web_04a55deb._.js.map} +1 -1
  113. package/web/.next/server/chunks/ssr/src_presentation_web_5f039db1._.js +2 -2
  114. package/web/.next/server/chunks/ssr/src_presentation_web_5f039db1._.js.map +1 -1
  115. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_2a52b697.js +1 -1
  116. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_2a52b697.js.map +1 -1
  117. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
  118. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
  119. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
  120. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
  121. package/web/.next/server/chunks/ssr/src_presentation_web_c4337169._.js +2 -2
  122. package/web/.next/server/chunks/ssr/src_presentation_web_c4337169._.js.map +1 -1
  123. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
  124. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js.map +1 -1
  125. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
  126. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js.map +1 -1
  127. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  128. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  129. package/web/.next/server/pages/500.html +2 -2
  130. package/web/.next/server/server-reference-manifest.js +1 -1
  131. package/web/.next/server/server-reference-manifest.json +34 -34
  132. package/web/.next/static/chunks/{373e10090c6410c4.js → 0e237ba1b2244a18.js} +1 -1
  133. package/web/.next/static/chunks/{3f6c44cbad751310.js → 133cd3e797f5e394.js} +1 -1
  134. package/web/.next/static/chunks/{f0dfeedb263eb906.js → 15903d6fbe2a6ca6.js} +1 -1
  135. package/web/.next/static/chunks/{64d063449c3be0ec.js → 4bdce057ac032126.js} +1 -1
  136. package/web/.next/static/chunks/{9821072252ddb89a.js → 501ce5d8bdf8f088.js} +1 -1
  137. package/web/.next/static/chunks/{310be54600c8ce20.js → 7aa01f1bf09ed016.js} +2 -2
  138. package/web/.next/static/chunks/91ecdc800a99fedf.js +1 -0
  139. package/web/.next/static/chunks/{94af5be1ccf8842e.js → eaefc7de14c204d3.js} +1 -1
  140. package/web/.next/static/chunks/{878643f90bc6360f.js → f746f850470ea93e.js} +2 -2
  141. package/web/.next/static/chunks/{694718e5449b3150.js → fc0969d0a83b0342.js} +1 -1
  142. package/web/.next/server/chunks/ssr/_fd4c574b._.js +0 -3
  143. package/web/.next/server/chunks/ssr/src_presentation_web_db779ef4._.js +0 -3
  144. package/web/.next/static/chunks/e418898e8e02f50a.js +0 -1
  145. /package/web/.next/static/{gBTe97SiNDA2hbG9dbfpb → hSz-kDGgMc7NVINRO5rze}/_buildManifest.js +0 -0
  146. /package/web/.next/static/{gBTe97SiNDA2hbG9dbfpb → hSz-kDGgMc7NVINRO5rze}/_clientMiddlewareManifest.json +0 -0
  147. /package/web/.next/static/{gBTe97SiNDA2hbG9dbfpb → hSz-kDGgMc7NVINRO5rze}/_ssgManifest.js +0 -0
@@ -45,7 +45,7 @@ export class AgentExecutorFactory {
45
45
  executor = new ClaudeCodeExecutorService(this.spawn);
46
46
  break;
47
47
  case 'cursor':
48
- executor = new CursorExecutorService(this.spawn);
48
+ executor = new CursorExecutorService(this.spawn, _authConfig);
49
49
  break;
50
50
  case 'dev':
51
51
  executor = new DevAgentExecutorService();
@@ -8,7 +8,7 @@
8
8
  * Uses constructor dependency injection for the spawn function
9
9
  * to enable testability without mocking node:child_process directly.
10
10
  */
11
- import type { AgentType, AgentFeature } from '../../../../../domain/generated/output.js';
11
+ import type { AgentType, AgentFeature, AgentConfig } from '../../../../../domain/generated/output.js';
12
12
  import type { IAgentExecutor, AgentExecutionOptions, AgentExecutionResult, AgentExecutionStreamEvent } from '../../../../../application/ports/output/agents/agent-executor.interface.js';
13
13
  import type { SpawnFunction } from '../types.js';
14
14
  /**
@@ -17,10 +17,11 @@ import type { SpawnFunction } from '../types.js';
17
17
  */
18
18
  export declare class CursorExecutorService implements IAgentExecutor {
19
19
  private readonly spawn;
20
+ private readonly authConfig?;
20
21
  readonly agentType: AgentType;
21
22
  /** When true, suppresses debug logging (set per-call via options.silent) */
22
23
  private silent;
23
- constructor(spawn: SpawnFunction);
24
+ constructor(spawn: SpawnFunction, authConfig?: AgentConfig | undefined);
24
25
  /** Debug logging — writes to stdout so it appears in the worker log file */
25
26
  private log;
26
27
  supportsFeature(feature: AgentFeature): boolean;
@@ -33,16 +34,35 @@ export declare class CursorExecutorService implements IAgentExecutor {
33
34
  private buildArgs;
34
35
  private buildStreamArgs;
35
36
  /**
36
- * Spawn the agent process, handling Windows specially via PowerShell.
37
+ * Resolve the cursor CLI's bundled node.exe and index.js paths on Windows.
37
38
  *
38
- * On Windows, cursor CLI ships as `agent.cmd` which requires `shell: true`,
39
- * but cmd.exe mangles long `-p` arguments (8191-char limit + special chars).
40
- * Solution: write prompt to a temp file, invoke agent via PowerShell which
41
- * reads the file and passes the content as `-p`. PowerShell handles long
42
- * strings natively (32K limit) and doesn't mangle arguments.
39
+ * The cursor CLI installs to `%LOCALAPPDATA%\cursor-agent\` with:
40
+ * - `agent.cmd` / `cursor-agent.ps1` (shell wrappers)
41
+ * - `versions/<YYYY.MM.DD-commit>/node.exe` + `index.js`
42
+ *
43
+ * Spawning via agent.cmd creates a problematic nesting chain:
44
+ * PowerShell → agent.cmd → PowerShell → cursor-agent.ps1 → node.exe
45
+ * This chain hangs on Windows CI runners. Direct invocation bypasses it.
46
+ *
47
+ * @returns `{ nodePath, indexPath }` or `undefined` if not found
48
+ */
49
+ private resolveCursorBinary;
50
+ /**
51
+ * Spawn the agent process, handling Windows specially.
52
+ *
53
+ * On Windows, the cursor CLI ships as `agent.cmd` which spawns PowerShell
54
+ * which runs `cursor-agent.ps1` which runs `node.exe index.js`. This triple
55
+ * nesting (PowerShell → cmd.exe → PowerShell → node.exe) hangs on Windows
56
+ * CI runners. Solution: resolve the cursor CLI's bundled `node.exe` and
57
+ * `index.js` directly, bypassing all shell wrappers.
43
58
  *
44
59
  * On Linux/macOS, spawn `agent` directly — no shell needed.
45
60
  */
61
+ /**
62
+ * Build the subprocess environment: strip CLAUDECODE, inject CURSOR_API_KEY
63
+ * from authConfig when token auth is configured.
64
+ */
65
+ private buildEnv;
46
66
  private spawnAgent;
47
67
  private parseStreamLine;
48
68
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cursor-executor.service.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,4EAA4E,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAuBjD;;;GAGG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAM9C,OAAO,CAAC,QAAQ,CAAC,KAAK;IALlC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAyB;IAEtD,4EAA4E;IAC5E,OAAO,CAAC,MAAM,CAAS;gBAEM,KAAK,EAAE,aAAa;IAEjD,4EAA4E;IAC5E,OAAO,CAAC,GAAG;IAMX,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAIzC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAoItF,aAAa,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,aAAa,CAAC,yBAAyB,CAAC;IAkF3C;;OAEG;IACH,OAAO,CAAC,cAAc;IAkCtB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,eAAe;IAOvB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,UAAU;IA2DlB,OAAO,CAAC,eAAe;CAgDxB"}
1
+ {"version":3,"file":"cursor-executor.service.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EACZ,WAAW,EACZ,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,4EAA4E,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAuBjD;;;GAGG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAOxD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAP9B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAyB;IAEtD,4EAA4E;IAC5E,OAAO,CAAC,MAAM,CAAS;gBAGJ,KAAK,EAAE,aAAa,EACpB,UAAU,CAAC,EAAE,WAAW,YAAA;IAG3C,4EAA4E;IAC5E,OAAO,CAAC,GAAG;IAMX,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAIzC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAiPtF,aAAa,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,aAAa,CAAC,yBAAyB,CAAC;IAiH3C;;OAEG;IACH,OAAO,CAAC,cAAc;IAyCtB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,eAAe;IAOvB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,mBAAmB;IAwC3B;;;;;;;;;;OAUG;IACH;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,UAAU;IA6FlB,OAAO,CAAC,eAAe;CAgDxB"}
@@ -8,9 +8,8 @@
8
8
  * Uses constructor dependency injection for the spawn function
9
9
  * to enable testability without mocking node:child_process directly.
10
10
  */
11
- import { writeFileSync, unlinkSync } from 'node:fs';
11
+ import { unlinkSync, readdirSync, existsSync } from 'node:fs';
12
12
  import { execFileSync } from 'node:child_process';
13
- import { tmpdir } from 'node:os';
14
13
  import { join } from 'node:path';
15
14
  import { getCurrentPhase, getLogPrefix } from '../../feature-agent/log-context.js';
16
15
  import { IS_WINDOWS } from '../../../../platform.js';
@@ -36,11 +35,13 @@ const SUPPORTED_FEATURES = new Set(['session-resume', 'streaming']);
36
35
  */
37
36
  export class CursorExecutorService {
38
37
  spawn;
38
+ authConfig;
39
39
  agentType = 'cursor';
40
40
  /** When true, suppresses debug logging (set per-call via options.silent) */
41
41
  silent = false;
42
- constructor(spawn) {
42
+ constructor(spawn, authConfig) {
43
43
  this.spawn = spawn;
44
+ this.authConfig = authConfig;
44
45
  }
45
46
  /** Debug logging — writes to stdout so it appears in the worker log file */
46
47
  log(message) {
@@ -58,19 +59,67 @@ export class CursorExecutorService {
58
59
  // stream-json is unreliable on Windows where shell: true can mangle args.
59
60
  const args = this.buildArgs(prompt, options);
60
61
  const { proc, tmpFile } = this.spawnAgent(prompt, args, options);
62
+ // Diagnostic: verify streams are connected and process is alive
63
+ this.log(`[diag] proc.pid=${proc.pid}, killed=${proc.killed}`);
64
+ this.log(`[diag] stdout=${!!proc.stdout}, stderr=${!!proc.stderr}, stdin=${!!proc.stdin}`);
65
+ this.log(`[diag] CURSOR_API_KEY set=${!!process.env.CURSOR_API_KEY}, len=${process.env.CURSOR_API_KEY?.length ?? 0}`);
66
+ this.log(`[diag] authConfig.token set=${!!this.authConfig?.token}, method=${this.authConfig?.authMethod ?? 'none'}`);
67
+ this.log(`[diag] platform=${process.platform}, cwd=${options?.cwd ?? '(inherited)'}`);
61
68
  return new Promise((resolve, reject) => {
62
69
  let lineBuffer = '';
63
70
  let stderr = '';
64
71
  let timedOut = false;
65
72
  let timeoutId;
73
+ let stdoutChunks = 0;
74
+ let stderrChunks = 0;
75
+ const startTime = Date.now();
66
76
  // Accumulated from JSON events or raw text fallback
67
77
  let resultText = '';
68
78
  let rawText = '';
69
79
  let sessionId;
70
80
  let metadata;
81
+ // Periodic heartbeat + zero-output watchdog.
82
+ // Cursor CLI on Windows sometimes starts but never produces any output.
83
+ // If no stdout/stderr arrives within SILENCE_TIMEOUT_MS, kill the process
84
+ // so retryExecute can retry instead of waiting 30 minutes.
85
+ const SILENCE_TIMEOUT_MS = 180_000; // 3 minutes of zero output = stuck
86
+ let lastOutputAt = Date.now();
87
+ const heartbeatId = setInterval(() => {
88
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
89
+ const silenceMs = Date.now() - lastOutputAt;
90
+ // Check if child process is actually alive (proc.killed only reflects OUR kills)
91
+ let processAlive = false;
92
+ if (proc.pid) {
93
+ try {
94
+ process.kill(proc.pid, 0); // signal 0 = check existence
95
+ processAlive = true;
96
+ }
97
+ catch {
98
+ processAlive = false;
99
+ }
100
+ }
101
+ this.log(`[diag] heartbeat ${elapsed}s: stdout=${stdoutChunks}, stderr=${stderrChunks}, silence=${Math.round(silenceMs / 1000)}s, alive=${processAlive}, pid=${proc.pid}`);
102
+ // Kill if zero output for too long
103
+ if (silenceMs > SILENCE_TIMEOUT_MS && !proc.killed) {
104
+ this.log(`[diag] SILENCE WATCHDOG: no output for ${Math.round(silenceMs / 1000)}s — killing process`);
105
+ timedOut = true;
106
+ if (process.platform === 'win32' && proc.pid) {
107
+ try {
108
+ execFileSync('taskkill', ['/F', '/T', '/PID', String(proc.pid)], { stdio: 'ignore' });
109
+ }
110
+ catch {
111
+ proc.kill();
112
+ }
113
+ }
114
+ else {
115
+ proc.kill();
116
+ }
117
+ }
118
+ }, 15_000);
71
119
  if (options?.timeout) {
72
120
  timeoutId = setTimeout(() => {
73
121
  timedOut = true;
122
+ this.log(`[diag] TIMEOUT after ${options.timeout}ms — killing process tree`);
74
123
  // On Windows, proc.kill() may not kill the entire process tree
75
124
  // (PowerShell + child agent process). Use taskkill /T for tree kill.
76
125
  if (process.platform === 'win32' && proc.pid) {
@@ -78,9 +127,11 @@ export class CursorExecutorService {
78
127
  execFileSync('taskkill', ['/F', '/T', '/PID', String(proc.pid)], {
79
128
  stdio: 'ignore',
80
129
  });
130
+ this.log(`[diag] taskkill /T /PID ${proc.pid} succeeded`);
81
131
  }
82
132
  catch {
83
133
  proc.kill();
134
+ this.log(`[diag] taskkill failed, fell back to proc.kill()`);
84
135
  }
85
136
  }
86
137
  else {
@@ -107,17 +158,36 @@ export class CursorExecutorService {
107
158
  if (parsed.duration_ms !== undefined) {
108
159
  metadata = { ...metadata, duration_ms: parsed.duration_ms };
109
160
  }
161
+ if (parsed.is_error) {
162
+ this.log(`[diag] result event has is_error=true: ${JSON.stringify(parsed).slice(0, 300)}`);
163
+ }
164
+ }
165
+ else if (parsed.type === 'error') {
166
+ this.log(`[diag] cursor error event: ${JSON.stringify(parsed).slice(0, 500)}`);
167
+ }
168
+ else if (parsed.type !== 'user' &&
169
+ parsed.type !== 'system' &&
170
+ parsed.type !== 'thinking' &&
171
+ parsed.type !== 'tool_call') {
172
+ this.log(`[diag] unknown event type="${parsed.type}": ${JSON.stringify(parsed).slice(0, 300)}`);
110
173
  }
111
- // user, system, thinking events: logged but don't affect result
112
174
  }
113
175
  catch {
114
176
  // Non-JSON output — accumulate as raw text fallback
115
- if (line.length > 0)
177
+ if (line.length > 0) {
116
178
  rawText += `${line}\n`;
179
+ this.log(`[diag] non-JSON line (${line.length} chars): ${line.slice(0, 200)}`);
180
+ }
117
181
  }
118
182
  };
119
183
  proc.stdout?.on('data', (chunk) => {
120
- lineBuffer += chunk.toString();
184
+ stdoutChunks++;
185
+ lastOutputAt = Date.now();
186
+ const data = chunk.toString();
187
+ if (stdoutChunks <= 3) {
188
+ this.log(`[diag] stdout chunk #${stdoutChunks}: ${data.length} bytes, first 200: ${data.slice(0, 200).replace(/\n/g, '\\n')}`);
189
+ }
190
+ lineBuffer += data;
121
191
  const lines = lineBuffer.split('\n');
122
192
  lineBuffer = lines.pop() ?? '';
123
193
  for (const line of lines) {
@@ -127,19 +197,23 @@ export class CursorExecutorService {
127
197
  }
128
198
  });
129
199
  proc.stderr?.on('data', (chunk) => {
200
+ stderrChunks++;
201
+ lastOutputAt = Date.now();
130
202
  const data = chunk.toString();
131
203
  stderr += data;
132
- this.log(`stderr: ${data.trimEnd()}`);
204
+ this.log(`[diag] stderr chunk #${stderrChunks}: ${data.trimEnd()}`);
133
205
  // Detect fatal errors early so callers don't waste time retrying
134
206
  if (data.includes('Cannot use this model')) {
135
207
  if (timeoutId)
136
208
  clearTimeout(timeoutId);
209
+ clearInterval(heartbeatId);
137
210
  proc.kill();
138
211
  reject(new Error(data.trim()));
139
212
  }
140
213
  });
141
214
  proc.on('error', (error) => {
142
- this.log(`Process error event: ${error.message}`);
215
+ this.log(`[diag] Process ERROR event: ${error.message} (code=${error.code})`);
216
+ clearInterval(heartbeatId);
143
217
  if (timeoutId)
144
218
  clearTimeout(timeoutId);
145
219
  if (error.code === 'ENOENT') {
@@ -150,6 +224,8 @@ export class CursorExecutorService {
150
224
  }
151
225
  });
152
226
  proc.on('close', (code) => {
227
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
228
+ clearInterval(heartbeatId);
153
229
  // Clean up temp file on Windows
154
230
  if (tmpFile) {
155
231
  try {
@@ -163,7 +239,7 @@ export class CursorExecutorService {
163
239
  processLine(lineBuffer.trim());
164
240
  // Use raw text as fallback when no JSON result was captured
165
241
  const finalText = resultText || rawText.trim();
166
- this.log(`Process closed with code ${code}, result=${finalText.length} chars`);
242
+ this.log(`[diag] Process CLOSE: code=${code}, elapsed=${elapsed}s, stdout_chunks=${stdoutChunks}, stderr_chunks=${stderrChunks}, result=${finalText.length} chars, raw=${rawText.length} chars`);
167
243
  if (timeoutId)
168
244
  clearTimeout(timeoutId);
169
245
  if (timedOut) {
@@ -171,9 +247,17 @@ export class CursorExecutorService {
171
247
  return;
172
248
  }
173
249
  if (code !== 0 && code !== null) {
174
- reject(new Error(stderr.trim() || `Process exited with code ${code}`));
250
+ const errMsg = stderr.trim() || `Process exited with code ${code}`;
251
+ this.log(`[diag] non-zero exit — rejecting with: ${errMsg.slice(0, 200)}`);
252
+ reject(new Error(errMsg));
175
253
  return;
176
254
  }
255
+ if (stdoutChunks === 0 && stderrChunks === 0) {
256
+ this.log(`[diag] WARNING: process exited with code 0 but produced ZERO output (stdout=0, stderr=0, elapsed=${elapsed}s)`);
257
+ }
258
+ if (!finalText && code === 0) {
259
+ this.log(`[diag] WARNING: process exited code=0 but result is empty (resultText=${resultText.length}, rawText=${rawText.length})`);
260
+ }
177
261
  const result = { result: finalText };
178
262
  if (sessionId)
179
263
  result.sessionId = sessionId;
@@ -184,20 +268,25 @@ export class CursorExecutorService {
184
268
  });
185
269
  }
186
270
  async *executeStream(prompt, options) {
271
+ this.silent = options?.silent ?? false;
187
272
  const args = this.buildStreamArgs(prompt, options);
188
273
  const { proc, tmpFile } = this.spawnAgent(prompt, args, options);
274
+ this.log(`[stream] pid=${proc.pid}, stdout=${!!proc.stdout}, stderr=${!!proc.stderr}`);
189
275
  let lineBuffer = '';
190
276
  let stderr = '';
277
+ let stdoutChunks = 0;
278
+ let stderrChunks = 0;
279
+ const startTime = Date.now();
191
280
  const queue = [];
192
281
  let resolve = null;
193
282
  let error = null;
194
- function enqueue(event) {
283
+ const enqueue = (event) => {
195
284
  queue.push(event);
196
285
  if (resolve) {
197
286
  resolve();
198
287
  resolve = null;
199
288
  }
200
- }
289
+ };
201
290
  function waitForItem() {
202
291
  if (queue.length > 0)
203
292
  return Promise.resolve();
@@ -205,8 +294,18 @@ export class CursorExecutorService {
205
294
  resolve = r;
206
295
  });
207
296
  }
297
+ // Heartbeat for stream mode too
298
+ const heartbeatId = setInterval(() => {
299
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
300
+ this.log(`[stream] heartbeat ${elapsed}s: stdout_chunks=${stdoutChunks}, stderr_chunks=${stderrChunks}, pid_killed=${proc.killed}`);
301
+ }, 30_000);
208
302
  proc.stdout?.on('data', (chunk) => {
209
- lineBuffer += chunk.toString();
303
+ stdoutChunks++;
304
+ const data = chunk.toString();
305
+ if (stdoutChunks <= 3) {
306
+ this.log(`[stream] stdout chunk #${stdoutChunks}: ${data.length} bytes`);
307
+ }
308
+ lineBuffer += data;
210
309
  const lines = lineBuffer.split('\n');
211
310
  lineBuffer = lines.pop() ?? '';
212
311
  for (const line of lines) {
@@ -219,13 +318,21 @@ export class CursorExecutorService {
219
318
  }
220
319
  });
221
320
  proc.stderr?.on('data', (chunk) => {
222
- stderr += chunk.toString();
321
+ stderrChunks++;
322
+ const data = chunk.toString();
323
+ stderr += data;
324
+ this.log(`[stream] stderr chunk #${stderrChunks}: ${data.trimEnd()}`);
223
325
  });
224
326
  proc.on('error', (err) => {
327
+ this.log(`[stream] Process ERROR: ${err.message}`);
328
+ clearInterval(heartbeatId);
225
329
  error = err;
226
330
  enqueue(null);
227
331
  });
228
332
  proc.on('close', (code) => {
333
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
334
+ clearInterval(heartbeatId);
335
+ this.log(`[stream] Process CLOSE: code=${code}, elapsed=${elapsed}s, stdout_chunks=${stdoutChunks}, stderr_chunks=${stderrChunks}`);
229
336
  if (tmpFile) {
230
337
  try {
231
338
  unlinkSync(tmpFile);
@@ -239,8 +346,10 @@ export class CursorExecutorService {
239
346
  if (event)
240
347
  enqueue(event);
241
348
  }
242
- if (code !== 0 && code !== null && stderr.trim()) {
243
- enqueue({ type: 'error', content: stderr.trim(), timestamp: new Date() });
349
+ if (code !== 0 && code !== null) {
350
+ const errMsg = stderr.trim() || `Process exited with code ${code}`;
351
+ this.log(`[stream] non-zero exit: ${errMsg}`);
352
+ enqueue({ type: 'error', content: errMsg, timestamp: new Date() });
244
353
  }
245
354
  enqueue(null);
246
355
  });
@@ -286,20 +395,24 @@ export class CursorExecutorService {
286
395
  }
287
396
  if (parsed.type === 'user')
288
397
  return; // Skip echoed input
398
+ // Log any unrecognized event type so nothing is silently dropped
399
+ if (!['assistant', 'tool_call', 'result', 'user', 'system', 'thinking'].includes(parsed.type)) {
400
+ this.log(`[diag] unhandled stream event type="${parsed.type}": ${line.slice(0, 200)}`);
401
+ }
289
402
  }
290
403
  catch {
291
404
  if (line.length > 0)
292
405
  this.log(`[raw] ${line}`);
293
406
  }
294
407
  }
295
- buildArgs(prompt, options) {
296
- const args = ['--yolo', '-p', prompt, '--output-format', 'json'];
408
+ buildArgs(_prompt, options) {
409
+ // Prompt is piped via stdin (not passed as arg) to avoid Windows command line
410
+ // length limits and argument escaping issues on Windows Server CI.
411
+ const args = ['--yolo', '-p', '--output-format', 'json'];
297
412
  if (options?.resumeSession)
298
413
  args.push('--resume', options.resumeSession);
299
414
  if (options?.model)
300
415
  args.push('--model', toCursorModelName(options.model));
301
- // Unsupported options silently omitted: systemPrompt, allowedTools, maxTurns, outputSchema
302
- // No auth flags — binary handles its own auth
303
416
  return args;
304
417
  }
305
418
  buildStreamArgs(prompt, options) {
@@ -310,44 +423,135 @@ export class CursorExecutorService {
310
423
  return args;
311
424
  }
312
425
  /**
313
- * Spawn the agent process, handling Windows specially via PowerShell.
426
+ * Resolve the cursor CLI's bundled node.exe and index.js paths on Windows.
427
+ *
428
+ * The cursor CLI installs to `%LOCALAPPDATA%\cursor-agent\` with:
429
+ * - `agent.cmd` / `cursor-agent.ps1` (shell wrappers)
430
+ * - `versions/<YYYY.MM.DD-commit>/node.exe` + `index.js`
431
+ *
432
+ * Spawning via agent.cmd creates a problematic nesting chain:
433
+ * PowerShell → agent.cmd → PowerShell → cursor-agent.ps1 → node.exe
434
+ * This chain hangs on Windows CI runners. Direct invocation bypasses it.
435
+ *
436
+ * @returns `{ nodePath, indexPath }` or `undefined` if not found
437
+ */
438
+ resolveCursorBinary() {
439
+ const localAppData = process.env.LOCALAPPDATA;
440
+ if (!localAppData) {
441
+ this.log('[diag] resolveCursorBinary: LOCALAPPDATA not set');
442
+ return undefined;
443
+ }
444
+ const versionsDir = join(localAppData, 'cursor-agent', 'versions');
445
+ if (!existsSync(versionsDir)) {
446
+ this.log(`[diag] resolveCursorBinary: versions dir not found: ${versionsDir}`);
447
+ return undefined;
448
+ }
449
+ // Find the latest version directory (format: YYYY.MM.DD-commit)
450
+ const versionPattern = /^\d{4}\.\d{1,2}\.\d{1,2}-[a-f0-9]+$/;
451
+ const versions = readdirSync(versionsDir)
452
+ .filter((d) => versionPattern.test(d))
453
+ .sort()
454
+ .reverse();
455
+ if (versions.length === 0) {
456
+ this.log(`[diag] resolveCursorBinary: no version dirs in ${versionsDir}`);
457
+ return undefined;
458
+ }
459
+ const versionDir = join(versionsDir, versions[0]);
460
+ const nodePath = join(versionDir, 'node.exe');
461
+ const indexPath = join(versionDir, 'index.js');
462
+ if (!existsSync(nodePath) || !existsSync(indexPath)) {
463
+ this.log(`[diag] resolveCursorBinary: missing binary — node=${existsSync(nodePath)}, index=${existsSync(indexPath)}`);
464
+ return undefined;
465
+ }
466
+ this.log(`[diag] resolveCursorBinary: resolved version=${versions[0]}`);
467
+ return { nodePath, indexPath };
468
+ }
469
+ /**
470
+ * Spawn the agent process, handling Windows specially.
314
471
  *
315
- * On Windows, cursor CLI ships as `agent.cmd` which requires `shell: true`,
316
- * but cmd.exe mangles long `-p` arguments (8191-char limit + special chars).
317
- * Solution: write prompt to a temp file, invoke agent via PowerShell which
318
- * reads the file and passes the content as `-p`. PowerShell handles long
319
- * strings natively (32K limit) and doesn't mangle arguments.
472
+ * On Windows, the cursor CLI ships as `agent.cmd` which spawns PowerShell
473
+ * which runs `cursor-agent.ps1` which runs `node.exe index.js`. This triple
474
+ * nesting (PowerShell cmd.exe PowerShell node.exe) hangs on Windows
475
+ * CI runners. Solution: resolve the cursor CLI's bundled `node.exe` and
476
+ * `index.js` directly, bypassing all shell wrappers.
320
477
  *
321
478
  * On Linux/macOS, spawn `agent` directly — no shell needed.
322
479
  */
323
- spawnAgent(prompt, args, options) {
480
+ /**
481
+ * Build the subprocess environment: strip CLAUDECODE, inject CURSOR_API_KEY
482
+ * from authConfig when token auth is configured.
483
+ */
484
+ buildEnv(extra) {
324
485
  const { CLAUDECODE: _, ...cleanEnv } = process.env;
486
+ // Inject CURSOR_API_KEY from stored settings so the cursor CLI authenticates
487
+ // even when the env var isn't inherited (e.g. direct node.exe invocation on Windows).
488
+ if (this.authConfig?.authMethod === 'token' && this.authConfig.token) {
489
+ cleanEnv.CURSOR_API_KEY = this.authConfig.token;
490
+ this.log(`[diag] buildEnv: injected CURSOR_API_KEY from authConfig (${this.authConfig.token.length} chars)`);
491
+ }
492
+ else {
493
+ this.log(`[diag] buildEnv: NO token injection (authMethod=${this.authConfig?.authMethod ?? 'none'}, hasToken=${!!this.authConfig?.token}, envKey=${!!cleanEnv.CURSOR_API_KEY})`);
494
+ }
495
+ const env = extra ? { ...cleanEnv, ...extra } : cleanEnv;
496
+ this.log(`[diag] buildEnv: final CURSOR_API_KEY in env=${!!env.CURSOR_API_KEY}, len=${env.CURSOR_API_KEY?.length ?? 0}`);
497
+ return env;
498
+ }
499
+ spawnAgent(prompt, args, options) {
325
500
  const cwd = options?.cwd;
326
501
  if (IS_WINDOWS) {
327
- // Write prompt to temp file to bypass cmd.exe argument mangling
328
- const tmpFile = join(tmpdir(), `shep-cursor-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.txt`);
329
- writeFileSync(tmpFile, prompt, 'utf8');
330
- // Build the agent args WITHOUT -p and the prompt (they go via temp file)
331
- const agentFlags = args.filter((a) => a !== '-p' && a !== prompt).join(' ');
332
- const safePath = tmpFile.replace(/'/g, "''");
333
- const psCmd = `$p = Get-Content -Raw '${safePath}'; & agent ${agentFlags} -p $p`;
334
- this.log(`Windows PowerShell mode: wrote ${prompt.length} chars to ${tmpFile}`);
335
- this.log(`PS command: ${psCmd.replace(prompt, `<${prompt.length} chars>`)}`);
336
- const proc = this.spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', psCmd], {
502
+ // Direct invocation: resolve cursor's bundled node.exe + index.js and
503
+ // spawn directly. This bypasses agent.cmd/PowerShell which can trigger
504
+ // GUI "Open with" dialogs on Windows when `agent` isn't on PATH.
505
+ const resolved = this.resolveCursorBinary();
506
+ if (resolved) {
507
+ const directArgs = [resolved.indexPath, ...args];
508
+ this.log(`Windows direct mode: ${resolved.nodePath}`);
509
+ this.log(`[diag] node.exe exists: ${existsSync(resolved.nodePath)}, index.js exists: ${existsSync(resolved.indexPath)}`);
510
+ this.log(`[diag] cwd: ${cwd ?? '(inherited)'}, cwd exists: ${cwd ? existsSync(cwd) : 'n/a'}`);
511
+ this.log(`Args: ${directArgs.map((a) => (a.length > 80 ? `${a.slice(0, 77)}...` : a)).join(' ')}`);
512
+ const spawnEnv = this.buildEnv({ CURSOR_INVOKED_AS: 'agent' });
513
+ const proc = this.spawn(resolved.nodePath, directArgs, {
514
+ cwd,
515
+ stdio: ['pipe', 'pipe', 'pipe'],
516
+ windowsHide: true,
517
+ env: spawnEnv,
518
+ });
519
+ this.log(`Direct PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
520
+ if (!proc.pid) {
521
+ this.log(`[diag] WARNING: spawn returned no PID — process may not have started`);
522
+ }
523
+ // Pipe prompt via stdin (like claude-code executor) to avoid Windows
524
+ // command line length limits that cause zero-output hangs on Windows Server.
525
+ this.log(`[diag] Piping ${prompt.length} chars via stdin`);
526
+ if (proc.stdin) {
527
+ proc.stdin.write(prompt);
528
+ proc.stdin.end();
529
+ }
530
+ return { proc, tmpFile: undefined };
531
+ }
532
+ // Fallback: spawn agent.cmd directly with shell: true
533
+ // (only used when cursor binary resolution fails)
534
+ const agentCmd = join(process.env.LOCALAPPDATA ?? '', 'cursor-agent', 'agent.cmd');
535
+ this.log(`[diag] Windows fallback: using agent.cmd at ${agentCmd}`);
536
+ const proc = this.spawn(agentCmd, args, {
337
537
  cwd,
338
538
  stdio: ['pipe', 'pipe', 'pipe'],
339
539
  windowsHide: true,
340
- env: cleanEnv,
540
+ shell: true,
541
+ env: this.buildEnv(),
341
542
  });
342
- this.log(`PowerShell PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
343
- if (proc.stdin)
543
+ this.log(`[diag] Fallback PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
544
+ this.log(`[diag] Piping ${prompt.length} chars via stdin`);
545
+ if (proc.stdin) {
546
+ proc.stdin.write(prompt);
344
547
  proc.stdin.end();
345
- return { proc, tmpFile };
548
+ }
549
+ return { proc, tmpFile: undefined };
346
550
  }
347
551
  // Linux/macOS: spawn agent directly, no shell needed
348
552
  const spawnOpts = {
349
553
  stdio: ['pipe', 'pipe', 'pipe'],
350
- env: cleanEnv,
554
+ env: this.buildEnv(),
351
555
  };
352
556
  if (cwd)
353
557
  spawnOpts.cwd = cwd;
@@ -355,8 +559,11 @@ export class CursorExecutorService {
355
559
  this.log(`Spawn cwd: ${cwd ?? '(inherited)'}`);
356
560
  const proc = this.spawn('agent', args, spawnOpts);
357
561
  this.log(`Subprocess PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
358
- if (proc.stdin)
562
+ this.log(`[diag] Piping ${prompt.length} chars via stdin`);
563
+ if (proc.stdin) {
564
+ proc.stdin.write(prompt);
359
565
  proc.stdin.end();
566
+ }
360
567
  return { proc, tmpFile: undefined };
361
568
  }
362
569
  parseStreamLine(line) {
@@ -1 +1 @@
1
- {"version":3,"file":"feature-agent-worker.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAEH,OAAO,kBAAkB,CAAC;AAe1B,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAW7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAwE1D;AAgCD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA0R/D"}
1
+ {"version":3,"file":"feature-agent-worker.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AAEH,OAAO,kBAAkB,CAAC;AAe1B,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAW7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAwE1D;AAgCD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAkS/D"}
@@ -182,6 +182,15 @@ export async function runWorker(args) {
182
182
  getDefaultBranch: (cwd) => gitPrService.getDefaultBranch(cwd),
183
183
  verifyMerge: (cwd, featureBranch, baseBranch, premergeBaseSha) => gitPrService.verifyMerge(cwd, featureBranch, baseBranch, premergeBaseSha),
184
184
  revParse: (cwd, ref) => gitPrService.revParse(cwd, ref),
185
+ commitAll: async (cwd, message) => {
186
+ try {
187
+ return await gitPrService.commitAll(cwd, message);
188
+ }
189
+ catch {
190
+ // commitAll throws when there's nothing to commit — return undefined
191
+ return undefined;
192
+ }
193
+ },
185
194
  localMergeSquash: (cwd, featureBranch, baseBranch, commitMessage, hasRemote) => gitPrService.localMergeSquash(cwd, featureBranch, baseBranch, commitMessage, hasRemote),
186
195
  featureRepository,
187
196
  gitPrService,
@@ -36,6 +36,11 @@ export interface MergeNodeDeps {
36
36
  * Resolve a branch ref to its current SHA.
37
37
  */
38
38
  revParse: (cwd: string, ref: string) => Promise<string>;
39
+ /**
40
+ * Stage all changes and commit in the given directory.
41
+ * Returns the commit hash, or undefined if there was nothing to commit.
42
+ */
43
+ commitAll: (cwd: string, message: string) => Promise<string | undefined>;
39
44
  gitPrService: IGitPrService;
40
45
  cleanupFeatureWorktreeUseCase: Pick<CleanupFeatureWorktreeUseCase, 'execute'>;
41
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"merge.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yEAAyE,CAAC;AAClH,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACd,MAAM,iEAAiE,CAAC;AAmBzE,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,uEAAuE,CAAC;AAE3H,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1E,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,iBAAiB,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC;IACnE;;OAEG;IACH,gBAAgB,EAAE,CAChB,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,SAAS,CAAC,EAAE,OAAO,KAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB;;;OAGG;IACH,WAAW,EAAE,CACX,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,MAAM,KACrB,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB;;OAEG;IACH,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,YAAY,EAAE,aAAa,CAAC;IAC5B,6BAA6B,EAAE,IAAI,CAAC,6BAA6B,EAAE,SAAS,CAAC,CAAC;CAC/E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,IAGnC,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CA+T7E"}
1
+ {"version":3,"file":"merge.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yEAAyE,CAAC;AAClH,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACd,MAAM,iEAAiE,CAAC;AAmBzE,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,uEAAuE,CAAC;AAE3H,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1E,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,iBAAiB,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC;IACnE;;OAEG;IACH,gBAAgB,EAAE,CAChB,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,SAAS,CAAC,EAAE,OAAO,KAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB;;;OAGG;IACH,WAAW,EAAE,CACX,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,MAAM,KACrB,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB;;OAEG;IACH,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD;;;OAGG;IACH,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACzE,YAAY,EAAE,aAAa,CAAC;IAC5B,6BAA6B,EAAE,IAAI,CAAC,6BAA6B,EAAE,SAAS,CAAC,CAAC;CAC/E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,IAGnC,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CA0V7E"}