Skip to content

feat: build knot CLI + Knotfile ecosystem#2

Merged
oxGrad merged 3 commits intomainfrom
claude/build-knot-cli-fgywP
Mar 31, 2026
Merged

feat: build knot CLI + Knotfile ecosystem#2
oxGrad merged 3 commits intomainfrom
claude/build-knot-cli-fgywP

Conversation

@oxGrad
Copy link
Copy Markdown
Owner

@oxGrad oxGrad commented Mar 31, 2026

Summary

  • Full CLI implementationtie, untie, status, plan, and validate commands backed by a Plan/Apply symlink engine with dry-run support, conflict detection, OS conditions, and ignore patterns
  • Knotfile — config file renamed from knot.yml to Knotfile (no extension, YAML syntax, upward auto-discovery)
  • Editor tooling ecosystem — JSON Schema, Neovim plugin (with 🪢 devicon), and YAML language server integration
  • CI/CD pipeline — GitHub Actions for PR checks, GoReleaser for semver releases, Homebrew distribution, and Dependabot

Changes

CLI (cmd/, internal/)

  • knot tie [pkg] --all — create symlinks; --dry-run previews without writing
  • knot untie [pkg] — remove symlinks
  • knot status — show per-file state ([OK] / [MISSING] / [CONFLICT] / [BROKEN])
  • knot plan [pkg] --all — dry-run with +/=/! summary and counts
  • knot validate — validates Knotfile structure with exit codes 0 (ok) / 1 (errors) / 2 (warnings); checks required fields, source dir existence, condition.os enum, and glob pattern syntax

Knotfile rename

knot.ymlKnotfile across FindConfigFile(), error messages, flag help text, and all test fixtures.

JSON Schema (schema/knotfile.schema.json)

Draft-07 schema with full field descriptions, enum on condition.os, additionalProperties: false, and a $id pointing to the raw GitHub URL for universal tool integration.

Neovim plugin (editors/neovim/)

File Purpose
ftdetect/knotfile.vim Exact filename match → filetype=knotfile
syntax/knotfile.vim YAML base + colored groups for all Knotfile keywords and OS values
after/ftplugin/knotfile.vim tabstop=2, commentstring=# %s
lua/knot/init.lua setup(): filetype registration, 🪢 devicon, treesitter yaml override, auto yamlls schema push
README.md lazy.nvim (with main = "knot"), packer.nvim, and manual install guides

YAML LS integration (editors/yaml-language-server/)

  • settings.json — VS Code workspace snippet
  • README.md — 4 integration methods: inline modeline, nvim-lspconfig, VS Code, global settings file

CI/CD (.github/, .goreleaser.yaml)

  • .github/workflows/ci.yml — three required status checks on every PR to main: Test (go test ./...), Build (go build ./...), Lint (golangci-lint)
  • .github/workflows/release.yml — triggered on v*.*.* semver tags; runs GoReleaser to build binaries, create a GitHub Release, and push a Homebrew formula to oxGrad/homebrew-tap
  • .github/dependabot.yml — weekly automated PRs for gomod and github-actions dependencies
  • .goreleaser.yaml — builds for linux/darwin/windows × amd64/arm64 (excluding windows/arm64), tar.gz/zip archives, checksums.txt, and knot.rb formula pushed to oxGrad/homebrew-tap

README (README.md)

Root readme with installation leading with Homebrew, full CLI reference, Knotfile format table, editor integration pointers, and release workflow docs.

Test plan

  • go build ./... compiles cleanly
  • go test ./... — 30 tests pass across config, resolver, and linker packages
  • knot --help shows all 6 commands with correct descriptions
  • knot validate --help shows exit code documentation
  • Enable branch protection on main: require status checks Test, Build, Lint
  • Add HOMEBREW_TAP_GITHUB_TOKEN repository secret (PAT with contents: write on oxGrad/homebrew-tap)
  • Create oxGrad/homebrew-tap repository if it doesn't exist yet
  • Push a v0.1.0 tag and verify GoReleaser creates the GitHub Release and Homebrew formula
  • brew install oxGrad/tap/knotknot --help works

claude added 3 commits March 31, 2026 14:15
Core change:
- Config file renamed from knot.yml to Knotfile (no extension, exact name,
  YAML syntax); FindConfigFile() and all error messages updated accordingly
- All test fixtures updated to use "Knotfile"

New: knot validate command (cmd/validate.go)
- Checks required fields (source, target) per package
- Verifies source directories exist on disk
- Validates condition.os against known values (darwin/linux/windows/freebsd)
- Validates ignore glob patterns via filepath.Match
- Exit codes: 0 = valid, 1 = errors, 2 = warnings only
- Sorted, deterministic output for all packages

New: JSON Schema (schema/knotfile.schema.json)
- Draft-07 schema with full descriptions for all fields
- Enum constraint on condition.os
- additionalProperties: false on all objects
- $id pointing to raw.githubusercontent.com for tool integration

New: Neovim plugin (editors/neovim/)
- ftdetect/knotfile.vim: filetype detection for files named exactly "Knotfile"
- syntax/knotfile.vim: YAML base + highlighted groups for packages/source/
  target/ignore/condition/os and known OS values
- after/ftplugin/knotfile.vim: tabstop=2, commentstring=# %s
- lua/knot/init.lua: setup(), treesitter yaml override, auto yamlls schema
  config via workspace/didChangeConfiguration
- README.md: lazy.nvim, packer.nvim, and manual install instructions

New: YAML LS integration (editors/yaml-language-server/)
- settings.json: VS Code workspace settings snippet
- README.md: 4 integration methods (modeline, nvim-lspconfig, VS Code,
  global settings)

https://claude.ai/code/session_018s5iF8MKZAobUjAuR1AHJ4
…type

Adds M._register_devicon() which calls devicons.set_icon() with the rope
knot emoji (🪢) and a soft violet color (#a78bfa). The call is guarded by
pcall so it is a no-op when nvim-web-devicons is not installed. setup()
calls _register_devicon() as step 2, before the treesitter and LSP setup.

https://claude.ai/code/session_018s5iF8MKZAobUjAuR1AHJ4
lazy.nvim: add `main = "knot"` so lazy.nvim calls require("knot").setup()
instead of require("neovim").setup() (derived from the dir's last component).
Also add `name = "knot.nvim"` to register the plugin under a stable name.

packer.nvim: the path must be the first positional element (not a `dir =`
key, which is lazy.nvim-only syntax), and wrapped in vim.fn.expand() since
packer does not expand ~ itself. ft changed to a list { "knotfile" } which
is the canonical packer form.

https://claude.ai/code/session_018s5iF8MKZAobUjAuR1AHJ4
@oxGrad oxGrad merged commit ffe2813 into main Mar 31, 2026
@oxGrad oxGrad deleted the claude/build-knot-cli-fgywP branch March 31, 2026 14:47
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.

2 participants