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
22 changes: 16 additions & 6 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<!--
- Version: 1.7.0 -> 1.8.0
- Modified Principles: None
- Version: 1.8.0 -> 1.9.0
- Modified Principles: Development Workflow (Clarified Commit Policy)
- Added Principles:
- Reuse First Architecture
- Agile Dependency Management
- Removed Principles: None
- Templates Requiring Updates: .specify/templates/plan-template.md (Checklist)
- Templates Requiring Updates: None
- Follow-up TODOs: None
-->

Expand Down Expand Up @@ -47,7 +47,14 @@ Code must be written to be readable and accessible, avoiding pitfalls like `null
- **Expressive Logic**: Prefer properly named functions and pattern matching (`match`) or monadic chains (`map`, `flatMap`) over excessive nesting of `if/else` blocks.
- **No Var**: Do not use `var`. Design algorithms so intermediate invalid or inconsistent values are not possible. Avoid `java.util.concurrent.atomic` unless absolutely necessary.

### VIII. Reuse First Architecture
### VIII. Agile Dependency Management

Dependencies should be selected with care but kept fresh. We prefer a "Selective but Fresh" approach:

- **Selective**: Only add dependencies when they provide significant value over the standard library or simple custom implementations.
- **Fresh**: When a dependency is accepted, it must be kept up-to-date with its latest stable version to ensure security, performance, and access to the latest features.

### IX. Reuse First Architecture

Before implementing new features, developers MUST analyze existing code for reusable components. If a new feature involves operations similar to existing ones (e.g., file persistence, configuration loading), existing stable implementations MUST be inspected for reuse. Common logic SHOULD be extracted into shared core components to avoid duplication and leverage proven stability.

Expand All @@ -59,6 +66,9 @@ Before implementing new features, developers MUST analyze existing code for reus
## Development Workflow

- **Atomic Features**: Each feature should be developed on a dedicated branch and result in a single, comprehensive commit upon completion.
- **Clean Git History**:
- **Active Development**: During the implementation of a feature (before merge), developers should amend the existing feature commit rather than creating new "fix" commits. This ensures that the final feature commit is a clean, self-contained unit of work.
- **Post-Merge**: "Fix" commits are reserved for addressing regressions or bugs discovered after a feature has been merged or released.
- **Linear History**: To maintain a clean and readable revision history, feature branches MUST be rebased onto the main branch before merging. Merge commits are to be avoided in favor of fast-forward merges.
- **Roadmap Tracking**: After a feature branch is merged, the `SPEC-ROADMAP.md` file must be updated to mark the corresponding feature as complete.
- **Specification Gating**: Before implementation can begin, the feature's `requirements.md` checklist MUST be reviewed and marked as complete. This serves as the formal quality gate after the clarification step.
Expand All @@ -74,4 +84,4 @@ This constitution is the supreme governing document for the `first` project. All

All pull requests and code reviews must include a check for compliance with this constitution. Any deviation from these principles must be explicitly justified and approved.

**Version**: 1.8.0 | **Ratified**: 2025-10-30 | **Last Amended**: 2025-12-09
**Version**: 1.9.0 | **Ratified**: 2025-10-30 | **Last Amended**: 2025-12-31
1 change: 1 addition & 0 deletions GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ This memories should be immediatelly available to Gemini:
- Scala 3.3.4 (Scala Native 0.5.9) + decline (CLI), os-lib (FS), upickle (JSON), sttp (HTTP) (005-coursier-install)
- Scala 3.3.4 + `com.monovore::decline` (CLI), `com.lihaoyi::os-lib` (File Ops), `org.ekrich::sconfig` (HOCON) (008-update-fctx)
- Filesystem (HOCON config files) (008-update-fctx)
- Scala 3.3.4 (Scala Native 0.5.x) (009-fctx-migration)

## Recent Changes
- 001-fctx-management-actions: Added Scala 3.3.4 + decline, sconfig, munit, scribe
Expand Down
9 changes: 5 additions & 4 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,11 @@ diverge a bit as we progress but just to have a main idea):
6. [x] We can swap between fctxs using the swap cli action
7. [x] We can use remote artifacts and contexts (http/https/gh)
8. [x] We can install the tool easily with `curl -fsSL https://raw.githubusercontent.com/oswaldo/first/main/install.sh | sh`
9. We can migrate and link contexts using `save --to` and `save --link`, and move them with `mv`
10. We can dogfood the tool for its own development
11. We can safely manage and remove fctxs using the new cleanup actions (`rm-def`, `rm-files`, `rm-all`).
12. We can perform all supported cli actions using a terminal interactive screen / mode
9. [x] We can update existing fctx definitions using the update cli action
10. We can migrate and link contexts using `save --to` and `save --link`, and move them with `mv`
11. We can dogfood the tool for its own development
12. We can safely manage and remove fctxs using the new cleanup actions (`rm-def`, `rm-files`, `rm-all`).
13. We can perform all supported cli actions using a terminal interactive screen / mode

## Future Ideas

Expand Down
2 changes: 1 addition & 1 deletion SPEC-ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ by running the specified command for each requirement.
/speckit.specify Enable installation via coursier so users can run 'cs install first'
```

6. [ ] **Context Update Operations**
6. [x] **Context Update Operations**
- **Description**: Implement an `update` command to modify existing fctx definitions without recreating them. This should support:
- `first update [context-name]` - Update the current (or specified) context with changes to tracked artifacts
- `first update [context-name] --add "new-file.txt,another-dir/"` - Add new artifacts to the context
Expand Down
34 changes: 34 additions & 0 deletions specs/009-fctx-migration/checklists/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Specification Quality Checklist: Context Migration & Linking

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2025-12-11
**Feature**: [Link to spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Notes

- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`
32 changes: 32 additions & 0 deletions specs/009-fctx-migration/contracts/cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# CLI Contracts

## Save Command

### `save`

Extended arguments:

- `--to <path>` (Optional): Specifies the target directory for the context definition. If provided, the context is saved to this directory instead of `.then/<name>`.
- `--link` (Flag): If set, after saving, the source files in the workspace are replaced with symbolic links pointing to the saved artifacts.

```bash
first save <context-name> --files <list> [--to <path>] [--link]
```

## Move Command

### `mv`

Moves a context definition to a new location.

- Argument 1: `context-name` (Required) - Name of the context to move.
- Argument 2: `new-path` (Required) - Destination directory path.

```bash
first mv <context-name> <new-path>
```

**Constraints**:

- `new-path` must not exist.
- `context-name` must be a known context.
33 changes: 33 additions & 0 deletions specs/009-fctx-migration/data-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Data Model

## Global Configuration (`~/.first/first.conf`)

The global configuration file acts as a central registry for known context definitions. This enables the tool to locate contexts that are not in standard project locations (e.g., when using `save --to` or `mv`).

```hocon
// List of absolute paths to known fctx definition files (fctx-def.conf)
fctx-files: [
"/home/user/.first/default/fctx-def.conf",
"/home/user/projects/my-context-repo/fctx-def.conf"
]

// [Existing field, may remain]
last-loaded {
name: "default",
at: "/home/user/projects/current"
}
```

## Context Definition (`fctx-def.conf`)

No changes to the data model itself, but `swapAs` field will be actively used and validated.

```hocon
artifacts: [
{
path: "config/app.conf"
swapAs: "symlink" // or "copy"
md5: "..."
}
]
```
155 changes: 155 additions & 0 deletions specs/009-fctx-migration/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Implementation Plan: Context Migration & Linking

**Branch**: `009-fctx-migration` | **Date**: 2025-12-11 | **Spec**: [Spec](spec.md)
**Input**: Feature specification from `specs/009-fctx-migration/spec.md`

## Summary

Implement `save --to <path>`, `save --link`, and `mv` commands to enable context migration and "dogfooding".
This requires introducing a `GlobalConfig` registry (`~/.first/first.conf`) to track context locations, enabling `first` to find contexts stored outside standard locations.
We will also implement symlink handling for `--link` and `mv`, ensuring workspace integrity.

## Technical Context

**Language/Version**: Scala 3.3.4 (Scala Native 0.5.x)
**Primary Dependencies**:

- `com.lihaoyi::os-lib`: File system operations (move, symlink, copy).
- `org.ekrich::sconfig`: HOCON configuration reading/writing.
- `com.monovore::decline`: CLI argument parsing.
**Storage**:
- Local Filesystem: `fctx-def.conf` (context definitions), `~/.first/first.conf` (global registry).
**Testing**: `munit` for unit/integration tests.
**Target Platform**: Linux/macOS (primary for symlinks), Windows (fallback behavior).
**Project Type**: CLI Tool.
**Constraints**:
- **Symlinks**: `os.symlink` behavior on Windows is restrictive. We will implement checks to warn/fallback on Windows.
- **Atomic Operations**: `mv` and `save` should be as atomic as possible.

## Constitution Check

- [x] **Non-Intrusive Tooling**: `first.conf` is in `~/.first`, not the project. `save --link` replaces files with symlinks, which is intrusive but explicit user intent.
- [x] **Full Context Swapping**: Enhances swapping by allowing contexts to live anywhere.
- [x] **Separation of Concerns**: Decouples context storage from project location.
- [x] **Developer as Author**: User explicitly controls locations (`--to`) and linking (`--link`).
- [x] **Vision-Driven**: Aligns with "Context Migration" and "Dogfooding" roadmap items.
- [x] **Type Safety**: Will use `Path` and specific types for config.
- [x] **Safe and Expressive**: specific error handling for IO.
- [x] **Reuse First**: Reusing `ConfigReader`, `ArtifactProcessor`.

## Project Structure

### Documentation

```text
specs/009-fctx-migration/
├── plan.md # This file
├── research.md # N/A (Research done inline)
├── data-model.md # GlobalConfig model
├── quickstart.md # Usage examples
└── tasks.md # Task breakdown
```

### Source Code

