Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Assets/Tests/Editor/ThirdPartyToolMigrationWizardWindowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,20 @@ public void ShouldRemoveMigrationSkill_ReturnsExpectedValue(
Assert.That(shouldRemove, Is.EqualTo(expected));
}

[TestCase(SkillInstallState.Installed, true)]
[TestCase(SkillInstallState.Outdated, true)]
[TestCase(SkillInstallState.Checking, true)]
[TestCase(SkillInstallState.Missing, false)]
public void ShouldShowTemporarySkillNote_ReturnsExpectedValue(
SkillInstallState installState,
bool expected)
{
// Verifies the temporary skill note shows for every non-missing install state.
bool shouldShow = ThirdPartyToolMigrationWizardStateRules.ShouldShowTemporarySkillNote(installState);

Assert.That(shouldShow, Is.EqualTo(expected));
}

[TestCase(0, 1, 1, 4, 1000, 100, true)]
[TestCase(10, 50, 1, 4, 1000, 100, false)]
[TestCase(10, 110, 1, 4, 1000, 100, true)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,15 @@ internal static bool ShouldRefreshAfterInterruptedMigration(
{
return isMigrationCompletionPending && !isCancellationRequested;
}

/// <summary>
/// Returns whether the temporary V3 migration skill note should be visible.
/// </summary>
internal static bool ShouldShowTemporarySkillNote(SkillInstallState installState)
{
// C# scan results are not a completion signal for this skill (docs/scripts remain
// in scope), so only the install state decides whether the temporary note shows.
return installState != SkillInstallState.Missing;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ internal static class ThirdPartyToolMigrationWizardText
"that call uloop.\nThe skill teaches your AI agent how to search, inspect context, and update only " +
"real V2 CLI usage. This window only installs or removes it.";
internal const string AiMigrationSkillUsageFoldoutTitle = "Prompt for your AI agent";
internal const string AiMigrationSkillTemporaryNoteText =
"This skill is temporary. Remove it once your docs and scripts are migrated to V3 CLI syntax.";
internal const string AiMigrationSkillPromptText =
"Use the v3-cli-invocation-migration skill in this project to update Unity CLI Loop V2 CLI usage " +
"for V3.\n\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal sealed class ThirdPartyToolMigrationWizardView
private readonly Button _migrateButton;
private readonly EnumField _migrationSkillTargetField;
private readonly Button _migrationSkillButton;
private readonly TextField _migrationSkillTemporaryNoteTextField;
private readonly Button _refreshButton;
private readonly Button _closeButton;

Expand All @@ -33,6 +34,7 @@ private ThirdPartyToolMigrationWizardView(
Button migrateButton,
EnumField migrationSkillTargetField,
Button migrationSkillButton,
TextField migrationSkillTemporaryNoteTextField,
Button refreshButton,
Button closeButton)
{
Expand All @@ -43,6 +45,9 @@ private ThirdPartyToolMigrationWizardView(
Debug.Assert(migrateButton != null, "migrateButton must not be null");
Debug.Assert(migrationSkillTargetField != null, "migrationSkillTargetField must not be null");
Debug.Assert(migrationSkillButton != null, "migrationSkillButton must not be null");
Debug.Assert(
migrationSkillTemporaryNoteTextField != null,
"migrationSkillTemporaryNoteTextField must not be null");
Debug.Assert(refreshButton != null, "refreshButton must not be null");
Debug.Assert(closeButton != null, "closeButton must not be null");

Expand All @@ -53,6 +58,7 @@ private ThirdPartyToolMigrationWizardView(
_migrateButton = migrateButton;
_migrationSkillTargetField = migrationSkillTargetField;
_migrationSkillButton = migrationSkillButton;
_migrationSkillTemporaryNoteTextField = migrationSkillTemporaryNoteTextField;
_refreshButton = refreshButton;
_closeButton = closeButton;
}
Expand Down Expand Up @@ -87,7 +93,7 @@ internal static ThirdPartyToolMigrationWizardView Create(
Button migrateButton = CreateMigrateButton(migrationButtonRow);
(Button refreshButton, Button closeButton) = CreateCSharpMigrationActionSection(mainScrollView);
CreateSectionDivider(mainScrollView);
(EnumField migrationSkillTargetField, Button migrationSkillButton) =
(EnumField migrationSkillTargetField, Button migrationSkillButton, TextField temporaryNoteTextField) =
CreateAiMigrationSkillSection(mainScrollView);
CreateFooter(mainScrollView);

Expand All @@ -99,6 +105,7 @@ internal static ThirdPartyToolMigrationWizardView Create(
migrateButton,
migrationSkillTargetField,
migrationSkillButton,
temporaryNoteTextField,
refreshButton,
closeButton);
view.BindEvents(refresh, migrate, migrationSkillTargetChanged, toggleMigrationSkill, close);
Expand All @@ -116,6 +123,9 @@ internal void SetMigrationSkillState(
installState);
_migrationSkillTargetField.SetEnabled(!isUpdating);
_migrationSkillButton.SetEnabled(!isUpdating);
ViewDataBinder.SetVisible(
_migrationSkillTemporaryNoteTextField,
ThirdPartyToolMigrationWizardStateRules.ShouldShowTemporarySkillNote(installState));
}

internal void ShowNotCheckedState(bool isMigrating)
Expand Down Expand Up @@ -284,7 +294,7 @@ private static void CreateSectionDivider(ScrollView mainScrollView)
mainScrollView.Add(divider);
}

private static (EnumField migrationSkillTargetField, Button migrationSkillButton)
private static (EnumField migrationSkillTargetField, Button migrationSkillButton, TextField temporaryNoteTextField)
CreateAiMigrationSkillSection(ScrollView mainScrollView)
{
VisualElement migrationSkillSection = new VisualElement();
Expand Down Expand Up @@ -334,9 +344,18 @@ private static (EnumField migrationSkillTargetField, Button migrationSkillButton
migrationSkillButton.AddToClassList("setup-button");
migrationSkillButton.AddToClassList("setup-button--primary");
buttonRow.Add(migrationSkillButton);

TextField temporaryNoteTextField = CreateSelectableText(
ThirdPartyToolMigrationWizardText.AiMigrationSkillTemporaryNoteText,
"setup-step__description-label");
actionSection.Add(temporaryNoteTextField);
// Missing is the default create-time install state; hide until SetMigrationSkillState
// reports an installed or outdated skill.
ViewDataBinder.SetVisible(temporaryNoteTextField, false);

CreateMigrationSkillUsageFoldout(actionSection);
CreateMigrationSkillPromptCopyButton(actionSection);
return (migrationSkillTargetField, migrationSkillButton);
return (migrationSkillTargetField, migrationSkillButton, temporaryNoteTextField);
}

private static void CreateMigrationSkillUsageFoldout(VisualElement content)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ syntax in agent-facing docs and automation.
4. Edit only files that clearly contain V2 `uloop` option syntax.
5. Repeat the searches after editing and report any remaining V2
candidates with the reason each one was left unchanged.
6. After every Required Search Pass reports zero remaining V2 candidates,
tell the user this temporary skill has finished its job and can be
removed with `uloop skills uninstall-v3-migration --<target>` (for
example `--claude` or `--codex`). Do not uninstall the skill yourself.

Prefer `rg` for searches when available. If `rg` is unavailable, use the best available project search tool. Candidate discovery should be repository search plus context inspection, not a generated candidate list.

Expand Down
Loading