@shepai/cli 1.52.2 → 1.53.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/apis/json-schema/CiFixRecord.yaml +24 -0
- package/apis/json-schema/Feature.yaml +3 -0
- package/apis/json-schema/PullRequest.yaml +10 -0
- package/apis/json-schema/SdlcLifecycle.yaml +1 -0
- package/apis/json-schema/WorkflowConfig.yaml +15 -0
- package/dist/packages/core/src/application/ports/output/repositories/feature-repository.interface.d.ts +8 -0
- package/dist/packages/core/src/application/ports/output/repositories/feature-repository.interface.d.ts.map +1 -1
- package/dist/packages/core/src/application/ports/output/services/git-pr-service.interface.d.ts +12 -0
- package/dist/packages/core/src/application/ports/output/services/git-pr-service.interface.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/check-and-unblock-features.use-case.d.ts +30 -0
- package/dist/packages/core/src/application/use-cases/features/check-and-unblock-features.use-case.d.ts.map +1 -0
- package/dist/packages/core/src/application/use-cases/features/check-and-unblock-features.use-case.js +74 -0
- package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.js +60 -16
- package/dist/packages/core/src/application/use-cases/features/create/types.d.ts +2 -0
- package/dist/packages/core/src/application/use-cases/features/create/types.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/delete-feature.use-case.js +11 -4
- package/dist/packages/core/src/application/use-cases/features/update/update-feature-lifecycle.use-case.d.ts +28 -0
- package/dist/packages/core/src/application/use-cases/features/update/update-feature-lifecycle.use-case.d.ts.map +1 -0
- package/dist/packages/core/src/application/use-cases/features/update/update-feature-lifecycle.use-case.js +53 -0
- package/dist/packages/core/src/domain/generated/output.d.ts +47 -1
- package/dist/packages/core/src/domain/generated/output.d.ts.map +1 -1
- package/dist/packages/core/src/domain/generated/output.js +1 -0
- package/dist/packages/core/src/domain/lifecycle-gates.d.ts +16 -0
- package/dist/packages/core/src/domain/lifecycle-gates.d.ts.map +1 -0
- package/dist/packages/core/src/domain/lifecycle-gates.js +19 -0
- package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.js +12 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/feature.mapper.d.ts +3 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/feature.mapper.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/feature.mapper.js +8 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations.js +28 -1
- package/dist/packages/core/src/infrastructure/repositories/sqlite-feature.repository.d.ts +1 -0
- package/dist/packages/core/src/infrastructure/repositories/sqlite-feature.repository.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/repositories/sqlite-feature.repository.js +12 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts +52 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.js +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-worker.js +7 -2
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/lifecycle-context.d.ts +27 -5
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/lifecycle-context.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/lifecycle-context.js +19 -16
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-helpers.d.ts +24 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-helpers.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-helpers.js +47 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-watch-fix-loop.d.ts +43 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-watch-fix-loop.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/ci-watch-fix-loop.js +118 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge-output-parser.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/{merge.node.d.ts → merge/merge.node.d.ts} +10 -4
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge/merge.node.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/{merge.node.js → merge/merge.node.js} +44 -17
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.d.ts +12 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/prompts/merge-prompts.js +38 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/state.d.ts +4 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/state.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/state.js +13 -0
- package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts +2 -0
- package/dist/packages/core/src/infrastructure/services/git/git-pr.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/git/git-pr.service.js +29 -1
- package/dist/src/presentation/cli/commands/feat/ls.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/ls.command.js +39 -3
- package/dist/src/presentation/cli/commands/feat/new.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/new.command.js +18 -0
- package/dist/src/presentation/cli/commands/feat/show.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/show.command.js +20 -0
- package/dist/src/presentation/web/app/actions/create-feature.d.ts +1 -0
- package/dist/src/presentation/web/app/actions/create-feature.d.ts.map +1 -1
- package/dist/src/presentation/web/app/actions/create-feature.js +2 -1
- package/dist/src/presentation/web/app/page.d.ts.map +1 -1
- package/dist/src/presentation/web/app/page.js +34 -6
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts +11 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +52 -5
- package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.js +4 -0
- package/dist/src/presentation/web/components/common/feature-node/feature-node.js +1 -1
- package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +13 -4
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +2 -0
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +26 -8
- package/dist/src/presentation/web/components/features/features-canvas/dependency-edge.d.ts +7 -0
- package/dist/src/presentation/web/components/features/features-canvas/dependency-edge.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/features-canvas/dependency-edge.js +20 -0
- package/dist/src/presentation/web/components/features/features-canvas/dependency-edge.stories.d.ts +6 -0
- package/dist/src/presentation/web/components/features/features-canvas/dependency-edge.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/features-canvas/dependency-edge.stories.js +58 -0
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +5 -1
- 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/page/server-reference-manifest.json +14 -14
- package/web/.next/server/app/page.js.nft.json +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/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_1fafa9db._.js +1 -1
- package/web/.next/server/chunks/ssr/_1fafa9db._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
- 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 +15 -15
- package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +14 -14
- package/web/.next/standalone/src/presentation/web/.next/server/app/page.js.nft.json +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/_1fafa9db._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +15 -15
- package/web/.next/standalone/src/presentation/web/app/actions/create-feature.ts +4 -1
- package/web/.next/standalone/src/presentation/web/app/page.tsx +36 -6
- package/web/.next/standalone/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx +199 -2
- package/web/.next/standalone/src/presentation/web/components/common/feature-node/derive-feature-state.ts +5 -0
- package/web/.next/standalone/src/presentation/web/components/common/feature-node/feature-node.tsx +1 -1
- package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +20 -1
- package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +43 -12
- package/web/.next/standalone/src/presentation/web/components/features/features-canvas/dependency-edge.stories.tsx +81 -0
- package/web/.next/standalone/src/presentation/web/components/features/features-canvas/dependency-edge.tsx +29 -0
- package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +9 -0
- package/web/.next/standalone/src/presentation/web/server.js +1 -1
- package/web/.next/static/chunks/48309f9ad2a84b15.css +2 -0
- package/web/.next/static/chunks/5123ae1490fd2606.js +10 -0
- package/web/.next/static/chunks/{31e5030eed01a941.js → 68fb91b6eca14536.js} +1 -1
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge-output-parser.d.ts.map +0 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/merge.node.d.ts.map +0 -1
- package/web/.next/static/chunks/23cae32e6f42833d.js +0 -10
- package/web/.next/static/chunks/59b8bd47023e0249.css +0 -2
- /package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/{merge-output-parser.d.ts → merge/merge-output-parser.d.ts} +0 -0
- /package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/{merge-output-parser.js → merge/merge-output-parser.js} +0 -0
- /package/web/.next/static/{-c2lwSbfsfZHNnfB5CogT → OXSWdTgy-1XJBN9IJtQPz}/_buildManifest.js +0 -0
- /package/web/.next/static/{-c2lwSbfsfZHNnfB5CogT → OXSWdTgy-1XJBN9IJtQPz}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{-c2lwSbfsfZHNnfB5CogT → OXSWdTgy-1XJBN9IJtQPz}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/src/infrastructure/services/agents/feature-agent/state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAE/E;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkFjC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,OAAO,sBAAsB,CAAC,KAAK,CAAC"}
|
|
@@ -75,4 +75,17 @@ export const FeatureAgentAnnotation = Annotation.Root({
|
|
|
75
75
|
reducer: (_prev, next) => next,
|
|
76
76
|
default: () => false,
|
|
77
77
|
}),
|
|
78
|
+
// --- CI watch/fix loop state ---
|
|
79
|
+
ciFixAttempts: Annotation({
|
|
80
|
+
reducer: (_prev, next) => next,
|
|
81
|
+
default: () => 0,
|
|
82
|
+
}),
|
|
83
|
+
ciFixHistory: Annotation({
|
|
84
|
+
reducer: (prev, next) => [...prev, ...next],
|
|
85
|
+
default: () => [],
|
|
86
|
+
}),
|
|
87
|
+
ciFixStatus: Annotation({
|
|
88
|
+
reducer: (_prev, next) => next,
|
|
89
|
+
default: () => 'idle',
|
|
90
|
+
}),
|
|
78
91
|
});
|
|
@@ -23,6 +23,8 @@ export declare class GitPrService implements IGitPrService {
|
|
|
23
23
|
deleteBranch(cwd: string, branch: string, deleteRemote?: boolean): Promise<void>;
|
|
24
24
|
getPrDiffSummary(cwd: string, baseBranch: string): Promise<DiffSummary>;
|
|
25
25
|
verifyMerge(cwd: string, featureBranch: string, baseBranch: string): Promise<boolean>;
|
|
26
|
+
getFailureLogs(runId: string, _branch: string, logMaxChars?: number): Promise<string>;
|
|
27
|
+
private truncateLog;
|
|
26
28
|
private parseGitError;
|
|
27
29
|
private parseGhError;
|
|
28
30
|
private parsePrNumberFromUrl;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-pr.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/git-pr.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wEAAwE,CAAC;AAC5G,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,aAAa,EACb,cAAc,EACf,MAAM,wEAAwE,CAAC;AAOhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,qBACa,YAAa,YAAW,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKxC,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuC9C,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKpD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuClE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,aAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAkBjE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"git-pr.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/git/git-pr.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wEAAwE,CAAC;AAC5G,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,aAAa,EACb,cAAc,EACf,MAAM,wEAAwE,CAAC;AAOhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,qBACa,YAAa,YAAW,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,YAAY;IAErE,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKxC,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuC9C,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKpD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAuClE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,aAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAkBjE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAwCjF,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhF,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAevE,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAWrF,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;IAW3F,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,aAAa;CAiBtB"}
|
|
@@ -155,7 +155,15 @@ let GitPrService = class GitPrService {
|
|
|
155
155
|
}
|
|
156
156
|
async watchCi(cwd, branch, timeoutMs) {
|
|
157
157
|
try {
|
|
158
|
-
|
|
158
|
+
// gh run watch requires a run ID — it does not support --branch.
|
|
159
|
+
// First, resolve the latest run ID for the branch via gh run list.
|
|
160
|
+
const { stdout: listOut } = await this.execFile('gh', ['run', 'list', '--branch', branch, '--json', 'databaseId', '--limit', '1'], { cwd });
|
|
161
|
+
const runs = JSON.parse(listOut);
|
|
162
|
+
if (runs.length === 0 || !runs[0].databaseId) {
|
|
163
|
+
return { status: 'pending' };
|
|
164
|
+
}
|
|
165
|
+
const runId = String(runs[0].databaseId);
|
|
166
|
+
const args = ['run', 'watch', runId, '--exit-status'];
|
|
159
167
|
const { stdout } = await this.execFile('gh', args, {
|
|
160
168
|
cwd,
|
|
161
169
|
...(timeoutMs ? { timeout: timeoutMs } : {}),
|
|
@@ -172,6 +180,10 @@ let GitPrService = class GitPrService {
|
|
|
172
180
|
if (message.includes('timed out') || message.includes('timeout')) {
|
|
173
181
|
throw new GitPrError(message, GitPrErrorCode.CI_TIMEOUT, cause);
|
|
174
182
|
}
|
|
183
|
+
// gh run watch --exit-status exits non-zero when the run fails
|
|
184
|
+
if (message.includes('exit code') || message.includes('exit status')) {
|
|
185
|
+
return { status: 'failure', logExcerpt: message };
|
|
186
|
+
}
|
|
175
187
|
throw new GitPrError(message, GitPrErrorCode.GIT_ERROR, cause);
|
|
176
188
|
}
|
|
177
189
|
}
|
|
@@ -202,6 +214,22 @@ let GitPrService = class GitPrService {
|
|
|
202
214
|
return false;
|
|
203
215
|
}
|
|
204
216
|
}
|
|
217
|
+
async getFailureLogs(runId, _branch, logMaxChars = 50_000) {
|
|
218
|
+
try {
|
|
219
|
+
const { stdout } = await this.execFile('gh', ['run', 'view', runId, '--log-failed'], {
|
|
220
|
+
cwd: undefined,
|
|
221
|
+
});
|
|
222
|
+
return this.truncateLog(stdout, logMaxChars, runId);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
throw this.parseGhError(error);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
truncateLog(output, maxChars, runId) {
|
|
229
|
+
if (output.length <= maxChars)
|
|
230
|
+
return output;
|
|
231
|
+
return `${output.slice(0, maxChars)}\n[Log truncated at ${maxChars} chars — full log available via gh run view ${runId}]`;
|
|
232
|
+
}
|
|
205
233
|
parseGitError(error) {
|
|
206
234
|
const message = error instanceof Error ? error.message : String(error);
|
|
207
235
|
const cause = error instanceof Error ? error : undefined;
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAmKpC,wBAAgB,eAAe,IAAI,OAAO,CAgGzC"}
|
|
@@ -46,6 +46,10 @@ const STATUS_PRIORITY = {
|
|
|
46
46
|
* Returns the current graph node as a phase label with an activity indicator.
|
|
47
47
|
*/
|
|
48
48
|
function formatStatus(feature, run) {
|
|
49
|
+
// Blocked features show "Waiting" — the parent relationship is conveyed by tree indentation
|
|
50
|
+
if (feature.lifecycle === 'Blocked') {
|
|
51
|
+
return `${colors.warning(symbols.dotEmpty)} ${colors.warning('Blocked')}`;
|
|
52
|
+
}
|
|
49
53
|
if (!run) {
|
|
50
54
|
return `${colors.muted(symbols.dotEmpty)} ${colors.muted(feature.lifecycle)}`;
|
|
51
55
|
}
|
|
@@ -172,11 +176,43 @@ export function createLsCommand() {
|
|
|
172
176
|
const paired = features
|
|
173
177
|
.map((feature, i) => ({ feature, run: runs[i], phases: timings[i] }))
|
|
174
178
|
.sort((a, b) => getStatusPriority(a.run) - getStatusPriority(b.run));
|
|
175
|
-
const
|
|
179
|
+
const childrenByParent = new Map();
|
|
180
|
+
const roots = [];
|
|
181
|
+
for (const entry of paired) {
|
|
182
|
+
const pid = entry.feature.parentId;
|
|
183
|
+
if (pid && entry.feature.lifecycle === 'Blocked') {
|
|
184
|
+
const list = childrenByParent.get(pid) ?? [];
|
|
185
|
+
list.push(entry);
|
|
186
|
+
childrenByParent.set(pid, list);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
roots.push(entry);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Flatten: parent then its blocked children
|
|
193
|
+
const ordered = [];
|
|
194
|
+
for (const entry of roots) {
|
|
195
|
+
ordered.push({ entry, indent: false });
|
|
196
|
+
const kids = childrenByParent.get(entry.feature.id);
|
|
197
|
+
if (kids) {
|
|
198
|
+
for (const kid of kids) {
|
|
199
|
+
ordered.push({ entry: kid, indent: true });
|
|
200
|
+
}
|
|
201
|
+
childrenByParent.delete(entry.feature.id);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Append orphaned blocked children (parent not in current list)
|
|
205
|
+
for (const kids of childrenByParent.values()) {
|
|
206
|
+
for (const kid of kids) {
|
|
207
|
+
ordered.push({ entry: kid, indent: false });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const rows = ordered.map(({ entry: { feature, run, phases }, indent }) => {
|
|
176
211
|
const repo = path.basename(feature.repositoryPath);
|
|
212
|
+
const prefix = indent ? `${colors.muted('└')} ` : '';
|
|
177
213
|
return [
|
|
178
|
-
feature.id.slice(0, 8),
|
|
179
|
-
truncate(feature.name, 30),
|
|
214
|
+
prefix + feature.id.slice(0, 8),
|
|
215
|
+
prefix + truncate(feature.name, indent ? 28 : 30),
|
|
180
216
|
formatStatus(feature, run),
|
|
181
217
|
colors.muted(repo),
|
|
182
218
|
formatGates(feature),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/new.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"new.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/new.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiDpC;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CA4G1C"}
|
|
@@ -14,6 +14,7 @@ import { createHash } from 'node:crypto';
|
|
|
14
14
|
import { join } from 'node:path';
|
|
15
15
|
import { container } from '../../../../../packages/core/src/infrastructure/di/container.js';
|
|
16
16
|
import { CreateFeatureUseCase } from '../../../../../packages/core/src/application/use-cases/features/create/create-feature.use-case.js';
|
|
17
|
+
import { SdlcLifecycle } from '../../../../../packages/core/src/domain/generated/output.js';
|
|
17
18
|
import { colors, messages, spinner } from '../../ui/index.js';
|
|
18
19
|
import { getShepHomeDir } from '../../../../../packages/core/src/infrastructure/services/filesystem/shep-directory.service.js';
|
|
19
20
|
import { getSettings, hasSettings } from '../../../../../packages/core/src/infrastructure/services/settings.service.js';
|
|
@@ -46,6 +47,7 @@ export function createNewCommand() {
|
|
|
46
47
|
.option('--allow-plan', 'Auto-approve through planning, pause at implementation')
|
|
47
48
|
.option('--allow-merge', 'Auto-approve merge phase')
|
|
48
49
|
.option('--allow-all', 'Run fully autonomous (no approval pauses)')
|
|
50
|
+
.option('--parent <fid>', 'Parent feature ID (full or partial prefix)')
|
|
49
51
|
.action(async (description, options) => {
|
|
50
52
|
try {
|
|
51
53
|
const useCase = container.resolve(CreateFeatureUseCase);
|
|
@@ -62,12 +64,25 @@ export function createNewCommand() {
|
|
|
62
64
|
allowMerge: options.allowMerge ?? defaults.allowMerge,
|
|
63
65
|
};
|
|
64
66
|
const push = options.push ?? defaults.push;
|
|
67
|
+
// Resolve parent feature ID if --parent flag is provided
|
|
68
|
+
let parentId;
|
|
69
|
+
if (options.parent) {
|
|
70
|
+
const featureRepo = container.resolve('IFeatureRepository');
|
|
71
|
+
const parentFeature = await featureRepo.findByIdPrefix(options.parent);
|
|
72
|
+
if (!parentFeature) {
|
|
73
|
+
messages.error(`Parent feature not found: ${options.parent}`);
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
parentId = parentFeature.id;
|
|
78
|
+
}
|
|
65
79
|
const result = await spinner('Thinking', () => useCase.execute({
|
|
66
80
|
userInput: description,
|
|
67
81
|
repositoryPath: repoPath,
|
|
68
82
|
approvalGates,
|
|
69
83
|
push,
|
|
70
84
|
openPr,
|
|
85
|
+
...(parentId !== undefined && { parentId }),
|
|
71
86
|
}));
|
|
72
87
|
const { feature, warning } = result;
|
|
73
88
|
const repoHash = createHash('sha256').update(repoPath).digest('hex').slice(0, 16);
|
|
@@ -78,6 +93,9 @@ export function createNewCommand() {
|
|
|
78
93
|
messages.warning(warning);
|
|
79
94
|
}
|
|
80
95
|
messages.success('Feature created');
|
|
96
|
+
if (feature.lifecycle === SdlcLifecycle.Blocked) {
|
|
97
|
+
messages.info(`Feature created in Blocked state — waiting for parent to reach Implementation`);
|
|
98
|
+
}
|
|
81
99
|
console.log(` ${colors.muted('ID:')} ${colors.accent(feature.id)}`);
|
|
82
100
|
console.log(` ${colors.muted('Name:')} ${feature.name}`);
|
|
83
101
|
console.log(` ${colors.muted('Branch:')} ${colors.accent(feature.branch)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"show.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/show.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,OAAO,KAAK,EAEV,QAAQ,EACR,WAAW,EACX,sBAAsB,EACvB,MAAM,8BAA8B,CAAC;AAmOtC;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EAAE,EACtB,UAAU,EAAE,QAAQ,GAAG,IAAI,EAC3B,UAAU,GAAE,sBAAsB,EAAO,GACxC,MAAM,EAAE,CAkJV;AAED,wBAAgB,iBAAiB,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"show.command.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/feat/show.command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,OAAO,KAAK,EAEV,QAAQ,EACR,WAAW,EACX,sBAAsB,EACvB,MAAM,8BAA8B,CAAC;AAmOtC;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,WAAW,EAAE,EACtB,UAAU,EAAE,QAAQ,GAAG,IAAI,EAC3B,UAAU,GAAE,sBAAsB,EAAO,GACxC,MAAM,EAAE,CAkJV;AAED,wBAAgB,iBAAiB,IAAI,OAAO,CAgL3C"}
|
|
@@ -368,6 +368,20 @@ export function createShowCommand() {
|
|
|
368
368
|
const lines = renderPhaseTimings(timings, run, rejections);
|
|
369
369
|
textBlocks.push({ title: 'Phase Timing', content: lines.join('\n') });
|
|
370
370
|
}
|
|
371
|
+
if (feature.pr?.ciFixHistory && feature.pr.ciFixHistory.length > 0) {
|
|
372
|
+
const fixLines = feature.pr.ciFixHistory.map((r) => {
|
|
373
|
+
const outcomeColor = r.outcome === 'fixed'
|
|
374
|
+
? colors.success
|
|
375
|
+
: r.outcome === 'timeout'
|
|
376
|
+
? colors.warning
|
|
377
|
+
: colors.error;
|
|
378
|
+
return ` #${r.attempt} ${outcomeColor(r.outcome)} ${colors.muted(r.startedAt)} ${r.failureSummary.slice(0, 60)}`;
|
|
379
|
+
});
|
|
380
|
+
textBlocks.push({
|
|
381
|
+
title: 'CI Fix History',
|
|
382
|
+
content: fixLines.join('\n'),
|
|
383
|
+
});
|
|
384
|
+
}
|
|
371
385
|
if (run?.status === 'waiting_approval') {
|
|
372
386
|
const phase = run.result?.startsWith('node:')
|
|
373
387
|
? (NODE_TO_PHASE[run.result.slice(5)] ?? run.result.slice(5))
|
|
@@ -456,6 +470,12 @@ export function createShowCommand() {
|
|
|
456
470
|
: colors.warning(feature.pr.ciStatus)
|
|
457
471
|
: null,
|
|
458
472
|
},
|
|
473
|
+
{
|
|
474
|
+
label: 'CI Fixes',
|
|
475
|
+
value: feature.pr.ciFixAttempts && feature.pr.ciFixAttempts > 0
|
|
476
|
+
? `${feature.pr.ciFixAttempts} attempt(s)`
|
|
477
|
+
: null,
|
|
478
|
+
},
|
|
459
479
|
],
|
|
460
480
|
},
|
|
461
481
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-feature.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/create-feature.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAEpE,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAQD,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"create-feature.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/actions/create-feature.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAEpE,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAQD,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAqBD,wBAAsB,aAAa,CACjC,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAkChD"}
|
|
@@ -12,7 +12,7 @@ function composeUserInput(name, description, attachments) {
|
|
|
12
12
|
return userInput;
|
|
13
13
|
}
|
|
14
14
|
export async function createFeature(input) {
|
|
15
|
-
const { name, description, repositoryPath, attachments, approvalGates, push, openPr } = input;
|
|
15
|
+
const { name, description, repositoryPath, attachments, approvalGates, push, openPr, parentId } = input;
|
|
16
16
|
if (!name?.trim()) {
|
|
17
17
|
return { error: 'name is required' };
|
|
18
18
|
}
|
|
@@ -33,6 +33,7 @@ export async function createFeature(input) {
|
|
|
33
33
|
approvalGates: gates,
|
|
34
34
|
push: push ?? false,
|
|
35
35
|
openPr: openPr ?? false,
|
|
36
|
+
...(parentId ? { parentId } : {}),
|
|
36
37
|
});
|
|
37
38
|
return { feature: result.feature };
|
|
38
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/page.tsx"],"names":[],"mappings":"AAcA,uFAAuF;AACvF,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAsBvC,wBAA8B,QAAQ,
|
|
1
|
+
{"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/page.tsx"],"names":[],"mappings":"AAcA,uFAAuF;AACvF,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAsBvC,wBAA8B,QAAQ,qDAsJrC"}
|
|
@@ -60,6 +60,14 @@ export default async function HomePage() {
|
|
|
60
60
|
: ((agentNode ? nodeToLifecyclePhase[agentNode] : undefined) ??
|
|
61
61
|
lifecycleMap[feature.lifecycle] ??
|
|
62
62
|
'requirements');
|
|
63
|
+
// Resolve blockedBy display name from parent feature
|
|
64
|
+
let blockedBy;
|
|
65
|
+
if (feature.parentId && feature.lifecycle === 'Blocked') {
|
|
66
|
+
const parentEntry = featuresWithRuns.find((e) => e.feature.id === feature.parentId);
|
|
67
|
+
if (parentEntry) {
|
|
68
|
+
blockedBy = parentEntry.feature.name;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
63
71
|
const nodeData = {
|
|
64
72
|
name: feature.name,
|
|
65
73
|
description: feature.description ?? feature.slug,
|
|
@@ -72,6 +80,7 @@ export default async function HomePage() {
|
|
|
72
80
|
progress: deriveProgress(feature),
|
|
73
81
|
...(run?.agentType && { agentType: run.agentType }),
|
|
74
82
|
...(run?.error && { errorMessage: run.error }),
|
|
83
|
+
...(blockedBy && { blockedBy }),
|
|
75
84
|
};
|
|
76
85
|
const featureNodeId = `feat-${feature.id}`;
|
|
77
86
|
nodes.push({
|
|
@@ -80,14 +89,33 @@ export default async function HomePage() {
|
|
|
80
89
|
position: { x: 0, y: 0 },
|
|
81
90
|
data: nodeData,
|
|
82
91
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
92
|
+
// Child features connect via parent→child dependency edge, not directly to repo
|
|
93
|
+
if (!feature.parentId) {
|
|
94
|
+
edges.push({
|
|
95
|
+
id: `edge-${repoNodeId}-${featureNodeId}`,
|
|
96
|
+
source: repoNodeId,
|
|
97
|
+
target: featureNodeId,
|
|
98
|
+
style: { strokeDasharray: '5 5' },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
89
101
|
});
|
|
90
102
|
}
|
|
103
|
+
// Add parent→child dependency edges
|
|
104
|
+
for (const { feature } of featuresWithRuns) {
|
|
105
|
+
if (feature.parentId) {
|
|
106
|
+
const parentNodeId = `feat-${feature.parentId}`;
|
|
107
|
+
const childNodeId = `feat-${feature.id}`;
|
|
108
|
+
// Only add edge if both nodes exist on the canvas
|
|
109
|
+
if (nodes.some((n) => n.id === parentNodeId) && nodes.some((n) => n.id === childNodeId)) {
|
|
110
|
+
edges.push({
|
|
111
|
+
id: `dep-${parentNodeId}-${childNodeId}`,
|
|
112
|
+
source: parentNodeId,
|
|
113
|
+
target: childNodeId,
|
|
114
|
+
type: 'dependencyEdge',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
91
119
|
// Use dagre LR layout for compact, automatic positioning
|
|
92
120
|
const laid = layoutWithDagre(nodes, edges, {
|
|
93
121
|
direction: 'LR',
|
package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { FileAttachment } from '../../../../../../packages/core/src/infrastructure/services/file-dialog.service.js';
|
|
2
2
|
import type { WorkflowDefaults } from '../../../app/actions/get-workflow-defaults.js';
|
|
3
3
|
export type { FileAttachment } from '../../../../../../packages/core/src/infrastructure/services/file-dialog.service.js';
|
|
4
|
+
/** Minimal feature descriptor for the parent selector. */
|
|
5
|
+
export interface ParentFeatureOption {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
4
9
|
export interface FeatureCreatePayload {
|
|
5
10
|
name: string;
|
|
6
11
|
description?: string;
|
|
@@ -13,6 +18,7 @@ export interface FeatureCreatePayload {
|
|
|
13
18
|
};
|
|
14
19
|
push: boolean;
|
|
15
20
|
openPr: boolean;
|
|
21
|
+
parentId?: string;
|
|
16
22
|
}
|
|
17
23
|
export interface FeatureCreateDrawerProps {
|
|
18
24
|
open: boolean;
|
|
@@ -21,6 +27,10 @@ export interface FeatureCreateDrawerProps {
|
|
|
21
27
|
repositoryPath: string;
|
|
22
28
|
isSubmitting?: boolean;
|
|
23
29
|
workflowDefaults?: WorkflowDefaults;
|
|
30
|
+
/** List of existing features available for selection as a parent. */
|
|
31
|
+
features?: ParentFeatureOption[];
|
|
32
|
+
/** Pre-select a parent feature when the drawer opens (e.g. from (+) button on a feature node). */
|
|
33
|
+
initialParentId?: string;
|
|
24
34
|
}
|
|
25
|
-
export declare function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, isSubmitting, workflowDefaults, }: FeatureCreateDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
export declare function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, isSubmitting, workflowDefaults, features, initialParentId, }: FeatureCreateDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
26
36
|
//# sourceMappingURL=feature-create-drawer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAC/F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAG5E,YAAY,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAE/F,0DAA0D;AAC1D,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE;QACb,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAcD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,qEAAqE;IACrE,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,kGAAkG;IAClG,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAoB,EACpB,gBAAgB,EAChB,QAAQ,EACR,eAAe,GAChB,EAAE,wBAAwB,2CAmR1B"}
|
package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useCallback, useEffect } from 'react';
|
|
4
|
-
import { XIcon, PlusIcon, FileIcon, FileTextIcon, ImageIcon, CodeIcon, Trash2Icon, } from 'lucide-react';
|
|
3
|
+
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
4
|
+
import { XIcon, PlusIcon, FileIcon, FileTextIcon, ImageIcon, CodeIcon, Trash2Icon, ChevronsUpDown, CheckIcon, } from 'lucide-react';
|
|
5
5
|
import { cn } from '../../../lib/utils.js';
|
|
6
6
|
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerFooter, } from '../../ui/drawer.js';
|
|
7
7
|
import { Button } from '../../ui/button.js';
|
|
@@ -12,6 +12,7 @@ import { CheckboxGroup } from '../../ui/checkbox-group.js';
|
|
|
12
12
|
import { CheckboxGroupItem } from '../../ui/checkbox-group-item.js';
|
|
13
13
|
import { Separator } from '../../ui/separator.js';
|
|
14
14
|
import { Badge } from '../../ui/badge.js';
|
|
15
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover.js';
|
|
15
16
|
import { pickFiles } from './pick-files.js';
|
|
16
17
|
const AUTO_APPROVE_OPTIONS = [
|
|
17
18
|
{ id: 'allowPrd', label: 'PRD', description: 'Auto-approve requirements move to planning.' },
|
|
@@ -23,7 +24,7 @@ const EMPTY_GATES = {
|
|
|
23
24
|
allowPlan: false,
|
|
24
25
|
allowMerge: false,
|
|
25
26
|
};
|
|
26
|
-
export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, isSubmitting = false, workflowDefaults, }) {
|
|
27
|
+
export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, isSubmitting = false, workflowDefaults, features, initialParentId, }) {
|
|
27
28
|
const defaultGates = workflowDefaults?.approvalGates ?? EMPTY_GATES;
|
|
28
29
|
const defaultPush = workflowDefaults?.push ?? false;
|
|
29
30
|
const defaultOpenPr = workflowDefaults?.openPr ?? false;
|
|
@@ -33,6 +34,7 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
33
34
|
const [approvalGates, setApprovalGates] = useState({ ...defaultGates });
|
|
34
35
|
const [push, setPush] = useState(defaultPush);
|
|
35
36
|
const [openPr, setOpenPr] = useState(defaultOpenPr);
|
|
37
|
+
const [parentId, setParentId] = useState(undefined);
|
|
36
38
|
// Sync state when workflowDefaults load asynchronously
|
|
37
39
|
useEffect(() => {
|
|
38
40
|
if (workflowDefaults) {
|
|
@@ -41,6 +43,12 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
41
43
|
setOpenPr(workflowDefaults.openPr);
|
|
42
44
|
}
|
|
43
45
|
}, [workflowDefaults]);
|
|
46
|
+
// Pre-select parent when initialParentId changes (e.g. (+) button on feature node)
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (open && initialParentId) {
|
|
49
|
+
setParentId(initialParentId);
|
|
50
|
+
}
|
|
51
|
+
}, [open, initialParentId]);
|
|
44
52
|
const resetForm = useCallback(() => {
|
|
45
53
|
setName('');
|
|
46
54
|
setDescription('');
|
|
@@ -48,6 +56,7 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
48
56
|
setApprovalGates({ ...defaultGates });
|
|
49
57
|
setPush(defaultPush);
|
|
50
58
|
setOpenPr(defaultOpenPr);
|
|
59
|
+
setParentId(undefined);
|
|
51
60
|
}, [defaultGates, defaultPush, defaultOpenPr]);
|
|
52
61
|
const handleOpenChange = useCallback((nextOpen) => {
|
|
53
62
|
if (!nextOpen) {
|
|
@@ -72,8 +81,19 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
72
81
|
},
|
|
73
82
|
push: push || openPr,
|
|
74
83
|
openPr,
|
|
84
|
+
...(parentId ? { parentId } : {}),
|
|
75
85
|
});
|
|
76
|
-
}, [
|
|
86
|
+
}, [
|
|
87
|
+
name,
|
|
88
|
+
description,
|
|
89
|
+
attachments,
|
|
90
|
+
approvalGates,
|
|
91
|
+
repositoryPath,
|
|
92
|
+
onSubmit,
|
|
93
|
+
push,
|
|
94
|
+
openPr,
|
|
95
|
+
parentId,
|
|
96
|
+
]);
|
|
77
97
|
const handleAddFiles = useCallback(async () => {
|
|
78
98
|
try {
|
|
79
99
|
const files = await pickFiles();
|
|
@@ -88,7 +108,34 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
88
108
|
const handleRemoveFile = useCallback((path) => {
|
|
89
109
|
setAttachments((prev) => prev.filter((f) => f.path !== path));
|
|
90
110
|
}, []);
|
|
91
|
-
|
|
111
|
+
const hasFeatures = features && features.length > 0;
|
|
112
|
+
return (_jsx(Drawer, { direction: "right", modal: false, handleOnly: true, open: open, onOpenChange: handleOpenChange, children: _jsxs(DrawerContent, { direction: "right", className: "w-96", showCloseButton: false, children: [_jsx("button", { type: "button", "aria-label": "Close", onClick: onClose, className: "ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden", children: _jsx(XIcon, { className: "size-4" }) }), _jsxs(DrawerHeader, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsx(DrawerTitle, { children: "NEW FEATURE" })] }), _jsx(DrawerDescription, { asChild: true, children: _jsx("div", { children: _jsx(Badge, { variant: "secondary", children: "Creating..." }) }) })] }), _jsx(Separator, {}), _jsx("div", { className: "flex-1 overflow-y-auto p-4", children: _jsxs("form", { id: "create-feature-form", onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "feature-name", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "FEATURE NAME" }), _jsx(Input, { id: "feature-name", placeholder: "e.g. GitHub OAuth Login", value: name, onChange: (e) => setName(e.target.value), required: true, disabled: isSubmitting })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "feature-description", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "DESCRIPTION" }), _jsx(Textarea, { id: "feature-description", placeholder: "Describe what this feature does...", value: description, onChange: (e) => setDescription(e.target.value), rows: 4, disabled: isSubmitting })] }), hasFeatures ? (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "parent-feature", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "PARENT FEATURE" }), _jsx(ParentFeatureCombobox, { features: features, value: parentId, onChange: setParentId, disabled: isSubmitting })] })) : null, _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "APPROVE" }), _jsx(CheckboxGroup, { label: "Autonomous Mode", description: "YOLO!", parentAriaLabel: "Auto approve all", options: AUTO_APPROVE_OPTIONS, value: approvalGates, onValueChange: setApprovalGates, disabled: isSubmitting })] }), _jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "GIT" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(CheckboxGroupItem, { id: "push", label: "Push", description: "Push branch to remote after implementation.", checked: push || openPr, onCheckedChange: setPush, disabled: openPr || isSubmitting }), _jsx(CheckboxGroupItem, { id: "open-pr", label: "Create PR", description: "Open a pull request after pushing.", checked: openPr, onCheckedChange: setOpenPr, disabled: isSubmitting })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: ["ATTACHMENTS", attachments.length > 0 && (_jsxs("span", { className: "text-muted-foreground/60 ml-1.5", children: ["(", attachments.length, ")"] }))] }), _jsxs(Button, { type: "button", variant: "outline", size: "xs", onClick: handleAddFiles, disabled: isSubmitting, children: [_jsx(PlusIcon, { className: "size-3" }), "Add Files"] })] }), attachments.length > 0 && (_jsx("div", { className: "flex flex-col gap-2", children: attachments.map((file) => (_jsx(AttachmentCard, { file: file, onRemove: () => handleRemoveFile(file.path), disabled: isSubmitting }, file.path))) }))] })] }) }), _jsx(Separator, {}), _jsxs(DrawerFooter, { className: "flex-row justify-end gap-2", children: [_jsx(Button, { variant: "outline", onClick: onClose, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { type: "submit", form: "create-feature-form", disabled: !name.trim() || isSubmitting, children: isSubmitting ? 'Creating...' : '+ Create Feature' })] })] }) }));
|
|
113
|
+
}
|
|
114
|
+
function ParentFeatureCombobox({ features, value, onChange, disabled, }) {
|
|
115
|
+
const [open, setOpen] = useState(false);
|
|
116
|
+
const [query, setQuery] = useState('');
|
|
117
|
+
const inputRef = useRef(null);
|
|
118
|
+
const selectedFeature = features.find((f) => f.id === value);
|
|
119
|
+
const filtered = query.trim()
|
|
120
|
+
? features.filter((f) => f.name.toLowerCase().includes(query.toLowerCase()) ||
|
|
121
|
+
f.id.toLowerCase().includes(query.toLowerCase()))
|
|
122
|
+
: features;
|
|
123
|
+
const handleSelect = useCallback((id) => {
|
|
124
|
+
onChange(id);
|
|
125
|
+
setOpen(false);
|
|
126
|
+
setQuery('');
|
|
127
|
+
}, [onChange]);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (open) {
|
|
130
|
+
setTimeout(() => inputRef.current?.focus(), 0);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
setQuery('');
|
|
134
|
+
}
|
|
135
|
+
}, [open]);
|
|
136
|
+
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs("button", { id: "parent-feature", type: "button", role: "combobox", "aria-expanded": open, "aria-label": "Parent Feature", disabled: disabled, "data-testid": "parent-feature-combobox", className: cn('border-input bg-background ring-offset-background focus:ring-ring flex h-9 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50', !selectedFeature && 'text-muted-foreground'), children: [_jsx("span", { className: "truncate", children: selectedFeature
|
|
137
|
+
? `${selectedFeature.name} (${selectedFeature.id.slice(0, 8)})`
|
|
138
|
+
: 'Select parent feature...' }), _jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), _jsx(PopoverContent, { className: "w-80 p-0", align: "start", "data-testid": "parent-feature-combobox-content", children: _jsxs("div", { className: "flex flex-col", children: [_jsx("div", { className: "border-b p-2", children: _jsx(Input, { ref: inputRef, placeholder: "Search features...", value: query, onChange: (e) => setQuery(e.target.value), className: "h-8 border-0 p-0 text-sm shadow-none focus-visible:ring-0", "data-testid": "parent-feature-search" }) }), _jsxs("div", { className: "max-h-48 overflow-y-auto py-1", role: "listbox", "aria-label": "Features", children: [_jsxs("button", { type: "button", role: "option", "aria-selected": value === undefined, onClick: () => handleSelect(undefined), className: cn('hover:bg-accent hover:text-accent-foreground flex w-full items-center gap-2 px-3 py-2 text-sm', value === undefined && 'bg-accent/50'), "data-testid": "parent-feature-option-none", children: [_jsx(CheckIcon, { className: cn('h-4 w-4 shrink-0', value !== undefined && 'invisible') }), _jsx("span", { className: "text-muted-foreground italic", children: "No parent" })] }), filtered.length === 0 && query ? (_jsx("p", { className: "text-muted-foreground px-3 py-2 text-sm", children: "No features found." })) : (filtered.map((f) => (_jsxs("button", { type: "button", role: "option", "aria-selected": value === f.id, onClick: () => handleSelect(f.id), className: cn('hover:bg-accent hover:text-accent-foreground flex w-full items-center gap-2 px-3 py-2 text-sm', value === f.id && 'bg-accent/50'), "data-testid": `parent-feature-option-${f.id}`, children: [_jsx(CheckIcon, { className: cn('h-4 w-4 shrink-0', value !== f.id && 'invisible') }), _jsxs("span", { className: "truncate", children: [f.name, ' ', _jsxs("span", { className: "text-muted-foreground font-mono text-xs", children: ["(", f.id.slice(0, 8), ")"] })] })] }, f.id))))] })] }) })] }));
|
|
92
139
|
}
|
|
93
140
|
/* ---------------------------------------------------------------------------
|
|
94
141
|
* Private sub-components & utilities
|
package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derive-feature-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/derive-feature-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,gBAAgB,
|
|
1
|
+
{"version":3,"file":"derive-feature-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/derive-feature-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,gBAAgB,CA4C9F;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAYvD"}
|
|
@@ -17,6 +17,10 @@ import { SdlcLifecycle, AgentRunStatus, TaskState } from '../../../../../../pack
|
|
|
17
17
|
* 6. No agent run → fall back to plan tasks / lifecycle
|
|
18
18
|
*/
|
|
19
19
|
export function deriveNodeState(feature, agentRun) {
|
|
20
|
+
// Blocked lifecycle takes priority — child waiting on parent regardless of agent run
|
|
21
|
+
if (feature.lifecycle === SdlcLifecycle.Blocked) {
|
|
22
|
+
return 'blocked';
|
|
23
|
+
}
|
|
20
24
|
if (agentRun) {
|
|
21
25
|
switch (agentRun.status) {
|
|
22
26
|
case AgentRunStatus.waitingApproval:
|
|
@@ -61,7 +61,7 @@ function getBadgeText(data) {
|
|
|
61
61
|
export function FeatureNode({ data, selected, }) {
|
|
62
62
|
const config = featureNodeStateConfig[data.state];
|
|
63
63
|
const Icon = config.icon;
|
|
64
|
-
return (_jsxs("div", { className: "group relative", children: [data.showHandles ? (_jsx(Handle, { type: "target", position: Position.Left, isConnectable: false, className: "opacity-0!" })) : null, _jsxs("div", { "data-testid": "feature-node-card", "aria-busy": data.state === 'creating' ? 'true' : undefined, className: cn('bg-card flex min-h-35 w-72 flex-col rounded-lg border p-3 shadow-sm', selected && 'ring-primary ring-2'), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { "data-testid": "feature-node-lifecycle-label", className: cn('text-[10px] font-semibold tracking-wider'), children: lifecycleDisplayLabels[data.lifecycle] }), data.onSettings ? (_jsx("button", { type: "button", "aria-label": "Settings", "data-testid": "feature-node-settings-button", onClick: (e) => {
|
|
64
|
+
return (_jsxs("div", { className: "group relative", children: [data.showHandles ? (_jsx(Handle, { type: "target", position: Position.Left, isConnectable: false, className: "opacity-0!" })) : null, _jsxs("div", { "data-testid": "feature-node-card", "aria-busy": data.state === 'creating' ? 'true' : undefined, className: cn('bg-card flex min-h-35 w-72 flex-col rounded-lg border p-3 shadow-sm', selected && 'ring-primary ring-2'), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { "data-testid": "feature-node-lifecycle-label", className: cn('text-[10px] font-semibold tracking-wider'), children: data.state === 'blocked' ? 'BLOCKED' : lifecycleDisplayLabels[data.lifecycle] }), data.onSettings ? (_jsx("button", { type: "button", "aria-label": "Settings", "data-testid": "feature-node-settings-button", onClick: (e) => {
|
|
65
65
|
e.stopPropagation();
|
|
66
66
|
data.onSettings?.();
|
|
67
67
|
}, className: "nodrag text-muted-foreground hover:text-foreground -mt-1 -mr-1 p-1", children: _jsx(Settings, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("h3", { className: "mt-1 truncate text-sm font-bold", children: data.name }), data.description ? (_jsx("p", { "data-testid": "feature-node-description", className: "text-muted-foreground mt-0.5 truncate text-xs", children: data.description })) : null, _jsx("div", { className: "mt-auto pt-2", children: data.state === 'creating' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(Icon, { className: "h-3.5 w-3.5 shrink-0 animate-spin" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "motion-safe:animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : data.state === 'running' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(AgentIcon, { agentType: data.agentType, className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : config.showProgressBar ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground flex items-center justify-end text-[10px]", children: _jsxs("span", { children: [data.progress, "%"] }) }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', config.progressClass), style: { width: `${data.progress}%` } }) })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground text-[10px]", children: _jsx("span", { children: data.featureId }) }), _jsxs("div", { "data-testid": "feature-node-badge", className: (() => {
|
package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map
CHANGED
|
@@ -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":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAgB1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAgB1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAW5E,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,2CAmZzF"}
|