mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-06 14:00:09 +02:00
fix: apply single skill overwrite immediately (#4057)
Co-authored-by: J <j@multica.ai> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -465,6 +465,66 @@ describe("RuntimeLocalSkillImportPanel", () => {
|
||||
expect(await screen.findByText("Updated")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies a single creator conflict when clicking overwrite", async () => {
|
||||
mockResolveRuntimeLocalSkillImport
|
||||
.mockResolvedValueOnce({
|
||||
status: "conflict",
|
||||
conflict: {
|
||||
existing_skill_id: "existing-skill-1",
|
||||
existing_created_by: "user-1",
|
||||
can_overwrite: true,
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
status: "updated",
|
||||
skill: {
|
||||
...MOCK_IMPORTED_SKILL_A,
|
||||
id: "existing-skill-1",
|
||||
},
|
||||
});
|
||||
|
||||
renderPanel();
|
||||
|
||||
expect(
|
||||
await screen.findByText("Review Helper", {}, { timeout: 5000 }),
|
||||
).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /Review Helper/i }));
|
||||
|
||||
const importButton = screen.getByRole("button", {
|
||||
name: /Import to Workspace/i,
|
||||
});
|
||||
await waitFor(() => expect(importButton).not.toBeDisabled(), {
|
||||
timeout: 5000,
|
||||
});
|
||||
fireEvent.click(importButton);
|
||||
|
||||
expect(
|
||||
await screen.findByText(/A skill with this name already exists/i),
|
||||
).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /^Overwrite$/i }));
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(mockResolveRuntimeLocalSkillImport).toHaveBeenLastCalledWith(
|
||||
"runtime-1",
|
||||
{
|
||||
skill_key: "review-helper",
|
||||
name: "Review Helper",
|
||||
description: "Review pull requests",
|
||||
supports_conflict: true,
|
||||
action: "overwrite",
|
||||
target_skill_id: "existing-skill-1",
|
||||
},
|
||||
);
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
|
||||
expect(await screen.findByText("Updated")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps bulk completion behavior when conflict resolution leaves one success", async () => {
|
||||
mockRuntimeLocalSkillsOptions.mockReturnValue({
|
||||
queryKey: ["runtimes", "local-skills", "runtime-1"],
|
||||
|
||||
@@ -307,12 +307,14 @@ function ConflictResolutionPanel({
|
||||
conflicts,
|
||||
resolutions,
|
||||
onChange,
|
||||
onResolveNow,
|
||||
onOverwriteAll,
|
||||
onSkipAll,
|
||||
}: {
|
||||
conflicts: BulkImportResult[];
|
||||
resolutions: Record<string, ConflictResolutionState>;
|
||||
onChange: (key: string, next: ConflictResolutionState) => void;
|
||||
onResolveNow?: (key: string, next: ConflictResolutionState) => void;
|
||||
onOverwriteAll: () => void;
|
||||
onSkipAll: () => void;
|
||||
}) {
|
||||
@@ -401,12 +403,17 @@ function ConflictResolutionPanel({
|
||||
variant={
|
||||
resolution.action === "overwrite" ? "default" : "outline"
|
||||
}
|
||||
onClick={() =>
|
||||
onChange(r.key, {
|
||||
onClick={() => {
|
||||
const next = {
|
||||
action: "overwrite",
|
||||
renameName: resolution.renameName,
|
||||
})
|
||||
}
|
||||
} satisfies ConflictResolutionState;
|
||||
if (single && r.conflict?.can_overwrite && onResolveNow) {
|
||||
onResolveNow(r.key, next);
|
||||
} else {
|
||||
onChange(r.key, next);
|
||||
}
|
||||
}}
|
||||
disabled={!r.conflict?.can_overwrite}
|
||||
>
|
||||
<RefreshCw className="h-3 w-3" />
|
||||
@@ -723,7 +730,9 @@ export function RuntimeLocalSkillImportPanel({
|
||||
}));
|
||||
};
|
||||
|
||||
const handleApplyConflictResolutions = async () => {
|
||||
const handleApplyConflictResolutions = async (
|
||||
resolutionOverrides: Record<string, ConflictResolutionState> = {},
|
||||
) => {
|
||||
if (!selectedRuntimeId || pendingConflicts.length === 0) return;
|
||||
|
||||
const conflicts = [...pendingConflicts];
|
||||
@@ -748,7 +757,8 @@ export function RuntimeLocalSkillImportPanel({
|
||||
}));
|
||||
|
||||
for (const r of conflicts) {
|
||||
const resolution = conflictResolutions[r.key];
|
||||
const resolution =
|
||||
resolutionOverrides[r.key] ?? conflictResolutions[r.key];
|
||||
if (!resolution) {
|
||||
applyResult(r.key, {
|
||||
status: "failed",
|
||||
@@ -893,6 +903,10 @@ export function RuntimeLocalSkillImportPanel({
|
||||
conflicts={pendingConflicts}
|
||||
resolutions={conflictResolutions}
|
||||
onChange={setConflictResolution}
|
||||
onResolveNow={(key, next) => {
|
||||
setConflictResolution(key, next);
|
||||
void handleApplyConflictResolutions({ [key]: next });
|
||||
}}
|
||||
onOverwriteAll={() => {
|
||||
setConflictResolutions((prev) => {
|
||||
const next = { ...prev };
|
||||
@@ -1143,7 +1157,7 @@ export function RuntimeLocalSkillImportPanel({
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
onClick={handleApplyConflictResolutions}
|
||||
onClick={() => void handleApplyConflictResolutions()}
|
||||
disabled={!canApplyConflictResolutions}
|
||||
>
|
||||
{t(($) => $.runtime_import.conflict_apply_button)}
|
||||
|
||||
Reference in New Issue
Block a user