@shepai/cli 1.17.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.
- package/dist/eslint.config.mjs +8 -1
- package/dist/src/application/ports/output/agents/agent-executor.interface.d.ts +4 -0
- package/dist/src/application/ports/output/agents/agent-executor.interface.d.ts.map +1 -1
- package/dist/src/infrastructure/services/agents/common/executors/claude-code-executor.service.d.ts.map +1 -1
- package/dist/src/infrastructure/services/agents/common/executors/claude-code-executor.service.js +4 -0
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/implement.node.d.ts.map +1 -1
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/implement.node.js +13 -3
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts +35 -1
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.d.ts.map +1 -1
- package/dist/src/infrastructure/services/agents/feature-agent/nodes/node-helpers.js +96 -1
- package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.js +1 -1
- package/dist/src/presentation/web/components/ui/alert.js +1 -1
- package/dist/src/presentation/web/components/ui/badge.js +4 -4
- package/dist/src/presentation/web/components/ui/button.js +3 -3
- package/dist/src/presentation/web/components/ui/sidebar.js +2 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/cache/.previewinfo +1 -1
- package/web/.next/cache/.rscinfo +1 -1
- package/web/.next/cache/.tsbuildinfo +1 -1
- package/web/.next/cache/config.json +3 -3
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +1 -1
- package/web/.next/required-server-files.json +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_not-found.html +2 -2
- package/web/.next/server/app/_not-found.rsc +8 -8
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +8 -8
- package/web/.next/server/app/_not-found.segments/_head.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/index.html +2 -2
- package/web/.next/server/app/index.rsc +3 -3
- package/web/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version.html +2 -2
- package/web/.next/server/app/version.rsc +3 -3
- package/web/.next/server/app/version.segments/_full.segment.rsc +3 -3
- package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/_index.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
- package/web/.next/server/chunks/ssr/_0f02ab66._.js +1 -1
- package/web/.next/server/chunks/ssr/_0f02ab66._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_19be0743._.js +1 -1
- package/web/.next/server/chunks/ssr/_4582f3c3._.js +1 -1
- package/web/.next/server/chunks/ssr/_4582f3c3._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_a0c04017._.js +1 -1
- package/web/.next/server/chunks/ssr/_a0c04017._.js.map +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_version_version-page-client_tsx_fe9db753._.js +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_version_version-page-client_tsx_fe9db753._.js.map +1 -1
- package/web/.next/server/pages/404.html +2 -2
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/static/chunks/{5130d7093ad39f4a.js → 26d260496abf3ed8.js} +1 -1
- package/web/.next/static/chunks/{ef1296dbe67d5aae.js → 8353f5690707d9a5.js} +1 -1
- package/web/.next/static/chunks/{523c5cfe0570712c.js → f74cb59a5a6c94f1.js} +1 -1
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- /package/web/.next/static/{Wgd1DC_QfVhpAi62LjpwI → iV3kEDpfUq_ttVqv3E57o}/_buildManifest.js +0 -0
- /package/web/.next/static/{Wgd1DC_QfVhpAi62LjpwI → iV3kEDpfUq_ttVqv3E57o}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{Wgd1DC_QfVhpAi62LjpwI → iV3kEDpfUq_ttVqv3E57o}/_ssgManifest.js +0 -0
package/dist/eslint.config.mjs
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* @see https://eslint.org/docs/latest/use/configure/configuration-files
|
|
9
9
|
* @see https://typescript-eslint.io/getting-started/
|
|
10
10
|
*/
|
|
11
|
+
import { dirname } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
11
13
|
import eslint from '@eslint/js';
|
|
12
14
|
import tseslint from 'typescript-eslint';
|
|
13
15
|
import prettierConfig from 'eslint-config-prettier';
|
|
@@ -16,6 +18,7 @@ import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
|
|
16
18
|
import nextPlugin from '@next/eslint-plugin-next';
|
|
17
19
|
import tailwindPlugin from 'eslint-plugin-tailwindcss';
|
|
18
20
|
import storybookPlugin from 'eslint-plugin-storybook';
|
|
21
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
22
|
export default tseslint.config(
|
|
20
23
|
// =============================================================================
|
|
21
24
|
// Global ignores (replaces .eslintignore)
|
|
@@ -193,6 +196,9 @@ eslint.configs.recommended,
|
|
|
193
196
|
react: {
|
|
194
197
|
version: 'detect',
|
|
195
198
|
},
|
|
199
|
+
tailwindcss: {
|
|
200
|
+
config: `${__dirname}/src/presentation/web/app/globals.css`,
|
|
201
|
+
},
|
|
196
202
|
},
|
|
197
203
|
rules: {
|
|
198
204
|
// React rules
|
|
@@ -216,7 +222,8 @@ eslint.configs.recommended,
|
|
|
216
222
|
'@next/next/no-img-element': 'warn',
|
|
217
223
|
'@next/next/no-sync-scripts': 'error',
|
|
218
224
|
// Tailwind CSS rules
|
|
219
|
-
|
|
225
|
+
// Class sorting is handled by prettier-plugin-tailwindcss
|
|
226
|
+
'tailwindcss/classnames-order': 'off',
|
|
220
227
|
'tailwindcss/no-contradicting-classname': 'error',
|
|
221
228
|
'tailwindcss/no-unnecessary-arbitrary-value': 'warn',
|
|
222
229
|
'tailwindcss/enforces-shorthand': 'warn',
|
|
@@ -70,6 +70,10 @@ export interface AgentExecutionOptions {
|
|
|
70
70
|
timeout?: number;
|
|
71
71
|
/** Suppress executor debug logging (e.g. for quick foreground calls) */
|
|
72
72
|
silent?: boolean;
|
|
73
|
+
/** When true, adds --strict-mcp-config to disable all MCP tools */
|
|
74
|
+
disableMcp?: boolean;
|
|
75
|
+
/** Restrict available built-in tools via --tools flag */
|
|
76
|
+
tools?: string[];
|
|
73
77
|
}
|
|
74
78
|
/**
|
|
75
79
|
* Port interface for executing prompts against an AI agent.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-executor.interface.d.ts","sourceRoot":"","sources":["../../../../../../src/application/ports/output/agents/agent-executor.interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAEtF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,KAAK,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,2DAA2D;IAC3D,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC;IACtC,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,mDAAmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAExF;;;;;;OAMG;IACH,aAAa,CACX,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,aAAa,CAAC,yBAAyB,CAAC,CAAC;IAE5C;;;;;OAKG;IACH,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC;CACjD"}
|
|
1
|
+
{"version":3,"file":"agent-executor.interface.d.ts","sourceRoot":"","sources":["../../../../../../src/application/ports/output/agents/agent-executor.interface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAEtF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,KAAK,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,2DAA2D;IAC3D,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC;IACtC,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,sCAAsC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,mDAAmD;IACnD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAExF;;;;;;OAMG;IACH,aAAa,CACX,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,aAAa,CAAC,yBAAyB,CAAC,CAAC;IAE5C;;;;;OAKG;IACH,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC;CACjD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code-executor.service.d.ts","sourceRoot":"","sources":["../../../../../../../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;AAUjD;;;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;
|
|
1
|
+
{"version":3,"file":"claude-code-executor.service.d.ts","sourceRoot":"","sources":["../../../../../../../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;AAUjD;;;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;IAMzB,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,eAAe;CAkExB"}
|
package/dist/src/infrastructure/services/agents/common/executors/claude-code-executor.service.js
CHANGED
|
@@ -269,6 +269,10 @@ export class ClaudeCodeExecutorService {
|
|
|
269
269
|
args.push('--json-schema', JSON.stringify(options.outputSchema));
|
|
270
270
|
if (options?.maxTurns)
|
|
271
271
|
args.push('--max-turns', String(options.maxTurns));
|
|
272
|
+
if (options?.disableMcp)
|
|
273
|
+
args.push('--strict-mcp-config');
|
|
274
|
+
if (options?.tools?.length)
|
|
275
|
+
args.push('--tools', options.tools.join(','));
|
|
272
276
|
return args;
|
|
273
277
|
}
|
|
274
278
|
buildStreamArgs(prompt, options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"implement.node.d.ts","sourceRoot":"","sources":["../../../../../../../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;
|
|
1
|
+
{"version":3,"file":"implement.node.d.ts","sourceRoot":"","sources":["../../../../../../../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;AA+DrD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,IAG5C,OAAO,iBAAiB,KAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAgK7E"}
|
|
@@ -10,7 +10,7 @@ import yaml from 'js-yaml';
|
|
|
10
10
|
import { writeFileSync } from 'node:fs';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
12
|
import { interrupt, isGraphBubbleUp } from '@langchain/langgraph';
|
|
13
|
-
import { createNodeLogger, readSpecFile, buildExecutorOptions, shouldInterrupt, safeYamlLoad, } from './node-helpers.js';
|
|
13
|
+
import { createNodeLogger, readSpecFile, buildExecutorOptions, shouldInterrupt, safeYamlLoad, retryExecute, getCompletedPhases, markPhaseComplete, } from './node-helpers.js';
|
|
14
14
|
import { reportNodeStart } from '../heartbeat.js';
|
|
15
15
|
import { buildImplementPhasePrompt, } from './prompts/implement.prompt.js';
|
|
16
16
|
/**
|
|
@@ -71,6 +71,9 @@ export function createImplementNode(executor) {
|
|
|
71
71
|
const totalTasks = tasksData.tasks.length;
|
|
72
72
|
let completedTasks = 0;
|
|
73
73
|
const totalPhases = planData.phases.length;
|
|
74
|
+
// --- Check for completed phases (skip on resume) ---
|
|
75
|
+
const completedPhaseIds = getCompletedPhases(state.specDir);
|
|
76
|
+
const retryOpts = { logger: log };
|
|
74
77
|
// --- Execute phases in order ---
|
|
75
78
|
for (let i = 0; i < totalPhases; i++) {
|
|
76
79
|
const phase = planData.phases[i];
|
|
@@ -81,6 +84,12 @@ export function createImplementNode(executor) {
|
|
|
81
84
|
.map((id) => taskMap.get(id))
|
|
82
85
|
.filter((t) => t !== undefined)
|
|
83
86
|
: tasksData.tasks.filter((t) => t.phaseId === phase.id);
|
|
87
|
+
// Skip already-completed phases
|
|
88
|
+
if (completedPhaseIds.includes(phase.id)) {
|
|
89
|
+
completedTasks += phaseTasks.length;
|
|
90
|
+
log.info(`Phase ${phase.id} "${phase.name}" — already complete, skipping`);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
84
93
|
if (phaseTasks.length === 0) {
|
|
85
94
|
log.info(`Phase ${phase.id} "${phase.name}" — no tasks, skipping`);
|
|
86
95
|
continue;
|
|
@@ -99,7 +108,7 @@ export function createImplementNode(executor) {
|
|
|
99
108
|
};
|
|
100
109
|
const prompt = buildImplementPhasePrompt(state, singleTaskPhase, [task], promptContext);
|
|
101
110
|
log.info(` [parallel] Task ${task.id}: "${task.title}" — ${prompt.length} chars`);
|
|
102
|
-
return executor
|
|
111
|
+
return retryExecute(executor, prompt, options, retryOpts);
|
|
103
112
|
}));
|
|
104
113
|
for (let j = 0; j < results.length; j++) {
|
|
105
114
|
log.info(` [parallel] Task ${phaseTasks[j].id} complete (${results[j].result.length} chars)`);
|
|
@@ -109,10 +118,11 @@ export function createImplementNode(executor) {
|
|
|
109
118
|
// Sequential: single executor call with all phase tasks
|
|
110
119
|
const prompt = buildImplementPhasePrompt(state, phase, phaseTasks, promptContext);
|
|
111
120
|
log.info(`Executing phase prompt — ${prompt.length} chars`);
|
|
112
|
-
const result = await executor
|
|
121
|
+
const result = await retryExecute(executor, prompt, options, retryOpts);
|
|
113
122
|
log.info(`Phase complete (${result.result.length} chars)`);
|
|
114
123
|
}
|
|
115
124
|
completedTasks += phaseTasks.length;
|
|
125
|
+
markPhaseComplete(state.specDir, phase.id, log);
|
|
116
126
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
117
127
|
messages.push(`[implement] Phase ${phase.id} "${phase.name}" — ${phaseTasks.length} task(s) done (${elapsed}s)`);
|
|
118
128
|
updateFeatureProgress(state.specDir, completedTasks, totalTasks, `implementing-${phase.id}`, null, log);
|
|
@@ -4,7 +4,7 @@
|
|
|
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 type { IAgentExecutor, AgentExecutionOptions } from '../../../../../application/ports/output/agents/agent-executor.interface.js';
|
|
7
|
+
import type { IAgentExecutor, AgentExecutionOptions, AgentExecutionResult } from '../../../../../application/ports/output/agents/agent-executor.interface.js';
|
|
8
8
|
import type { FeatureAgentState } from '../state.js';
|
|
9
9
|
/**
|
|
10
10
|
* Create a scoped logger that prefixes messages with the node name.
|
|
@@ -34,6 +34,40 @@ export declare function safeYamlLoad(content: string): unknown;
|
|
|
34
34
|
* for human approval given the configured approval mode.
|
|
35
35
|
*/
|
|
36
36
|
export declare function shouldInterrupt(nodeName: string, approvalMode: string | undefined): boolean;
|
|
37
|
+
export type ErrorCategory = 'retryable-api' | 'retryable-network' | 'non-retryable' | 'unknown';
|
|
38
|
+
/**
|
|
39
|
+
* Classify an error message into a retry category.
|
|
40
|
+
*
|
|
41
|
+
* - **retryable-api**: Transient API errors (rate-limit, overload, bad request)
|
|
42
|
+
* - **retryable-network**: Network-level failures (DNS, timeouts, connection refused)
|
|
43
|
+
* - **non-retryable**: Logic / filesystem / syntax errors that won't resolve on retry
|
|
44
|
+
* - **unknown**: Anything unrecognised (callers may choose to retry cautiously)
|
|
45
|
+
*/
|
|
46
|
+
export declare function classifyError(errorMessage: string): ErrorCategory;
|
|
47
|
+
export interface RetryOptions {
|
|
48
|
+
/** Maximum number of execution attempts (default 3). */
|
|
49
|
+
maxAttempts?: number;
|
|
50
|
+
/** Base delay in ms before the first retry (default 2000). Doubles each retry. */
|
|
51
|
+
baseDelayMs?: number;
|
|
52
|
+
/** Optional logger for retry messages. */
|
|
53
|
+
logger?: NodeLogger;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Execute a prompt via the given executor with automatic retry and
|
|
57
|
+
* exponential back-off for transient errors.
|
|
58
|
+
*
|
|
59
|
+
* Non-retryable errors are thrown immediately. Unknown errors are
|
|
60
|
+
* retried (conservative stance: could be transient).
|
|
61
|
+
*/
|
|
62
|
+
export declare function retryExecute(executor: IAgentExecutor, prompt: string, options: AgentExecutionOptions, retryOpts?: RetryOptions): Promise<AgentExecutionResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Read completed phases from feature.yaml.
|
|
65
|
+
*/
|
|
66
|
+
export declare function getCompletedPhases(specDir: string): string[];
|
|
67
|
+
/**
|
|
68
|
+
* Mark a phase as complete in feature.yaml by adding its ID to completedPhases.
|
|
69
|
+
*/
|
|
70
|
+
export declare function markPhaseComplete(specDir: string, phaseId: string, log?: NodeLogger): void;
|
|
37
71
|
/**
|
|
38
72
|
* Execute a node with consistent logging and error handling.
|
|
39
73
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-helpers.d.ts","sourceRoot":"","sources":["../../../../../../../src/infrastructure/services/agents/feature-agent/nodes/node-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,
|
|
1
|
+
{"version":3,"file":"node-helpers.d.ts","sourceRoot":"","sources":["../../../../../../../src/infrastructure/services/agents/feature-agent/nodes/node-helpers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,+DAA+D,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM;kBAE/B,MAAM,GAAG,IAAI;mBAIZ,MAAM,GAAG,IAAI;EAK/B;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE7D;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMtE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,GAAG,qBAAqB,CAMpF;AAeD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQrD;AAUD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAI3F;AAMD,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,mBAAmB,GAAG,eAAe,GAAG,SAAS,CAAC;AAMhG;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,CAKjE;AAED,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,qBAAqB,EAC9B,SAAS,CAAC,EAAE,YAAY,GACvB,OAAO,CAAC,oBAAoB,CAAC,CA6B/B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAW5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,CAwB1F;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,UAAU,KAAK,MAAM,GACjE,CAAC,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAgDnE"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* and error handling so every node follows the same patterns.
|
|
6
6
|
*/
|
|
7
7
|
import yaml from 'js-yaml';
|
|
8
|
-
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { interrupt, isGraphBubbleUp } from '@langchain/langgraph';
|
|
11
11
|
import { reportNodeStart } from '../heartbeat.js';
|
|
@@ -42,6 +42,8 @@ export function readSpecFile(specDir, filename) {
|
|
|
42
42
|
export function buildExecutorOptions(state) {
|
|
43
43
|
return {
|
|
44
44
|
cwd: state.worktreePath || state.repositoryPath,
|
|
45
|
+
maxTurns: 50,
|
|
46
|
+
disableMcp: true,
|
|
45
47
|
};
|
|
46
48
|
}
|
|
47
49
|
/**
|
|
@@ -85,6 +87,99 @@ export function shouldInterrupt(nodeName, approvalMode) {
|
|
|
85
87
|
const autoApproved = AUTO_APPROVED_NODES[approvalMode] ?? [];
|
|
86
88
|
return !autoApproved.includes(nodeName);
|
|
87
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
|
+
}
|
|
88
183
|
/**
|
|
89
184
|
* Execute a node with consistent logging and error handling.
|
|
90
185
|
*
|
package/dist/src/presentation/web/components/common/sidebar-section-header/sidebar-section-header.js
CHANGED
|
@@ -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
|
|
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('
|
|
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('
|
|
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
|
|
8
|
-
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80
|
|
9
|
-
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/80
|
|
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("
|
|
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
|
|
10
|
-
outline: 'bg-background hover:bg-accent hover:text-accent-foreground dark:
|
|
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
|
|
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
|
|
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',
|