From 036e3172580fbf59bdec8131b72e30cf140420aa Mon Sep 17 00:00:00 2001 From: hatayama Date: Sat, 4 Jul 2026 12:11:15 +0900 Subject: [PATCH 1/2] Move CLI UI and tool docs helpers out of clicore Split spinner, CLI Vibe logging, and tool option documentation helpers into common/ui, common/vibelog, and common/tooldocs. Keep thin clicore wrappers so existing dispatcher and project-runner call sites continue to behave the same while release input stamps track the new packages. --- cli/common/clicore/cli_vibe_test.go | 53 ------------------- cli/common/clicore/tooldocs_aliases.go | 43 +++++++++++++++ cli/common/clicore/ui_aliases.go | 26 +++++++++ cli/common/clicore/vibelog_aliases.go | 27 ++++++++++ .../clicore/vibelog_test_helpers_test.go | 28 ++++++++++ cli/common/tooldocs/string_helpers.go | 13 +++++ .../{clicore => tooldocs}/tool_option_help.go | 28 ++++++---- .../{clicore => tooldocs}/tool_options.go | 24 +++++---- cli/common/{clicore => ui}/spinner.go | 10 ++-- cli/common/{clicore => ui}/spinner_test.go | 20 ++++--- cli/common/{clicore => vibelog}/cli_vibe.go | 2 +- cli/common/vibelog/cli_vibe_test.go | 28 ++++++++++ cli/dispatcher/shared-inputs-stamp.json | 2 +- cli/project-runner/shared-inputs-stamp.json | 2 +- .../automation/release_trigger_guard.go | 3 ++ scripts/stamp-release-inputs.sh | 5 +- 16 files changed, 218 insertions(+), 96 deletions(-) delete mode 100644 cli/common/clicore/cli_vibe_test.go create mode 100644 cli/common/clicore/tooldocs_aliases.go create mode 100644 cli/common/clicore/ui_aliases.go create mode 100644 cli/common/clicore/vibelog_aliases.go create mode 100644 cli/common/clicore/vibelog_test_helpers_test.go create mode 100644 cli/common/tooldocs/string_helpers.go rename cli/common/{clicore => tooldocs}/tool_option_help.go (83%) rename cli/common/{clicore => tooldocs}/tool_options.go (76%) rename cli/common/{clicore => ui}/spinner.go (90%) rename cli/common/{clicore => ui}/spinner_test.go (84%) rename cli/common/{clicore => vibelog}/cli_vibe.go (99%) create mode 100644 cli/common/vibelog/cli_vibe_test.go diff --git a/cli/common/clicore/cli_vibe_test.go b/cli/common/clicore/cli_vibe_test.go deleted file mode 100644 index 717ccd48c5..0000000000 --- a/cli/common/clicore/cli_vibe_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package clicore - -import ( - "os" - "path/filepath" - "testing" -) - -func enableCliVibeLog(t *testing.T) { - t.Helper() - t.Setenv(CLIVibeLogEnvName, "1") -} - -// readOnlyCliVibeLog reads the single CLI Vibe log file written for projectRoot. -// Duplicated from internal/cli's test helper of the same name: test helpers -// cannot be shared across packages, and both packages exercise CLI Vibe logging. -func readOnlyCliVibeLog(t *testing.T, projectRoot string) string { - t.Helper() - logFiles, err := filepath.Glob(filepath.Join(projectRoot, CLIVibeLogDirectory, CLIVibeLogPrefix+"_*.json")) - if err != nil { - t.Fatalf("failed to glob CLI Vibe logs: %v", err) - } - if len(logFiles) != 1 { - t.Fatalf("expected one CLI Vibe log, got %d: %#v", len(logFiles), logFiles) - } - content, err := os.ReadFile(logFiles[0]) - if err != nil { - t.Fatalf("failed to read CLI Vibe log: %v", err) - } - return string(content) -} - -// Verifies CLI Vibe logs are skipped unless ULOOP_DEBUG is enabled. -func TestWriteCliVibeLogSkipsWhenDebugDisabled(t *testing.T) { - t.Setenv(CLIVibeLogEnvName, "") - projectRoot := t.TempDir() - - err := WriteCLIVibeLog(projectRoot, CLIVibeLogEntry{ - Level: "INFO", - Operation: "test_operation", - Message: "test message", - }) - if err != nil { - t.Fatalf("writeCliVibeLog should skip without error: %v", err) - } - logFiles, err := filepath.Glob(filepath.Join(projectRoot, CLIVibeLogDirectory, CLIVibeLogPrefix+"_*.json")) - if err != nil { - t.Fatalf("failed to glob CLI Vibe logs: %v", err) - } - if len(logFiles) != 0 { - t.Fatalf("expected no CLI Vibe logs, got %d: %#v", len(logFiles), logFiles) - } -} diff --git a/cli/common/clicore/tooldocs_aliases.go b/cli/common/clicore/tooldocs_aliases.go new file mode 100644 index 0000000000..9f3c03c94c --- /dev/null +++ b/cli/common/clicore/tooldocs_aliases.go @@ -0,0 +1,43 @@ +package clicore + +import "github.com/hatayama/unity-cli-loop/common/tooldocs" + +const ( + DynamicCodeFileFlagName = tooldocs.DynamicCodeFileFlagName + DynamicCodeFileOptionName = tooldocs.DynamicCodeFileOptionName + DynamicCodeFileOptionUsage = tooldocs.DynamicCodeFileOptionUsage + DynamicCodeFileOptionDescription = tooldocs.DynamicCodeFileOptionDescription + + ProjectPathFlagName = tooldocs.ProjectPathFlagName + ReloadExternalSceneChangesPropertyName = tooldocs.ReloadExternalSceneChangesPropertyName +) + +type OptionHelpEntry = tooldocs.OptionHelpEntry + +func VisibleOptionHelpEntriesForTool(tool ToolDefinition) []OptionHelpEntry { + return tooldocs.VisibleOptionHelpEntriesForTool(tool) +} + +func FindProperty(tool ToolDefinition, kebabName string) (string, ToolProperty, bool, bool) { + return tooldocs.FindProperty(tool, kebabName) +} + +func OptionNameForProperty(toolName string, propertyName string, property ToolProperty) string { + return tooldocs.OptionNameForProperty(toolName, propertyName, property) +} + +func VisibleOptionNamesForTool(tool ToolDefinition) []string { + return tooldocs.VisibleOptionNamesForTool(tool) +} + +func IsBooleanProperty(property ToolProperty) bool { + return tooldocs.IsBooleanProperty(property) +} + +func IsNegatedBooleanProperty(property ToolProperty) bool { + return tooldocs.IsNegatedBooleanProperty(property) +} + +func OptionSummary(toolName string, propertyName string, property ToolProperty) string { + return tooldocs.OptionSummary(toolName, propertyName, property) +} diff --git a/cli/common/clicore/ui_aliases.go b/cli/common/clicore/ui_aliases.go new file mode 100644 index 0000000000..23c44faf4e --- /dev/null +++ b/cli/common/clicore/ui_aliases.go @@ -0,0 +1,26 @@ +package clicore + +import ( + "io" + + "github.com/hatayama/unity-cli-loop/common/ui" + "github.com/hatayama/unity-cli-loop/common/unityipc" +) + +type TerminalSpinner = ui.TerminalSpinner + +func NewToolSpinner(stderr io.Writer, command string) *TerminalSpinner { + return ui.NewToolSpinner(stderr, shouldShowToolFeedback(command)) +} + +func NewLaunchSpinner(stdout io.Writer, stderr io.Writer) *TerminalSpinner { + return ui.NewLaunchSpinner(stdout, stderr) +} + +func NewSpinnerProgressFunc(spinner *TerminalSpinner, executingMessage string) unityipc.ProgressFunc { + return ui.NewSpinnerProgressFunc(spinner, executingMessage) +} + +func shouldShowToolFeedback(command string) bool { + return command != ExecuteDynamicCodeCommandName +} diff --git a/cli/common/clicore/vibelog_aliases.go b/cli/common/clicore/vibelog_aliases.go new file mode 100644 index 0000000000..6a2d803676 --- /dev/null +++ b/cli/common/clicore/vibelog_aliases.go @@ -0,0 +1,27 @@ +package clicore + +import "github.com/hatayama/unity-cli-loop/common/vibelog" + +const ( + CLIVibeLogDirectory = vibelog.CLIVibeLogDirectory + CLIVibeLogPrefix = vibelog.CLIVibeLogPrefix + CLIVibeLogEnvName = vibelog.CLIVibeLogEnvName +) + +type CLIVibeLogEntry = vibelog.CLIVibeLogEntry + +func NewCLIVibeCorrelationID() string { + return vibelog.NewCLIVibeCorrelationID() +} + +func WriteCLIVibeLog(projectRoot string, entry CLIVibeLogEntry) error { + return vibelog.WriteCLIVibeLog(projectRoot, entry) +} + +func IsCLIVibeLogEnabled() bool { + return vibelog.IsCLIVibeLogEnabled() +} + +func ProjectIdentity(projectRoot string) string { + return vibelog.ProjectIdentity(projectRoot) +} diff --git a/cli/common/clicore/vibelog_test_helpers_test.go b/cli/common/clicore/vibelog_test_helpers_test.go new file mode 100644 index 0000000000..debb32ddf8 --- /dev/null +++ b/cli/common/clicore/vibelog_test_helpers_test.go @@ -0,0 +1,28 @@ +package clicore + +import ( + "os" + "path/filepath" + "testing" +) + +func enableCliVibeLog(t *testing.T) { + t.Helper() + t.Setenv(CLIVibeLogEnvName, "1") +} + +func readOnlyCliVibeLog(t *testing.T, projectRoot string) string { + t.Helper() + logFiles, err := filepath.Glob(filepath.Join(projectRoot, CLIVibeLogDirectory, CLIVibeLogPrefix+"_*.json")) + if err != nil { + t.Fatalf("failed to glob CLI Vibe logs: %v", err) + } + if len(logFiles) != 1 { + t.Fatalf("expected one CLI Vibe log, got %d: %#v", len(logFiles), logFiles) + } + content, err := os.ReadFile(logFiles[0]) + if err != nil { + t.Fatalf("failed to read CLI Vibe log: %v", err) + } + return string(content) +} diff --git a/cli/common/tooldocs/string_helpers.go b/cli/common/tooldocs/string_helpers.go new file mode 100644 index 0000000000..bb47ae67ca --- /dev/null +++ b/cli/common/tooldocs/string_helpers.go @@ -0,0 +1,13 @@ +package tooldocs + +import "strings" + +func FirstHelpLine(description string) string { + for _, line := range strings.Split(description, "\n") { + trimmed := strings.TrimSpace(line) + if trimmed != "" { + return trimmed + } + } + return "" +} diff --git a/cli/common/clicore/tool_option_help.go b/cli/common/tooldocs/tool_option_help.go similarity index 83% rename from cli/common/clicore/tool_option_help.go rename to cli/common/tooldocs/tool_option_help.go index a18983bf4c..e72f80a9ff 100644 --- a/cli/common/clicore/tool_option_help.go +++ b/cli/common/tooldocs/tool_option_help.go @@ -1,9 +1,11 @@ -package clicore +package tooldocs import ( "fmt" "sort" "strings" + + "github.com/hatayama/unity-cli-loop/common/tools" ) // --code-file is CLI-side sugar for execute-dynamic-code: it loads a C# source file into @@ -17,6 +19,12 @@ const ( const DynamicCodeFileOptionDescription = "Read C# code from a file instead of --code when shell quoting would alter multiline code" +const ( + compileCommandName = "compile" + executeDynamicCodeCommandName = "execute-dynamic-code" + runTestsCommandName = "run-tests" +) + // OptionHelpEntry is one row of a tool's --help option listing. type OptionHelpEntry struct { Name string @@ -24,7 +32,7 @@ type OptionHelpEntry struct { Description string } -func VisibleOptionHelpEntriesForTool(tool ToolDefinition) []OptionHelpEntry { +func VisibleOptionHelpEntriesForTool(tool tools.ToolDefinition) []OptionHelpEntry { schema := tool.EffectiveInputSchema() entries := make([]OptionHelpEntry, 0, len(schema.Properties)) for propertyName, property := range schema.Properties { @@ -47,14 +55,14 @@ func VisibleOptionHelpEntriesForTool(tool ToolDefinition) []OptionHelpEntry { return entries } -func optionUsage(optionName string, property ToolProperty) string { +func optionUsage(optionName string, property tools.ToolProperty) string { if IsBooleanProperty(property) { return optionName } return optionName + " <" + optionValueName(property) + ">" } -func optionValueName(property ToolProperty) string { +func optionValueName(property tools.ToolProperty) string { switch strings.ToLower(property.Type) { case "integer": return "integer" @@ -69,7 +77,7 @@ func optionValueName(property ToolProperty) string { } } -func optionDescription(toolName string, propertyName string, property ToolProperty) string { +func optionDescription(toolName string, propertyName string, property tools.ToolProperty) string { if isRunTestsSaveBeforeRunOption(toolName, propertyName, property) { return OptionSummary(toolName, propertyName, property) + "; default: auto-save enabled" } @@ -100,7 +108,7 @@ func defaultValueText(value any) string { return fmt.Sprint(value) } -func OptionSummary(toolName string, propertyName string, property ToolProperty) string { +func OptionSummary(toolName string, propertyName string, property tools.ToolProperty) string { if IsNegatedBooleanProperty(property) { if isRunTestsSaveBeforeRunOption(toolName, propertyName, property) { return "Fail before execution if unsaved editor changes remain instead of auto-saving them" @@ -118,8 +126,8 @@ func OptionSummary(toolName string, propertyName string, property ToolProperty) return FirstHelpLine(property.Description) } -func appendDynamicCodeFileOptionName(tool ToolDefinition, options []string) []string { - if tool.Name != ExecuteDynamicCodeCommandName { +func appendDynamicCodeFileOptionName(tool tools.ToolDefinition, options []string) []string { + if tool.Name != executeDynamicCodeCommandName { return options } for _, option := range options { @@ -130,8 +138,8 @@ func appendDynamicCodeFileOptionName(tool ToolDefinition, options []string) []st return append(options, DynamicCodeFileOptionName) } -func appendDynamicCodeFileOptionHelpEntry(tool ToolDefinition, entries []OptionHelpEntry) []OptionHelpEntry { - if tool.Name != ExecuteDynamicCodeCommandName { +func appendDynamicCodeFileOptionHelpEntry(tool tools.ToolDefinition, entries []OptionHelpEntry) []OptionHelpEntry { + if tool.Name != executeDynamicCodeCommandName { return entries } for _, entry := range entries { diff --git a/cli/common/clicore/tool_options.go b/cli/common/tooldocs/tool_options.go similarity index 76% rename from cli/common/clicore/tool_options.go rename to cli/common/tooldocs/tool_options.go index 6d7cd92c1e..d08ad45ea9 100644 --- a/cli/common/clicore/tool_options.go +++ b/cli/common/tooldocs/tool_options.go @@ -1,8 +1,10 @@ -package clicore +package tooldocs import ( "sort" "strings" + + "github.com/hatayama/unity-cli-loop/common/tools" ) const ( @@ -10,17 +12,17 @@ const ( ReloadExternalSceneChangesPropertyName = "ReloadExternalSceneChanges" ) -func FindProperty(tool ToolDefinition, kebabName string) (string, ToolProperty, bool, bool) { +func FindProperty(tool tools.ToolDefinition, kebabName string) (string, tools.ToolProperty, bool, bool) { schema := tool.EffectiveInputSchema() for propertyName, property := range schema.Properties { if OptionNameForProperty(tool.Name, propertyName, property) == kebabName { return propertyName, property, IsNegatedBooleanProperty(property), true } } - return "", ToolProperty{}, false, false + return "", tools.ToolProperty{}, false, false } -func OptionNameForProperty(toolName string, propertyName string, property ToolProperty) string { +func OptionNameForProperty(toolName string, propertyName string, property tools.ToolProperty) string { kebabName := pascalToKebab(propertyName) if IsNegatedBooleanProperty(property) { if isRunTestsSaveBeforeRunOption(toolName, propertyName, property) { @@ -34,19 +36,19 @@ func OptionNameForProperty(toolName string, propertyName string, property ToolPr return kebabName } -func isRunTestsSaveBeforeRunOption(toolName string, propertyName string, property ToolProperty) bool { - return toolName == RunTestsCommandName && +func isRunTestsSaveBeforeRunOption(toolName string, propertyName string, property tools.ToolProperty) bool { + return toolName == runTestsCommandName && propertyName == "SaveBeforeRun" && IsNegatedBooleanProperty(property) } -func isCompileReloadExternalSceneChangesOption(toolName string, propertyName string, property ToolProperty) bool { - return toolName == CompileCommandName && +func isCompileReloadExternalSceneChangesOption(toolName string, propertyName string, property tools.ToolProperty) bool { + return toolName == compileCommandName && propertyName == ReloadExternalSceneChangesPropertyName && IsNegatedBooleanProperty(property) } -func VisibleOptionNamesForTool(tool ToolDefinition) []string { +func VisibleOptionNamesForTool(tool tools.ToolDefinition) []string { schema := tool.EffectiveInputSchema() options := make([]string, 0, len(schema.Properties)) for propertyName, property := range schema.Properties { @@ -60,11 +62,11 @@ func VisibleOptionNamesForTool(tool ToolDefinition) []string { return options } -func IsBooleanProperty(property ToolProperty) bool { +func IsBooleanProperty(property tools.ToolProperty) bool { return strings.EqualFold(property.Type, "boolean") } -func IsNegatedBooleanProperty(property ToolProperty) bool { +func IsNegatedBooleanProperty(property tools.ToolProperty) bool { defaultValue, ok := property.EffectiveDefault().(bool) return IsBooleanProperty(property) && ok && defaultValue } diff --git a/cli/common/clicore/spinner.go b/cli/common/ui/spinner.go similarity index 90% rename from cli/common/clicore/spinner.go rename to cli/common/ui/spinner.go index fa281cb210..34a5f6d3be 100644 --- a/cli/common/clicore/spinner.go +++ b/cli/common/ui/spinner.go @@ -1,4 +1,4 @@ -package clicore +package ui import ( "fmt" @@ -27,8 +27,8 @@ type TerminalSpinner struct { mutex sync.Mutex } -func NewToolSpinner(stderr io.Writer, command string) *TerminalSpinner { - return newSpinner(stderr, shouldShowToolFeedback(command) && isTerminalWriter(stderr), "Connecting to Unity...") +func NewToolSpinner(stderr io.Writer, showFeedback bool) *TerminalSpinner { + return newSpinner(stderr, showFeedback && isTerminalWriter(stderr), "Connecting to Unity...") } func NewLaunchSpinner(stdout io.Writer, stderr io.Writer) *TerminalSpinner { @@ -133,7 +133,3 @@ func isTerminalWriter(writer io.Writer) bool { return info.Mode()&os.ModeCharDevice != 0 } - -func shouldShowToolFeedback(command string) bool { - return command != ExecuteDynamicCodeCommandName -} diff --git a/cli/common/clicore/spinner_test.go b/cli/common/ui/spinner_test.go similarity index 84% rename from cli/common/clicore/spinner_test.go rename to cli/common/ui/spinner_test.go index 9f515f872c..46cffcba4a 100644 --- a/cli/common/clicore/spinner_test.go +++ b/cli/common/ui/spinner_test.go @@ -1,4 +1,4 @@ -package clicore +package ui import ( "bytes" @@ -86,16 +86,14 @@ func TestSpinnerProgressFuncMapsConnectionEventsToExecutingMessage(t *testing.T) } } -func TestToolFeedbackSkipsExecuteDynamicCode(t *testing.T) { - // Verifies that execute-dynamic-code keeps the CLI hot path quiet. - if shouldShowToolFeedback(ExecuteDynamicCodeCommandName) { - t.Fatal("execute-dynamic-code should skip spinner feedback on the hot path") - } -} +func TestNewToolSpinnerRespectsFeedbackFlag(t *testing.T) { + // Verifies callers can disable tool spinner feedback for hot paths. + var stderr bytes.Buffer -func TestToolFeedbackKeepsOtherUnityTools(t *testing.T) { - // Verifies that regular Unity tools still show interactive feedback. - if !shouldShowToolFeedback("get-logs") { - t.Fatal("non-hot-path Unity tools should keep spinner feedback") + spinner := NewToolSpinner(&stderr, false) + spinner.Stop() + + if stderr.Len() != 0 { + t.Fatalf("disabled tool spinner wrote output: %q", stderr.String()) } } diff --git a/cli/common/clicore/cli_vibe.go b/cli/common/vibelog/cli_vibe.go similarity index 99% rename from cli/common/clicore/cli_vibe.go rename to cli/common/vibelog/cli_vibe.go index 1834fb7b98..da6578a4c8 100644 --- a/cli/common/clicore/cli_vibe.go +++ b/cli/common/vibelog/cli_vibe.go @@ -1,4 +1,4 @@ -package clicore +package vibelog import ( "crypto/sha256" diff --git a/cli/common/vibelog/cli_vibe_test.go b/cli/common/vibelog/cli_vibe_test.go new file mode 100644 index 0000000000..a7ccf8a20e --- /dev/null +++ b/cli/common/vibelog/cli_vibe_test.go @@ -0,0 +1,28 @@ +package vibelog + +import ( + "path/filepath" + "testing" +) + +// Verifies CLI Vibe logs are skipped unless ULOOP_DEBUG is enabled. +func TestWriteCliVibeLogSkipsWhenDebugDisabled(t *testing.T) { + t.Setenv(CLIVibeLogEnvName, "") + projectRoot := t.TempDir() + + err := WriteCLIVibeLog(projectRoot, CLIVibeLogEntry{ + Level: "INFO", + Operation: "test_operation", + Message: "test message", + }) + if err != nil { + t.Fatalf("writeCliVibeLog should skip without error: %v", err) + } + logFiles, err := filepath.Glob(filepath.Join(projectRoot, CLIVibeLogDirectory, CLIVibeLogPrefix+"_*.json")) + if err != nil { + t.Fatalf("failed to glob CLI Vibe logs: %v", err) + } + if len(logFiles) != 0 { + t.Fatalf("expected no CLI Vibe logs, got %d: %#v", len(logFiles), logFiles) + } +} diff --git a/cli/dispatcher/shared-inputs-stamp.json b/cli/dispatcher/shared-inputs-stamp.json index 8ce2066d24..e578723c40 100644 --- a/cli/dispatcher/shared-inputs-stamp.json +++ b/cli/dispatcher/shared-inputs-stamp.json @@ -1,4 +1,4 @@ { "schemaVersion": 1, - "sharedInputsHash": "55edb6d89876ab6462b5ca689085f4d6f43f8166" + "sharedInputsHash": "18bb36e362cd532c38d5b3dd9011775d87641af9" } diff --git a/cli/project-runner/shared-inputs-stamp.json b/cli/project-runner/shared-inputs-stamp.json index f12c0cdefc..15a51f03dd 100644 --- a/cli/project-runner/shared-inputs-stamp.json +++ b/cli/project-runner/shared-inputs-stamp.json @@ -1,4 +1,4 @@ { "schemaVersion": 1, - "sharedInputsHash": "c0a61ce5c6fb1066a532cb0195fbb99da66220ae" + "sharedInputsHash": "43d6a192a0ce179eacd91c6ff86f40f99d45ddcf" } diff --git a/cli/release-automation/internal/automation/release_trigger_guard.go b/cli/release-automation/internal/automation/release_trigger_guard.go index acb3e15d8c..0f8e6ec552 100644 --- a/cli/release-automation/internal/automation/release_trigger_guard.go +++ b/cli/release-automation/internal/automation/release_trigger_guard.go @@ -155,9 +155,12 @@ var sharedCommonPackageRoots = []string{ "cli/common/errors/", "cli/common/project/", "cli/common/skillscan/", + "cli/common/tooldocs/", "cli/common/tools/", + "cli/common/ui/", "cli/common/unityipc/", "cli/common/unityprocess/", + "cli/common/vibelog/", } var dispatcherOnlyCommonPackageRoots = []string{ diff --git a/scripts/stamp-release-inputs.sh b/scripts/stamp-release-inputs.sh index 49e8ed04f1..4ef2f41f63 100755 --- a/scripts/stamp-release-inputs.sh +++ b/scripts/stamp-release-inputs.sh @@ -22,9 +22,12 @@ list_shared_common_inputs() { 'cli/common/errors/' \ 'cli/common/project/' \ 'cli/common/skillscan/' \ + 'cli/common/tooldocs/' \ 'cli/common/tools/' \ + 'cli/common/ui/' \ 'cli/common/unityipc/' \ - 'cli/common/unityprocess/' | + 'cli/common/unityprocess/' \ + 'cli/common/vibelog/' | grep -E '\.go$|\.ps1$|/go\.mod$|/go\.sum$' | grep -v '_test\.go$' || true } From 970e8e6a1927863d982dc9ae0530cb27b8c67ea3 Mon Sep 17 00:00:00 2001 From: hatayama Date: Sat, 4 Jul 2026 12:18:22 +0900 Subject: [PATCH 2/2] Address UI helper extraction review feedback Delegate clicore FirstHelpLine to tooldocs and align the CLI Vibe log test name with the exported API initialism. --- cli/common/clicore/string_helpers.go | 10 ++-------- cli/common/vibelog/cli_vibe_test.go | 4 ++-- cli/dispatcher/shared-inputs-stamp.json | 2 +- cli/project-runner/shared-inputs-stamp.json | 2 +- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/cli/common/clicore/string_helpers.go b/cli/common/clicore/string_helpers.go index a15ae45fdc..00f1067592 100644 --- a/cli/common/clicore/string_helpers.go +++ b/cli/common/clicore/string_helpers.go @@ -1,15 +1,9 @@ package clicore -import "strings" +import "github.com/hatayama/unity-cli-loop/common/tooldocs" func FirstHelpLine(description string) string { - for _, line := range strings.Split(description, "\n") { - trimmed := strings.TrimSpace(line) - if trimmed != "" { - return trimmed - } - } - return "" + return tooldocs.FirstHelpLine(description) } func FirstNonEmpty(values ...string) string { diff --git a/cli/common/vibelog/cli_vibe_test.go b/cli/common/vibelog/cli_vibe_test.go index a7ccf8a20e..9d4e52af52 100644 --- a/cli/common/vibelog/cli_vibe_test.go +++ b/cli/common/vibelog/cli_vibe_test.go @@ -6,7 +6,7 @@ import ( ) // Verifies CLI Vibe logs are skipped unless ULOOP_DEBUG is enabled. -func TestWriteCliVibeLogSkipsWhenDebugDisabled(t *testing.T) { +func TestWriteCLIVibeLogSkipsWhenDebugDisabled(t *testing.T) { t.Setenv(CLIVibeLogEnvName, "") projectRoot := t.TempDir() @@ -16,7 +16,7 @@ func TestWriteCliVibeLogSkipsWhenDebugDisabled(t *testing.T) { Message: "test message", }) if err != nil { - t.Fatalf("writeCliVibeLog should skip without error: %v", err) + t.Fatalf("WriteCLIVibeLog should skip without error: %v", err) } logFiles, err := filepath.Glob(filepath.Join(projectRoot, CLIVibeLogDirectory, CLIVibeLogPrefix+"_*.json")) if err != nil { diff --git a/cli/dispatcher/shared-inputs-stamp.json b/cli/dispatcher/shared-inputs-stamp.json index e578723c40..0d7b073611 100644 --- a/cli/dispatcher/shared-inputs-stamp.json +++ b/cli/dispatcher/shared-inputs-stamp.json @@ -1,4 +1,4 @@ { "schemaVersion": 1, - "sharedInputsHash": "18bb36e362cd532c38d5b3dd9011775d87641af9" + "sharedInputsHash": "1d28b9b0722bb9c7908f235151e307936d1ef054" } diff --git a/cli/project-runner/shared-inputs-stamp.json b/cli/project-runner/shared-inputs-stamp.json index 15a51f03dd..c32fa1e576 100644 --- a/cli/project-runner/shared-inputs-stamp.json +++ b/cli/project-runner/shared-inputs-stamp.json @@ -1,4 +1,4 @@ { "schemaVersion": 1, - "sharedInputsHash": "43d6a192a0ce179eacd91c6ff86f40f99d45ddcf" + "sharedInputsHash": "b68505047708ceedd9b13fdb5f74176482b41e62" }