From 6f87a862463c29222462d476f2d427394dfd89d4 Mon Sep 17 00:00:00 2001 From: Maxim Uvarov Date: Fri, 9 Jan 2026 22:57:34 -0300 Subject: [PATCH 01/14] chore: format --- dotnu/commands.nu | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/dotnu/commands.nu b/dotnu/commands.nu index 4d13045..0a83d97 100644 --- a/dotnu/commands.nu +++ b/dotnu/commands.nu @@ -288,10 +288,12 @@ export def find-examples []: string -> table table table table table { | where {|p| $p.0.end < $p.1.start } | each {|p| let content = $bytes | bytes at $p.0.end..<$p.1.start | decode utf8 - {content: $content, start: $p.0.end, end: $p.1.start, shape: (classify-gap $content)} + {content: $content start: $p.0.end end: $p.1.start shape: (classify-gap $content)} } $tokens | select content start end shape | append $gaps | sort-by start @@ -1045,7 +1047,7 @@ export def split-statements []: string -> table table Date: Fri, 9 Jan 2026 23:05:00 -0300 Subject: [PATCH 02/14] docs: add import usage and coverage check command to CLAUDE.md Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 3f19afb..8bce414 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,6 +26,11 @@ nu toolkit.nu release --major # major bump **Important**: Always use `nu toolkit.nu test` (not `test-unit` or `test-integration` separately). The combined command provides proper test output and summary. +```bash +# Check test coverage - requires both source AND test files +nu -c 'use dotnu/; ["dotnu/*.nu" "tests/test_commands.nu" "toolkit.nu"] | each { glob $in } | flatten | dotnu dependencies ...$in | dotnu filter-commands-with-no-tests' +``` + ## Architecture ### Module Structure @@ -38,6 +43,10 @@ dotnu/ **Export convention**: All commands in `commands.nu` are exported by default (for internal use, testing, and development). The public API is managed through `mod.nu`, which selectively re-exports only the user-facing commands. To add a command to the public API, add it to the list in `mod.nu`. +**Imports**: +- `use dotnu/` - import public API commands +- `use dotnu/commands.nu *` - import all commands (including internal) + **mod.nu** exports these public commands: - `dependencies` - Analyze command call chains - `extract-command-code` - Extract command with its dependencies From 56e023b034635e51872de27993decee85c3fabb6 Mon Sep 17 00:00:00 2001 From: claude Date: Fri, 9 Jan 2026 23:13:19 -0300 Subject: [PATCH 03/14] fix: detect export use [...commands] pattern in list-exported-commands Add AST-based detection for selective re-export syntax used in mod.nu files. When --export flag is used and no 'export def' statements are found, fall back to parsing 'export use module.nu [commands]' pattern to extract the re-exported command names. Co-Authored-By: Claude Opus 4.5 --- dotnu/commands.nu | 49 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/dotnu/commands.nu b/dotnu/commands.nu index 0a83d97..747ef9d 100644 --- a/dotnu/commands.nu +++ b/dotnu/commands.nu @@ -168,14 +168,26 @@ export def 'list-exported-commands' [ $path: path --export # use only commands that are exported ] { - open $path -r - | lines - | if $export { - where $it =~ '^export def ' + let source = open $path -r + + if $export { + # Try export def pattern first + let from_def = $source + | lines + | where $it =~ '^export def ' | extract-command-name | replace-main-with-module-name $path + + # Try export use [...] pattern via AST if no export def found + let from_use = if ($from_def | is-empty) { + $source | extract-export-use-commands + } else { [] } + + $from_def | append $from_use } else { - where $it =~ '^(export )?def ' + $source + | lines + | where $it =~ '^(export )?def ' | extract-command-name | where $it starts-with 'main' | str replace 'main ' '' @@ -964,6 +976,33 @@ export def capture-marker [ } } +# Extract command names from `export use module.nu [commands]` pattern using AST +# +# When a module uses selective re-export like: +# export use commands.nu ["cmd1" "cmd2"] +# This extracts the command names from the list. +export def extract-export-use-commands []: string -> list { + let tokens = ast --flatten $in | flatten span + + # Find export use internal call + let export_use_matches = $tokens + | enumerate + | where { $in.item.content == 'export use' and $in.item.shape == 'shape_internalcall' } + + if ($export_use_matches | is-empty) { return [] } + + let export_use_idx = $export_use_matches | first | get index + + # Get tokens after export use: module path, then list with command names + # Pattern: [export use] [module.nu] [ [cmd1] [cmd2] ... ] + # shape_string tokens inside shape_list are the command names + $tokens + | skip ($export_use_idx + 2) # Skip "export use" and module path + | where shape == 'shape_string' + | get content + | each { str trim -c '"' } +} + # Complete AST output by filling gaps with synthetic tokens # # `ast --flatten` omits certain syntax elements (semicolons, assignment operators, etc). From d4a39d6c839a51135ce9aec79072e03d0e4cfa86 Mon Sep 17 00:00:00 2001 From: claude Date: Fri, 9 Jan 2026 23:18:03 -0300 Subject: [PATCH 04/14] refactor: unify export detection with AST-based extract-exported-commands Replace mixed regex/AST approach with a single function that handles both `export def` and `export use [...commands]` patterns using AST parsing. Co-Authored-By: Claude Opus 4.5 --- dotnu/commands.nu | 58 ++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/dotnu/commands.nu b/dotnu/commands.nu index 747ef9d..05a82e3 100644 --- a/dotnu/commands.nu +++ b/dotnu/commands.nu @@ -171,19 +171,9 @@ export def 'list-exported-commands' [ let source = open $path -r if $export { - # Try export def pattern first - let from_def = $source - | lines - | where $it =~ '^export def ' - | extract-command-name + $source + | extract-exported-commands | replace-main-with-module-name $path - - # Try export use [...] pattern via AST if no export def found - let from_use = if ($from_def | is-empty) { - $source | extract-export-use-commands - } else { [] } - - $from_def | append $from_use } else { $source | lines @@ -976,31 +966,33 @@ export def capture-marker [ } } -# Extract command names from `export use module.nu [commands]` pattern using AST +# Extract exported command names using AST # -# When a module uses selective re-export like: -# export use commands.nu ["cmd1" "cmd2"] -# This extracts the command names from the list. -export def extract-export-use-commands []: string -> list { +# Handles both patterns: +# - `export def cmd-name []` → extracts cmd-name +# - `export use module.nu ["cmd1" "cmd2"]` → extracts cmd1, cmd2 +export def extract-exported-commands []: string -> list { let tokens = ast --flatten $in | flatten span - # Find export use internal call - let export_use_matches = $tokens - | enumerate - | where { $in.item.content == 'export use' and $in.item.shape == 'shape_internalcall' } - - if ($export_use_matches | is-empty) { return [] } - - let export_use_idx = $export_use_matches | first | get index - - # Get tokens after export use: module path, then list with command names - # Pattern: [export use] [module.nu] [ [cmd1] [cmd2] ... ] - # shape_string tokens inside shape_list are the command names $tokens - | skip ($export_use_idx + 2) # Skip "export use" and module path - | where shape == 'shape_string' - | get content - | each { str trim -c '"' } + | enumerate + | where { $in.item.content in ['export def' 'export use'] } + | each {|match| + let idx = $match.index + if $match.item.content == 'export def' { + # Command name is next token + $tokens | get ($idx + 1) | get content | str trim -c "'" | str trim -c '"' + } else { + # export use: skip module path, get shape_string tokens from list + $tokens + | skip ($idx + 2) + | take while { $in.shape in ['shape_string' 'shape_list'] } + | where shape == 'shape_string' + | get content + | str trim -c '"' + } + } + | flatten } # Complete AST output by filling gaps with synthetic tokens From b2aca2afc7678d7a5432d71c4d7d35cc3baf85ee Mon Sep 17 00:00:00 2001 From: claude Date: Fri, 9 Jan 2026 23:27:39 -0300 Subject: [PATCH 05/14] refactor!: split list-exported-commands into two focused commands BREAKING CHANGE: Remove `list-exported-commands` command. Replace with two specialized commands: - `list-module-exports` - lists all exported definitions (export def + export use) - `list-module-interface` - lists module's callable interface (main commands) This provides clearer separation between finding what a module exports vs what commands become available when using a module. Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 3 +- README.md | 41 +++++++++++----------- dotnu/commands.nu | 44 ++++++++++++++---------- dotnu/mod.nu | 3 +- tests/output-yaml/coverage-untested.yaml | 4 +-- tests/test_commands.nu | 10 +++--- 6 files changed, 58 insertions(+), 47 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8bce414..cec4f7f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -51,7 +51,8 @@ dotnu/ - `dependencies` - Analyze command call chains - `extract-command-code` - Extract command with its dependencies - `filter-commands-with-no-tests` - Find untested commands -- `list-exported-commands` - List module's exported commands +- `list-module-exports` - List all exported definitions (export def + export use) +- `list-module-interface` - List module's callable interface (main commands) - `embeds-*` / `embed-add` - Literate programming tools - `set-x` / `generate-numd` - Script profiling diff --git a/README.md b/README.md index 40e98c5..b9a881c 100644 --- a/README.md +++ b/README.md @@ -397,29 +397,30 @@ dotnu extract-command-code --help # => ``` -### `dotnu list-exported-commands` +### `dotnu list-module-exports` -List commands defined in a module file. Use `--export` to show only exported commands. +List all exported definitions from a module file. Finds commands from `export def` and `export use [...commands]` patterns. ```nushell -dotnu list-exported-commands --help -# => Usage: -# => > list-exported-commands {flags} <$path> -# => -# => Flags: -# => -h, --help: Display the help message for this command -# => --export: use only commands that are exported -# => -# => Parameters: -# => $path -# => -# => Input/output types: -# => ╭───┬───────┬────────╮ -# => │ # │ input │ output │ -# => ├───┼───────┼────────┤ -# => │ 0 │ any │ any │ -# => ╰───┴───────┴────────╯ -# => +dotnu list-module-exports dotnu/mod.nu | first 5 +# => ╭───┬─────────────────────╮ +# => │ 0 │ dependencies │ +# => │ 1 │ embed-add │ +# => │ 2 │ embeds-capture-start│ +# => │ 3 │ embeds-capture-stop │ +# => │ 4 │ embeds-remove │ +# => ╰───┴─────────────────────╯ +``` + +### `dotnu list-module-interface` + +List module's callable interface - the `main` and `main subcommand` patterns that become available when you `use` the module. + +```nushell +dotnu list-module-interface tests/assets/b/example-mod1.nu +# => ╭───┬──────╮ +# => │ 0 │ main │ +# => ╰───┴──────╯ ``` ### `dotnu module-commands-code-to-record` diff --git a/dotnu/commands.nu b/dotnu/commands.nu index 05a82e3..c2b5996 100644 --- a/dotnu/commands.nu +++ b/dotnu/commands.nu @@ -162,26 +162,34 @@ export def 'extract-command-code' [ } } -# todo: `list-exported-commands` should be a completion for Nushell CLI - -export def 'list-exported-commands' [ +# List all exported definitions from a module file +# +# Finds commands from `export def` and `export use [...commands]` patterns. +export def 'list-module-exports' [ $path: path - --export # use only commands that are exported -] { - let source = open $path -r +]: nothing -> list { + open $path -r + | extract-exported-commands + | replace-main-with-module-name $path + | if ($in | is-empty) { + print 'No command found' + return + } else { } +} - if $export { - $source - | extract-exported-commands - | replace-main-with-module-name $path - } else { - $source - | lines - | where $it =~ '^(export )?def ' - | extract-command-name - | where $it starts-with 'main' - | str replace 'main ' '' - } +# List module's callable interface (main commands) +# +# Finds `def main` and `def 'main subcommand'` patterns - the commands +# available when you `use` the module. +export def 'list-module-interface' [ + $path: path +]: nothing -> list { + open $path -r + | lines + | where $it =~ '^(export )?def ' + | extract-command-name + | where $it starts-with 'main' + | str replace 'main ' '' | if ($in | is-empty) { print 'No command found' return diff --git a/dotnu/mod.nu b/dotnu/mod.nu index f80a0b3..576c2b9 100644 --- a/dotnu/mod.nu +++ b/dotnu/mod.nu @@ -10,7 +10,8 @@ export use commands.nu [ "extract-command-code" "filter-commands-with-no-tests" "generate-numd" - "list-exported-commands" + "list-module-exports" + "list-module-interface" "module-commands-code-to-record" "set-x" ] diff --git a/tests/output-yaml/coverage-untested.yaml b/tests/output-yaml/coverage-untested.yaml index f9bb5ce..ca7a80e 100644 --- a/tests/output-yaml/coverage-untested.yaml +++ b/tests/output-yaml/coverage-untested.yaml @@ -20,6 +20,6 @@ # untested: ($untested | get caller) # } # | to yaml -public_api_count: 14 -tested_count: 14 +public_api_count: 15 +tested_count: 15 untested: [] diff --git a/tests/test_commands.nu b/tests/test_commands.nu index 353bbae..0801ead 100644 --- a/tests/test_commands.nu +++ b/tests/test_commands.nu @@ -324,12 +324,12 @@ def "extract-command-code handles quoted command names" [] { } # ============================================================================= -# Tests for list-exported-commands +# Tests for list-module-exports # ============================================================================= @test -def "list-exported-commands finds exported commands" [] { - let result = list-exported-commands tests/assets/b/example-mod1.nu --export +def "list-module-exports finds exported commands" [] { + let result = list-module-exports tests/assets/b/example-mod1.nu # Should find exported commands assert ('lscustom' in $result) @@ -339,8 +339,8 @@ def "list-exported-commands finds exported commands" [] { } @test -def "list-exported-commands excludes non-exported when flag set" [] { - let result = list-exported-commands tests/assets/b/example-mod1.nu --export +def "list-module-exports excludes non-exported" [] { + let result = list-module-exports tests/assets/b/example-mod1.nu # Private commands should not be in exported list assert ('sort-by-custom' not-in $result) From afb021f2cc0d72509ce248af9f79007dcebfa974 Mon Sep 17 00:00:00 2001 From: claude Date: Fri, 9 Jan 2026 23:29:03 -0300 Subject: [PATCH 06/14] docs: fix formatting in README examples --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index b9a881c..3046267 100644 --- a/README.md +++ b/README.md @@ -203,13 +203,13 @@ dotnu dependencies --help # => ╰───┴───────┴────────╯ # => # => Examples: -# => +# => Analyze command dependencies in a module # => > dotnu dependencies ...(glob tests/assets/module-say/say/*.nu) # => ╭───┬──────────┬────────────────────┬──────────┬──────╮ # => │ # │ caller │ filename_of_caller │ callee │ step │ # => ├───┼──────────┼────────────────────┼──────────┼──────┤ -# => │ 0 │ hello │ hello.nu │ │ 0 │ -# => │ 1 │ question │ ask.nu │ │ 0 │ +# => │ 0 │ question │ ask.nu │ │ 0 │ +# => │ 1 │ hello │ hello.nu │ │ 0 │ # => │ 2 │ say │ mod.nu │ hello │ 0 │ # => │ 3 │ say │ mod.nu │ hi │ 0 │ # => │ 4 │ say │ mod.nu │ question │ 0 │ @@ -240,13 +240,13 @@ dotnu filter-commands-with-no-tests --help # => ╰───┴───────┴────────╯ # => # => Examples: -# => +# => Find commands not covered by tests # => > dependencies ...(glob tests/assets/module-say/say/*.nu) | filter-commands-with-no-tests # => ╭───┬──────────┬────────────────────╮ # => │ # │ caller │ filename_of_caller │ # => ├───┼──────────┼────────────────────┤ -# => │ 0 │ hello │ hello.nu │ -# => │ 1 │ question │ ask.nu │ +# => │ 0 │ question │ ask.nu │ +# => │ 1 │ hello │ hello.nu │ # => │ 2 │ say │ mod.nu │ # => ╰───┴──────────┴────────────────────╯ # => @@ -283,7 +283,7 @@ dotnu set-x --help # => ╰───┴───────┴────────╯ # => # => Examples: -# => +# => Generate script with timing instrumentation # => > set-x tests/assets/set-x-demo.nu --echo | lines | first 3 | to text # => mut $prev_ts = ( date now ) # => print ("> sleep 0.5sec" | nu-highlight) @@ -403,13 +403,13 @@ List all exported definitions from a module file. Finds commands from `export de ```nushell dotnu list-module-exports dotnu/mod.nu | first 5 -# => ╭───┬─────────────────────╮ -# => │ 0 │ dependencies │ -# => │ 1 │ embed-add │ -# => │ 2 │ embeds-capture-start│ -# => │ 3 │ embeds-capture-stop │ -# => │ 4 │ embeds-remove │ -# => ╰───┴─────────────────────╯ +# => ╭───┬──────────────────────╮ +# => │ 0 │ dependencies │ +# => │ 1 │ embed-add │ +# => │ 2 │ embeds-capture-start │ +# => │ 3 │ embeds-capture-stop │ +# => │ 4 │ embeds-remove │ +# => ╰───┴──────────────────────╯ ``` ### `dotnu list-module-interface` From 33660cc1a0bd719ccd4cd58f9f39048437ac3586 Mon Sep 17 00:00:00 2001 From: claude Date: Sat, 10 Jan 2026 00:03:08 -0300 Subject: [PATCH 07/14] fix: escape $ in examples-update to prevent regex backreference loss The str replace -r command interprets $ as a regex backreference, causing result values containing $ to lose those characters. Escaping $ as $$ before replacement preserves the literal dollar signs. Co-Authored-By: Claude Opus 4.5 --- dotnu/commands.nu | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dotnu/commands.nu b/dotnu/commands.nu index c2b5996..6cd845c 100644 --- a/dotnu/commands.nu +++ b/dotnu/commands.nu @@ -269,8 +269,10 @@ export def 'examples-update' [ # Using full original text ensures unique matches even with duplicate results let updated = $results | reduce --fold $content {|item acc| # Build new example by replacing just the result value in the original + # Escape $ as $$ to prevent regex backreference interpretation + let escaped_result = $item.new_result | str replace -a '$' '$$' let new_example = $item.original - | str replace -r '\} --result .+$' $"} --result ($item.new_result)" + | str replace -r '\} --result .+$' $"} --result ($escaped_result)" $acc | str replace $item.original $new_example } From 0b3952c1f6aea4d814ef836122a85ca626acee02 Mon Sep 17 00:00:00 2001 From: claude Date: Sat, 10 Jan 2026 00:09:23 -0300 Subject: [PATCH 08/14] fix: parse list/record result values in examples-update by tracking bracket depth Previously only the first token `[` was captured when parsing --result values like `['no-run' 'try']`. Now tracks bracket depth to find matching closing bracket for proper parsing of list and record result values. Co-Authored-By: Claude Opus 4.5 --- dotnu/commands.nu | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/dotnu/commands.nu b/dotnu/commands.nu index 6cd845c..bf7607d 100644 --- a/dotnu/commands.nu +++ b/dotnu/commands.nu @@ -337,14 +337,38 @@ export def find-examples []: string -> table Date: Sat, 10 Jan 2026 00:11:33 -0300 Subject: [PATCH 09/14] test: add regression tests for examples-update fixes Add three regression tests to prevent reintroduction of fixed bugs: - find-examples parses list result values (bracket depth fix) - find-examples parses record result values (bracket depth fix) - examples-update preserves dollar signs in results ($ escaping fix) Co-Authored-By: Claude Opus 4.5 --- tests/test_commands.nu | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/test_commands.nu b/tests/test_commands.nu index 0801ead..dee9674 100644 --- a/tests/test_commands.nu +++ b/tests/test_commands.nu @@ -592,6 +592,29 @@ def foo [] {}' assert ($result | first | get code | str contains "let x = 1") } +@test +def "find-examples parses list result values" [] { + let input = "@example \"list\" { ['a' 'b'] } --result ['a' 'b'] +def foo [] {}" + + let result = $input | find-examples + + assert equal ($result | length) 1 + # Verify the full list is captured, not just the opening bracket + assert ($result | first | get original | str contains "['a' 'b']") +} + +@test +def "find-examples parses record result values" [] { + let input = '@example "record" { {a: 1} } --result {a: 1} +def foo [] {}' + + let result = $input | find-examples + + assert equal ($result | length) 1 + assert ($result | first | get original | str contains "{a: 1}") +} + # ============================================================================= # Tests for execute-example # ============================================================================= @@ -669,6 +692,20 @@ def "examples-update preserves file when no examples" [] { assert equal $result $content } +@test +def "examples-update preserves dollar signs in results" [] { + # Test that $var references in result strings are not lost + # (regression test for regex backreference bug) + let temp = $nu.temp-path | path join 'test-examples-dollar.nu' + '@example "test" { "has $a variable" } --result "old" +export def dummy [] { 1 }' | save -f $temp + + let result = examples-update $temp --echo + + # The result should contain the $a, not have it stripped + assert ($result | str contains '$a') +} + # split-statements tests @test From c3c51dbfdbbf7c92c1611c1db52bbc11695dfe28 Mon Sep 17 00:00:00 2001 From: claude Date: Wed, 11 Feb 2026 22:33:51 -0300 Subject: [PATCH 10/14] chore: apply nushell style fixes (record commas, where shorthand) --- dotnu/commands.nu | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dotnu/commands.nu b/dotnu/commands.nu index bf7607d..d365974 100644 --- a/dotnu/commands.nu +++ b/dotnu/commands.nu @@ -348,7 +348,7 @@ export def find-examples []: string -> table table list { $tokens | enumerate - | where { $in.item.content in ['export def' 'export use'] } + | where item.content in ['export def' 'export use'] | each {|match| let idx = $match.index if $match.item.content == 'export def' { From 16a00d016d4cecaba136ca7015e537c0c94db189 Mon Sep 17 00:00:00 2001 From: claude Date: Wed, 11 Feb 2026 22:33:56 -0300 Subject: [PATCH 11/14] fix: replace $nu.temp-path with $nu.temp-dir in tests --- tests/test_commands.nu | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_commands.nu b/tests/test_commands.nu index dee9674..86fe679 100644 --- a/tests/test_commands.nu +++ b/tests/test_commands.nu @@ -390,7 +390,7 @@ def "embeds-update preserves script structure" [] { @test def "embeds-setup sets capture path in env" [] { # Test without --auto-commit to avoid git operations - let test_path = ($nu.temp-path | path join 'test-capture.nu') + let test_path = ($nu.temp-dir | path join 'test-capture.nu') embeds-setup $test_path assert equal $env.dotnu.embeds-capture-path $test_path @@ -398,7 +398,7 @@ def "embeds-setup sets capture path in env" [] { @test def "embeds-setup adds .nu extension if missing" [] { - let test_path = ($nu.temp-path | path join 'test-capture') + let test_path = ($nu.temp-dir | path join 'test-capture') embeds-setup $test_path assert ($env.dotnu.embeds-capture-path | str ends-with '.nu') @@ -622,7 +622,7 @@ def foo [] {}' @test def "execute-example runs simple expression" [] { # Create temp file for context - let temp = $nu.temp-path | path join 'test-execute-example.nu' + let temp = $nu.temp-dir | path join 'test-execute-example.nu' 'export def dummy [] { 1 }' | save -f $temp let result = execute-example '1 + 1' $temp @@ -632,7 +632,7 @@ def "execute-example runs simple expression" [] { @test def "execute-example returns error record on failure" [] { - let temp = $nu.temp-path | path join 'test-execute-example.nu' + let temp = $nu.temp-dir | path join 'test-execute-example.nu' 'export def dummy [] { 1 }' | save -f $temp let result = execute-example 'nonexistent-command' $temp @@ -642,7 +642,7 @@ def "execute-example returns error record on failure" [] { @test def "execute-example handles multiline result" [] { - let temp = $nu.temp-path | path join 'test-execute-example.nu' + let temp = $nu.temp-dir | path join 'test-execute-example.nu' 'export def dummy [] { 1 }' | save -f $temp let result = execute-example '[1, 2, 3]' $temp @@ -656,7 +656,7 @@ def "execute-example handles multiline result" [] { @test def "examples-update updates result values" [] { - let temp = $nu.temp-path | path join 'test-examples-update.nu' + let temp = $nu.temp-dir | path join 'test-examples-update.nu' '@example "add" { 1 + 1 } --result 0 export def dummy [] { 1 }' | save -f $temp @@ -668,7 +668,7 @@ export def dummy [] { 1 }' | save -f $temp @test def "examples-update handles multiple examples" [] { - let temp = $nu.temp-path | path join 'test-examples-update.nu' + let temp = $nu.temp-dir | path join 'test-examples-update.nu' '@example "first" { 1 + 1 } --result 0 export def foo [] {} @@ -683,7 +683,7 @@ export def bar [] {}' | save -f $temp @test def "examples-update preserves file when no examples" [] { - let temp = $nu.temp-path | path join 'test-examples-update.nu' + let temp = $nu.temp-dir | path join 'test-examples-update.nu' let content = 'export def foo [] { 1 }' $content | save -f $temp @@ -696,7 +696,7 @@ def "examples-update preserves file when no examples" [] { def "examples-update preserves dollar signs in results" [] { # Test that $var references in result strings are not lost # (regression test for regex backreference bug) - let temp = $nu.temp-path | path join 'test-examples-dollar.nu' + let temp = $nu.temp-dir | path join 'test-examples-dollar.nu' '@example "test" { "has $a variable" } --result "old" export def dummy [] { 1 }' | save -f $temp From 1860655b56c974cac02345c249fdcf50770ab15e Mon Sep 17 00:00:00 2001 From: claude Date: Wed, 11 Feb 2026 22:40:40 -0300 Subject: [PATCH 12/14] fix: count braces in split-statements to handle match blocks and multi-brace tokens The depth tracker had two bugs: - Tokens with both { and } were assumed net-zero, but a token like "{ }\n}" (else block + closing brace) has net -1 - match block opening braces appear as shape_gap, not shape_block, so they were invisible to depth tracking Count all { and } in shape_block, shape_closure, and shape_gap tokens instead of binary has/hasn't check. Co-Authored-By: Claude Opus 4.6 --- dotnu/commands.nu | 14 ++++---------- tests/output-yaml/coverage-untested.yaml | 8 ++++++-- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/dotnu/commands.nu b/dotnu/commands.nu index d365974..c295ca8 100644 --- a/dotnu/commands.nu +++ b/dotnu/commands.nu @@ -1112,16 +1112,10 @@ export def split-statements []: string -> table Date: Wed, 11 Feb 2026 22:46:23 -0300 Subject: [PATCH 13/14] fix: strip single quotes from export use entries in extract-exported-commands `export use` with single-quoted names (e.g., `'capture start'`) were not getting quotes stripped, causing mismatches with the dependency graph. Co-Authored-By: Claude Opus 4.6 --- dotnu/commands.nu | 1 + 1 file changed, 1 insertion(+) diff --git a/dotnu/commands.nu b/dotnu/commands.nu index c295ca8..22c1863 100644 --- a/dotnu/commands.nu +++ b/dotnu/commands.nu @@ -1024,6 +1024,7 @@ export def extract-exported-commands []: string -> list { | where shape == 'shape_string' | get content | str trim -c '"' + | str trim -c "'" } } | flatten From d2a45c460baaa3fc0ce8c1c9662657b53543ddae Mon Sep 17 00:00:00 2001 From: claude Date: Wed, 11 Feb 2026 22:56:05 -0300 Subject: [PATCH 14/14] test: add list-module-interface tests and update coverage fixture --- tests/output-yaml/coverage-untested.yaml | 3 +-- tests/test_commands.nu | 30 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/output-yaml/coverage-untested.yaml b/tests/output-yaml/coverage-untested.yaml index a448e2e..9e9463b 100644 --- a/tests/output-yaml/coverage-untested.yaml +++ b/tests/output-yaml/coverage-untested.yaml @@ -21,9 +21,8 @@ # } # | to yaml public_api_count: 15 -tested_count: 11 +tested_count: 12 untested: -- list-module-interface - embed-add - embeds-capture-start - embeds-capture-stop diff --git a/tests/test_commands.nu b/tests/test_commands.nu index 86fe679..02d0e18 100644 --- a/tests/test_commands.nu +++ b/tests/test_commands.nu @@ -323,6 +323,36 @@ def "extract-command-code handles quoted command names" [] { assert ($result =~ 'command-3') } +# ============================================================================= +# Tests for list-module-interface +# ============================================================================= + +@test +def "list-module-interface finds main command" [] { + let result = list-module-interface tests/assets/b/example-mod1.nu + + assert equal $result ['main'] +} + +@test +def "list-module-interface returns null when no main" [] { + let result = list-module-interface tests/assets/b/example-mod2.nu + + assert equal $result null +} + +@test +def "list-module-interface strips main prefix from subcommands" [] { + let temp = $nu.temp-dir | path join 'test-module-interface.nu' + "export def main [] {}\nexport def 'main sub1' [] {}\nexport def 'main sub2' [] {}" | save -f $temp + + let result = list-module-interface $temp + + assert ('main' in $result) + assert ('sub1' in $result) + assert ('sub2' in $result) +} + # ============================================================================= # Tests for list-module-exports # =============================================================================