@shepai/cli 1.108.0 → 1.109.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/dist/packages/core/src/application/use-cases/upgrade/upgrade-cli.use-case.d.ts +9 -1
  2. package/dist/packages/core/src/application/use-cases/upgrade/upgrade-cli.use-case.d.ts.map +1 -1
  3. package/dist/packages/core/src/application/use-cases/upgrade/upgrade-cli.use-case.js +31 -2
  4. package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
  5. package/dist/packages/core/src/infrastructure/di/container.js +8 -4
  6. package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations.d.ts.map +1 -1
  7. package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations.js +12 -0
  8. package/dist/packages/core/src/infrastructure/platform.d.ts +7 -0
  9. package/dist/packages/core/src/infrastructure/platform.d.ts.map +1 -0
  10. package/dist/packages/core/src/infrastructure/platform.js +6 -0
  11. package/dist/packages/core/src/infrastructure/services/agents/common/executors/claude-code-executor.service.d.ts.map +1 -1
  12. package/dist/packages/core/src/infrastructure/services/agents/common/executors/claude-code-executor.service.js +2 -1
  13. package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.d.ts.map +1 -1
  14. package/dist/packages/core/src/infrastructure/services/agents/common/executors/cursor-executor.service.js +7 -0
  15. package/dist/packages/core/src/infrastructure/services/git/worktree.service.d.ts.map +1 -1
  16. package/dist/packages/core/src/infrastructure/services/git/worktree.service.js +2 -1
  17. package/dist/packages/core/src/infrastructure/services/pr-sync/pr-sync-watcher.service.d.ts +13 -2
  18. package/dist/packages/core/src/infrastructure/services/pr-sync/pr-sync-watcher.service.d.ts.map +1 -1
  19. package/dist/packages/core/src/infrastructure/services/pr-sync/pr-sync-watcher.service.js +80 -3
  20. package/dist/packages/core/src/infrastructure/services/web-server.service.d.ts.map +1 -1
  21. package/dist/packages/core/src/infrastructure/services/web-server.service.js +15 -0
  22. package/dist/src/presentation/cli/commands/ui.command.d.ts.map +1 -1
  23. package/dist/src/presentation/cli/commands/ui.command.js +3 -1
  24. package/dist/src/presentation/web/app/actions/get-feature-drawer-data.d.ts +3 -0
  25. package/dist/src/presentation/web/app/actions/get-feature-drawer-data.d.ts.map +1 -1
  26. package/dist/src/presentation/web/app/actions/get-feature-drawer-data.js +7 -32
  27. package/dist/src/presentation/web/app/api/cli-upgrade/route.d.ts.map +1 -1
  28. package/dist/src/presentation/web/app/api/cli-upgrade/route.js +14 -1
  29. package/dist/src/presentation/web/app/api/tools/[id]/launch/route.js +1 -1
  30. package/dist/src/presentation/web/components/common/version-badge/version-badge.d.ts.map +1 -1
  31. package/dist/src/presentation/web/components/common/version-badge/version-badge.js +6 -1
  32. package/dist/src/presentation/web/components/common/version-badge/version-badge.stories.d.ts.map +1 -1
  33. package/dist/src/presentation/web/components/common/version-badge/version-badge.stories.js +7 -0
  34. package/dist/src/presentation/web/dev-server.js +3 -1
  35. package/dist/src/presentation/web/hooks/use-cli-upgrade.d.ts +1 -1
  36. package/dist/src/presentation/web/hooks/use-cli-upgrade.d.ts.map +1 -1
  37. package/dist/src/presentation/web/hooks/use-cli-upgrade.js +40 -1
  38. package/dist/src/presentation/web/lib/skills.js +1 -1
  39. package/dist/tsconfig.build.tsbuildinfo +1 -1
  40. package/package.json +1 -1
  41. package/web/.next/BUILD_ID +1 -1
  42. package/web/.next/build-manifest.json +2 -2
  43. package/web/.next/fallback-build-manifest.json +2 -2
  44. package/web/.next/prerender-manifest.json +3 -3
  45. package/web/.next/required-server-files.js +2 -2
  46. package/web/.next/required-server-files.json +2 -2
  47. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +20 -20
  48. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  49. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  50. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +26 -26
  51. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  52. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  53. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +26 -26
  54. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  55. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  56. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +18 -18
  57. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  58. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  59. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +20 -20
  60. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  61. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  62. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +26 -26
  63. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  64. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  65. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +26 -26
  66. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  67. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  68. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +18 -18
  69. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  70. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  71. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +18 -18
  72. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  73. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  74. package/web/.next/server/app/_global-error.html +2 -2
  75. package/web/.next/server/app/_global-error.rsc +1 -1
  76. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  77. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  78. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  79. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  80. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  81. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
  82. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  83. package/web/.next/server/app/settings/page/server-reference-manifest.json +5 -5
  84. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  85. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  86. package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
  87. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  88. package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
  89. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  90. package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
  91. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  92. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  93. package/web/.next/server/chunks/[root-of-the-server]__b4102cc7._.js +1 -1
  94. package/web/.next/server/chunks/[root-of-the-server]__b4102cc7._.js.map +1 -1
  95. package/web/.next/server/chunks/[root-of-the-server]__d2c18946._.js +7 -4
  96. package/web/.next/server/chunks/[root-of-the-server]__d2c18946._.js.map +1 -1
  97. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  98. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  99. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_a1104008.js +1 -1
  100. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_a1104008.js.map +1 -1
  101. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_92642200.js +1 -1
  102. package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_92642200.js.map +1 -1
  103. package/web/.next/server/chunks/ssr/[root-of-the-server]__039967e3._.js +1 -1
  104. package/web/.next/server/chunks/ssr/[root-of-the-server]__039967e3._.js.map +1 -1
  105. package/web/.next/server/chunks/ssr/[root-of-the-server]__07a39ff2._.js +1 -1
  106. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +1 -1
  107. package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js.map +1 -1
  108. package/web/.next/server/chunks/ssr/[root-of-the-server]__24dd0927._.js +2 -2
  109. package/web/.next/server/chunks/ssr/[root-of-the-server]__2773312c._.js +1 -1
  110. package/web/.next/server/chunks/ssr/[root-of-the-server]__2773312c._.js.map +1 -1
  111. package/web/.next/server/chunks/ssr/[root-of-the-server]__5d87af2e._.js +1 -1
  112. package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js +1 -1
  113. package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js.map +1 -1
  114. package/web/.next/server/chunks/ssr/[root-of-the-server]__89e8a5cc._.js +2 -2
  115. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +2 -2
  116. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  117. package/web/.next/server/chunks/ssr/[root-of-the-server]__cbd8062c._.js +1 -1
  118. package/web/.next/server/chunks/ssr/[root-of-the-server]__cbd8062c._.js.map +1 -1
  119. package/web/.next/server/chunks/ssr/[root-of-the-server]__e70c5627._.js +1 -1
  120. package/web/.next/server/chunks/ssr/[root-of-the-server]__e70c5627._.js.map +1 -1
  121. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +1 -1
  122. package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js.map +1 -1
  123. package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
  124. package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
  125. package/web/.next/server/chunks/ssr/{_091ab7f5._.js → _3153d48a._.js} +2 -2
  126. package/web/.next/server/chunks/ssr/{_091ab7f5._.js.map → _3153d48a._.js.map} +1 -1
  127. package/web/.next/server/chunks/ssr/_3a4c94e3._.js +1 -1
  128. package/web/.next/server/chunks/ssr/_3a4c94e3._.js.map +1 -1
  129. package/web/.next/server/chunks/ssr/{_8affa5ab._.js → _3cf088ad._.js} +2 -2
  130. package/web/.next/server/chunks/ssr/{_8affa5ab._.js.map → _3cf088ad._.js.map} +1 -1
  131. package/web/.next/server/chunks/ssr/{_a2f904c8._.js → _60c0ee90._.js} +2 -2
  132. package/web/.next/server/chunks/ssr/{_a2f904c8._.js.map → _60c0ee90._.js.map} +1 -1
  133. package/web/.next/server/chunks/ssr/_8dacd1e7._.js +1 -1
  134. package/web/.next/server/chunks/ssr/_8dacd1e7._.js.map +1 -1
  135. package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
  136. package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
  137. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  138. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  139. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_888633e1.js +1 -1
  140. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_888633e1.js.map +1 -1
  141. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
  142. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
  143. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
  144. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
  145. package/web/.next/server/chunks/ssr/src_presentation_web_ce7cbc3c._.js +2 -2
  146. package/web/.next/server/chunks/ssr/src_presentation_web_ce7cbc3c._.js.map +1 -1
  147. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
  148. package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js.map +1 -1
  149. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
  150. package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js.map +1 -1
  151. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  152. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  153. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  154. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  155. package/web/.next/server/chunks/ssr/src_presentation_web_e2604413._.js +2 -2
  156. package/web/.next/server/chunks/ssr/src_presentation_web_e2604413._.js.map +1 -1
  157. package/web/.next/server/pages/500.html +2 -2
  158. package/web/.next/server/server-reference-manifest.js +1 -1
  159. package/web/.next/server/server-reference-manifest.json +31 -31
  160. package/web/.next/static/chunks/{41ad7066036d3e01.js → 0eb46e039de29709.js} +1 -1
  161. package/web/.next/static/chunks/{b48405e5d661490f.js → 12ed4b1027304c53.js} +1 -1
  162. package/web/.next/static/chunks/{fecdfab54bbebc7f.js → 3cc01700406a9660.js} +2 -2
  163. package/web/.next/static/chunks/{089b3c720afef02f.js → 43a4b98f3ac48b44.js} +2 -2
  164. package/web/.next/static/chunks/{3ac71f5bdd986ebf.js → 591115964a2af7de.js} +1 -1
  165. package/web/.next/static/chunks/{2946ec503df0563c.js → 7bc70c154d2825a4.js} +1 -1
  166. package/web/.next/static/chunks/{55e50bf848962a77.js → 80865db7b5a35784.js} +1 -1
  167. package/web/.next/static/chunks/{3fc0c2bdfd985563.js → 888bfe33d066e5c9.js} +1 -1
  168. package/web/.next/static/chunks/bb855bbee5f9e6ea.js +1 -0
  169. package/web/.next/static/chunks/{1e5bd91d971ede3c.js → e60be61a5255ae15.js} +1 -1
  170. package/web/.next/static/chunks/{d7817aae0db61e25.js → f42fca4eab903eec.js} +1 -1
  171. package/web/.next/static/chunks/3bb35e0689e8b6b7.js +0 -1
  172. /package/web/.next/static/{9T2DFw6aFI0waogTIo2px → yzbpkR6_oV_y71iaTXR8w}/_buildManifest.js +0 -0
  173. /package/web/.next/static/{9T2DFw6aFI0waogTIo2px → yzbpkR6_oV_y71iaTXR8w}/_clientMiddlewareManifest.json +0 -0
  174. /package/web/.next/static/{9T2DFw6aFI0waogTIo2px → yzbpkR6_oV_y71iaTXR8w}/_ssgManifest.js +0 -0
