Skip to content

Config and command refactor#6

Open
cpplain wants to merge 140 commits intomainfrom
config-command-refactor
Open

Config and command refactor#6
cpplain wants to merge 140 commits intomainfrom
config-command-refactor

Conversation

@cpplain
Copy link
Owner

@cpplain cpplain commented Feb 22, 2026

No description provided.

Add Lorah AI pair programming setup to manage the refactoring of lnk to a
stow-like flag-based interface. Includes specification, task tracking,
progress logs, and session prompts for initialization and implementation
phases.
Create comprehensive task breakdown for stow-like CLI refactor with 24
testable requirements across 4 implementation phases:

- Phase 1: Config file support (.lnkconfig, .lnkignore)
- Phase 2: Options-based API (package-centric linking)
- Phase 3: CLI rewrite (flag-based interface)
- Phase 4: Internal function updates

Document initial codebase inventory and refactoring plan summary.
Implement Phase 1 of CLI refactor - stow-like config file format:
- Add .lnkconfig file discovery and parsing (CLI flags format)
- Add .lnkignore file parsing (gitignore syntax)
- Implement config discovery precedence chain
- Add FlagConfig struct for new config format

Discovery order: .lnkconfig in source dir → XDG config → ~/.lnkconfig

Tasks completed: LoadConfig for .lnkconfig format, Parse .lnkignore file
Add MergedConfig struct and MergeFlagConfig() function to merge
configuration from multiple sources with proper precedence:
- Target: CLI flag > .lnkconfig > default (~)
- Ignore patterns: Combined from all sources (built-in, config,
  .lnkignore, CLI) to allow maximum flexibility

Extract getBuiltInIgnorePatterns() for reusability and add
.lnkconfig and .lnkignore to built-in ignore patterns.

Add comprehensive tests covering all merging scenarios including
precedence verification, subdirectories, and combined sources.

Completes Phase 1 (Config file support) of the CLI refactor.
Add LinkOptions struct to support the new package-based linking interface.
This struct replaces the config-based API with fields for SourceDir,
TargetDir, Packages, IgnorePatterns, and DryRun.

Part of Phase 2 of the CLI refactor to support stow-like usage.
Add CreateLinksWithOptions function that handles package-based linking using
LinkOptions struct. Includes helper collectPlannedLinksWithPatterns that uses
ignore patterns directly instead of Config objects.

Features:
- Supports multiple packages in single operation
- Handles package "." for flat repository structure
- Handles nested package paths (e.g., "private/home")
- Skips non-existent packages gracefully
- Follows same 3-phase execution as CreateLinks

Includes comprehensive unit tests covering single/multiple packages,
ignore patterns, dry-run mode, and error cases.
Add RemoveLinksWithOptions function that removes symlinks for specific
packages, supporting the new stow-like interface. Includes helper function
findManagedLinksForPackages for filtering managed links by package source.

- Add findManagedLinksForPackages to find symlinks pointing to specific packages
- Implement RemoveLinksWithOptions with package-based filtering
- Support multiple packages, dry-run mode, and "." for flat repos
- Add createTestSymlink helper for test utilities
- Add comprehensive unit tests (8 test cases)
Add StatusWithOptions function that displays symlink status for specific
packages instead of using config-based mappings. Reuses existing display
logic and findManagedLinksForPackages helper for consistency.

Features:
- Shows status for only specified packages
- Supports multiple packages in single operation
- Separates active and broken links
- Supports JSON output format
- Comprehensive unit tests with 8 test cases
Add PruneWithOptions function to remove broken symlinks using the new
package-based API. Unlike the old PruneLinks function, this supports:
- Optional packages (defaults to "." if none specified)
- Multiple package targeting in a single operation
- DryRun flag without confirmation prompts (consistent with other *WithOptions functions)

Includes comprehensive unit tests covering edge cases like partial
pruning, mixed active/broken links, and graceful handling of no
broken links.
Update collectPlannedLinks to accept ignorePatterns directly instead of
requiring Config and LinkMapping objects. This simplifies the function
signature and aligns it with the new package-based API pattern used by
collectPlannedLinksWithPatterns.

