@shepai/cli 1.16.0 → 1.18.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 (126) hide show
  1. package/dist/eslint.config.mjs +8 -1
  2. package/dist/src/application/ports/output/agents/agent-executor-provider.interface.d.ts +5 -0
  3. package/dist/src/application/ports/output/agents/agent-executor-provider.interface.d.ts.map +1 -0
  4. package/dist/src/application/ports/output/agents/agent-executor-provider.interface.js +1 -0
  5. package/dist/src/application/ports/output/agents/agent-executor.interface.d.ts +4 -0
  6. package/dist/src/application/ports/output/agents/agent-executor.interface.d.ts.map +1 -1
  7. package/dist/src/application/ports/output/agents/feature-agent-process.interface.d.ts +2 -0
  8. package/dist/src/application/ports/output/agents/feature-agent-process.interface.d.ts.map +1 -1
  9. package/dist/src/application/ports/output/agents/index.d.ts +1 -0
  10. package/dist/src/application/ports/output/agents/index.d.ts.map +1 -1
  11. package/dist/src/application/ports/output/index.d.ts +1 -1
  12. package/dist/src/application/ports/output/index.d.ts.map +1 -1
  13. package/dist/src/application/use-cases/features/create-feature.use-case.d.ts +3 -3
  14. package/dist/src/application/use-cases/features/create-feature.use-case.d.ts.map +1 -1
  15. package/dist/src/application/use-cases/features/create-feature.use-case.js +9 -7
  16. package/dist/src/application/use-cases/features/resume-feature.use-case.d.ts.map +1 -1
  17. package/dist/src/application/use-cases/features/resume-feature.use-case.js +6 -1
  18. package/dist/src/infrastructure/di/container.d.ts.map +1 -1
  19. package/dist/src/infrastructure/di/container.js +9 -2
  20. package/dist/src/infrastructure/services/agents/common/agent-executor-factory.service.d.ts +1 -1
  21. package/dist/src/infrastructure/services/agents/common/agent-executor-factory.service.d.ts.map +1 -1
  22. package/dist/src/infrastructure/services/agents/common/agent-executor-factory.service.js +6 -2
  23. package/dist/src/infrastructure/services/agents/common/agent-executor-provider.service.d.ts +9 -0
  24. package/dist/src/infrastructure/services/agents/common/agent-executor-provider.service.d.ts.map +1 -0
  25. package/dist/src/infrastructure/services/agents/common/agent-executor-provider.service.js +11 -0
  26. package/dist/src/infrastructure/services/agents/common/agent-runner.service.d.ts +3 -3
  27. package/dist/src/infrastructure/services/agents/common/agent-runner.service.d.ts.map +1 -1
  28. package/dist/src/infrastructure/services/agents/common/agent-runner.service.js +4 -4
  29. package/dist/src/infrastructure/services/agents/common/agent-validator.service.d.ts +1 -9
  30. package/dist/src/infrastructure/services/agents/common/agent-validator.service.d.ts.map +1 -1
  31. package/dist/src/infrastructure/services/agents/common/agent-validator.service.js +1 -0
  32. package/dist/src/infrastructure/services/agents/common/executors/claude-code-executor.service.d.ts +1 -7
  33. package/dist/src/infrastructure/services/agents/common/executors/claude-code-executor.service.d.ts.map +1 -1
  34. package/dist/src/infrastructure/services/agents/common/executors/claude-code-executor.service.js +4 -0
  35. package/dist/src/infrastructure/services/agents/common/executors/cursor-executor.service.d.ts +38 -0
  36. package/dist/src/infrastructure/services/agents/common/executors/cursor-executor.service.d.ts.map +1 -0
  37. package/dist/src/infrastructure/services/agents/common/executors/cursor-executor.service.js +296 -0
  38. package/dist/src/infrastructure/services/agents/common/types.d.ts +23 -0
  39. package/dist/src/infrastructure/services/agents/common/types.d.ts.map +1 -0
  40. package/dist/src/infrastructure/services/agents/common/types.js +7 -0
  41. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts +2 -0
  42. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.d.ts.map +1 -1
  43. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-process.service.js +6 -0
  44. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts +2 -0
  45. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
  46. package/dist/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +32 -12
  47. package/dist/src/infrastructure/services/agents/feature-agent/nodes/implement.node.d.ts.map +1 -1
  48. package/dist/src/infrastructure/services/agents/feature-agent/nodes/implement.node.js +19 -10
  49. package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts +41 -1
  50. package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts.map +1 -1
  51. package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.js +125 -6
  52. package/dist/src/presentation/cli/commands/feat/ls.command.d.ts.map +1 -1
  53. package/dist/src/presentation/cli/commands/feat/ls.command.js +42 -7
  54. package/dist/src/presentation/tui/prompts/agent-select.prompt.d.ts +1 -1
  55. package/dist/src/presentation/tui/prompts/agent-select.prompt.js +1 -1
  56. package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.js +1 -1
  57. package/dist/src/presentation/web/components/ui/alert.js +1 -1
  58. package/dist/src/presentation/web/components/ui/badge.js +4 -4
  59. package/dist/src/presentation/web/components/ui/button.js +3 -3
  60. package/dist/src/presentation/web/components/ui/sidebar.js +2 -2
  61. package/dist/tsconfig.build.tsbuildinfo +1 -1
  62. package/package.json +1 -1
  63. package/web/.next/BUILD_ID +1 -1
  64. package/web/.next/build-manifest.json +2 -2
  65. package/web/.next/cache/.previewinfo +1 -1
  66. package/web/.next/cache/.rscinfo +1 -1
  67. package/web/.next/cache/.tsbuildinfo +1 -1
  68. package/web/.next/cache/config.json +3 -3
  69. package/web/.next/fallback-build-manifest.json +2 -2
  70. package/web/.next/prerender-manifest.json +3 -3
  71. package/web/.next/required-server-files.js +1 -1
  72. package/web/.next/required-server-files.json +1 -1
  73. package/web/.next/server/app/_global-error.html +2 -2
  74. package/web/.next/server/app/_global-error.rsc +1 -1
  75. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  76. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  77. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  78. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  79. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  80. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  81. package/web/.next/server/app/_not-found.html +2 -2
  82. package/web/.next/server/app/_not-found.rsc +8 -8
  83. package/web/.next/server/app/_not-found.segments/_full.segment.rsc +8 -8
  84. package/web/.next/server/app/_not-found.segments/_head.segment.rsc +3 -3
  85. package/web/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  86. package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  87. package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  88. package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  89. package/web/.next/server/app/index.html +2 -2
  90. package/web/.next/server/app/index.rsc +3 -3
  91. package/web/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  92. package/web/.next/server/app/index.segments/_full.segment.rsc +3 -3
  93. package/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  94. package/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  95. package/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  96. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  97. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  98. package/web/.next/server/app/version.html +2 -2
  99. package/web/.next/server/app/version.rsc +3 -3
  100. package/web/.next/server/app/version.segments/_full.segment.rsc +3 -3
  101. package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
  102. package/web/.next/server/app/version.segments/_index.segment.rsc +2 -2
  103. package/web/.next/server/app/version.segments/_tree.segment.rsc +1 -1
  104. package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +2 -2
  105. package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
  106. package/web/.next/server/chunks/ssr/_0f02ab66._.js +1 -1
  107. package/web/.next/server/chunks/ssr/_0f02ab66._.js.map +1 -1
  108. package/web/.next/server/chunks/ssr/_19be0743._.js +1 -1
  109. package/web/.next/server/chunks/ssr/_4582f3c3._.js +1 -1
  110. package/web/.next/server/chunks/ssr/_4582f3c3._.js.map +1 -1
  111. package/web/.next/server/chunks/ssr/_a0c04017._.js +1 -1
  112. package/web/.next/server/chunks/ssr/_a0c04017._.js.map +1 -1
  113. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_version_version-page-client_tsx_fe9db753._.js +1 -1
  114. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_version_version-page-client_tsx_fe9db753._.js.map +1 -1
  115. package/web/.next/server/pages/404.html +2 -2
  116. package/web/.next/server/pages/500.html +2 -2
  117. package/web/.next/server/server-reference-manifest.js +1 -1
  118. package/web/.next/server/server-reference-manifest.json +1 -1
  119. package/web/.next/static/chunks/{5130d7093ad39f4a.js → 26d260496abf3ed8.js} +1 -1
  120. package/web/.next/static/chunks/{ef1296dbe67d5aae.js → 8353f5690707d9a5.js} +1 -1
  121. package/web/.next/static/chunks/{523c5cfe0570712c.js → f74cb59a5a6c94f1.js} +1 -1
  122. package/web/.next/trace +1 -1
  123. package/web/.next/trace-build +1 -1
  124. /package/web/.next/static/{KqMr5DLdrvbaA-agLuHP1 → iV3kEDpfUq_ttVqv3E57o}/_buildManifest.js +0 -0
  125. /package/web/.next/static/{KqMr5DLdrvbaA-agLuHP1 → iV3kEDpfUq_ttVqv3E57o}/_clientMiddlewareManifest.json +0 -0
  126. /package/web/.next/static/{KqMr5DLdrvbaA-agLuHP1 → iV3kEDpfUq_ttVqv3E57o}/_ssgManifest.js +0 -0