@@ -11,6 +11,7 @@
11
11
  * 4. Return result with old/new version info
12
12
  */
13
13
  import type { IVersionService } from '../../ports/output/services/version-service.interface.js';
14
+ import type { IDaemonService } from '../../ports/output/services/daemon-service.interface.js';
14
15
  export interface UpgradeResult {
15
16
  status: 'up-to-date' | 'upgraded' | 'error';
16
17
  currentVersion: string;
@@ -19,9 +20,16 @@ export interface UpgradeResult {
19
20
  }
20
21
  export declare class UpgradeCliUseCase {
21
22
  private readonly versionService;
22
- constructor(versionService: IVersionService);
23
+ private readonly daemonService;
24
+ constructor(versionService: IVersionService, daemonService: IDaemonService);
23
25
  execute(onOutput?: (data: string) => void): Promise<UpgradeResult>;
24
26
  private getLatestVersion;
27
+ /**
28
+ * Schedule a daemon self-restart after upgrade.
29
+ * Reads the current daemon port, spawns a new daemon process with the
30
+ * upgraded code, then exits the current process after a brief delay.
31
+ */
32
+ scheduleDaemonRestart(): Promise<void>;
25
33
  private runNpmInstall;
26
34
  }
27
35
  //# sourceMappingURL=upgrade-cli.use-case.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade-cli.use-case.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/application/use-cases/upgrade/upgrade-cli.use-case.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAEhG,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID,qBACa,iBAAiB;IAG1B,OAAO,CAAC,QAAQ,CAAC,cAAc;gBAAd,cAAc,EAAE,eAAe;IAG5C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC;IAkCxE,OAAO,CAAC,gBAAgB;IA6CxB,OAAO,CAAC,aAAa;CAuBtB"}
1
+ {"version":3,"file":"upgrade-cli.use-case.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/application/use-cases/upgrade/upgrade-cli.use-case.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAChG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yDAAyD,CAAC;AAE9F,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAOD,qBACa,iBAAiB;IAG1B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAE/B,OAAO,CAAC,QAAQ,CAAC,aAAa;gBAFb,cAAc,EAAE,eAAe,EAE/B,aAAa,EAAE,cAAc;IAG1C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC;IAkCxE,OAAO,CAAC,gBAAgB;IA6CxB;;;;OAIG;IACG,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B5C,OAAO,CAAC,aAAa;CAuBtB"}
@@ -25,10 +25,14 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
25
25
  import { injectable, inject } from 'tsyringe';
26
26
  import { spawn } from 'node:child_process';
27
27
  const VERSION_CHECK_TIMEOUT_MS = 10_000;
28
+ /** Delay (ms) before the current process exits to allow the SSE response to flush. */
29
+ const SELF_RESTART_DELAY_MS = 1_000;
28
30
  let UpgradeCliUseCase = class UpgradeCliUseCase {
29
31
  versionService;
30
- constructor(versionService) {
32
+ daemonService;
33
+ constructor(versionService, daemonService) {
31
34
  this.versionService = versionService;
35
+ this.daemonService = daemonService;
32
36
  }
33
37
  async execute(onOutput) {
34
38
  const { version: currentVersion } = this.versionService.getVersion();
@@ -100,6 +104,30 @@ let UpgradeCliUseCase = class UpgradeCliUseCase {
100
104
  });
101
105
  });
102
106
  }
107
+ /**
108
+ * Schedule a daemon self-restart after upgrade.
109
+ * Reads the current daemon port, spawns a new daemon process with the
110
+ * upgraded code, then exits the current process after a brief delay.
111
+ */
112
+ async scheduleDaemonRestart() {
113
+ const state = await this.daemonService.read();
114
+ const port = state?.port;
115
+ // Delete daemon.json so the new process can write a fresh one
116
+ await this.daemonService.delete();
117
+ // Spawn new daemon process using the upgraded CLI binary.
118
+ // Use process.argv[1] which points to the shep CLI entry point —
119
+ // after npm install this resolves to the upgraded version on disk.
120
+ const child = spawn(process.execPath, [...process.execArgv, process.argv[1], '_serve', ...(port ? ['--port', String(port)] : [])], {
121
+ detached: true,
122
+ stdio: 'ignore',
123
+ windowsHide: true,
124
+ });
125
+ child.unref();
126
+ // Give the response stream time to flush, then exit the current process
127
+ setTimeout(() => {
128
+ process.exit(0);
129
+ }, SELF_RESTART_DELAY_MS);
130
+ }
103
131
  runNpmInstall(onOutput) {
104
132
  return new Promise((resolve, reject) => {
105
133
  const child = spawn('npm', ['i', '-g', '@shepai/cli@latest'], {
@@ -123,6 +151,7 @@ let UpgradeCliUseCase = class UpgradeCliUseCase {
123
151
  UpgradeCliUseCase = __decorate([
124
152
  injectable(),
125
153
  __param(0, inject('IVersionService')),
126
- __metadata("design:paramtypes", [Object])
154
+ __param(1, inject('IDaemonService')),
155
+ __metadata("design:paramtypes", [Object, Object])
127
156
  ], UpgradeCliUseCase);
128
157
  export { UpgradeCliUseCase };
@@ -1 +1 @@
1
- {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAkHrC;;;;;;GAMG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,SAAS,CAAC,CA6TrE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAmHrC;;;;;;GAMG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,SAAS,CAAC,CA+TrE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;GAGG;AACH,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -18,6 +18,7 @@ import { SQLiteRepositoryRepository } from '../repositories/sqlite-repository.re
18
18
  import { AgentValidatorService } from '../services/agents/common/agent-validator.service.js';
19
19
  import { promisify } from 'node:util';
20
20
  import { execFile } from 'node:child_process';
21
+ import { IS_WINDOWS } from '../platform.js';
21
22
  import { VersionService } from '../services/version.service.js';
22
23
  import { WorktreeService } from '../services/git/worktree.service.js';
23
24
  import { ToolInstallerServiceImpl } from '../services/tool-installer/tool-installer.service.js';
@@ -129,7 +130,7 @@ export async function initializeContainer() {
129
130
  // On Windows, agent CLIs ship as .cmd/.ps1 scripts (e.g. cursor's `agent.cmd`).
130
131
  // execFile without shell: true cannot resolve .cmd extensions, causing ENOENT.
131
132
  const execFileAsync = promisify(execFile);
132
- const execFn = process.platform === 'win32'
133
+ const execFn = IS_WINDOWS
133
134
  ? (file, args, options) => execFileAsync(file, args, { ...options, shell: true, windowsHide: true })
134
135
  : execFileAsync;
135
136
  container.registerInstance('ExecFunction', execFn);
@@ -190,12 +191,15 @@ export async function initializeContainer() {
190
191
  container.register('IAgentExecutorFactory', {
191
192
  useFactory: () => {
192
193
  // Wrap spawn to ensure stdio is explicitly set to 'pipe'.
193
- // On Windows, .cmd scripts (e.g. cursor's `agent.cmd`) need shell: true.
194
+ // Do NOT force shell: true here each executor controls its own spawn
195
+ // options via buildSpawnOptions(). Forcing shell: true globally causes
196
+ // DEP0190 argument escaping issues on Windows that mangle long prompts.
197
+ // Executors that need shell: true (e.g. cursor for .cmd scripts) set it
198
+ // themselves; native executables (claude, gemini) must NOT use shell: true.
194
199
  const spawnWithPipe = (command, args, options) => {
195
200
  return spawn(command, args, {
196
- ...options,
197
201
  stdio: 'pipe',
198
- ...(process.platform === 'win32' ? { shell: true, windowsHide: true } : {}),
202
+ ...options,
199
203
  });
200
204
  };
201
205
  return new AgentExecutorFactory(spawnWithPipe);
@@ -1 +1 @@
1
- {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/persistence/sqlite/migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAggB3C;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAA4C,CAAC;AAE/E;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B9E"}
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/persistence/sqlite/migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AA4gB3C;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAA4C,CAAC;AAE/E;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B9E"}
@@ -499,6 +499,18 @@ WHERE repository_path NOT IN (SELECT path FROM repositories WHERE path IS NOT NU
499
499
  // Migration 032: Change envDeploy feature flag default to enabled (1).
500
500
  sql: `UPDATE settings SET feature_flag_env_deploy = 1 WHERE feature_flag_env_deploy = 0;`,
501
501
  },
502
+ {
503
+ version: 33,
504
+ sql: `
505
+ -- Migration 033: Create pr_sync_lock table for cross-process poll coordination
506
+ CREATE TABLE IF NOT EXISTS pr_sync_lock (
507
+ id INTEGER PRIMARY KEY CHECK (id = 1),
508
+ locked_by TEXT NOT NULL,
509
+ locked_at INTEGER NOT NULL,
510
+ expires_at INTEGER NOT NULL
511
+ );
512
+ `,
513
+ },
502
514
  ];
503
515
  /**
504
516
  * The latest schema version (highest migration version number).
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Platform detection constants.
3
+ *
4
+ * Evaluated once at module load — safe to use in hot paths.
5
+ */
6
+ export declare const IS_WINDOWS: boolean;
7
+ //# sourceMappingURL=platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../../../../packages/core/src/infrastructure/platform.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,UAAU,SAA+B,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Platform detection constants.
3
+ *
4
+ * Evaluated once at module load — safe to use in hot paths.
5
+ */
6
+ export const IS_WINDOWS = process.platform === 'win32';
@@ -1 +1 @@
1
- {"version":3,"file":"claude-code-executor.service.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/common/executors/claude-code-executor.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,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;AAYjD;;;GAGG;AACH,qBAAa,yBAA0B,YAAW,cAAc;IAMlD,OAAO,CAAC,QAAQ,CAAC,KAAK;IALlC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAA8B;IAE3D,4EAA4E;IAC5E,OAAO,CAAC,MAAM,CAAS;gBAEM,KAAK,EAAE,aAAa;IAEjD,4EAA4E;IAC5E,OAAO,CAAC,GAAG;IAML,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAmHtF,aAAa,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,aAAa,CAAC,yBAAyB,CAAC;IA+F3C;;;OAGG;IACH,OAAO,CAAC,cAAc;IAmCtB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAI/C,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,iBAAiB;IAyBzB,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,eAAe;CAkExB"}
1
+ {"version":3,"file":"claude-code-executor.service.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/common/executors/claude-code-executor.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,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;AAajD;;;GAGG;AACH,qBAAa,yBAA0B,YAAW,cAAc;IAMlD,OAAO,CAAC,QAAQ,CAAC,KAAK;IALlC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAA8B;IAE3D,4EAA4E;IAC5E,OAAO,CAAC,MAAM,CAAS;gBAEM,KAAK,EAAE,aAAa;IAEjD,4EAA4E;IAC5E,OAAO,CAAC,GAAG;IAML,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAmHtF,aAAa,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,aAAa,CAAC,yBAAyB,CAAC;IA+F3C;;;OAGG;IACH,OAAO,CAAC,cAAc;IAmCtB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAI/C,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,iBAAiB;IAyBzB,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,eAAe;CAkExB"}
@@ -9,6 +9,7 @@
9
9
  * to enable testability without mocking node:child_process directly.
10
10
  */
11
11
  import { getCurrentPhase, getLogPrefix } from '../../feature-agent/log-context.js';
12
+ import { IS_WINDOWS } from '../../../../platform.js';
12
13
  /** Features supported by Claude Code CLI */
13
14
  const SUPPORTED_FEATURES = new Set([
14
15
  'session-resume',
@@ -297,7 +298,7 @@ export class ClaudeCodeExecutorService {
297
298
  // Do NOT use shell=true — it causes DEP0190 argument escaping issues
298
299
  // and mangles prompts with special characters. The claude CLI is a
299
300
  // native .exe, so spawn() finds it on PATH without shell.
300
- if (process.platform === 'win32') {
301
+ if (IS_WINDOWS) {
301
302
  spawnOpts.windowsHide = true;
302
303
  }
303
304
  // Strip CLAUDECODE env var to prevent "nested session" error when shep
@@ -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;AAEH,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;AAsBjD;;;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;IA0GtF,aAAa,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,aAAa,CAAC,yBAAyB,CAAC;IA8E3C;;OAEG;IACH,OAAO,CAAC,cAAc;IAkCtB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,iBAAiB;IAazB,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;AAEH,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;IA0GtF,aAAa,CAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,aAAa,CAAC,yBAAyB,CAAC;IA8E3C;;OAEG;IACH,OAAO,CAAC,cAAc;IAkCtB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,eAAe;CAgDxB"}
@@ -9,6 +9,7 @@
9
9
  * to enable testability without mocking node:child_process directly.
10
10
  */
11
11
  import { getCurrentPhase, getLogPrefix } from '../../feature-agent/log-context.js';
12
+ import { IS_WINDOWS } from '../../../../platform.js';
12
13
  /**
13
14
  * Map canonical model IDs (used across shep) to Cursor CLI model names.
14
15
  * Cursor uses short names like "sonnet-4.6" instead of "claude-sonnet-4-6".
@@ -274,6 +275,12 @@ export class CursorExecutorService {
274
275
  const spawnOpts = {};
275
276
  if (options?.cwd)
276
277
  spawnOpts.cwd = options.cwd;
278
+ // On Windows, the cursor agent is a .cmd script which requires shell: true
279
+ // to resolve via PATH. Also hide the console window.
280
+ if (IS_WINDOWS) {
281
+ spawnOpts.shell = true;
282
+ spawnOpts.windowsHide = true;
283
+ }
277
284
  // Strip CLAUDECODE env var to prevent "nested session" error when shep
278
285
  // is invoked from within a Claude Code session.
279
286
  const { CLAUDECODE: _, ...cleanEnv } = process.env;
@@ -1 +1 @@
1
- {"version":3,"file":"worktree.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/worktree.service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,0EAA0E,CAAC;AAOlF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEjD,qBACa,eAAgB,YAAW,gBAAgB;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC;IAuBlB,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAW5D,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAO/C,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1D,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWhE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWtE,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC1D,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAMzD,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,aAAa;IAgCrB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,qBAAqB;CAsB9B"}
1
+ {"version":3,"file":"worktree.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/worktree.service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,0EAA0E,CAAC;AAQlF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEjD,qBACa,eAAgB,YAAW,gBAAgB;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,YAAY,CAAC;IAuBlB,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAW5D,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAO/C,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1D,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWhE,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWtE,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC1D,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAMzD,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,aAAa;IAgCrB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,qBAAqB;CAsB9B"}
@@ -25,6 +25,7 @@ const GIT_AUTO_INIT_USER = 'shep-ai[bot]';
25
25
  const GIT_AUTO_INIT_EMAIL = 'bot@shep.bot';
26
26
  import { WorktreeError, WorktreeErrorCode, } from '../../../application/ports/output/services/worktree-service.interface.js';
27
27
  import { getShepHomeDir } from '../filesystem/shep-directory.service.js';
28
+ import { IS_WINDOWS } from '../../platform.js';
28
29
  let WorktreeService = class WorktreeService {
29
30
  execFile;
30
31
  constructor(execFile) {
@@ -181,7 +182,7 @@ let WorktreeService = class WorktreeService {
181
182
  normalizeWorktreePath(input) {
182
183
  // On Windows, git outputs forward slashes but path.normalize uses backslashes.
183
184
  // Normalize to forward slashes before comparing, case-insensitive.
184
- if (process.platform === 'win32') {
185
+ if (IS_WINDOWS) {
185
186
  let normalized = path.normalize(input).replace(/\\/g, '/').replace(/\/+$/, '');
186
187
  try {
187
188
  normalized = realpathSync(normalized).replace(/\\/g, '/');
@@ -13,6 +13,7 @@ import type { IFeatureRepository } from '../../../application/ports/output/repos
13
13
  import type { IAgentRunRepository } from '../../../application/ports/output/agents/agent-run-repository.interface.js';
14
14
  import type { IGitPrService } from '../../../application/ports/output/services/git-pr-service.interface.js';
15
15
  import type { INotificationService } from '../../../application/ports/output/services/notification-service.interface.js';
16
+ import type Database from 'better-sqlite3';
16
17
  export declare class PrSyncWatcherService {
17
18
  private readonly featureRepo;
18
19
  private readonly agentRunRepo;
@@ -21,12 +22,22 @@ export declare class PrSyncWatcherService {
21
22
  private readonly pollIntervalMs;
22
23
  private readonly trackedFeatures;
23
24
  private readonly skippedRepos;
25
+ private readonly rateLimitedUntil;
26
+ private readonly db;
27
+ private readonly processId;
24
28
  private intervalId;
25
- constructor(featureRepo: IFeatureRepository, agentRunRepo: IAgentRunRepository, gitPrService: IGitPrService, notificationService: INotificationService, pollIntervalMs?: number);
29
+ private pollCycle;
30
+ constructor(featureRepo: IFeatureRepository, agentRunRepo: IAgentRunRepository, gitPrService: IGitPrService, notificationService: INotificationService, pollIntervalMs?: number, db?: Database.Database | null);
26
31
  isRunning(): boolean;
27
32
  start(): void;
28
33
  stop(): void;
34
+ /** Attempt to acquire the cross-process poll lock. Returns true if acquired. */
35
+ private tryAcquireLock;
36
+ /** Check if a feature should be polled this cycle based on its unchanged cycles count. */
37
+ private shouldPollFeature;
29
38
  private poll;
39
+ private isRateLimited;
40
+ private handleRateLimitError;
30
41
  private processRepository;
31
42
  private processFeature;
32
43
  /** Mark associated agent run as completed so the UI reflects "done" state. */
@@ -39,7 +50,7 @@ export declare class PrSyncWatcherService {
39
50
  *
40
51
  * @throws Error if the watcher is already initialized
41
52
  */
42
- export declare function initializePrSyncWatcher(featureRepo: IFeatureRepository, agentRunRepo: IAgentRunRepository, gitPrService: IGitPrService, notificationService: INotificationService, pollIntervalMs?: number): void;
53
+ export declare function initializePrSyncWatcher(featureRepo: IFeatureRepository, agentRunRepo: IAgentRunRepository, gitPrService: IGitPrService, notificationService: INotificationService, pollIntervalMs?: number, db?: Database.Database | null): void;
43
54
  /**
44
55
  * Get the PR sync watcher singleton.
45
56
  *
@@ -1 +1 @@
1
- {"version":3,"file":"pr-sync-watcher.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/pr-sync/pr-sync-watcher.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gFAAgF,CAAC;AACzH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4EAA4E,CAAC;AACtH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wEAAwE,CAAC;AAK5G,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8EAA8E,CAAC;AAkBzH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgB;IAC7C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAuB;IAC3D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqC;IACrE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,UAAU,CAA+C;gBAG/D,WAAW,EAAE,kBAAkB,EAC/B,YAAY,EAAE,mBAAmB,EACjC,YAAY,EAAE,aAAa,EAC3B,mBAAmB,EAAE,oBAAoB,EACzC,cAAc,GAAE,MAAiC;IASnD,SAAS,IAAI,OAAO;IAIpB,KAAK,IAAI,IAAI;IAcb,IAAI,IAAI,IAAI;YASE,IAAI;YAuCJ,iBAAiB;YA6CjB,cAAc;IAyK5B,8EAA8E;YAChE,gBAAgB;IAW9B,OAAO,CAAC,gBAAgB;CAoBzB;AAMD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,kBAAkB,EAC/B,YAAY,EAAE,mBAAmB,EACjC,YAAY,EAAE,aAAa,EAC3B,mBAAmB,EAAE,oBAAoB,EACzC,cAAc,CAAC,EAAE,MAAM,GACtB,IAAI,CAYN;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,oBAAoB,CAQvD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
1
+ {"version":3,"file":"pr-sync-watcher.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/pr-sync/pr-sync-watcher.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gFAAgF,CAAC;AACzH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4EAA4E,CAAC;AACtH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wEAAwE,CAAC;AAK5G,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8EAA8E,CAAC;AACzH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAqB3C,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgB;IAC7C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAuB;IAC3D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqC;IACrE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAC9D,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA2B;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,SAAS,CAAK;gBAGpB,WAAW,EAAE,kBAAkB,EAC/B,YAAY,EAAE,mBAAmB,EACjC,YAAY,EAAE,aAAa,EAC3B,mBAAmB,EAAE,oBAAoB,EACzC,cAAc,GAAE,MAAiC,EACjD,EAAE,GAAE,QAAQ,CAAC,QAAQ,GAAG,IAAW;IAWrC,SAAS,IAAI,OAAO;IAIpB,KAAK,IAAI,IAAI;IAcb,IAAI,IAAI,IAAI;IASZ,gFAAgF;IAChF,OAAO,CAAC,cAAc;IAiBtB,0FAA0F;IAC1F,OAAO,CAAC,iBAAiB;YAUX,IAAI;IA6ClB,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,oBAAoB;YAYd,iBAAiB;YA+CjB,cAAc;IAoL5B,8EAA8E;YAChE,gBAAgB;IAW9B,OAAO,CAAC,gBAAgB;CAoBzB;AAMD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,kBAAkB,EAC/B,YAAY,EAAE,mBAAmB,EACjC,YAAY,EAAE,aAAa,EAC3B,mBAAmB,EAAE,oBAAoB,EACzC,cAAc,CAAC,EAAE,MAAM,EACvB,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,GAC5B,IAAI,CAaN;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,oBAAoB,CAQvD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
@@ -11,6 +11,8 @@
11
11
  */
12
12
  import { SdlcLifecycle, PrStatus, CiStatus, AgentRunStatus, NotificationEventType, NotificationSeverity, } from '../../../domain/generated/output.js';
13
13
  const DEFAULT_POLL_INTERVAL_MS = 30_000;
14
+ const LOCK_TTL_MS = 60_000;
15
+ const RATE_LIMIT_BACKOFF_MS = 5 * 60_000;
14
16
  const TAG = '[PrSyncWatcher]';
15
17
  const CI_STATUS_MAP = {
16
18
  success: CiStatus.Success,
@@ -25,13 +27,19 @@ export class PrSyncWatcherService {
25
27
  pollIntervalMs;
26
28
  trackedFeatures = new Map();
27
29
  skippedRepos = new Set();
30
+ rateLimitedUntil = new Map();
31
+ db;
32
+ processId;
28
33
  intervalId = null;
29
- constructor(featureRepo, agentRunRepo, gitPrService, notificationService, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS) {
34
+ pollCycle = 0;
35
+ constructor(featureRepo, agentRunRepo, gitPrService, notificationService, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, db = null) {
30
36
  this.featureRepo = featureRepo;
31
37
  this.agentRunRepo = agentRunRepo;
32
38
  this.gitPrService = gitPrService;
33
39
  this.notificationService = notificationService;
34
40
  this.pollIntervalMs = pollIntervalMs;
41
+ this.db = db;
42
+ this.processId = `${process.pid}-${Date.now()}`;
35
43
  }
36
44
  isRunning() {
37
45
  return this.intervalId !== null;
@@ -55,8 +63,40 @@ export class PrSyncWatcherService {
55
63
  console.log(`${TAG} Stopped`);
56
64
  }
57
65
  }
66
+ /** Attempt to acquire the cross-process poll lock. Returns true if acquired. */
67
+ tryAcquireLock() {
68
+ if (!this.db)
69
+ return true; // no DB → single-process mode, always proceed
70
+ const now = Date.now();
71
+ const expiresAt = now + LOCK_TTL_MS;
72
+ const stmt = this.db.prepare(`
73
+ INSERT OR REPLACE INTO pr_sync_lock (id, locked_by, locked_at, expires_at)
74
+ SELECT 1, ?, ?, ?
75
+ WHERE NOT EXISTS (
76
+ SELECT 1 FROM pr_sync_lock WHERE id = 1 AND locked_by != ? AND expires_at > ?
77
+ )
78
+ `);
79
+ const result = stmt.run(this.processId, now, expiresAt, this.processId, now);
80
+ return result.changes > 0;
81
+ }
82
+ /** Check if a feature should be polled this cycle based on its unchanged cycles count. */
83
+ shouldPollFeature(featureId) {
84
+ const tracked = this.trackedFeatures.get(featureId);
85
+ if (!tracked)
86
+ return true; // new feature, always poll
87
+ const uc = tracked.unchangedCycles;
88
+ if (uc <= 2)
89
+ return true; // poll every cycle
90
+ if (uc <= 5)
91
+ return this.pollCycle % 2 === 0; // poll every 2nd cycle
92
+ return this.pollCycle % 4 === 0; // poll every 4th cycle
93
+ }
58
94
  async poll() {
59
95
  try {
96
+ if (!this.tryAcquireLock()) {
97
+ return; // another process holds the lock — skip this cycle
98
+ }
99
+ this.pollCycle++;
60
100
  const allFeatures = await this.featureRepo.list({ lifecycle: SdlcLifecycle.Review });
61
101
  // Include features with a valid repositoryPath (with or without PR data)
62
102
  const features = allFeatures.filter((f) => f.repositoryPath);
@@ -89,9 +129,32 @@ export class PrSyncWatcherService {
89
129
  console.warn(`${TAG} Poll failed: ${msg}`);
90
130
  }
91
131
  }
132
+ isRateLimited(repoPath) {
133
+ const until = this.rateLimitedUntil.get(repoPath);
134
+ if (until === undefined)
135
+ return false;
136
+ if (Date.now() >= until) {
137
+ this.rateLimitedUntil.delete(repoPath);
138
+ // eslint-disable-next-line no-console
139
+ console.log(`${TAG} Rate limit backoff expired for ${repoPath}`);
140
+ return false;
141
+ }
142
+ return true;
143
+ }
144
+ handleRateLimitError(repoPath, error) {
145
+ const msg = error.message;
146
+ if (msg.includes('API rate limit exceeded') || msg.includes('rate limit')) {
147
+ const backoffUntil = Date.now() + RATE_LIMIT_BACKOFF_MS;
148
+ this.rateLimitedUntil.set(repoPath, backoffUntil);
149
+ // eslint-disable-next-line no-console
150
+ console.warn(`${TAG} Rate limited for ${repoPath}, backing off until ${new Date(backoffUntil).toISOString()}`);
151
+ }
152
+ }
92
153
  async processRepository(repoPath, features) {
93
154
  if (this.skippedRepos.has(repoPath))
94
155
  return;
156
+ if (this.isRateLimited(repoPath))
157
+ return;
95
158
  // Skip repos without a git remote — gh pr list will always fail
96
159
  try {
97
160
  if (!(await this.gitPrService.hasRemote(repoPath))) {
@@ -110,6 +173,8 @@ export class PrSyncWatcherService {
110
173
  }
111
174
  catch (error) {
112
175
  const msg = error instanceof Error ? error.message : String(error);
176
+ if (error instanceof Error)
177
+ this.handleRateLimitError(repoPath, error);
113
178
  // eslint-disable-next-line no-console
114
179
  console.warn(`${TAG} listPrStatuses failed for ${repoPath}: ${msg}`);
115
180
  return;
@@ -153,10 +218,15 @@ export class PrSyncWatcherService {
153
218
  ciStatus: undefined,
154
219
  mergeable: undefined,
155
220
  featureName: feature.name,
221
+ unchangedCycles: 0,
156
222
  });
157
223
  await this.featureRepo.update(feature);
158
224
  return;
159
225
  }
226
+ // Check exponential backoff — skip features that haven't changed recently
227
+ if (!this.shouldPollFeature(feature.id)) {
228
+ return;
229
+ }
160
230
  const pr = feature.pr;
161
231
  const prStatusInfo = statusByNumber.get(pr.number);
162
232
  let needsUpdate = false;
@@ -169,6 +239,7 @@ export class PrSyncWatcherService {
169
239
  ciStatus: pr.ciStatus,
170
240
  mergeable: pr.mergeable,
171
241
  featureName: feature.name,
242
+ unchangedCycles: 0,
172
243
  });
173
244
  }
174
245
  const tracked = this.trackedFeatures.get(feature.id);
@@ -223,12 +294,18 @@ export class PrSyncWatcherService {
223
294
  }
224
295
  catch (error) {
225
296
  const msg = error instanceof Error ? error.message : String(error);
297
+ if (error instanceof Error)
298
+ this.handleRateLimitError(feature.repositoryPath, error);
226
299
  // eslint-disable-next-line no-console
227
300
  console.warn(`${TAG} getCiStatus failed for "${feature.name}": ${msg}`);
228
301
  }
229
302
  if (needsUpdate) {
303
+ tracked.unchangedCycles = 0;
230
304
  await this.featureRepo.update(feature);
231
305
  }
306
+ else {
307
+ tracked.unchangedCycles++;
308
+ }
232
309
  }
233
310
  /** Mark associated agent run as completed so the UI reflects "done" state. */
234
311
  async completeAgentRun(feature) {
@@ -264,11 +341,11 @@ let watcherInstance = null;
264
341
  *
265
342
  * @throws Error if the watcher is already initialized
266
343
  */
267
- export function initializePrSyncWatcher(featureRepo, agentRunRepo, gitPrService, notificationService, pollIntervalMs) {
344
+ export function initializePrSyncWatcher(featureRepo, agentRunRepo, gitPrService, notificationService, pollIntervalMs, db) {
268
345
  if (watcherInstance !== null) {
269
346
  throw new Error('PR sync watcher already initialized. Cannot re-initialize.');
270
347
  }
271
- watcherInstance = new PrSyncWatcherService(featureRepo, agentRunRepo, gitPrService, notificationService, pollIntervalMs);
348
+ watcherInstance = new PrSyncWatcherService(featureRepo, agentRunRepo, gitPrService, notificationService, pollIntervalMs, db ?? null);
272
349
  }
273
350
  /**
274
351
  * Get the PR sync watcher singleton.
@@ -1 +1 @@
1
- {"version":3,"file":"web-server.service.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/services/web-server.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yEAAyE,CAAC;AAIjH,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,OAAO,IAAI,CAAC;IAC3B,gBAAgB,EAAE,OAAO,IAAI,CAAC,YAAY,CAAC;CAC5C;AAOD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,IAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,CAsB7D;AAED,qBACa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,GAAG,CAAwB;IACnC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;gBAEzB,IAAI,GAAE,OAAO,CAAC,aAAa,CAAM;IAI7C;;;;;OAKG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BjE;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAuB5B"}
1
+ {"version":3,"file":"web-server.service.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/services/web-server.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yEAAyE,CAAC;AAKjH,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,OAAO,IAAI,CAAC;IAC3B,gBAAgB,EAAE,OAAO,IAAI,CAAC,YAAY,CAAC;CAC5C;AAOD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,IAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,CAsB7D;AAED,qBACa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,GAAG,CAAwB;IACnC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;gBAEzB,IAAI,GAAE,OAAO,CAAC,aAAa,CAAM;IAI7C;;;;;OAKG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA0CjE;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAuB5B"}
@@ -22,6 +22,7 @@ import next from 'next';
22
22
  import http from 'node:http';
23
23
  import path from 'node:path';
24
24
  import fs from 'node:fs';
25
+ import { IS_WINDOWS } from '../platform.js';
25
26
  const defaultDeps = {
26
27
  createNextApp: next,
27
28
  createHttpServer: http.createServer,
@@ -70,6 +71,20 @@ let WebServerService = class WebServerService {
70
71
  * @param dev - Whether to run in development mode (default: auto-detect)
71
72
  */
72
73
  async start(port, dir, dev = true) {
74
+ // On Windows, Next.js uses path.relative(CWD, dir) internally then
75
+ // reconstructs with path.join(CWD, relative). When CWD and dir are on
76
+ // different drives (e.g. D:\project vs C:\...\web), path.relative returns
77
+ // the absolute path (can't compute relative across drives), and path.join
78
+ // produces a mangled path like D:\project\C:\...\web\. Fix by ensuring
79
+ // CWD is on the same drive as dir.
80
+ if (IS_WINDOWS) {
81
+ const getDriveLetter = (p) => p.match(/^[a-zA-Z]:/)?.[0]?.toUpperCase();
82
+ const cwdDrive = getDriveLetter(process.cwd());
83
+ const dirDrive = getDriveLetter(dir);
84
+ if (cwdDrive && dirDrive && cwdDrive !== dirDrive) {
85
+ process.chdir(dir);
86
+ }
87
+ }
73
88
  const app = this.deps.createNextApp({
74
89
  dev,
75
90
  dir,
@@ -1 +1 @@
1
- {"version":3,"file":"ui.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/ui.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AA+B1D;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAkFzC"}
1
+ {"version":3,"file":"ui.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/ui.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAgC1D;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CA0FzC"}
@@ -21,6 +21,7 @@ import { setVersionEnvVars } from '../../../../packages/core/src/infrastructure/
21
21
  import { resolveWebDir } from '../../../../packages/core/src/infrastructure/services/web-server.service.js';
22
22
  import { initializeNotificationWatcher, getNotificationWatcher, } from '../../../../packages/core/src/infrastructure/services/notifications/notification-watcher.service.js';
23
23
  import { initializePrSyncWatcher, getPrSyncWatcher, } from '../../../../packages/core/src/infrastructure/services/pr-sync/pr-sync-watcher.service.js';
24
+ import { getExistingConnection } from '../../../../packages/core/src/infrastructure/persistence/sqlite/connection.js';
24
25
  import { BrowserOpenerService } from '../../../../packages/core/src/infrastructure/services/browser-opener.service.js';
25
26
  import { colors, fmt, messages } from '../ui/index.js';
26
27
  function parsePort(value) {
@@ -66,7 +67,8 @@ Examples:
66
67
  getNotificationWatcher().start();
67
68
  // Start PR sync watcher to detect PR/CI status transitions on GitHub
68
69
  const gitPrService = container.resolve('IGitPrService');
69
- initializePrSyncWatcher(featureRepo, runRepo, gitPrService, notificationService);
70
+ const db = getExistingConnection();
71
+ initializePrSyncWatcher(featureRepo, runRepo, gitPrService, notificationService, undefined, db);
70
72
  getPrSyncWatcher().start();
71
73
  const url = `http://localhost:${port}`;
72
74
  messages.success(`Server ready at ${fmt.code(url)}`);
@@ -3,6 +3,9 @@ import type { FeatureNodeData } from '../../components/common/feature-node/index
3
3
  * Fetches full FeatureNodeData for a given feature ID.
4
4
  * Used by the drawer for targeted data syncing without triggering
5
5
  * a full router.refresh() / server component re-render.
6
+ *
7
+ * CI status and mergeable status are read from the DB (already updated
8
+ * by PrSyncWatcherService) instead of making duplicate GitHub API calls.
6
9
  */
7
10
  export declare function getFeatureDrawerData(featureId: string): Promise<FeatureNodeData | null>;
8
11
  //# sourceMappingURL=get-feature-drawer-data.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-feature-drawer-data.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/get-feature-drawer-data.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAQxE;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAsD7F"}
1
+ {"version":3,"file":"get-feature-drawer-data.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/get-feature-drawer-data.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAExE;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAgC7F"}
@@ -1,16 +1,13 @@
1
1
  'use server';
2
2
  import { resolve } from '../../lib/server-container.js';
3
- import { CiStatus } from '../../../../../packages/core/src/domain/generated/output.js';
4
3
  import { buildFeatureNodeData } from '../build-feature-node-data.js';
5
- const CI_STATUS_MAP = {
6
- success: CiStatus.Success,
7
- failure: CiStatus.Failure,
8
- pending: CiStatus.Pending,
9
- };
10
4
  /**
11
5
  * Fetches full FeatureNodeData for a given feature ID.
12
6
  * Used by the drawer for targeted data syncing without triggering
13
7
  * a full router.refresh() / server component re-render.
8
+ *
9
+ * CI status and mergeable status are read from the DB (already updated
10
+ * by PrSyncWatcherService) instead of making duplicate GitHub API calls.
14
11
  */
15
12
  export async function getFeatureDrawerData(featureId) {
16
13
  try {
@@ -23,37 +20,15 @@ export async function getFeatureDrawerData(featureId) {
23
20
  if (!feature)
24
21
  return null;
25
22
  const run = feature.agentRunId ? await agentRunRepo.findById(feature.agentRunId) : null;
26
- const [repo, baseBranch, artifact, remoteUrl, liveMergeable, liveCiResult] = await Promise.all([
23
+ // CI status and mergeable status are read from the feature record (updated by
24
+ // PrSyncWatcherService every 30s) — no duplicate GitHub API calls needed.
25
+ // Only getDefaultBranch and getRemoteUrl are kept as they are local git operations.
26
+ const [repo, baseBranch, artifact, remoteUrl] = await Promise.all([
27
27
  repoRepo.findByPath(feature.repositoryPath).catch(() => null),
28
28
  gitPrService.getDefaultBranch(feature.repositoryPath).catch(() => 'main'),
29
29
  getArtifact.execute(featureId).catch(() => null),
30
30
  gitPrService.getRemoteUrl(feature.repositoryPath).catch(() => null),
31
- feature.pr?.number
32
- ? gitPrService
33
- .getMergeableStatus(feature.repositoryPath, feature.pr.number)
34
- .catch(() => undefined)
35
- : Promise.resolve(undefined),
36
- feature.branch
37
- ? gitPrService.getCiStatus(feature.repositoryPath, feature.branch).catch(() => undefined)
38
- : Promise.resolve(undefined),
39
31
  ]);
40
- // Merge live PR status into feature data
41
- if (feature.pr) {
42
- const liveCiStatus = liveCiResult
43
- ? (CI_STATUS_MAP[liveCiResult.status] ?? CiStatus.Pending)
44
- : undefined;
45
- const updates = {};
46
- if (liveMergeable !== undefined)
47
- updates.mergeable = liveMergeable;
48
- if (liveCiStatus !== undefined)
49
- updates.ciStatus = liveCiStatus;
50
- if (Object.keys(updates).length > 0) {
51
- feature.pr = { ...feature.pr, ...updates };
52
- featureRepo.update(feature).catch(() => {
53
- /* best-effort persist */
54
- });
55
- }
56
- }
57
32
  return buildFeatureNodeData(feature, run, {
58
33
  repositoryName: repo?.name,
59
34
  baseBranch,
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/app/api/cli-upgrade/route.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAEvC,wBAAsB,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,CA+B9C"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/app/api/cli-upgrade/route.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAEvC,wBAAsB,IAAI,IAAI,OAAO,CAAC,QAAQ,CAAC,CA4C9C"}