PowerShell completion module combining Tab completion (NativeAOT) + inline predictions (managed DLL) with generic learning and cross-session persistence. See README.md for features, installation, configuration, and usage.
- ArgumentCompleter (
pscue-completer.exe): NativeAOT exe, <10ms startup, computes completions locally with full dynamic arguments support - Module (
PSCue.Module.dll): Long-lived, implementsICommandPredictor+IFeedbackProvider(7.4+), provides PowerShell module functions - Learning System:
- CommandHistory: Ring buffer tracking last 100 commands
- CommandParser: Parses commands into typed arguments (Verb, Flag, Parameter, ParameterValue, Standalone)
- ArgumentGraph: Knowledge graph of command → arguments with frequency + recency scoring
- ArgumentSequences: Tracks consecutive argument pairs for multi-word suggestions (up to 50 per command)
- ParameterStats: Tracks parameters and their known values
- ParameterValuePairs: Tracks bound parameter-value pairs
- ContextAnalyzer: Detects command sequences and workflow patterns
- SequencePredictor: ML-based n-gram prediction for next commands
- WorkflowLearner: Learns command → next command transitions with timing data
- GenericPredictor: Generates context-aware suggestions (values only after parameters, flags otherwise)
- Hybrid CommandPredictor: Blends known completions + generic learning + ML predictions + workflow patterns
- PcdCompletionEngine: Enhanced directory navigation with fuzzy matching, frecency scoring, distance awareness
- Persistence:
- PersistenceManager: SQLite-based cross-session storage
- Tables: commands, arguments, co_occurrences, flag_combinations, argument_sequences, command_history, command_sequences, workflow_transitions, parameters, parameter_values
- Auto-save: Every 5 minutes + on module unload
- Concurrent Access: SQLite WAL mode handles multiple PowerShell sessions safely
- Additive Merging: Frequencies summed, timestamps use max (most recent)
# Build (requires .NET 10.0 SDK — ArgumentCompleter targets net10.0, Module targets net9.0)
dotnet build src/PSCue.Module/ -c Release -f net9.0
dotnet publish src/PSCue.ArgumentCompleter/ -c Release -r win-x64
# Test
dotnet test test/PSCue.ArgumentCompleter.Tests/
dotnet test test/PSCue.Module.Tests/
# Run specific test groups
dotnet test --filter "FullyQualifiedName~Persistence"
dotnet test --filter "FullyQualifiedName~FeedbackProvider"
dotnet test --filter "FullyQualifiedName~CommandPredictor"
dotnet test --filter "FullyQualifiedName~SequencePredictor"
dotnet test --filter "FullyQualifiedName~WorkflowLearner"
# Install locally
./install-local.ps1
# Dev testing (isolated from production install)
./install-local.ps1 -Force -InstallPath D:\temp\PSCue-dev
# Then in a new PowerShell session:
# $env:PSCUE_DATA_DIR = "D:\temp\PSCue-dev\data"
# Import-Module "D:\temp\PSCue-dev\PSCue.psd1"
# Cleanup: Remove-Item -Recurse -Force D:\temp\PSCue-dev- NativeAOT for ArgumentCompleter: <10ms startup required for Tab responsiveness
- Shared logic in PSCue.Shared: NativeAOT exe can't be referenced by Module.dll at runtime
- ArgumentCompleter computes locally: Tab completion always computes locally with full dynamic arguments. Fast enough (<50ms) and simpler.
- NestedModules in manifest: Required for
IModuleAssemblyInitializerto trigger - Concurrent logging:
FileShare.ReadWrite+AutoFlushfor multi-process debug logging - PowerShell module functions: Direct in-process access, no IPC overhead
- ArgumentCompleter startup: <10ms
- Total Tab completion: <50ms
- Module function calls: <5ms
- Database queries: <10ms
- PCD tab completion: <10ms
- PCD best-match navigation: <50ms
- Put shared completion logic in
PSCue.Shared - DynamicArguments (git branches, scoop packages) are computed locally by ArgumentCompleter
- Learning happens automatically via
FeedbackProvider- no manual tracking needed - When adding support for new commands, add the completer registration in
module/PSCue.psm1as well - Command aliases: Use
Aliasproperty onCommandclass, include in tooltip like"Create a new tab (alias: nt)" - Parameter aliases: Use
Aliasproperty onCommandParameterclass, include in tooltip like"Only list directories (-d)"- IMPORTANT: Do NOT create separate parameter entries for short and long forms (e.g.,
-dand--diffas separate parameters) - Instead, define the long form with the short form as an alias:
new("--diff", "Compare files (-d)") { Alias = "-d" } - This prevents duplicate suggestions and keeps the completion list clean
- IMPORTANT: Do NOT create separate parameter entries for short and long forms (e.g.,
// Test module functions directly using PSCueModule static properties
[Fact]
public void TestLearningAccess()
{
var graph = PSCueModule.KnowledgeGraph;
Assert.NotNull(graph);
// Test learning operations
graph.RecordUsage("git", new[] { "status" }, null);
var suggestions = graph.GetSuggestions("git", Array.Empty<string>());
Assert.Contains(suggestions, s => s.Argument == "status");
}- ArgumentCompleter slow: DynamicArguments (git branches, scoop packages) are computed on every Tab press. This is expected and fast (<50ms).
- NativeAOT reference errors: Put shared code in PSCue.Shared, not ArgumentCompleter
- Module functions return null: Module may not be fully initialized. Check PSCueModule.KnowledgeGraph != null before use.
- Corrupted database prevents initialization: Use
Clear-PSCueLearning -Forceto delete database files without requiring module initialization. - Partial word completion: When implementing predictor features, always check if the command line ends with a space. If not, the last word is being completed and suggestions should be filtered by
StartsWith(wordToComplete). - Initialize methods must record baseline: When adding new
Initialize*methods in ArgumentGraph (used by PersistenceManager during load), always record the loaded values in_baselinedictionary. Without baseline tracking, delta calculations will return full counts, causing values to be added again on save, leading to exponential growth and eventual overflow. - PCD exact match not first: When frecency scoring dominates match quality, use a boost multiplier for exact matches. Small match score components (0.1) can be overwhelmed by frequency/recency scores. Solution: Apply 100× boost for exact matches (matchScore >= 1.0) to ensure they always rank first.
- Inconsistent trailing separators: Directory completions must have trailing separators in BOTH tab completion (ArgumentCompleter) and inline predictions (ICommandPredictor). Check both code paths when modifying directory suggestion logic.
- Path normalization requires workingDirectory: When calling
ArgumentGraph.RecordUsage()for navigation commands (cd, sl, chdir), MUST provide theworkingDirectoryparameter. If null/empty, path normalization (including symlink resolution) is skipped. This causes duplicate entries for symlinked paths. Always pass a valid working directory for proper deduplication. - PCD best-match returns 0 suggestions: If
GetSuggestions()returns no matches, check thatGetLearnedDirectories()is requesting enough paths from ArgumentGraph. The default pool size should be 200+ to ensure less-frequently-used directories are searchable. Also verifyCalculateMatchScore()checks BOTH full paths and directory names. - PCD attempts Set-Location on non-existent path: Always loop through ALL suggestions and verify existence before navigation. Never call
Set-Locationon paths that don't exist - show helpful error messages instead. This handles race conditions and stale database entries gracefully. - PCD tab completion behavior: CompletionText must match native cd exactly - use .\ prefix for child directories, ..\ for siblings, and single quotes for spaces. ListItemText should be clean (no prefixes/separators/quotes). See module/Functions/PCD.ps1 for implementation.
- Testing with non-existent paths: Use
skipExistenceCheck: trueparameter inPcdCompletionEngine.GetSuggestions()when testing with mock/non-existent paths. Production code filters non-existent paths by default. - Release builds missing dependencies: The release workflow (.github/workflows/release.yml) MUST use
dotnet publish(notdotnet build) for PSCue.Module to include all dependencies, especially theruntimes/directory with native SQLite libraries.dotnet buildonly outputs primary assemblies, whiledotnet publishcreates a complete deployable package. The remote install script (install-remote.ps1) recursively copies all directories from the release archive to handleruntimes/,Functions/, and any future subdirectories. - install-local.ps1 dependency list: The local install script has a hardcoded
$Dependenciesarray listing DLLs to copy from thepublish/directory. When adding new NuGet packages (e.g., Spectre.Console), you MUST add the DLL to this list or the module will fail at runtime with assembly-not-found errors. Consider replacing this with a bulk copy approach (likeinstall-remote.ps1does) to avoid this pitfall. - PCD interactive selector excludes current dir and
..:PcdInteractiveSelectorexplicitly filters out the current directory and the..parent shortcut. This is intentional — navigating to where you already are is useless, and..is always available viapcd ..directly. When testing, ensure the learned paths are not just the current directory or its parent. - FeedbackProvider uses PowerShell
$PWDfor path normalization: TheFeedbackProvideruses the PowerShell$PWDvariable (notSystem.Environment.CurrentDirectory) to get the current working directory for path normalization. This is important becauseSet-Locationin PowerShell does not update the process CWD. Always usePSCmdlet.SessionState.Path.CurrentLocationor invoke$PWDvia PowerShell when you need the true PowerShell working directory. - Navigation timestamp tracking: For navigation commands (cd, Set-Location, sl, chdir), the FeedbackProvider records the absolute destination path (from context.CurrentLocation after navigation) with trailing separator, not the relative path typed. The
pcdfunction manually records navigations under the 'cd' command since FeedbackProvider only sees the 'pcd' command, not the internal Set-Location calls. Without this, pcd navigations wouldn't update learned directory timestamps.
- Active work:
TODO.md| Completed work:docs/COMPLETED.md - Database functions:
docs/DATABASE-FUNCTIONS.md - Troubleshooting:
docs/TROUBLESHOOTING.md
- When running ./install-local.ps1, always use -Force
- Don't reference phases in code, e.g. "// Phase 20: Parse command to understand parameter-value context" or "// Add multi-word suggestions (Phase 17.4)"