@shepai/cli 1.122.1 → 1.122.2-pr381.7d9e407

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 (148) 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 +187 -29
  5. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts.map +1 -1
  6. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.js +21 -4
  7. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
  8. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +9 -0
  9. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.d.ts +5 -0
  10. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.d.ts.map +1 -1
  11. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.js +82 -54
  12. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts +1 -1
  13. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.js +2 -2
  14. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/validate.node.d.ts +7 -1
  15. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/validate.node.d.ts.map +1 -1
  16. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/validate.node.js +24 -2
  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]__2698e9f4._.js +1 -1
  79. package/web/.next/server/chunks/ssr/[root-of-the-server]__2698e9f4._.js.map +1 -1
  80. package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js +1 -1
  81. package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js.map +1 -1
  82. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +1 -1
  83. package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js.map +1 -1
  84. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +2 -2
  85. package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
  86. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +1 -1
  87. package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js.map +1 -1
  88. package/web/.next/server/chunks/ssr/[root-of-the-server]__8c553503._.js +2 -2
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__8e3bf5d7._.js +1 -1
  90. package/web/.next/server/chunks/ssr/[root-of-the-server]__8e3bf5d7._.js.map +1 -1
  91. package/web/.next/server/chunks/ssr/[root-of-the-server]__a022445f._.js +2 -2
  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]__c07f9f79._.js +1 -1
  94. package/web/.next/server/chunks/ssr/[root-of-the-server]__c07f9f79._.js.map +1 -1
  95. package/web/.next/server/chunks/ssr/[root-of-the-server]__c605a375._.js +1 -1
  96. package/web/.next/server/chunks/ssr/[root-of-the-server]__c605a375._.js.map +1 -1
  97. package/web/.next/server/chunks/ssr/_0dc2cfd3._.js +1 -1
  98. package/web/.next/server/chunks/ssr/_0dc2cfd3._.js.map +1 -1
  99. package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
  100. package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
  101. package/web/.next/server/chunks/ssr/_204ecb5a._.js +3 -0
  102. package/web/.next/server/chunks/ssr/{_469c5451._.js.map → _204ecb5a._.js.map} +1 -1
  103. package/web/.next/server/chunks/ssr/_2f6f48b8._.js +1 -1
  104. package/web/.next/server/chunks/ssr/_2f6f48b8._.js.map +1 -1
  105. package/web/.next/server/chunks/ssr/{_92d851b4._.js → _434878d3._.js} +2 -2
  106. package/web/.next/server/chunks/ssr/{_92d851b4._.js.map → _434878d3._.js.map} +1 -1
  107. package/web/.next/server/chunks/ssr/{_b294a667._.js → _510be09a._.js} +2 -2
  108. package/web/.next/server/chunks/ssr/{_b294a667._.js.map → _510be09a._.js.map} +1 -1
  109. package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
  110. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
  111. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  112. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.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/{e6a2fb8707e079ff.js → 19401b65e1298cb0.js} +1 -1
  133. package/web/.next/static/chunks/{941c13dcdb1978a4.js → 2c0191c062d5f7ae.js} +2 -2
  134. package/web/.next/static/chunks/{9df4681de92ebfc3.js → 4da275e6ef808c4d.js} +1 -1
  135. package/web/.next/static/chunks/{78ab7b134f0649ac.js → 50bf223ed146d4ed.js} +1 -1
  136. package/web/.next/static/chunks/5a9e671eb04de758.js +1 -0
  137. package/web/.next/static/chunks/6a8fb1992b738cc9.js +1 -0
  138. package/web/.next/static/chunks/{37ac7fe1cd493293.js → 89ffd07cdb158389.js} +1 -1
  139. package/web/.next/static/chunks/{50dd80c8aafa3b20.js → a637e16d364af323.js} +1 -1
  140. package/web/.next/static/chunks/bc1ce100834f414a.js +1 -0
  141. package/web/.next/static/chunks/{658fc2f5f65cf3b5.js → df88f3e08b196bbf.js} +2 -2
  142. package/web/.next/server/chunks/ssr/_469c5451._.js +0 -3
  143. package/web/.next/static/chunks/11e4439096f8d652.js +0 -1
  144. package/web/.next/static/chunks/df85e52b1afb7340.js +0 -1
  145. package/web/.next/static/chunks/e9c3fbbeb418cbfa.js +0 -1
  146. /package/web/.next/static/{hsK1x0unrjI_pUsvQhn6P → NKkt3_PEmNxKD_ihRAzNE}/_buildManifest.js +0 -0
  147. /package/web/.next/static/{hsK1x0unrjI_pUsvQhn6P → NKkt3_PEmNxKD_ihRAzNE}/_clientMiddlewareManifest.json +0 -0
  148. /package/web/.next/static/{hsK1x0unrjI_pUsvQhn6P → NKkt3_PEmNxKD_ihRAzNE}/_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;AAMH,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;IA2MtF,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;IA+ElB,OAAO,CAAC,eAAe;CAgDxB"}