@@ -4,7 +4,8 @@
4
4
  * Provides consistent logging, spec file reading, executor invocation,
5
5
  * and error handling so every node follows the same patterns.
6
6
  */
7
- import { readFileSync } from 'node:fs';
7
+ import yaml from 'js-yaml';
8
+ import { readFileSync, writeFileSync } from 'node:fs';
8
9
  import { join } from 'node:path';
9
10
  import { interrupt, isGraphBubbleUp } from '@langchain/langgraph';
10
11
  import { reportNodeStart } from '../heartbeat.js';
@@ -41,8 +42,34 @@ export function readSpecFile(specDir, filename) {
41
42
  export function buildExecutorOptions(state) {
42
43
  return {
43
44
  cwd: state.worktreePath || state.repositoryPath,
45
+ maxTurns: 50,
46
+ disableMcp: true,
44
47
  };
45
48
  }
49
+ /**
50
+ * Sanitize YAML content to handle common AI-generated issues.
51
+ * Quotes unquoted list items containing `{` or `}` characters
52
+ * which js-yaml interprets as flow mappings.
53
+ */
54
+ function sanitizeYamlBraces(content) {
55
+ return content.replace(/^(\s*- )(?!['"])(.+[{}].+)$/gm, (_match, prefix, value) => `${prefix}"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`);
56
+ }
57
+ /**
58
+ * Parse YAML with automatic sanitization of common AI-generated issues.
59
+ * First attempts normal parsing; on failure, sanitizes unquoted braces
60
+ * in list items and retries.
61
+ */
62
+ export function safeYamlLoad(content) {
63
+ try {
64
+ return yaml.load(content);
65
+ }
66
+ catch (firstError) {
67
+ const sanitized = sanitizeYamlBraces(content);
68
+ if (sanitized === content)
69
+ throw firstError; // nothing to sanitize
70
+ return yaml.load(sanitized);
71
+ }
72
+ }
46
73
  /** Nodes that are auto-approved (no interrupt) for each approval mode. */
47
74
  const AUTO_APPROVED_NODES = {
48
75
  interactive: [],
@@ -60,6 +87,99 @@ export function shouldInterrupt(nodeName, approvalMode) {
60
87
  const autoApproved = AUTO_APPROVED_NODES[approvalMode] ?? [];
61
88
  return !autoApproved.includes(nodeName);
62
89
  }
90
+ const API_ERROR_RE = /API Error: (400|429|5\d{2})/;
91
+ const NETWORK_ERROR_RE = /ECONNREFUSED|ETIMEDOUT|ENOTFOUND|timed out/i;
92
+ const NON_RETRYABLE_RE = /Process exited with code|ENOENT|SyntaxError/;
93
+ /**
94
+ * Classify an error message into a retry category.
95
+ *
96
+ * - **retryable-api**: Transient API errors (rate-limit, overload, bad request)
97
+ * - **retryable-network**: Network-level failures (DNS, timeouts, connection refused)
98
+ * - **non-retryable**: Logic / filesystem / syntax errors that won't resolve on retry
99
+ * - **unknown**: Anything unrecognised (callers may choose to retry cautiously)
100
+ */
101
+ export function classifyError(errorMessage) {
102
+ if (API_ERROR_RE.test(errorMessage))
103
+ return 'retryable-api';
104
+ if (NETWORK_ERROR_RE.test(errorMessage))
105
+ return 'retryable-network';
106
+ if (NON_RETRYABLE_RE.test(errorMessage))
107
+ return 'non-retryable';
108
+ return 'unknown';
109
+ }
110
+ /**
111
+ * Execute a prompt via the given executor with automatic retry and
112
+ * exponential back-off for transient errors.
113
+ *
114
+ * Non-retryable errors are thrown immediately. Unknown errors are
115
+ * retried (conservative stance: could be transient).
116
+ */
117
+ export async function retryExecute(executor, prompt, options, retryOpts) {
118
+ const maxAttempts = retryOpts?.maxAttempts ?? 3;
119
+ const baseDelayMs = retryOpts?.baseDelayMs ?? 2000;
120
+ const log = retryOpts?.logger;
121
+ let lastError;
122
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
123
+ try {
124
+ return await executor.execute(prompt, options);
125
+ }
126
+ catch (err) {
127
+ lastError = err instanceof Error ? err : new Error(String(err));
128
+ const category = classifyError(lastError.message);
129
+ if (category === 'non-retryable') {
130
+ throw lastError;
131
+ }
132
+ if (attempt < maxAttempts) {
133
+ const delayMs = baseDelayMs * Math.pow(2, attempt - 1);
134
+ log?.info(`Attempt ${attempt}/${maxAttempts} failed (${category}), retrying in ${delayMs}ms`);
135
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
136
+ }
137
+ }
138
+ }
139
+ throw lastError;
140
+ }
141
+ /**
142
+ * Read completed phases from feature.yaml.
143
+ */
144
+ export function getCompletedPhases(specDir) {
145
+ const content = readSpecFile(specDir, 'feature.yaml');
146
+ if (!content)
147
+ return [];
148
+ try {
149
+ const data = yaml.load(content);
150
+ const status = (data?.status ?? {});
151
+ const phases = status.completedPhases;
152
+ return Array.isArray(phases) ? phases : [];
153
+ }
154
+ catch {
155
+ return [];
156
+ }
157
+ }
158
+ /**
159
+ * Mark a phase as complete in feature.yaml by adding its ID to completedPhases.
160
+ */
161
+ export function markPhaseComplete(specDir, phaseId, log) {
162
+ try {
163
+ const content = readSpecFile(specDir, 'feature.yaml');
164
+ if (!content)
165
+ return;
166
+ const data = yaml.load(content);
167
+ if (!data || typeof data !== 'object')
168
+ return;
169
+ const status = (data.status ?? {});
170
+ const completedPhases = Array.isArray(status.completedPhases)
171
+ ? [...status.completedPhases]
172
+ : [];
173
+ if (!completedPhases.includes(phaseId)) {
174
+ completedPhases.push(phaseId);
175
+ }
176
+ data.status = { ...status, completedPhases };
177
+ writeFileSync(join(specDir, 'feature.yaml'), yaml.dump(data, { indent: 2, lineWidth: -1 }), 'utf-8');
178
+ }
179
+ catch (err) {
180
+ log?.error(`Failed to mark phase complete: ${err instanceof Error ? err.message : String(err)}`);
181
+ }
182
+ }
63
183
  /**
64
184
  * Execute a node with consistent logging and error handling.
65
185
  *
@@ -104,11 +224,10 @@ export function executeNode(nodeName, executor, buildPrompt) {
104
224
  const message = err instanceof Error ? err.message : String(err);
105
225
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
106
226
  log.error(`${message} (after ${elapsed}s)`);
107
- return {
108
- currentNode: nodeName,
109
- error: message,
110
- messages: [`[${nodeName}] Error: ${message}`],
111
- };
227
+ // Throw so LangGraph does NOT checkpoint this node as "completed".
228
+ // The worker catch block marks the run as failed, and on resume
229
+ // LangGraph re-executes from the last successfully checkpointed node.
230
+ throw new Error(`[${nodeName}] ${message}`);
112
231
  }
113
232
  };
114
233
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ls.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/ls.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoEpC,wBAAgB,eAAe,IAAI,OAAO,CAqDzC"}
1
+ {"version":3,"file":"ls.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/ls.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsGpC,wBAAgB,eAAe,IAAI,OAAO,CAqDzC"}
@@ -10,6 +10,7 @@
10
10
  * $ shep feat ls
11
11
  * $ shep feat ls --repo /path/to/project
12
12
  */
13
+ import path from 'node:path';
13
14
  import { Command } from 'commander';
14
15
  import { container } from '../../../../infrastructure/di/container.js';
15
16
  import { ListFeaturesUseCase } from '../../../../application/use-cases/features/list-features.use-case.js';
@@ -65,6 +66,39 @@ function isProcessAlive(pid) {
65
66
  return false;
66
67
  }
67
68
  }
69
+ /** Format a duration in ms to a compact human-readable string. */
70
+ function formatDuration(ms) {
71
+ const seconds = Math.floor(ms / 1000);
72
+ if (seconds < 60)
73
+ return `${seconds}s`;
74
+ const minutes = Math.floor(seconds / 60);
75
+ if (minutes < 60)
76
+ return `${minutes}m`;
77
+ const hours = Math.floor(minutes / 60);
78
+ const remMin = minutes % 60;
79
+ if (hours < 24)
80
+ return remMin > 0 ? `${hours}h ${remMin}m` : `${hours}h`;
81
+ const days = Math.floor(hours / 24);
82
+ return `${days}d`;
83
+ }
84
+ /** Format the time column based on run status. */
85
+ function formatTime(feature, run) {
86
+ const now = Date.now();
87
+ if (run) {
88
+ const isRunning = run.status === 'running' || run.status === 'pending';
89
+ if (isRunning && run.startedAt) {
90
+ const started = new Date(run.startedAt).getTime();
91
+ return colors.info(formatDuration(now - started));
92
+ }
93
+ if (run.status === 'completed' && run.completedAt) {
94
+ const completed = new Date(run.completedAt).getTime();
95
+ return colors.muted(`${formatDuration(now - completed)} ago`);
96
+ }
97
+ }
98
+ // Fallback to updatedAt
99
+ const updated = new Date(feature.updatedAt).getTime();
100
+ return colors.muted(`${formatDuration(now - updated)} ago`);
101
+ }
68
102
  export function createLsCommand() {
69
103
  return new Command('ls')
70
104
  .description('List features')
@@ -79,15 +113,15 @@ export function createLsCommand() {
79
113
  const runs = await Promise.all(features.map((f) => f.agentRunId ? runRepo.findById(f.agentRunId) : Promise.resolve(null)));
80
114
  const rows = features.map((feature, i) => {
81
115
  const run = runs[i];
82
- const updated = feature.updatedAt instanceof Date
83
- ? feature.updatedAt.toLocaleDateString()
84
- : String(feature.updatedAt);
116
+ const repo = path.basename(feature.repositoryPath);
117
+ const agent = run?.agentType ?? colors.muted('—');
85
118
  return [
86
119
  feature.id.slice(0, 8),
87
120
  feature.name.slice(0, 30),
88
121
  formatStatus(feature, run),
89
- colors.muted(feature.branch),
90
- colors.muted(updated),
122
+ colors.muted(repo),
123
+ agent,
124
+ formatTime(feature, run),
91
125
  ];
92
126
  });
93
127
  renderListView({
@@ -96,8 +130,9 @@ export function createLsCommand() {
96
130
  { label: 'ID', width: 10 },
97
131
  { label: 'Name', width: 32 },
98
132
  { label: 'Status', width: 20 },
99
- { label: 'Branch', width: 28 },
100
- { label: 'Updated', width: 12 },
133
+ { label: 'Repo', width: 20 },
134
+ { label: 'Agent', width: 14 },
135
+ { label: 'Time', width: 12 },
101
136
  ],
102
137
  rows,
103
138
  emptyMessage: 'No features found',
@@ -32,7 +32,7 @@ export declare function createAgentSelectConfig(): {
32
32
  }, {
33
33
  readonly name: "Cursor";
34
34
  readonly value: AgentType.Cursor;
35
- readonly disabled: "(Coming Soon)";
35
+ readonly description: "Cursor AI coding agent";
36
36
  }];
37
37
  readonly theme: {
38
38
  readonly prefix: {
@@ -39,7 +39,7 @@ export function createAgentSelectConfig() {
39
39
  {
40
40
  name: 'Cursor',
41
41
  value: AgentType.Cursor,
42
- disabled: '(Coming Soon)',
42
+ description: 'Cursor AI coding agent',
43
43
  },
44
44
  ],
45
45
  theme: shepTheme,
@@ -7,5 +7,5 @@ import { useSidebar } from '../../ui/sidebar';
7
7
  import { cn } from '../../../lib/utils';
8
8
  export function SidebarSectionHeader({ label, onFolderClick, onMenuClick, className, }) {
9
9
  const { state, isMobile } = useSidebar();
10
- return (_jsx(SidebarGroup, { "data-testid": "sidebar-section-header", className: cn('px-2 py-1', className), children: _jsxs("div", { className: "text-sidebar-foreground flex h-7 w-full shrink-0 items-center px-2 text-sm font-medium", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: label }), _jsxs("span", { className: "text-sidebar-foreground/70 flex shrink-0 items-center gap-0.5", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: onFolderClick, className: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground ring-sidebar-ring flex size-7 items-center justify-center rounded-md p-0 outline-hidden transition-colors focus-visible:ring-2 [&>svg]:size-4", "aria-label": "Open features folder", children: _jsx(Folder, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "right", align: "center", hidden: state !== 'collapsed' || isMobile, children: "Open features folder" })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: onMenuClick, className: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground ring-sidebar-ring flex size-7 items-center justify-center rounded-md p-0 outline-hidden transition-colors focus-visible:ring-2 [&>svg]:size-4", "aria-label": "Features options", children: _jsx(GripHorizontal, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "right", align: "center", hidden: state !== 'collapsed' || isMobile, children: "Features options" })] })] })] }) }));
10
+ return (_jsx(SidebarGroup, { "data-testid": "sidebar-section-header", className: cn('px-2 py-1', className), children: _jsxs("div", { className: "text-sidebar-foreground flex h-7 w-full shrink-0 items-center px-2 text-sm font-medium", children: [_jsx("span", { className: "min-w-0 flex-1 truncate", children: label }), _jsxs("span", { className: "text-sidebar-foreground/70 flex shrink-0 items-center gap-0.5", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: onFolderClick, className: "ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex size-7 items-center justify-center rounded-md p-0 outline-hidden transition-colors focus-visible:ring-2 [&>svg]:size-4", "aria-label": "Open features folder", children: _jsx(Folder, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "right", align: "center", hidden: state !== 'collapsed' || isMobile, children: "Open features folder" })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: onMenuClick, className: "ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground flex size-7 items-center justify-center rounded-md p-0 outline-hidden transition-colors focus-visible:ring-2 [&>svg]:size-4", "aria-label": "Features options", children: _jsx(GripHorizontal, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "right", align: "center", hidden: state !== 'collapsed' || isMobile, children: "Features options" })] })] })] }) }));
11
11
  }
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
3
  import { cva } from 'class-variance-authority';