Changes:
- Update collectPlannedLinks signature to take ignorePatterns []string
- Replace shouldIgnoreEntry call with direct MatchesPattern usage
- Remove unused shouldIgnoreEntry helper function
- Update CreateLinks call site to pass config.IgnorePatterns

This refactoring maintains backward compatibility with the old
config-based CreateLinks API while preparing for the new flag-based CLI.
…sed link discovery

Add FindManagedLinksForSources function that finds symlinks pointing to
specified source directories without requiring a Config object. This
completes the package-based API for Phase 2 of the CLI refactor.

The function walks a target directory, identifies symlinks that point to
any of the provided source paths, and returns ManagedLink structs with
broken link detection.

Added comprehensive unit tests covering:
- Single and multiple source directories
- Broken link detection
- System directory skipping
- Relative and nested package paths
- Empty sources handling
Replace subcommand-based routing (lnk create, lnk status) with
stow-like flag-based interface (lnk -C, lnk -S, lnk home).

Action flags (mutually exclusive):
- -C/--create (default), -R/--remove, -S/--status, -P/--prune
- -A/--adopt, -O/--orphan (placeholders for Phase 4)

Directory flags:
- -s/--source DIR (default: .)
- -t/--target DIR (default: ~)

Other flags:
- --ignore PATTERN (repeatable)
- -n/--dry-run, -v/--verbose, -q/--quiet
- --no-color, -V/--version, -h/--help

Packages are now positional arguments (at least one required
for link operations). Examples:
- lnk . (flat repo)
- lnk home (nested repo)
- lnk home private/home (multiple packages)

Integrates with .lnkconfig and .lnkignore config files via
MergeFlagConfig(). All *WithOptions functions now wired up
to CLI action dispatch.

BREAKING CHANGE: Removes subcommand interface. Old commands
like 'lnk create' must now use 'lnk home' or 'lnk -C home'.
Config files are now optional (CLI flags sufficient).
Add new package-based adopt functionality for the flag-based CLI interface:
- AdoptOptions struct for passing adopt parameters
- AdoptWithOptions function supporting multiple file adoption
- CLI integration with -A/--adopt flag + package + file paths
- Comprehensive unit tests (9 test cases)

Usage: lnk -A <package> <file1> [file2...] [-s source] [-t target] [-n]

Example: lnk -A home ~/.bashrc ~/.vimrc

The new interface eliminates the need for config files and makes adoption
more intuitive by explicitly specifying the package destination and files
to adopt.
Add new options-based API for orphaning files from repository management,
supporting the new flag-based CLI interface. This completes Phase 4 of
the CLI refactor.

Changes:
- Add OrphanOptions struct with SourceDir, TargetDir, Paths, and DryRun
- Implement OrphanWithOptions() function supporting multiple paths
- Support orphaning directories (finds all managed links within)
- Graceful error handling (continues on failures)
- Add comprehensive unit tests (9 test cases)
- Update CLI to use OrphanWithOptions for --orphan flag
- Update help text to remove "not yet implemented" note
Updated all e2e tests to use the new flag-based CLI syntax:
- Changed from subcommands (lnk create) to action flags (lnk -C home)
- Updated test assertions to match new command syntax and output
- Adapted tests for macOS sandbox constraints that block dotfile creation
- Fixed setupTestEnv to skip re-creating existing test files

All e2e tests now pass with the new CLI interface. Tests verify:
- Action flags: -C/--create, -R/--remove, -S/--status, -P/--prune, -A/--adopt, -O/--orphan
- Directory flags: -s/--source, -t/--target
- Package arguments and multiple package support
- Error handling and edge cases

Files modified:
- e2e/e2e_test.go - Core command tests
- e2e/workflows_test.go - Workflow and integration tests
- e2e/helpers_test.go - Test setup helper
- .lorah/tasks.json - Mark Task 23 as complete
- .lorah/progress.md - Document session progress
Ran all 12 verification examples from spec.md to validate the new flag-based
CLI interface. All examples work correctly:

- Flat repo linking (lnk .)
- Nested package operations (lnk home, lnk home private/home)
- Explicit source/target flags
- All action flags (-C, -R, -S, -P, -A, -O)
- Config file integration (.lnkconfig and .lnkignore)
- Adopt and orphan operations

