datpkgr, app-agnostic package manager kit
Language-agnostic core for building package managers. Written in Nim.
nimble install datpkgr
datpkgr extracts the reusable package-manager logic from clue into a library. It owns filesystem, database, resolver, git, version discovery and high-level operations. Language specifics are injected via three callbacks so the same core can back Nim (*.nimble), Python (pyproject.toml) or any manifest.
Single LocalDriver rooted at ~/.<appName> via flysystem. No global state. Every operation takes an explicit DatpkgrConfig.
- app and language agnostic package manager
- depth-first search resolver, no PhD required
- single filesystem root, safe by default
- git-first version discovery with local cache
- parallel installs and editable develop mode
- clean installs and automatic prune of orphans
- multiple registries with simple source handling
- hermetic, no network needed for tests
requires "datpkgr >= 0.1.0"nimble install datpkgr
# or develop mode
clue developRequires nim >= 2.2.10, semver, boogie, openparser, sweetsyntax, threading, flysystem.
import datpkgr/config
import datpkgr/types
let cfg = newDatpkgrConfig("myapp")
# optional: silence logs in tests
cfg.callbacks.log = proc(lvl: LogLevel, msg: string) {.gcsafe.} = discardCustom manifest (example: Nimble):
import datpkgr/config
import myapp/nimbleparser # your parser returning Manifest
let cfg = newDatpkgrConfig("clue")
cfg.manifestParser = nimbleManifestParser
cfg.manifestFinder = nimbleManifestFinder
cfg.manifestFileName = proc(pkg: string): string = pkg & ".nimble"
cfg.toolchainName = "nim"
cfg.defaultRegistryUrl = "https://raw.githubusercontent.com/nim-lang/packages/master/packages.json"
cfg.defaultSourceName = "nim-lang"
cfg.legacyRegistryPath = getHomeDir() / ".nimble/packages_official.json"You can also set cfg.callbacks.onFetch to get live progress:
cfg.callbacks.onFetch = proc(name: string, versions: int, cached: bool) {.gcsafe.} =
echo name, " -> ", versions, if cached: " (cached)" else: ""import datpkgr/operations
let ok = cfg.installPackage("semver", "", verbose = true)
if not ok:
echo "install failed"
# with URL, refresh, features, constraint, source filter, build hook
let ok2 = cfg.installPackage("mypkg", "1.2.3", refresh = false,
features = @["ssl"], url = "https://github.com/org/mypkg.git",
constraint = parseConstraint(">= 1.0.0"),
sourceFilter = "nim-lang",
buildHook = proc(pkgName, preferRef, backend: string): bool =
# call your builder here, return true on success
true
)import datpkgr/resolver
import pkg/semver
var registry: PackageRegistry
registry.addPackage("X", parseVersion("1.0.0"), @[])
registry.addPackage("X", parseVersion("2.0.0"), @[])
proc provider(name: string, version: Version, features: seq[string]): seq[Dependency] =
@[]
let res = registry.resolveDetailed(
@[Dependency(name: "X", constraint: parseConstraint(">= 1.0.0"))],
provider
)
echo res.packages # @[X@2.0.0]Constraint syntax: *, "" -> vcAny, 1.2.3/= 1.2.3/==1.2.3 -> vcExact, >=, >, <=, <, ~/~> (tilge, < next minor), ^ (caret, < next major/minor/patch), 0.0.0 sentinel means any.
# editable symlink in ~/.myapp/develop/mypkg -> /path/to/mypkg
discard cfg.developPackage("/path/to/mypkg")
# uninstall with confirm callback (return true to confirm)
proc confirm(msg: string): bool = true
discard cfg.uninstallPackage("mypkg", "1.0.0", confirm)
# prune orphans
cfg.prunePackages()
# versions available
let vers = cfg.versionsFor("semver")
for v in vers: echo v.versionimport datpkgr/git
echo toGitSshUrl("https://github.com/org/repo") # git@github.com:org/repo.git
let env = gitEnv(nonInteractive = true)
let ok = cfg.clonePackage("https://github.com/org/repo.git", "/tmp/dest")
discard cfg.checkoutTag("/tmp/dest", "v1.0.0")| Module | Purpose |
|---|---|
types |
Source, Package, PkgRef, PkgDependency, Manifest, DepEntry, ManifestParser/Finder, re-exports resolver |
config |
DatpkgrConfig, Callbacks, newDatpkgrConfig, manifestNameForPkg/findManifest*/parseManifest, dbPath/pkgsPath etc., isInsidePkgs/safeRemove*, log* |
store |
loadSources/saveSources, seedPackagesTable, initDatpkgr, fetchPkgMeta, withDatpkgrDB, migrations |
git |
toGitSshUrl, gitEnv/gitExec, clonePackage, checkout*, gitHeadInfo, failedClones |
versions |
tagForVersion, installCleanCopy, discoverVersions/discoverVersionsBatch, headVersion, getDeps, readManifestContent |
install |
recordInstall, resolveInstalledPath, isDevInstall, collectInstalledDepNames, pruneOrphans, allInstalledPaths |
resolver |
parseConstraint, satisfies, addPackage, resolve/resolveDetailed |
operations |
installPackage, updatePackage/updateAllPackages, developPackage, uninstallPackage, versionsFor, pure helpers |
src/datpkgr.nim re-exports all eight.
CLI apps that integrate with datpkgr:
- clue • an alternative package manager for Nim development
- tim engine • a DSL with built-in template engine, source-to-source transpialtion and more
- bro stylesheets • An alternative to Dart SASS/SCSS fully written in Nim
- dfkup • A scripting language made in Nim
MIT. Made by Humans from OpenPeeps. Copyright OpenPeeps and Contributors, All rights reserved.
- Found a bug? Create a new Issue
- Want to help? Fork it!
![]() |
Switch to Open-Source LLMs via OpenCode GO, choosing from a variety of powerful models such as DeepSeek, Qwen, Kimi, GLM-5, MiniMax, MiMo. Use our referral link to get started! |