4
4
  import { cn } from '../../lib/utils';
5
- const alertVariants = cva('[&>svg]:text-foreground relative w-full rounded-lg border px-4 py-3 text-sm [&>svg]:absolute [&>svg]:top-4 [&>svg]:left-4 [&>svg+div]:translate-y-[-3px] [&>svg~*]:pl-7', {
5
+ const alertVariants = cva('relative w-full rounded-lg border px-4 py-3 text-sm [&>svg]:absolute [&>svg]:top-4 [&>svg]:left-4 [&>svg]:text-foreground [&>svg+div]:translate-y-[-3px] [&>svg~*]:pl-7', {
6
6
  variants: {
7
7
  variant: {
8
8
  default: 'bg-background text-foreground',
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cva } from 'class-variance-authority';
3
3
  import { cn } from '../../lib/utils';
4
- const badgeVariants = cva('focus:ring-ring inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:ring-2 focus:ring-offset-2 focus:outline-none', {
4
+ const badgeVariants = cva('inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-none', {
5
5
  variants: {
6
6
  variant: {
7
- default: 'bg-primary text-primary-foreground hover:bg-primary/80 border-transparent shadow',
8
- secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent',
9
- destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent shadow',
7
+ default: 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
8
+ secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
9
+ destructive: 'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
10
10
  outline: 'text-foreground',
11
11
  },
12
12
  },
@@ -2,12 +2,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cva } from 'class-variance-authority';
3
3
  import { Slot } from 'radix-ui';
4
4
  import { cn } from '../../lib/utils';
5
- const buttonVariants = cva("focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
5
+ const buttonVariants = cva("inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
6
6
  variants: {
7
7
  variant: {
8
8
  default: 'bg-primary text-primary-foreground hover:bg-primary/90',
9
- destructive: 'bg-destructive hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white',
10
- outline: 'bg-background hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border shadow-xs',
9
+ destructive: 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40',
10
+ outline: 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50',
11
11
  secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
12
12
  ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
13
13
  link: 'text-primary underline-offset-4 hover:underline',
@@ -148,11 +148,11 @@ function SidebarMenu({ className, ...props }) {
148
148
  function SidebarMenuItem({ className, ...props }) {
149
149
  return (_jsx("li", { "data-slot": "sidebar-menu-item", "data-sidebar": "menu-item", className: cn('group/menu-item relative', className), ...props }));
150
150
  }
151
- const sidebarMenuButtonVariants = cva('peer/menu-button ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0', {
151
+ const sidebarMenuButtonVariants = cva('peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0', {
152
152
  variants: {
153
153
  variant: {
154
154
  default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
155
- outline: 'bg-background hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]',
155
+ outline: 'bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]',
156
156
  },
157
157
  size: {
158
158
  default: 'h-8 text-sm',