All 24 tasks are now complete:
- Phase 1: Config file support (3 tasks)
- Phase 2: Options-based API (7 tasks)
- Phase 3: CLI rewrite (6 tasks)
- Phase 4: Internal function updates (3 tasks)
- Testing (5 tasks)

Build status: All unit tests pass, all e2e tests pass, binary builds successfully.
Reset progress tracking and update spec/tasks for the next phase of
work focused on removing legacy code retained from the CLI
refactoring.
Create comprehensive task list and progress notes for removing ~900 lines
of legacy code from the JSON config-based CLI. Tasks cover 6 phases:
removing legacy functions, types, and constants; simplifying naming by
dropping WithOptions suffixes; cleaning up tests; and updating documentation.

All tasks marked as pending, ready for systematic cleanup execution.
Remove 9 legacy functions from config.go:
- getDefaultConfig(), LoadConfig(), loadConfigFromFile()
- LoadConfigWithOptions(), Config.Save(), Config.GetMapping()
- Config.ShouldIgnore(), Config.Validate(), DetermineSourceMapping()

Remove ensureSourceDirExists() from adopt.go (dead code).

Also remove unused encoding/json import from config.go.

These legacy functions used the old JSON config system with LinkMappings.
The new flag-based system (MergeFlagConfig, LoadFlagConfig) remains.
Test failures in config_test.go are expected and will be cleaned up in
a later task.
Removed 4 legacy functions from linker.go (~200 lines):
- CreateLinks(config *Config, dryRun bool)
- RemoveLinks(config *Config, dryRun bool, force bool)
- removeLinks(config *Config, dryRun bool, skipConfirm bool)
- PruneLinks(config *Config, dryRun bool, force bool)

These functions used the old Config struct and are replaced by
CreateLinksWithOptions, RemoveLinksWithOptions, and PruneWithOptions.
Removed the legacy Status(config *Config) function from status.go as part of the cleanup effort. StatusWithOptions remains and will be renamed to Status in Phase 0.

This removes 93 lines of legacy code. Only test files reference the removed function, which will be cleaned up in Task 25.
Remove Adopt(source string, config *Config, sourceDir string, dryRun bool)
from adopt.go (98 lines). This function used the old JSON config system
with LinkMappings.

The new AdoptWithOptions() function remains and is used by the flag-based
CLI interface. Test failures in adopt_test.go are expected and will be
cleaned up in Task 26.
Removed Orphan(link string, config *Config, dryRun bool, force bool) from
orphan.go (121 lines). Production code uses OrphanWithOptions instead.
Remove ErrNoLinkMappings error constant from errors.go as it was only used
by the old JSON-based config system with LinkMappings. The new flag-based
config system does not need this validation.
Removed ConfigFileName = ".lnk.json" constant from constants.go. This
constant was part of the old JSON-based config system and is no longer
used. The new flag-based config system uses FlagConfigFileName instead.

Phase 1 (Remove Legacy Types and Constants) is now complete.
Removed 4 legacy test functions that used the old Config type with LinkMappings parameter pattern:
- TestCreateLinks
- TestRemoveLinks
- TestPruneLinks
- TestLinkerEdgeCases

Kept 3 tests for the new LinkOptions-based API:
- TestCreateLinksWithOptions
- TestRemoveLinksWithOptions
- TestPruneWithOptions

File reduced from 1659 lines to 767 lines (892 lines removed).
cpplain added 30 commits March 16, 2026 11:49
Step 1 (os.Stat) failure after prior links are already orphaned would
skip the rollback path, leaving partial disk state and violating the
atomicity guarantee.
…sclassifying permission-denied symlinks as broken
…ion error

The Section 8 example used "permission denied" paired with the adopt hint,
but Section 6 scopes that hint exclusively to regular file/directory collisions.
Update AGENTS.md, README.md, CHANGELOG.md, and orphan design doc to
reflect the new subcommand-based CLI (replacing action flags), required
positional source-dir argument, removal of --target/--quiet/--source
flags, and removal of .lnkconfig support.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant