From df44585df555d95e45b3b2ebda27fe82cf65482c Mon Sep 17 00:00:00 2001 From: Ewan Croft Date: Tue, 11 Aug 2026 22:47:20 +0100 Subject: [PATCH] feat(croft-click-core): add shared ~/.ewanc26 state dir helper Write-capable pkgs tools each kept their own dotfile directly under the home directory (~/.malachite, ~/.jasper, ...). Add getToolStateDir() under a new Node-only ./paths subpath export (kept out of the browser-safe index.ts) that resolves ~/.ewanc26/ and auto-migrates a tool's legacy directory into place on first use, so credentials/logs/state only need to be found, backed up, or cleaned up in one place. --- packages/croft-click-core/package.json | 5 +++ packages/croft-click-core/src/paths.ts | 48 ++++++++++++++++++++++++++ pnpm-lock.yaml | 3 ++ 3 files changed, 56 insertions(+) create mode 100644 packages/croft-click-core/src/paths.ts diff --git a/packages/croft-click-core/package.json b/packages/croft-click-core/package.json index f692bda..eda04cd 100644 --- a/packages/croft-click-core/package.json +++ b/packages/croft-click-core/package.json @@ -24,6 +24,10 @@ ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" + }, + "./paths": { + "types": "./dist/paths.d.ts", + "import": "./dist/paths.js" } }, "main": "./dist/index.js", @@ -43,6 +47,7 @@ "multiformats": "^13.3.1" }, "devDependencies": { + "@types/node": "^25.5.0", "typescript": "^5.9.3" } } diff --git a/packages/croft-click-core/src/paths.ts b/packages/croft-click-core/src/paths.ts new file mode 100644 index 0000000..3023a51 --- /dev/null +++ b/packages/croft-click-core/src/paths.ts @@ -0,0 +1,48 @@ +/** + * Shared local save-directory resolution for pkgs' write-capable CLI tools. + * + * Node-only — deliberately not re-exported from `index.ts`, which stays + * environment-agnostic for browser consumers (e.g. malachite-web). + * Import via `@ewanc26/croft-click-core/paths`. + * + * Every tool used to keep its own dotfile directly under the home directory + * (~/.malachite, ~/.jasper, ...). They now share one root, ~/.ewanc26/, + * so credentials, logs, and caches only need to be found/backed up/cleaned + * up in one place. `EWANC26_STATE_DIR` overrides the root for all tools. + */ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +export function getEwanc26RootDir(): string { + return process.env.EWANC26_STATE_DIR || path.join(os.homedir(), '.ewanc26'); +} + +/** + * Resolve a tool's state directory under the shared ~/.ewanc26 root. + * + * If the unified directory doesn't exist yet but a legacy per-tool + * directory does (`~/.`, or the transitional `~/.pkgs/`), the + * legacy directory is renamed into place so existing credentials/logs/state + * survive the switch without user action. + */ +export function getToolStateDir(tool: string): string { + const target = path.join(getEwanc26RootDir(), tool); + + if (!fs.existsSync(target)) { + const legacyCandidates = [ + path.join(os.homedir(), `.${tool}`), + path.join(os.homedir(), '.pkgs', tool), + ]; + + for (const legacy of legacyCandidates) { + if (fs.existsSync(legacy)) { + fs.mkdirSync(path.dirname(target), { recursive: true }); + fs.renameSync(legacy, target); + break; + } + } + } + + return target; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5bfa22..bcc94f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -173,6 +173,9 @@ importers: specifier: ^13.3.1 version: 13.4.2 devDependencies: + '@types/node': + specifier: ^25.5.0 + version: 25.6.0 typescript: specifier: ^5.9.3 version: 5.9.3 -- 2.51.2