@@ -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 { writeFileSync, unlinkSync } from 'node:fs';
11
+ import { writeFileSync, unlinkSync, readdirSync, existsSync } from 'node:fs';
12
12
  import { execFileSync } from 'node:child_process';
13
13
  import { tmpdir } from 'node:os';
14
14
  import { join } from 'node:path';
@@ -36,11 +36,13 @@ const SUPPORTED_FEATURES = new Set(['session-resume', 'streaming']);
36
36
  */
37
37
  export class CursorExecutorService {
38
38
  spawn;
39
+ authConfig;
39
40
  agentType = 'cursor';
40
41
  /** When true, suppresses debug logging (set per-call via options.silent) */
41
42
  silent = false;
42
- constructor(spawn) {
43
+ constructor(spawn, authConfig) {
43
44
  this.spawn = spawn;
45
+ this.authConfig = authConfig;
44
46
  }
45
47
  /** Debug logging — writes to stdout so it appears in the worker log file */
46
48
  log(message) {
@@ -58,19 +60,34 @@ export class CursorExecutorService {
58
60
  // stream-json is unreliable on Windows where shell: true can mangle args.
59
61
  const args = this.buildArgs(prompt, options);
60
62
  const { proc, tmpFile } = this.spawnAgent(prompt, args, options);
63
+ // Diagnostic: verify streams are connected and process is alive
64
+ this.log(`[diag] proc.pid=${proc.pid}, killed=${proc.killed}`);
65
+ this.log(`[diag] stdout=${!!proc.stdout}, stderr=${!!proc.stderr}, stdin=${!!proc.stdin}`);
66
+ this.log(`[diag] CURSOR_API_KEY set=${!!process.env.CURSOR_API_KEY}, len=${process.env.CURSOR_API_KEY?.length ?? 0}`);
67
+ this.log(`[diag] authConfig.token set=${!!this.authConfig?.token}, method=${this.authConfig?.authMethod ?? 'none'}`);
68
+ this.log(`[diag] platform=${process.platform}, cwd=${options?.cwd ?? '(inherited)'}`);
61
69
  return new Promise((resolve, reject) => {
62
70
  let lineBuffer = '';
63
71
  let stderr = '';
64
72
  let timedOut = false;
65
73
  let timeoutId;
74
+ let stdoutChunks = 0;
75
+ let stderrChunks = 0;
76
+ const startTime = Date.now();
66
77
  // Accumulated from JSON events or raw text fallback
67
78
  let resultText = '';
68
79
  let rawText = '';
69
80
  let sessionId;
70
81
  let metadata;
82
+ // Periodic heartbeat to prove the executor is alive and waiting
83
+ const heartbeatId = setInterval(() => {
84
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
85
+ this.log(`[diag] heartbeat ${elapsed}s: stdout_chunks=${stdoutChunks}, stderr_chunks=${stderrChunks}, pid_killed=${proc.killed}`);
86
+ }, 15_000);
71
87
  if (options?.timeout) {
72
88
  timeoutId = setTimeout(() => {
73
89
  timedOut = true;
90
+ this.log(`[diag] TIMEOUT after ${options.timeout}ms — killing process tree`);
74
91
  // On Windows, proc.kill() may not kill the entire process tree
75
92
  // (PowerShell + child agent process). Use taskkill /T for tree kill.
76
93
  if (process.platform === 'win32' && proc.pid) {
@@ -78,9 +95,11 @@ export class CursorExecutorService {
78
95
  execFileSync('taskkill', ['/F', '/T', '/PID', String(proc.pid)], {
79
96
  stdio: 'ignore',
80
97
  });
98
+ this.log(`[diag] taskkill /T /PID ${proc.pid} succeeded`);
81
99
  }
82
100
  catch {
83
101
  proc.kill();
102
+ this.log(`[diag] taskkill failed, fell back to proc.kill()`);
84
103
  }
85
104
  }
86
105
  else {
@@ -107,17 +126,35 @@ export class CursorExecutorService {
107
126
  if (parsed.duration_ms !== undefined) {
108
127
  metadata = { ...metadata, duration_ms: parsed.duration_ms };
109
128
  }
129
+ if (parsed.is_error) {
130
+ this.log(`[diag] result event has is_error=true: ${JSON.stringify(parsed).slice(0, 300)}`);
131
+ }
132
+ }
133
+ else if (parsed.type === 'error') {
134
+ this.log(`[diag] cursor error event: ${JSON.stringify(parsed).slice(0, 500)}`);
135
+ }
136
+ else if (parsed.type !== 'user' &&
137
+ parsed.type !== 'system' &&
138
+ parsed.type !== 'thinking' &&
139
+ parsed.type !== 'tool_call') {
140
+ this.log(`[diag] unknown event type="${parsed.type}": ${JSON.stringify(parsed).slice(0, 300)}`);
110
141
  }
111
- // user, system, thinking events: logged but don't affect result
112
142
  }
113
143
  catch {
114
144
  // Non-JSON output — accumulate as raw text fallback
115
- if (line.length > 0)
145
+ if (line.length > 0) {
116
146
  rawText += `${line}\n`;
147
+ this.log(`[diag] non-JSON line (${line.length} chars): ${line.slice(0, 200)}`);
148
+ }
117
149
  }
118
150
  };
119
151
  proc.stdout?.on('data', (chunk) => {
120
- lineBuffer += chunk.toString();
152
+ stdoutChunks++;
153
+ const data = chunk.toString();
154
+ if (stdoutChunks <= 3) {
155
+ this.log(`[diag] stdout chunk #${stdoutChunks}: ${data.length} bytes, first 200: ${data.slice(0, 200).replace(/\n/g, '\\n')}`);
156
+ }
157
+ lineBuffer += data;
121
158
  const lines = lineBuffer.split('\n');
122
159
  lineBuffer = lines.pop() ?? '';
123
160
  for (const line of lines) {
@@ -127,19 +164,22 @@ export class CursorExecutorService {
127
164
  }
128
165
  });
129
166
  proc.stderr?.on('data', (chunk) => {
167
+ stderrChunks++;
130
168
  const data = chunk.toString();
131
169
  stderr += data;
132
- this.log(`stderr: ${data.trimEnd()}`);
170
+ this.log(`[diag] stderr chunk #${stderrChunks}: ${data.trimEnd()}`);
133
171
  // Detect fatal errors early so callers don't waste time retrying
134
172
  if (data.includes('Cannot use this model')) {
135
173
  if (timeoutId)
136
174
  clearTimeout(timeoutId);
175
+ clearInterval(heartbeatId);
137
176
  proc.kill();
138
177
  reject(new Error(data.trim()));
139
178
  }
140
179
  });
141
180
  proc.on('error', (error) => {
142
- this.log(`Process error event: ${error.message}`);
181
+ this.log(`[diag] Process ERROR event: ${error.message} (code=${error.code})`);
182
+ clearInterval(heartbeatId);
143
183
  if (timeoutId)
144
184
  clearTimeout(timeoutId);
145
185
  if (error.code === 'ENOENT') {
@@ -150,6 +190,8 @@ export class CursorExecutorService {
150
190
  }
151
191
  });
152
192
  proc.on('close', (code) => {
193
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
194
+ clearInterval(heartbeatId);
153
195
  // Clean up temp file on Windows
154
196
  if (tmpFile) {
155
197
  try {
@@ -163,7 +205,7 @@ export class CursorExecutorService {
163
205
  processLine(lineBuffer.trim());
164
206
  // Use raw text as fallback when no JSON result was captured
165
207
  const finalText = resultText || rawText.trim();
166
- this.log(`Process closed with code ${code}, result=${finalText.length} chars`);
208
+ 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
209
  if (timeoutId)
168
210
  clearTimeout(timeoutId);
169
211
  if (timedOut) {
@@ -171,9 +213,17 @@ export class CursorExecutorService {
171
213
  return;
172
214
  }
173
215
  if (code !== 0 && code !== null) {
174
- reject(new Error(stderr.trim() || `Process exited with code ${code}`));
216
+ const errMsg = stderr.trim() || `Process exited with code ${code}`;
217
+ this.log(`[diag] non-zero exit — rejecting with: ${errMsg.slice(0, 200)}`);
218
+ reject(new Error(errMsg));
175
219
  return;
176
220
  }
221
+ if (stdoutChunks === 0 && stderrChunks === 0) {
222
+ this.log(`[diag] WARNING: process exited with code 0 but produced ZERO output (stdout=0, stderr=0, elapsed=${elapsed}s)`);
223
+ }
224
+ if (!finalText && code === 0) {
225
+ this.log(`[diag] WARNING: process exited code=0 but result is empty (resultText=${resultText.length}, rawText=${rawText.length})`);
226
+ }
177
227
  const result = { result: finalText };
178
228
  if (sessionId)
179
229
  result.sessionId = sessionId;
@@ -184,20 +234,25 @@ export class CursorExecutorService {
184
234
  });
185
235
  }
186
236
  async *executeStream(prompt, options) {
237
+ this.silent = options?.silent ?? false;
187
238
  const args = this.buildStreamArgs(prompt, options);
188
239
  const { proc, tmpFile } = this.spawnAgent(prompt, args, options);
240
+ this.log(`[stream] pid=${proc.pid}, stdout=${!!proc.stdout}, stderr=${!!proc.stderr}`);
189
241
  let lineBuffer = '';
190
242
  let stderr = '';
243
+ let stdoutChunks = 0;
244
+ let stderrChunks = 0;
245
+ const startTime = Date.now();
191
246
  const queue = [];
192
247
  let resolve = null;
193
248
  let error = null;
194
- function enqueue(event) {
249
+ const enqueue = (event) => {
195
250
  queue.push(event);
196
251
  if (resolve) {
197
252
  resolve();
198
253
  resolve = null;
199
254
  }
200
- }
255
+ };
201
256
  function waitForItem() {
202
257
  if (queue.length > 0)
203
258
  return Promise.resolve();
@@ -205,8 +260,18 @@ export class CursorExecutorService {
205
260
  resolve = r;
206
261
  });
207
262
  }
263
+ // Heartbeat for stream mode too
264
+ const heartbeatId = setInterval(() => {
265
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
266
+ this.log(`[stream] heartbeat ${elapsed}s: stdout_chunks=${stdoutChunks}, stderr_chunks=${stderrChunks}, pid_killed=${proc.killed}`);
267
+ }, 30_000);
208
268
  proc.stdout?.on('data', (chunk) => {
209
- lineBuffer += chunk.toString();
269
+ stdoutChunks++;
270
+ const data = chunk.toString();
271
+ if (stdoutChunks <= 3) {
272
+ this.log(`[stream] stdout chunk #${stdoutChunks}: ${data.length} bytes`);
273
+ }
274
+ lineBuffer += data;
210
275
  const lines = lineBuffer.split('\n');
211
276
  lineBuffer = lines.pop() ?? '';
212
277
  for (const line of lines) {
@@ -219,13 +284,21 @@ export class CursorExecutorService {
219
284
  }
220
285
  });
221
286
  proc.stderr?.on('data', (chunk) => {
222
- stderr += chunk.toString();
287
+ stderrChunks++;
288
+ const data = chunk.toString();
289
+ stderr += data;
290
+ this.log(`[stream] stderr chunk #${stderrChunks}: ${data.trimEnd()}`);
223
291
  });
224
292
  proc.on('error', (err) => {
293
+ this.log(`[stream] Process ERROR: ${err.message}`);
294
+ clearInterval(heartbeatId);
225
295
  error = err;
226
296
  enqueue(null);
227
297
  });
228
298
  proc.on('close', (code) => {
299
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
300
+ clearInterval(heartbeatId);
301
+ this.log(`[stream] Process CLOSE: code=${code}, elapsed=${elapsed}s, stdout_chunks=${stdoutChunks}, stderr_chunks=${stderrChunks}`);
229
302
  if (tmpFile) {
230
303
  try {
231
304
  unlinkSync(tmpFile);
@@ -239,8 +312,10 @@ export class CursorExecutorService {
239
312
  if (event)
240
313
  enqueue(event);
241
314
  }
242
- if (code !== 0 && code !== null && stderr.trim()) {
243
- enqueue({ type: 'error', content: stderr.trim(), timestamp: new Date() });
315
+ if (code !== 0 && code !== null) {
316
+ const errMsg = stderr.trim() || `Process exited with code ${code}`;
317
+ this.log(`[stream] non-zero exit: ${errMsg}`);
318
+ enqueue({ type: 'error', content: errMsg, timestamp: new Date() });
244
319
  }
245
320
  enqueue(null);
246
321
  });
@@ -286,6 +361,10 @@ export class CursorExecutorService {
286
361
  }
287
362
  if (parsed.type === 'user')
288
363
  return; // Skip echoed input
364
+ // Log any unrecognized event type so nothing is silently dropped
365
+ if (!['assistant', 'tool_call', 'result', 'user', 'system', 'thinking'].includes(parsed.type)) {
366
+ this.log(`[diag] unhandled stream event type="${parsed.type}": ${line.slice(0, 200)}`);
367
+ }
289
368
  }
290
369
  catch {
291
370
  if (line.length > 0)
@@ -310,36 +389,115 @@ export class CursorExecutorService {
310
389
  return args;
311
390
  }
312
391
  /**
313
- * Spawn the agent process, handling Windows specially via PowerShell.
392
+ * Resolve the cursor CLI's bundled node.exe and index.js paths on Windows.
393
+ *
394
+ * The cursor CLI installs to `%LOCALAPPDATA%\cursor-agent\` with:
395
+ * - `agent.cmd` / `cursor-agent.ps1` (shell wrappers)
396
+ * - `versions/<YYYY.MM.DD-commit>/node.exe` + `index.js`
314
397
  *
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.
398
+ * Spawning via agent.cmd creates a problematic nesting chain:
399
+ * PowerShell agent.cmd PowerShell cursor-agent.ps1 node.exe
400
+ * This chain hangs on Windows CI runners. Direct invocation bypasses it.
401
+ *
402
+ * @returns `{ nodePath, indexPath }` or `undefined` if not found
403
+ */
404
+ resolveCursorBinary() {
405
+ const localAppData = process.env.LOCALAPPDATA;
406
+ if (!localAppData) {
407
+ this.log('[diag] resolveCursorBinary: LOCALAPPDATA not set');
408
+ return undefined;
409
+ }
410
+ const versionsDir = join(localAppData, 'cursor-agent', 'versions');
411
+ if (!existsSync(versionsDir)) {
412
+ this.log(`[diag] resolveCursorBinary: versions dir not found: ${versionsDir}`);
413
+ return undefined;
414
+ }
415
+ // Find the latest version directory (format: YYYY.MM.DD-commit)
416
+ const versionPattern = /^\d{4}\.\d{1,2}\.\d{1,2}-[a-f0-9]+$/;
417
+ const versions = readdirSync(versionsDir)
418
+ .filter((d) => versionPattern.test(d))
419
+ .sort()
420
+ .reverse();
421
+ if (versions.length === 0) {
422
+ this.log(`[diag] resolveCursorBinary: no version dirs in ${versionsDir}`);
423
+ return undefined;
424
+ }
425
+ const versionDir = join(versionsDir, versions[0]);
426
+ const nodePath = join(versionDir, 'node.exe');
427
+ const indexPath = join(versionDir, 'index.js');
428
+ if (!existsSync(nodePath) || !existsSync(indexPath)) {
429
+ this.log(`[diag] resolveCursorBinary: missing binary — node=${existsSync(nodePath)}, index=${existsSync(indexPath)}`);
430
+ return undefined;
431
+ }
432
+ this.log(`[diag] resolveCursorBinary: resolved version=${versions[0]}`);
433
+ return { nodePath, indexPath };
434
+ }
435
+ /**
436
+ * Spawn the agent process, handling Windows specially.
437
+ *
438
+ * On Windows, the cursor CLI ships as `agent.cmd` which spawns PowerShell
439
+ * which runs `cursor-agent.ps1` which runs `node.exe index.js`. This triple
440
+ * nesting (PowerShell → cmd.exe → PowerShell → node.exe) hangs on Windows
441
+ * CI runners. Solution: resolve the cursor CLI's bundled `node.exe` and
442
+ * `index.js` directly, bypassing all shell wrappers.
320
443
  *
321
444
  * On Linux/macOS, spawn `agent` directly — no shell needed.
322
445
  */
323
- spawnAgent(prompt, args, options) {
446
+ /**
447
+ * Build the subprocess environment: strip CLAUDECODE, inject CURSOR_API_KEY
448
+ * from authConfig when token auth is configured.
449
+ */
450
+ buildEnv(extra) {
324
451
  const { CLAUDECODE: _, ...cleanEnv } = process.env;
452
+ // Inject CURSOR_API_KEY from stored settings so the cursor CLI authenticates
453
+ // even when the env var isn't inherited (e.g. direct node.exe invocation on Windows).
454
+ if (this.authConfig?.authMethod === 'token' && this.authConfig.token) {
455
+ cleanEnv.CURSOR_API_KEY = this.authConfig.token;
456
+ this.log(`[diag] buildEnv: injected CURSOR_API_KEY from authConfig (${this.authConfig.token.length} chars)`);
457
+ }
458
+ else {
459
+ this.log(`[diag] buildEnv: NO token injection (authMethod=${this.authConfig?.authMethod ?? 'none'}, hasToken=${!!this.authConfig?.token}, envKey=${!!cleanEnv.CURSOR_API_KEY})`);
460
+ }
461
+ const env = extra ? { ...cleanEnv, ...extra } : cleanEnv;
462
+ this.log(`[diag] buildEnv: final CURSOR_API_KEY in env=${!!env.CURSOR_API_KEY}, len=${env.CURSOR_API_KEY?.length ?? 0}`);
463
+ return env;
464
+ }
465
+ spawnAgent(prompt, args, options) {
325
466
  const cwd = options?.cwd;
326
467
  if (IS_WINDOWS) {
327
- // Write prompt to temp file to bypass cmd.exe argument mangling
468
+ const resolved = this.resolveCursorBinary();
469
+ if (resolved) {
470
+ // Direct invocation: node.exe index.js <args>
471
+ // No shell wrappers, no nesting, no hangs.
472
+ const directArgs = [resolved.indexPath, ...args];
473
+ this.log(`Windows direct mode: ${resolved.nodePath}`);
474
+ this.log(`Args: ${directArgs.map((a) => (a.length > 80 ? `${a.slice(0, 77)}...` : a)).join(' ')}`);
475
+ const proc = this.spawn(resolved.nodePath, directArgs, {
476
+ cwd,
477
+ stdio: ['pipe', 'pipe', 'pipe'],
478
+ windowsHide: true,
479
+ env: this.buildEnv({ CURSOR_INVOKED_AS: 'agent' }),
480
+ });
481
+ this.log(`Direct PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
482
+ if (proc.stdin)
483
+ proc.stdin.end();
484
+ return { proc, tmpFile: undefined };
485
+ }
486
+ // Fallback: if direct resolution fails, use PowerShell + temp file
487
+ this.log('[diag] Windows fallback: cursor binary not resolved, using PowerShell + temp file');
328
488
  const tmpFile = join(tmpdir(), `shep-cursor-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.txt`);
329
489
  writeFileSync(tmpFile, prompt, 'utf8');
330
- // Build the agent args WITHOUT -p and the prompt (they go via temp file)
331
490
  const agentFlags = args.filter((a) => a !== '-p' && a !== prompt).join(' ');
332
491
  const safePath = tmpFile.replace(/'/g, "''");
333
492
  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>`)}`);
493
+ this.log(`[diag] PS command: ${psCmd.replace(prompt, `<${prompt.length} chars>`)}`);
336
494
  const proc = this.spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', psCmd], {
337
495
  cwd,
338
496
  stdio: ['pipe', 'pipe', 'pipe'],
339
497
  windowsHide: true,
340
- env: cleanEnv,
498
+ env: this.buildEnv(),
341
499
  });
342
- this.log(`PowerShell PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
500
+ this.log(`[diag] PowerShell PID: ${proc.pid ?? 'undefined (spawn may have failed)'}`);
343
501
  if (proc.stdin)
344
502
  proc.stdin.end();
345
503
  return { proc, tmpFile };
@@ -347,7 +505,7 @@ export class CursorExecutorService {
347
505
  // Linux/macOS: spawn agent directly, no shell needed
348
506
  const spawnOpts = {
349
507
  stdio: ['pipe', 'pipe', 'pipe'],
350
- env: cleanEnv,
508
+ env: this.buildEnv(),
351
509
  };
352
510
  if (cwd)
353
511
  spawnOpts.cwd = cwd;
@@ -1 +1 @@
1
- {"version":3,"file":"feature-agent-graph.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AAOpG,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AASlF,OAAO,EAAE,sBAAsB,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE5E;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,cAAc,CAAC;IACzB,aAAa,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;CACjD;AA0HD;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,qBAAqB,GAAG,cAAc,EACtD,YAAY,CAAC,EAAE,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAwEnC"}
1
+ {"version":3,"file":"feature-agent-graph.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AAOpG,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAclF,OAAO,EAAE,sBAAsB,EAAE,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE5E;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,cAAc,CAAC;IACzB,aAAa,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;CACjD;AAwID;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,qBAAqB,GAAG,cAAc,EACtD,YAAY,CAAC,EAAE,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAgFnC"}
@@ -11,7 +11,7 @@ import { createRepairNode } from './nodes/repair.node.js';
11
11
  import { validateSpecAnalyze, validateSpecRequirements } from './nodes/schemas/spec.schema.js';
12
12
  import { validateResearch } from './nodes/schemas/research.schema.js';
13
13
  import { validatePlan, validateTasks } from './nodes/schemas/plan.schema.js';
14
- import { readSpecFile, safeYamlLoad, createNodeLogger } from './nodes/node-helpers.js';
14
+ import { readSpecFile, safeYamlLoad, createNodeLogger, getCompletedPhases, } from './nodes/node-helpers.js';
15
15
  // Re-export state types for consumers
16
16
  export { FeatureAgentAnnotation } from './state.js';
17
17
  /**
@@ -54,6 +54,18 @@ function createPlanTasksValidator() {
54
54
  const log = createNodeLogger('validate:plan+tasks');
55
55
  return async (state) => {
56
56
  log.activate();
57
+ // Skip validation when the successor phase (implement) is already completed.
58
+ // This proves validation already passed and the pipeline progressed beyond it.
59
+ // Same logic as createValidateNode's successorPhase guard.
60
+ if (getCompletedPhases(state.specDir).includes('implement')) {
61
+ log.info("Successor phase 'implement' already completed, skipping validation");
62
+ return {
63
+ lastValidationTarget: 'plan.yaml+tasks.yaml',
64
+ lastValidationErrors: [],
65
+ validationRetries: 0,
66
+ messages: ["[validate:plan+tasks] SKIP (successor 'implement' already completed)"],
67
+ };
68
+ }
57
69
  log.info('Validating plan.yaml and tasks.yaml');
58
70
  const allErrors = [];
59
71
  // Validate plan.yaml
@@ -145,9 +157,14 @@ export function createFeatureAgentGraph(depsOrExecutor, checkpointer) {
145
157
  .addNode('plan', createPlanNode(executor))
146
158
  .addNode('implement', createImplementNode(executor))
147
159
  // --- Validate nodes ---
148
- .addNode('validate_spec_analyze', createValidateNode('spec.yaml', validateSpecAnalyze))
149
- .addNode('validate_spec_requirements', createValidateNode('spec.yaml', validateSpecRequirements))
150
- .addNode('validate_research', createValidateNode('research.yaml', validateResearch))
160
+ // Each validate node receives its SUCCESSOR phase name (the phase that runs
161
+ // after validation passes). On resume, validation is skipped only when the
162
+ // successor is already completed — proving this validation already passed.
163
+ // Using the successor (not the producer) avoids skipping validation when the
164
+ // producer ran successfully but validation itself failed.
165
+ .addNode('validate_spec_analyze', createValidateNode('spec.yaml', validateSpecAnalyze, 'requirements'))
166
+ .addNode('validate_spec_requirements', createValidateNode('spec.yaml', validateSpecRequirements, 'research'))
167
+ .addNode('validate_research', createValidateNode('research.yaml', validateResearch, 'plan'))
151
168
  .addNode('validate_plan_tasks', createPlanTasksValidator())
152
169
  // --- Repair nodes ---
153
170
  .addNode('repair_spec_analyze', createRepairNode('spec.yaml', executor))
@@ -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"}