```text
src/main/scala/first/
├── config/
│ ├── GlobalConfig.scala # [NEW] Handles ~/.first/first.conf
│ └── ConfigWriter.scala # [NEW] Helper for writing structured config (extracted from FctxWriter?)
├── core/
│ ├── Mv.scala # [NEW] Core logic for mv
│ └── SymlinkManager.scala # [NEW] Helper for safely managing symlinks
├── cli/
│ ├── MvCommand.scala # [NEW] CLI entry point
│ └── SaveCommand.scala # [MODIFY] Add --to and --link args
└── ...
```

## Proposed Changes

### 1. Global Configuration Registry (`GlobalConfig.scala`)

Use `~/.first/first.conf` to track contexts.

```hocon
fctx-files: [
"/abs/path/to/ctx1/fctx-def.conf",
"/other/path/ctx2/fctx-def.conf"
]
```

- **[NEW] `src/main/scala/first/config/GlobalConfig.scala`**:
- `addContext(path: Path): Either[Error, Unit]`
- `removeContext(path: Path): Either[Error, Unit]`
- `updateContext(oldPath: Path, newPath: Path): Either[Error, Unit]`
- `listContextPaths(): List[Path]`
- **[MODIFY] `src/main/scala/first/config/ConfigReader.scala`**:
- Update `discoverFctxDefPaths` to include paths from `GlobalConfig`.

### 2. Save Command Improvements (`save --to`, `save --link`)

- **[MODIFY] `src/main/scala/first/cli/SaveCommand.scala`**:
- Add `to: Option[Path]` and `link: Boolean = false` to `SaveOpts`.
- **[MODIFY] `src/main/scala/first/core/Save.scala`**:
- Logic for `--to`: Use provided path as `fctxConfDir`. Register with `GlobalConfig`.
- Logic for `--link`: After processing artifacts, replace source files with symlinks to `artifactsDir`.
- Use `SymlinkManager` helper.

### 3. Move Command (`mv`)

- **[NEW] `src/main/scala/first/cli/MvCommand.scala`**:
- `first mv <name> <new-path>`
- **[NEW] `src/main/scala/first/core/Mv.scala`**:
- Resolve current context path.
- Check destination.
- `os.move`.
- Update `GlobalConfig`.
- Scan CWD for symlinks pointing to old path and update them to new path.

### 4. Symlink Handling (`SymlinkManager`)

- **[NEW] `src/main/scala/first/core/SymlinkManager.scala`**:
- `createSymlink(source: Path, dest: Path): Either[Error, Unit]`
- Checks OS (warn if Windows).
- Handles relative vs absolute linking constraints.

### 5. Update Command Improvements (`update --link`)

- **[MODIFY] `src/main/scala/first/cli/UpdateCommand.scala`**:
- Add `link: Boolean = false`.
- **[MODIFY] `src/main/scala/first/core/Update.scala`**:
- When processing added artifacts (`newProcessed`), if `link` is true:
- Invoke `SymlinkManager.createSymlink(source, dest)` similar to `Save`.

## Verification Plan

### Automated Tests

- **Unit Tests**:
- `GlobalConfigTests`: Verify reading/writing `first.conf`.
- `SymlinkManagerTests`: Verify symlink creation (skip on Windows/CI if needed, or use mocks).
- `MvTests`: Verify logic (mocking file sys or using temp folders).
- `SaveTests`: Test `--to` and `--link` logic.

### Manual Verification

1. **Save to Custom Location**:
- Run `first save test-ctx --to /tmp/test-ctx --files "foo.txt"`.
- Verify `/tmp/test-ctx/fctx-def.conf` exists.
- Verify `first ls` shows `test-ctx`.

2. **Save and Link**:
- Run `first save link-ctx --files "bar.txt" --link`.
- Verify `bar.txt` is now a symlink pointing to `.then/link-ctx/artifacts/bar.txt`.

3. **Move Context**:
- Run `first mv link-ctx /tmp/moved-ctx`.
- Verify `.then/link-ctx` is gone.
- Verify `/tmp/moved-ctx` exists.
- Verify `bar.txt` symlink now points to `/tmp/moved-ctx/artifacts/bar.txt`.
- Verify `first load link-ctx` still works.

4. **Edge Cases**:
- Try moving to existing path (should fail).
- Try moving unknown context (should fail).
31 changes: 31 additions & 0 deletions specs/009-fctx-migration/quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Quickstart: Context Migration & Linking

## Saving to a Custom Location

Save a context to a specific directory (e.g., for sharing via git):

```bash
first save my-shared-ctx --to ~/git/shared-contexts --files "style.conf,rules.xml"
```

## Linking Local Files

Move local files to the context and replace them with symlinks:

```bash
first save local-dev --files "local.properties" --link
```

## Moving a Context

Move an existing context to a new location:

```bash
first mv my-shared-ctx ~/new-location/my-shared-ctx
```

The tool will automatically:

1. Move the context definition.
2. Update the global registry.
3. Update any active symlinks in your current workspace to point to the new location.
Loading