@shepai/cli 1.149.1 → 1.150.0-pr471.902d313

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 (155) hide show
  1. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/evidence.node.d.ts.map +1 -1
  2. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/evidence.node.js +2 -1
  3. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/fast-implement.node.d.ts.map +1 -1
  4. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/fast-implement.node.js +11 -42
  5. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/implement.node.d.ts.map +1 -1
  6. package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/implement.node.js +2 -3
  7. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  8. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +29 -2
  9. package/dist/src/presentation/web/components/common/feature-drawer/use-feature-actions.d.ts +3 -0
  10. package/dist/src/presentation/web/components/common/feature-drawer/use-feature-actions.d.ts.map +1 -1
  11. package/dist/src/presentation/web/components/common/feature-drawer/use-feature-actions.js +32 -1
  12. package/dist/src/presentation/web/components/common/open-action-menu/open-action-menu.d.ts.map +1 -1
  13. package/dist/src/presentation/web/components/common/open-action-menu/open-action-menu.js +3 -3
  14. package/dist/src/presentation/web/components/common/open-action-menu/open-action-menu.stories.d.ts.map +1 -1
  15. package/dist/src/presentation/web/components/common/open-action-menu/open-action-menu.stories.js +3 -0
  16. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  17. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +18 -0
  18. package/dist/tsconfig.build.tsbuildinfo +1 -1
  19. package/package.json +1 -1
  20. package/web/.next/BUILD_ID +1 -1
  21. package/web/.next/build-manifest.json +2 -2
  22. package/web/.next/fallback-build-manifest.json +2 -2
  23. package/web/.next/prerender-manifest.json +3 -3
  24. package/web/.next/required-server-files.js +3 -3
  25. package/web/.next/required-server-files.json +3 -3
  26. package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +28 -28
  27. package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
  28. package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
  29. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +28 -28
  30. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  31. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  32. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +36 -36
  33. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  34. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  35. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +36 -36
  36. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  37. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  38. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +26 -26
  39. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  40. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  41. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +28 -28
  42. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  43. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  44. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +36 -36
  45. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  46. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  47. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +36 -36
  48. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  49. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  50. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +26 -26
  51. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  52. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  53. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +26 -26
  54. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  55. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  56. package/web/.next/server/app/_global-error.html +2 -2
  57. package/web/.next/server/app/_global-error.rsc +1 -1
  58. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  59. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  60. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  61. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  62. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  63. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +3 -3
  64. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  65. package/web/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  66. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  67. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  68. package/web/.next/server/app/skills/page/server-reference-manifest.json +8 -8
  69. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  70. package/web/.next/server/app/tools/page/server-reference-manifest.json +8 -8
  71. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  72. package/web/.next/server/app/version/page/server-reference-manifest.json +3 -3
  73. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  74. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  75. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  76. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  77. package/web/.next/server/chunks/ssr/[root-of-the-server]__2138fa7e._.js +2 -2
  78. package/web/.next/server/chunks/ssr/[root-of-the-server]__29580090._.js +1 -1
  79. package/web/.next/server/chunks/ssr/[root-of-the-server]__29580090._.js.map +1 -1
  80. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  81. package/web/.next/server/chunks/ssr/[root-of-the-server]__3ef34e4c._.js +1 -1
  82. package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js +1 -1
  83. package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js.map +1 -1
  84. package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js +1 -1
  85. package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js.map +1 -1
  86. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +3 -3
  87. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  88. package/web/.next/server/chunks/ssr/[root-of-the-server]__c094882b._.js +1 -1
  89. package/web/.next/server/chunks/ssr/[root-of-the-server]__c094882b._.js.map +1 -1
  90. package/web/.next/server/chunks/ssr/[root-of-the-server]__d48c5b11._.js +1 -1
  91. package/web/.next/server/chunks/ssr/[root-of-the-server]__d48c5b11._.js.map +1 -1
  92. package/web/.next/server/chunks/ssr/[root-of-the-server]__dac5dbf1._.js +1 -1
  93. package/web/.next/server/chunks/ssr/[root-of-the-server]__dac5dbf1._.js.map +1 -1
  94. package/web/.next/server/chunks/ssr/[root-of-the-server]__fae8b355._.js +1 -1
  95. package/web/.next/server/chunks/ssr/[root-of-the-server]__fae8b355._.js.map +1 -1
  96. package/web/.next/server/chunks/ssr/_05c23ad9._.js +1 -1
  97. package/web/.next/server/chunks/ssr/_05c23ad9._.js.map +1 -1
  98. package/web/.next/server/chunks/ssr/_0c5f56e3._.js +2 -2
  99. package/web/.next/server/chunks/ssr/_0c5f56e3._.js.map +1 -1
  100. package/web/.next/server/chunks/ssr/_16eb4fec._.js +1 -1
  101. package/web/.next/server/chunks/ssr/_16eb4fec._.js.map +1 -1
  102. package/web/.next/server/chunks/ssr/_1b719e7f._.js +1 -1
  103. package/web/.next/server/chunks/ssr/_1b719e7f._.js.map +1 -1
  104. package/web/.next/server/chunks/ssr/_37e8548b._.js +1 -1
  105. package/web/.next/server/chunks/ssr/_37e8548b._.js.map +1 -1
  106. package/web/.next/server/chunks/ssr/_55d763e2._.js +1 -1
  107. package/web/.next/server/chunks/ssr/_55d763e2._.js.map +1 -1
  108. package/web/.next/server/chunks/ssr/_6256a985._.js +1 -1
  109. package/web/.next/server/chunks/ssr/_6256a985._.js.map +1 -1
  110. package/web/.next/server/chunks/ssr/_64bdfc6f._.js +2 -2
  111. package/web/.next/server/chunks/ssr/_64bdfc6f._.js.map +1 -1
  112. package/web/.next/server/chunks/ssr/_8fcc39d4._.js +1 -1
  113. package/web/.next/server/chunks/ssr/_9c5afe42._.js +3 -0
  114. package/web/.next/server/chunks/ssr/{_ee80b25c._.js.map → _9c5afe42._.js.map} +1 -1
  115. package/web/.next/server/chunks/ssr/_b71645b4._.js +1 -1
  116. package/web/.next/server/chunks/ssr/_b71645b4._.js.map +1 -1
  117. package/web/.next/server/chunks/ssr/{_2b873e3b._.js → _d6e950e9._.js} +2 -2
  118. package/web/.next/server/chunks/ssr/_d6e950e9._.js.map +1 -0
  119. package/web/.next/server/chunks/ssr/_d8575088._.js +1 -1
  120. package/web/.next/server/chunks/ssr/_d8575088._.js.map +1 -1
  121. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  122. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  123. package/web/.next/server/chunks/ssr/{src_presentation_web_91545e86._.js → src_presentation_web_4821386e._.js} +2 -2
  124. package/web/.next/server/chunks/ssr/{src_presentation_web_91545e86._.js.map → src_presentation_web_4821386e._.js.map} +1 -1
  125. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js +1 -1
  126. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js.map +1 -1
  127. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js +1 -1
  128. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js.map +1 -1
  129. package/web/.next/server/chunks/ssr/src_presentation_web_app_actions_open-ide_ts_baaca5d5._.js +1 -1
  130. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  131. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  132. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  133. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  134. package/web/.next/server/pages/500.html +2 -2
  135. package/web/.next/server/server-reference-manifest.js +1 -1
  136. package/web/.next/server/server-reference-manifest.json +44 -44
  137. package/web/.next/static/chunks/{932fdc72f19fba16.js → 29592ee94d1dfe16.js} +1 -1
  138. package/web/.next/static/chunks/2a69ae46f2d670d7.js +5 -0
  139. package/web/.next/static/chunks/{e23f0c8da44fe4ca.js → 49a66916caf0ada7.js} +1 -1
  140. package/web/.next/static/chunks/{42a1e8928c2a8b1e.js → 531213d3d708207f.js} +1 -1
  141. package/web/.next/static/chunks/6165454dbc7b6804.js +1 -0
  142. package/web/.next/static/chunks/{70917246e2079de0.js → 6e3377bb4787e6cb.js} +1 -1
  143. package/web/.next/static/chunks/{9522b3e38757bf73.js → 7ac8a1e723543fde.js} +1 -1
  144. package/web/.next/static/chunks/{ee210d2383098e91.js → 92c95676375f0e58.js} +2 -2
  145. package/web/.next/static/chunks/{213bc4d2c2c7885a.js → a624c0b71dd16424.js} +1 -1
  146. package/web/.next/static/chunks/{6b7b66ce14353296.js → c509c047eeefe599.js} +1 -1
  147. package/web/.next/static/chunks/{fbf933fea8dd21f4.js → cad7da3eb163aad3.js} +1 -1
  148. package/web/.next/static/chunks/{d3e9784c18cc85e9.js → ec9b83aaa577d5d8.js} +1 -1
  149. package/web/.next/server/chunks/ssr/_2b873e3b._.js.map +0 -1
  150. package/web/.next/server/chunks/ssr/_ee80b25c._.js +0 -3
  151. package/web/.next/static/chunks/c8c503403d49f600.js +0 -1
  152. package/web/.next/static/chunks/faf6b0ec1cf9125c.js +0 -5
  153. /package/web/.next/static/{0-b0NgavBiu5Fo1rzdeX2 → Wet2q3jRm5z9R1HGr5e3e}/_buildManifest.js +0 -0
  154. /package/web/.next/static/{0-b0NgavBiu5Fo1rzdeX2 → Wet2q3jRm5z9R1HGr5e3e}/_clientMiddlewareManifest.json +0 -0
  155. /package/web/.next/static/{0-b0NgavBiu5Fo1rzdeX2 → Wet2q3jRm5z9R1HGr5e3e}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"evidence.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/evidence.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AAEpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqDrD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,IAG3C,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAoJ7E"}
1
+ {"version":3,"file":"evidence.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/evidence.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AAEpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqDrD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc,IAG3C,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAqJ7E"}
@@ -82,8 +82,9 @@ export function createEvidenceNode(executor) {
82
82
  };
83
83
  }
84
84
  // --- Configuration ---
85
+ // Use feature-level state for commitEvidence; fall back to global for retries config
86
+ const commitEvidence = state.commitEvidence;
85
87
  const settings = hasSettings() ? getSettings() : undefined;
86
- const commitEvidence = settings?.workflow.commitEvidence ?? false;
87
88
  const maxRetries = settings?.workflow.evidenceRetries ?? DEFAULT_MAX_RETRIES;
88
89
  const options = buildExecutorOptions(state);
89
90
  const tasks = parseTasks(state.specDir);
@@ -1 +1 @@
1
- {"version":3,"file":"fast-implement.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/fast-implement.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AAEpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAiBrD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,cAAc,IAGhD,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CA0F7E"}
1
+ {"version":3,"file":"fast-implement.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/fast-implement.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAcrD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,cAAc,IAGhD,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CA6F7E"}
@@ -11,14 +11,12 @@
11
11
  */
12
12
  import { execSync } from 'node:child_process';
13
13
  import { isGraphBubbleUp } from '@langchain/langgraph';
14
- import { createNodeLogger, buildExecutorOptions, retryExecute, getCompletedPhases, markPhaseComplete, saveEvidenceManifest, } from './node-helpers.js';
14
+ import { createNodeLogger, buildExecutorOptions, retryExecute, getCompletedPhases, markPhaseComplete, } from './node-helpers.js';
15
15
  import { reportNodeStart } from '../heartbeat.js';
16
16
  import { recordPhaseStart, recordPhaseEnd } from '../phase-timing-context.js';
17
17
  import { updateNodeLifecycle } from '../lifecycle-context.js';
18
18
  import { buildFastImplementPrompt } from './prompts/fast-implement.prompt.js';
19
- import { buildEvidencePrompt } from './prompts/evidence-prompts.js';
20
- import { parseEvidenceRecords } from './evidence-output-parser.js';
21
- import { hasSettings, getSettings } from '../../../settings.service.js';
19
+ import { createEvidenceNode } from './evidence.node.js';
22
20
  /**
23
21
  * Factory that creates the fast-implement node function.
24
22
  *
@@ -62,11 +60,15 @@ export function createFastImplementNode(executor) {
62
60
  if (!hasWorktreeChanges(cwd) && !hasNewCommits(cwd)) {
63
61
  throw new Error('[fast-implement] Agent produced no file changes — it may have entered plan mode or asked questions instead of implementing. Retrying.');
64
62
  }
65
- // --- Evidence sub-agent: capture proof of completion (settings-gated) ---
66
- const evidenceEnabled = hasSettings() && getSettings().workflow.enableEvidence;
67
- const evidence = evidenceEnabled ? await collectEvidence(executor, state, log) : [];
68
- if (!evidenceEnabled) {
69
- log.info('Evidence collection disabled via settings — skipping');
63
+ // --- Evidence sub-agent: capture proof of completion (feature-gated) ---
64
+ let evidence = [];
65
+ if (state.enableEvidence) {
66
+ const evidenceNode = createEvidenceNode(executor);
67
+ const evidenceResult = await evidenceNode(state);
68
+ evidence = evidenceResult.evidence ?? [];
69
+ }
70
+ else {
71
+ log.info('Evidence collection disabled — skipping');
70
72
  }
71
73
  await recordPhaseEnd(timingId, durationMs, {
72
74
  inputTokens: result.usage?.inputTokens,
@@ -106,39 +108,6 @@ export function createFastImplementNode(executor) {
106
108
  }
107
109
  };
108
110
  }
109
- /**
110
- * Sub-agent call to collect evidence after fast implementation completes.
111
- * Graceful degradation: returns empty array on any failure so evidence
112
- * collection never blocks the workflow.
113
- */
114
- async function collectEvidence(executor, state, log) {
115
- try {
116
- log.info('Collecting evidence (sub-agent)');
117
- const commitEvidence = hasSettings() && getSettings().workflow.commitEvidence;
118
- const prompt = buildEvidencePrompt(state, { commitEvidence });
119
- const options = buildExecutorOptions(state);
120
- const result = await retryExecute(executor, prompt, options, { logger: log });
121
- try {
122
- const evidence = parseEvidenceRecords(result.result);
123
- log.info(`Parsed ${evidence.length} evidence record(s)`);
124
- saveEvidenceManifest(state, evidence, log);
125
- return evidence;
126
- }
127
- catch (parseErr) {
128
- const msg = parseErr instanceof Error ? parseErr.message : String(parseErr);
129
- log.error(`Warning: evidence parsing failed: ${msg} — continuing with empty evidence`);
130
- return [];
131
- }
132
- }
133
- catch (err) {
134
- // Re-throw LangGraph control-flow exceptions
135
- if (isGraphBubbleUp(err))
136
- throw err;
137
- const msg = err instanceof Error ? err.message : String(err);
138
- log.error(`Evidence collection failed: ${msg} — continuing without evidence`);
139
- return [];
140
- }
141
- }
142
111
  /**
143
112
  * Check whether the worktree has any uncommitted changes (new, modified, or deleted files).
144
113
  * Uses `git status --porcelain` which outputs one line per changed file, or empty if clean.
@@ -1 +1 @@
1
- {"version":3,"file":"implement.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/implement.node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqErD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,IAG5C,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAiQ7E"}
1
+ {"version":3,"file":"implement.node.d.ts","sourceRoot":"","sources":["../../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/nodes/implement.node.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+DAA+D,CAAC;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAoErD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,IAG5C,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAiQ7E"}
@@ -16,7 +16,6 @@ import { recordPhaseStart, recordPhaseEnd, recordApprovalWaitStart, updatePhaseP
16
16
  import { updateNodeLifecycle } from '../lifecycle-context.js';
17
17
  import { buildImplementPhasePrompt, } from './prompts/implement.prompt.js';
18
18
  import { createEvidenceNode } from './evidence.node.js';
19
- import { hasSettings, getSettings } from '../../../settings.service.js';
20
19
  /**
21
20
  * Update feature.yaml with current implementation progress.
22
21
  * Silently no-ops if the file is missing or unparseable.
@@ -190,8 +189,8 @@ export function createImplementNode(executor) {
190
189
  log.info(`All phases complete — ${completedTasks}/${totalTasks} tasks (${elapsed}s)`);
191
190
  updateFeatureProgress(state.specDir, totalTasks, totalTasks, 'implementation-complete', null, log);
192
191
  messages.push(`[implement] Complete: ${totalTasks} tasks across ${totalPhases} phases (${elapsed}s)`);
193
- // --- Evidence sub-agent: capture proof of completion (settings-gated) ---
194
- const evidenceEnabled = hasSettings() && getSettings().workflow.enableEvidence;
192
+ // --- Evidence sub-agent: capture proof of completion (feature-gated) ---
193
+ const evidenceEnabled = state.enableEvidence;
195
194
  let evidenceResult = {};
196
195
  if (evidenceEnabled) {
197
196
  const evidenceNode = createEvidenceNode(executor);
@@ -1 +1 @@
1
- {"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAK/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CA4oB1F"}
1
+ {"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAkDA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAK/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CAsuB1F"}
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useState, useCallback, useEffect, useRef } from 'react';
4
4
  import { useRouter, usePathname } from 'next/navigation';
5
5
  import { toast } from 'sonner';
6
- import { Loader2, Trash2, Play, Square, Copy, Check, Code2, ExternalLink } from 'lucide-react';
6
+ import { Loader2, Trash2, Play, Square, Copy, Check, Code2, ExternalLink, Archive, ArchiveRestore, } from 'lucide-react';
7
7
  import { approveFeature } from '../../../app/actions/approve-feature.js';
8
8
  import { resumeFeature } from '../../../app/actions/resume-feature.js';
9
9
  import { startFeature } from '../../../app/actions/start-feature.js';
@@ -106,6 +106,19 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
106
106
  // ── Delete state ───────────────────────────────────────────────────────
107
107
  const [isDeleting, setIsDeleting] = useState(false);
108
108
  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
109
+ // ── Archive state ─────────────────────────────────────────────────────
110
+ const [isArchiving, setIsArchiving] = useState(false);
111
+ // Reset archive loading spinner when the feature state or feature ID
112
+ // changes (e.g. state flips to 'archived' / 'done' after the server
113
+ // action, or the user navigates to a different feature drawer).
114
+ const archiveResetKey = `${featureNode?.featureId}:${featureNode?.state}`;
115
+ const prevArchiveResetKeyRef = useRef(archiveResetKey);
116
+ useEffect(() => {
117
+ if (archiveResetKey !== prevArchiveResetKeyRef.current) {
118
+ prevArchiveResetKeyRef.current = archiveResetKey;
119
+ setIsArchiving(false);
120
+ }
121
+ }, [archiveResetKey]);
109
122
  // ── Shared reject state ────────────────────────────────────────────────
110
123
  const [isRejecting, setIsRejecting] = useState(false);
111
124
  const isRejectingRef = useRef(false);
@@ -290,6 +303,20 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
290
303
  }));
291
304
  router.push('/');
292
305
  }, [router]);
306
+ const handleArchive = useCallback((featureId) => {
307
+ setIsArchiving(true);
308
+ window.dispatchEvent(new CustomEvent('shep:feature-archive-requested', {
309
+ detail: { featureId },
310
+ }));
311
+ router.push('/');
312
+ }, [router]);
313
+ const handleUnarchive = useCallback((featureId) => {
314
+ setIsArchiving(true);
315
+ window.dispatchEvent(new CustomEvent('shep:feature-unarchive-requested', {
316
+ detail: { featureId },
317
+ }));
318
+ router.push('/');
319
+ }, [router]);
293
320
  const handleRetry = useCallback(async (featureId) => {
294
321
  const result = await resumeFeature(featureId);
295
322
  if (result.error) {
@@ -376,7 +403,7 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
376
403
  const repoName = featureNode.repositoryName ??
377
404
  featureNode.repositoryPath.split('/').filter(Boolean).at(-1) ??
378
405
  '';
379
- header = (_jsxs(_Fragment, { children: [_jsxs("div", { "data-testid": "feature-drawer-header", children: [_jsx(DrawerTitle, { children: featureNode.name }), repoName ? (_jsxs("div", { className: "flex items-center gap-1.5 pt-0.5", children: [_jsx(Code2, { className: "text-muted-foreground size-3.5 shrink-0" }), featureNode.remoteUrl ? (_jsxs("a", { href: featureNode.remoteUrl, target: "_blank", rel: "noopener noreferrer", className: "text-muted-foreground hover:text-foreground inline-flex items-center gap-1 text-xs transition-colors", "data-testid": "feature-drawer-repo-link", children: [repoName, _jsx(ExternalLink, { className: "size-3" })] })) : (_jsx("span", { className: "text-muted-foreground text-xs", children: repoName }))] })) : null, _jsx(DrawerDescription, { className: "sr-only", children: featureNode.name })] }), featureActionsInput ? (_jsxs("div", { className: "flex items-center gap-2 pt-2", "data-testid": "feature-drawer-actions", children: [_jsx(OpenActionMenu, { actions: featureActions, repositoryPath: featureActionsInput.repositoryPath, worktreePath: featureActionsInput.worktreePath, showSpecs: !!featureActionsInput.specPath }), featureFlags.envDeploy && featureDeployTarget ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { children: _jsx(ActionButton, { label: isFeatureDeployActive ? 'Stop Dev Server' : 'Start Dev Server', onClick: isFeatureDeployActive ? deployAction.stop : deployAction.deploy, loading: deployAction.deployLoading || deployAction.stopLoading, error: !!deployAction.deployError, icon: isFeatureDeployActive ? Square : Play, iconOnly: true, variant: "outline", size: "icon-sm" }) }) }), _jsx(TooltipContent, { children: isFeatureDeployActive ? 'Stop Dev Server' : 'Start Dev Server' })] }) }), isFeatureDeployActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: featureDeployTarget?.targetId })) : null] })) : null, _jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [_jsx("code", { className: "bg-muted text-muted-foreground rounded px-1.5 py-0.5 font-mono text-xs", children: shortId }), _jsx("button", { type: "button", onClick: handleCopyId, className: "text-muted-foreground hover:text-foreground inline-flex items-center rounded p-0.5 transition-colors", "aria-label": "Copy feature ID", "data-testid": "feature-drawer-copy-id", children: idCopied ? (_jsx(Check, { className: "size-3.5 text-green-600" })) : (_jsx(Copy, { className: "size-3.5" })) })] }), featureNode.featureId ? (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Delete feature", disabled: isDeleting, className: "text-muted-foreground hover:text-destructive", "data-testid": "feature-drawer-delete", onClick: () => setDeleteDialogOpen(true), children: isDeleting ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(Trash2, { className: "size-4" })) }), _jsx(DeleteFeatureDialog, { open: deleteDialogOpen, onOpenChange: setDeleteDialogOpen, onConfirm: (cleanup, cascadeDelete, closePr) => handleDelete(featureNode.featureId, cleanup, cascadeDelete, closePr), isDeleting: isDeleting, featureName: featureNode.name, featureId: featureNode.featureId, hasChildren: featureNode.hasChildren, hasOpenPr: !!featureNode.pr && featureNode.pr.status === 'Open' })] })) : null] })) : null] }));
406
+ header = (_jsxs(_Fragment, { children: [_jsxs("div", { "data-testid": "feature-drawer-header", children: [_jsx(DrawerTitle, { children: featureNode.name }), repoName ? (_jsxs("div", { className: "flex items-center gap-1.5 pt-0.5", children: [_jsx(Code2, { className: "text-muted-foreground size-3.5 shrink-0" }), featureNode.remoteUrl ? (_jsxs("a", { href: featureNode.remoteUrl, target: "_blank", rel: "noopener noreferrer", className: "text-muted-foreground hover:text-foreground inline-flex items-center gap-1 text-xs transition-colors", "data-testid": "feature-drawer-repo-link", children: [repoName, _jsx(ExternalLink, { className: "size-3" })] })) : (_jsx("span", { className: "text-muted-foreground text-xs", children: repoName }))] })) : null, _jsx(DrawerDescription, { className: "sr-only", children: featureNode.name })] }), featureActionsInput ? (_jsxs("div", { className: "flex items-center gap-2 pt-2", "data-testid": "feature-drawer-actions", children: [featureNode?.state !== 'done' ? (_jsx(OpenActionMenu, { actions: featureActions, repositoryPath: featureActionsInput.repositoryPath, worktreePath: featureActionsInput.worktreePath, showSpecs: !!featureActionsInput.specPath })) : null, featureNode?.state !== 'done' && featureFlags.envDeploy && featureDeployTarget ? (_jsxs(_Fragment, { children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { children: _jsx(ActionButton, { label: isFeatureDeployActive ? 'Stop Dev Server' : 'Start Dev Server', onClick: isFeatureDeployActive ? deployAction.stop : deployAction.deploy, loading: deployAction.deployLoading || deployAction.stopLoading, error: !!deployAction.deployError, icon: isFeatureDeployActive ? Square : Play, iconOnly: true, variant: "outline", size: "icon-sm" }) }) }), _jsx(TooltipContent, { children: isFeatureDeployActive ? 'Stop Dev Server' : 'Start Dev Server' })] }) }), isFeatureDeployActive ? (_jsx(DeploymentStatusBadge, { status: deployAction.status, url: deployAction.url, targetId: featureDeployTarget?.targetId })) : null] })) : null, _jsxs("div", { className: "ml-auto flex items-center gap-1.5", children: [_jsx("code", { className: "bg-muted text-muted-foreground rounded px-1.5 py-0.5 font-mono text-xs", children: shortId }), _jsx("button", { type: "button", onClick: handleCopyId, className: "text-muted-foreground hover:text-foreground inline-flex items-center rounded p-0.5 transition-colors", "aria-label": "Copy feature ID", "data-testid": "feature-drawer-copy-id", children: idCopied ? (_jsx(Check, { className: "size-3.5 text-green-600" })) : (_jsx(Copy, { className: "size-3.5" })) })] }), featureNode.featureId ? (_jsxs(_Fragment, { children: [featureNode.state === 'archived' ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Unarchive feature", disabled: isArchiving, className: "text-muted-foreground hover:text-primary", "data-testid": "feature-drawer-unarchive", onClick: () => handleUnarchive(featureNode.featureId), children: isArchiving ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(ArchiveRestore, { className: "size-4" })) }) }), _jsx(TooltipContent, { children: "Unarchive feature" })] }) })) : featureNode.state !== 'deleting' ? (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Archive feature", disabled: isArchiving, className: "text-muted-foreground hover:text-foreground", "data-testid": "feature-drawer-archive", onClick: () => handleArchive(featureNode.featureId), children: isArchiving ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(Archive, { className: "size-4" })) }) }), _jsx(TooltipContent, { children: "Archive feature" })] }) })) : null, _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Delete feature", disabled: isDeleting, className: "text-muted-foreground hover:text-destructive", "data-testid": "feature-drawer-delete", onClick: () => setDeleteDialogOpen(true), children: isDeleting ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(Trash2, { className: "size-4" })) }), _jsx(DeleteFeatureDialog, { open: deleteDialogOpen, onOpenChange: setDeleteDialogOpen, onConfirm: (cleanup, cascadeDelete, closePr) => handleDelete(featureNode.featureId, cleanup, cascadeDelete, closePr), isDeleting: isDeleting, featureName: featureNode.name, featureId: featureNode.featureId, hasChildren: featureNode.hasChildren, hasOpenPr: !!featureNode.pr && featureNode.pr.status === 'Open' })] })) : null] })) : null] }));
380
407
  }
381
408
  // ── Body ──────────────────────────────────────────────────────────────
382
409
  let body = null;
@@ -8,14 +8,17 @@ export interface FeatureActionsInput {
8
8
  export interface FeatureActionsState {
9
9
  openInIde: () => Promise<void>;
10
10
  openInShell: () => Promise<void>;
11
+ openFolder: () => Promise<void>;
11
12
  openSpecsFolder: () => Promise<void>;
12
13
  rebaseOnMain: () => Promise<void>;
13
14
  ideLoading: boolean;
14
15
  shellLoading: boolean;
16
+ folderLoading: boolean;
15
17
  specsLoading: boolean;
16
18
  rebaseLoading: boolean;
17
19
  ideError: string | null;
18
20
  shellError: string | null;
21
+ folderError: string | null;
19
22
  specsError: string | null;
20
23
  rebaseError: string | null;
21
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-feature-actions.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/use-feature-actions.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,GAAG,mBAAmB,CA6IxF"}
1
+ {"version":3,"file":"use-feature-actions.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/use-feature-actions.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,GAAG,mBAAmB,CA6KxF"}
@@ -8,20 +8,23 @@ const ERROR_CLEAR_DELAY = 5000;
8
8
  export function useFeatureActions(input) {
9
9
  const [ideLoading, setIdeLoading] = useState(false);
10
10
  const [shellLoading, setShellLoading] = useState(false);
11
+ const [folderLoading, setFolderLoading] = useState(false);
11
12
  const [specsLoading, setSpecsLoading] = useState(false);
12
13
  const [rebaseLoading, setRebaseLoading] = useState(false);
13
14
  const [ideError, setIdeError] = useState(null);
14
15
  const [shellError, setShellError] = useState(null);
16
+ const [folderError, setFolderError] = useState(null);
15
17
  const [specsError, setSpecsError] = useState(null);
16
18
  const [rebaseError, setRebaseError] = useState(null);
17
19
  const ideTimerRef = useRef(null);
18
20
  const shellTimerRef = useRef(null);
21
+ const folderTimerRef = useRef(null);
19
22
  const specsTimerRef = useRef(null);
20
23
  const rebaseTimerRef = useRef(null);
21
24
  // Clear timers on unmount — read .current inside cleanup so we get the
22
25
  // actual timer value at teardown time, not the always-null value at mount.
23
26
  useEffect(() => {
24
- const refs = [ideTimerRef, shellTimerRef, specsTimerRef, rebaseTimerRef];
27
+ const refs = [ideTimerRef, shellTimerRef, folderTimerRef, specsTimerRef, rebaseTimerRef];
25
28
  return () => {
26
29
  for (const ref of refs) {
27
30
  if (ref.current)
@@ -59,6 +62,31 @@ export function useFeatureActions(input) {
59
62
  }, [input]);
60
63
  const handleOpenIde = useCallback(() => performAction(openIde, setIdeLoading, setIdeError, ideTimerRef, ideLoading), [performAction, ideLoading]);
61
64
  const handleOpenShell = useCallback(() => performAction(openShell, setShellLoading, setShellError, shellTimerRef, shellLoading), [performAction, shellLoading]);
65
+ const handleOpenFolder = useCallback(async () => {
66
+ if (!input || folderLoading)
67
+ return;
68
+ if (folderTimerRef.current)
69
+ clearTimeout(folderTimerRef.current);
70
+ setFolderLoading(true);
71
+ setFolderError(null);
72
+ try {
73
+ const folderPath = input.worktreePath ?? input.repositoryPath;
74
+ const result = await openFolder(folderPath);
75
+ if (!result.success) {
76
+ const errorMessage = result.error ?? 'An unexpected error occurred';
77
+ setFolderError(errorMessage);
78
+ folderTimerRef.current = setTimeout(() => setFolderError(null), ERROR_CLEAR_DELAY);
79
+ }
80
+ }
81
+ catch (err) {
82
+ const errorMessage = err instanceof Error ? err.message : 'An unexpected error occurred';
83
+ setFolderError(errorMessage);
84
+ folderTimerRef.current = setTimeout(() => setFolderError(null), ERROR_CLEAR_DELAY);
85
+ }
86
+ finally {
87
+ setFolderLoading(false);
88
+ }
89
+ }, [input, folderLoading]);
62
90
  const handleOpenSpecsFolder = useCallback(async () => {
63
91
  if (!input?.specPath || specsLoading)
64
92
  return;
@@ -110,14 +138,17 @@ export function useFeatureActions(input) {
110
138
  return {
111
139
  openInIde: handleOpenIde,
112
140
  openInShell: handleOpenShell,
141
+ openFolder: handleOpenFolder,
113
142
  openSpecsFolder: handleOpenSpecsFolder,
114
143
  rebaseOnMain: handleRebaseOnMain,
115
144
  ideLoading,
116
145
  shellLoading,
146
+ folderLoading,
117
147
  specsLoading,
118
148
  rebaseLoading,
119
149
  ideError,
120
150
  shellError,
151
+ folderError,
121
152
  specsError,
122
153
  rebaseError,
123
154
  };
@@ -1 +1 @@
1
- {"version":3,"file":"open-action-menu.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/open-action-menu/open-action-menu.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAIpD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,cAAc,EACd,YAAY,EACZ,SAAS,GACV,EAAE,mBAAmB,2CAoFrB"}
1
+ {"version":3,"file":"open-action-menu.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/open-action-menu/open-action-menu.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAIpD,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,cAAc,EACd,YAAY,EACZ,SAAS,GACV,EAAE,mBAAmB,2CAqGrB"}
@@ -12,7 +12,7 @@ export function OpenActionMenu({ actions, repositoryPath, worktreePath, showSpec
12
12
  setCopied(true);
13
13
  setTimeout(() => setCopied(false), COPY_FEEDBACK_DELAY);
14
14
  };
15
- const anyLoading = actions.ideLoading || actions.shellLoading || actions.specsLoading;
16
- const anyError = actions.ideError ?? actions.shellError ?? actions.specsError;
17
- return (_jsxs(DropdownMenu, { modal: false, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "gap-1.5", disabled: anyLoading, children: [anyLoading ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : anyError ? (_jsx(CircleAlert, { className: "text-destructive size-4" })) : (_jsx(FolderOpen, { className: "size-4" })), "Open", _jsx(ChevronDown, { className: "size-3 opacity-60" })] }) }), _jsxs(DropdownMenuContent, { align: "start", className: "w-48", children: [_jsx(DropdownMenuLabel, { children: "Open in" }), _jsxs(DropdownMenuItem, { onClick: actions.openInIde, disabled: actions.ideLoading, className: "gap-2", children: [actions.ideLoading ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : actions.ideError ? (_jsx(CircleAlert, { className: "text-destructive size-4" })) : (_jsx(Code2, { className: "size-4" })), "IDE"] }), _jsxs(DropdownMenuItem, { onClick: actions.openInShell, disabled: actions.shellLoading, className: "gap-2", children: [actions.shellLoading ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : actions.shellError ? (_jsx(CircleAlert, { className: "text-destructive size-4" })) : (_jsx(Terminal, { className: "size-4" })), "Terminal"] }), _jsxs(DropdownMenuItem, { onClick: actions.openSpecsFolder, disabled: actions.specsLoading || !showSpecs, className: "gap-2", children: [actions.specsLoading ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : actions.specsError ? (_jsx(CircleAlert, { className: "text-destructive size-4" })) : (_jsx(FolderOpen, { className: "size-4" })), "Specs Folder"] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: handleCopyPath, className: "gap-2", children: [copied ? _jsx(Check, { className: "size-4 text-green-600" }) : _jsx(Copy, { className: "size-4" }), copied ? 'Copied!' : 'Copy path'] })] })] }));
15
+ const anyLoading = actions.ideLoading || actions.shellLoading || actions.folderLoading || actions.specsLoading;
16
+ const anyError = actions.ideError ?? actions.shellError ?? actions.folderError ?? actions.specsError;
17
+ return (_jsxs(DropdownMenu, { modal: false, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", className: "gap-1.5", disabled: anyLoading, children: [anyLoading ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : anyError ? (_jsx(CircleAlert, { className: "text-destructive size-4" })) : (_jsx(FolderOpen, { className: "size-4" })), "Open", _jsx(ChevronDown, { className: "size-3 opacity-60" })] }) }), _jsxs(DropdownMenuContent, { align: "start", className: "w-48", children: [_jsx(DropdownMenuLabel, { children: "Open in" }), _jsxs(DropdownMenuItem, { onClick: actions.openInIde, disabled: actions.ideLoading, className: "gap-2", children: [actions.ideLoading ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : actions.ideError ? (_jsx(CircleAlert, { className: "text-destructive size-4" })) : (_jsx(Code2, { className: "size-4" })), "IDE"] }), _jsxs(DropdownMenuItem, { onClick: actions.openInShell, disabled: actions.shellLoading, className: "gap-2", children: [actions.shellLoading ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : actions.shellError ? (_jsx(CircleAlert, { className: "text-destructive size-4" })) : (_jsx(Terminal, { className: "size-4" })), "Terminal"] }), _jsxs(DropdownMenuItem, { onClick: actions.openFolder, disabled: actions.folderLoading, className: "gap-2", children: [actions.folderLoading ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : actions.folderError ? (_jsx(CircleAlert, { className: "text-destructive size-4" })) : (_jsx(FolderOpen, { className: "size-4" })), "Folder"] }), _jsxs(DropdownMenuItem, { onClick: actions.openSpecsFolder, disabled: actions.specsLoading || !showSpecs, className: "gap-2", children: [actions.specsLoading ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : actions.specsError ? (_jsx(CircleAlert, { className: "text-destructive size-4" })) : (_jsx(FolderOpen, { className: "size-4" })), "Specs Folder"] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: handleCopyPath, className: "gap-2", children: [copied ? _jsx(Check, { className: "size-4 text-green-600" }) : _jsx(Copy, { className: "size-4" }), copied ? 'Copied!' : 'Copy path'] })] })] }));
18
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"open-action-menu.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/open-action-menu/open-action-menu.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAkBpD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,cAAc,CAOrC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,cAAc,CAAC,CAAC;AAE7C,4DAA4D;AAC5D,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,wCAAwC;AACxC,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF,wCAAwC;AACxC,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,4CAA4C;AAC5C,eAAO,MAAM,SAAS,EAAE,KAMvB,CAAC;AAEF,sEAAsE;AACtE,eAAO,MAAM,gBAAgB,EAAE,KAO9B,CAAC"}
1
+ {"version":3,"file":"open-action-menu.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/open-action-menu/open-action-menu.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAqBpD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,cAAc,CAOrC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,cAAc,CAAC,CAAC;AAE7C,4DAA4D;AAC5D,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,wCAAwC;AACxC,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF,wCAAwC;AACxC,eAAO,MAAM,OAAO,EAAE,KAMrB,CAAC;AAEF,4CAA4C;AAC5C,eAAO,MAAM,SAAS,EAAE,KAMvB,CAAC;AAEF,sEAAsE;AACtE,eAAO,MAAM,gBAAgB,EAAE,KAO9B,CAAC"}
@@ -3,14 +3,17 @@ import { OpenActionMenu } from './open-action-menu.js';
3
3
  const defaultActions = {
4
4
  openInIde: fn().mockName('openInIde'),
5
5
  openInShell: fn().mockName('openInShell'),
6
+ openFolder: fn().mockName('openFolder'),
6
7
  openSpecsFolder: fn().mockName('openSpecsFolder'),
7
8
  rebaseOnMain: fn().mockName('rebaseOnMain'),
8
9
  ideLoading: false,
9
10
  shellLoading: false,
11
+ folderLoading: false,
10
12
  specsLoading: false,
11
13
  rebaseLoading: false,
12
14
  ideError: null,
13
15
  shellError: null,
16
+ folderError: null,
14
17
  specsError: null,
15
18
  rebaseError: null,
16
19
  };
@@ -1 +1 @@
1
- {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,eAAe,CAAC;AAKpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAwB5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAsYzF"}
1
+ {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,eAAe,CAAC;AAKpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAwB5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CA0ZzF"}
@@ -197,6 +197,24 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
197
197
  window.addEventListener('shep:feature-delete-requested', handler);
198
198
  return () => window.removeEventListener('shep:feature-delete-requested', handler);
199
199
  }, [handleDeleteFeature]);
200
+ // Listen for archive requests from the feature drawer.
201
+ useEffect(() => {
202
+ const handler = (e) => {
203
+ const { featureId } = e.detail;
204
+ handleArchiveFeature(featureId);
205
+ };
206
+ window.addEventListener('shep:feature-archive-requested', handler);
207
+ return () => window.removeEventListener('shep:feature-archive-requested', handler);
208
+ }, [handleArchiveFeature]);
209
+ // Listen for unarchive requests from the feature drawer.
210
+ useEffect(() => {
211
+ const handler = (e) => {
212
+ const { featureId } = e.detail;
213
+ handleUnarchiveFeature(featureId);
214
+ };
215
+ window.addEventListener('shep:feature-unarchive-requested', handler);
216
+ return () => window.removeEventListener('shep:feature-unarchive-requested', handler);
217
+ }, [handleUnarchiveFeature]);
200
218
  // Wire callbacks into derived node data (via ref — no re-render).
201
219
  useEffect(() => {
202
220
  setCallbacks({