@the-coded/pstash 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schemas.ts","../src/config/loader.ts","../src/config/templates.ts"],"sourcesContent":["/**\n * @module schemas\n *\n * Zod as Single Source of Truth (SSoT) for all types.\n *\n * **Rule**: No manual `interface` or `type` declarations anywhere in the codebase.\n * All types are derived via `z.infer<typeof Schema>`.\n *\n * Validation happens at every I/O boundary:\n * - Reading `.stash.json` and `.project.json` files\n * - Reading `~/.pstashrc` config\n * - Parsing CLI options\n *\n * @example\n * // Parse and validate untrusted data\n * const metadata = StashMetadataSchema.parse(JSON.parse(rawJson))\n *\n * // Get TypeScript type from schema\n * type Metadata = z.infer<typeof StashMetadataSchema>\n */\n\nimport { z } from \"zod\"\n\n// ─── File-level Schema ─────────────────────────────────────────────────────\n\n/**\n * Represents a single file entry within a stash.\n *\n * @example\n * const file: StashFile = {\n * name: \"README.md\",\n * size: 1024,\n * hash: \"sha256:a1b2c3d4e5f6\"\n * }\n */\nexport const StashFileSchema = z.object({\n /** Original filename (basename only, no path) */\n name: z.string(),\n /** File size in bytes */\n size: z.number().nonnegative(),\n /**\n * SHA-256 integrity hash, prefixed with \"sha256:\".\n * First 12 hex chars used (48 bits) — sufficient for integrity checking.\n * @example \"sha256:a1b2c3d4e5f6\"\n */\n hash: z.string(),\n})\n\nexport type StashFile = z.infer<typeof StashFileSchema>\n\n// ─── Stash Metadata Schema (.stash.json) ───────────────────────────────────\n\n/**\n * Metadata stored in `.stash.json` inside each stash directory.\n * Created by `Stasher.save()`, read by `Stasher.loadMetadata()`.\n *\n * @example\n * // Stash directory structure:\n * // my-personal-stash/scena/2026-03-12_01-05_k7x2/.stash.json\n */\nexport const StashMetadataSchema = z.object({\n /**\n * Unique stash identifier.\n * Format: `YYYY-MM-DD_HH-mm_XXXX` (timestamp + 4-char nanoid suffix).\n * The suffix prevents collisions when multiple machines stash in the same minute.\n * @example \"2026-03-12_01-05_k7x2\"\n */\n id: z.string(),\n /** Project name (derived from git remote or directory name) */\n project: z.string(),\n /** ISO 8601 creation timestamp */\n timestamp: z.string().datetime(),\n /**\n * ISO 8601 timestamp of the last `pstash update` on this stash.\n * Absent when the stash has never been updated.\n */\n updatedAt: z.string().datetime().optional(),\n /** Human-readable description of what was stashed */\n message: z.string(),\n /**\n * User-defined tags for filtering and organization.\n * @example [\"docs\", \"planning\", \"wip\"]\n */\n tags: z.array(z.string()).default([]),\n /** Git branch at time of stash (if inside a git repo) */\n branch: z.string().optional(),\n /** Git commit hash at time of stash (if inside a git repo) */\n commit: z.string().optional(),\n /**\n * Machine identifier of who created the stash.\n * Format: `username@hostname` using `os.userInfo().username` (cross-platform).\n * @example \"gab@macmini\"\n */\n user: z.string().optional(),\n /** List of stashed files with size and integrity hash */\n files: z.array(StashFileSchema),\n /** Total size of all files in bytes */\n totalSize: z.number().nonnegative(),\n /** Whether files are stored as tar.gz (Phase 3 feature) */\n compressed: z.boolean().default(false),\n})\n\nexport type StashMetadata = z.infer<typeof StashMetadataSchema>\n\n// ─── Project Metadata Schema (.project.json) ───────────────────────────────\n\n/**\n * Metadata stored in `.project.json` at the project directory root.\n * Managed by `Indexer` — updated after every save/delete.\n *\n * @example\n * // my-personal-stash/scena/.project.json\n * {\n * \"name\": \"scena\",\n * \"stashCount\": 3,\n * \"totalSize\": \"268 KB\"\n * }\n */\nexport const ProjectMetadataSchema = z.object({\n /** Project name (matches directory name in stash repo) */\n name: z.string(),\n /** Git remote URL for this project (informational only) */\n remote: z.string().optional(),\n /**\n * Alternative names that map to this project.\n * @example [\"scena-cli\", \"e2e-gen\"] → resolves to \"scena\"\n */\n aliases: z.array(z.string()).default([]),\n /** Total number of stash entries for this project */\n stashCount: z.number().nonnegative(),\n /**\n * Human-readable total size of all stashes.\n * Formatted via `pretty-bytes`.\n * @example \"268 KB\"\n */\n totalSize: z.string(),\n /** ISO 8601 timestamp of first stash creation */\n createdAt: z.string().datetime(),\n /** ISO 8601 timestamp of last stash modification */\n updatedAt: z.string().datetime(),\n})\n\nexport type ProjectMetadata = z.infer<typeof ProjectMetadataSchema>\n\n// ─── Project Config Entry (inside GlobalConfig) ────────────────────────────\n\n/**\n * Per-project configuration inside `~/.pstashrc`.\n * All fields are optional — only needed to customize behavior.\n */\nexport const ProjectConfigSchema = z.object({\n /**\n * Alternative names that resolve to this project.\n * @example [\"e2e-gen\", \"scena-cli\"] both map to \"scena\"\n */\n aliases: z.array(z.string()).default([]),\n /**\n * Override the detected git remote for this project.\n * Useful if the project name differs from the remote.\n */\n remote: z.string().optional(),\n /** Absolute path to the project directory */\n path: z.string().optional(),\n})\n\nexport type ProjectConfig = z.infer<typeof ProjectConfigSchema>\n\n// ─── Global Config Schema (~/.pstashrc) ────────────────────────────────────\n\n/**\n * Global configuration stored at `~/.pstashrc`.\n *\n * - Location: `os.homedir() + \"/.pstashrc\"` (cross-platform)\n * - Never stored inside the data repo (`my-personal-stash`)\n * - Loaded and validated by `src/config/loader.ts`\n *\n * @example\n * {\n * \"version\": \"1.0.0\",\n * \"remote\": \"git@github.com:user/my-personal-stash.git\",\n * \"localPath\": \"~/.pstash\",\n * \"autoSync\": true,\n * \"projects\": {},\n * \"defaults\": { \"removeAfterSave\": false }\n * }\n */\nexport const GlobalConfigSchema = z.object({\n /** Config schema version for future migrations */\n version: z.string(),\n /** SSH or HTTPS URL of the personal stash data repository (SSH or HTTPS) */\n remote: z.string().min(1, \"Remote URL is required\"),\n /**\n * Local path for the cloned stash data repo.\n * Supports `~` expansion: `\"~/.pstash\"` → `\"/Users/gab/.pstash\"`.\n * Resolved by `resolveLocalPath()` in `config/loader.ts`.\n */\n localPath: z.string().default(\"~/.pstash\"),\n /**\n * Whether to automatically pull before and push after write operations\n * (save, pop, drop, clean) and pull before read operations (list).\n * Can be overridden per-operation with `--no-sync`.\n */\n autoSync: z.boolean().default(true),\n /**\n * Per-project overrides (aliases, custom remote, path).\n * Key is the canonical project name.\n */\n projects: z.record(z.string(), ProjectConfigSchema).default({}),\n /** Default behavior settings (can be overridden per-command) */\n defaults: z.object({\n /** If true, `pstash pop` keeps the stash after restoring (like apply) */\n keepOnPop: z.boolean().default(false),\n /** If true, compress stash files as tar.gz (Phase 3) */\n compression: z.boolean().default(false),\n /**\n * If true, delete source files after `pstash save`.\n * Can be overridden per-operation: `--rm` to force delete, `--keep` to force keep.\n */\n removeAfterSave: z.boolean().default(false),\n }),\n})\n\nexport type GlobalConfig = z.infer<typeof GlobalConfigSchema>\n\n// ─── CLI Options Schemas ────────────────────────────────────────────────────\n\n/**\n * Options for the `pstash save` command.\n *\n * @example\n * pstash save -t docs -t wip \"planning notes\" *.md\n */\nexport const SaveOptionsSchema = z.object({\n /** Required description of what was stashed */\n message: z.string().min(1, \"Message is required\"),\n /**\n * File patterns to stash (glob patterns supported via globby).\n * @example [\"*.md\", \"src/**\\/*.ts\"]\n */\n files: z.array(z.string()).min(1, \"At least one file pattern required\"),\n /** Tags for filtering and organization */\n tags: z.array(z.string()).default([]),\n /** Override auto-detected project name */\n project: z.string().optional(),\n /** Whether to push to remote after saving */\n push: z.boolean().default(true),\n /** Whether to compress the stash (Phase 3) */\n compress: z.boolean().default(false),\n /**\n * Whether to remove source files after saving.\n * `undefined` = use `config.defaults.removeAfterSave`.\n * `true` (--rm) = always remove.\n * `false` (--keep) = always keep.\n */\n removeAfterSave: z.boolean().optional(),\n})\n\nexport type SaveOptions = z.infer<typeof SaveOptionsSchema>\n\n/**\n * Options for the `pstash list` command.\n */\nexport const ListOptionsSchema = z.object({\n /** Show stashes from all projects (not just current) */\n all: z.boolean().default(false),\n /** Filter by specific project name */\n project: z.string().optional(),\n /** Filter by tag */\n tag: z.string().optional(),\n /**\n * Show stashes created after this timespec.\n * @example \"7d\" (7 days ago), \"2w\", \"1m\", \"2026-03-01\"\n */\n since: z.string().optional(),\n /** Show stashes created before this timespec */\n until: z.string().optional(),\n /** Show preview of the first non-empty line of up to the first 3 files per stash */\n preview: z.boolean().default(false),\n /** Output as JSON for scripting */\n json: z.boolean().default(false),\n})\n\nexport type ListOptions = z.infer<typeof ListOptionsSchema>\n\n/**\n * Options for `pstash pop` and `pstash apply` commands.\n */\nexport const RestoreOptionsSchema = z.object({\n /**\n * Index of the stash to restore (0-based, newest first).\n * If undefined, shows interactive selector.\n */\n stashIndex: z.number().int().nonnegative().optional(),\n /**\n * Glob pattern for partial restore (Phase 3, requires micromatch).\n * @example \"*.md\" to restore only markdown files\n */\n files: z.string().optional(),\n /**\n * Destination directory for restored files.\n * Defaults to `process.cwd()`.\n */\n dest: z.string().optional(),\n /** If true, keep the stash after restoring (apply behavior) */\n keep: z.boolean().default(false),\n /** If true, overwrite existing files without error */\n force: z.boolean().default(false),\n})\n\nexport type RestoreOptions = z.infer<typeof RestoreOptionsSchema>\n\n/**\n * Options for `pstash init` command.\n */\nexport const InitOptionsSchema = z.object({\n /** SSH or HTTPS URL for the data repo (SSH or HTTPS) */\n remote: z.string().min(1).optional(),\n /** Local path to clone to (default: ~/.pstash) */\n path: z.string().optional(),\n})\n\nexport type InitOptions = z.infer<typeof InitOptionsSchema>\n\n/**\n * Options for `pstash sync` command.\n */\nexport const SyncOptionsSchema = z.object({\n /** Pull only (skip push) */\n pull: z.boolean().default(false),\n /** Push only (skip pull) */\n push: z.boolean().default(false),\n})\n\nexport type SyncOptions = z.infer<typeof SyncOptionsSchema>\n\n/**\n * Options for `pstash status` command.\n */\nexport const StatusOptionsSchema = z.object({\n /** Show status for all projects */\n all: z.boolean().default(false),\n /** Output as JSON for scripting */\n json: z.boolean().default(false),\n})\n\nexport type StatusOptions = z.infer<typeof StatusOptionsSchema>\n\n/**\n * Options for `pstash drop` command.\n */\nexport const DropOptionsSchema = z.object({\n /** Index of the stash to drop (0-based). If undefined, shows interactive selector. */\n stashIndex: z.number().int().nonnegative().optional(),\n /** Limit to a specific project */\n project: z.string().optional(),\n /** Drop all stashes with this tag */\n tag: z.string().optional(),\n /** Drop all stashes in the project (requires confirmation) */\n all: z.boolean().default(false),\n /** Skip confirmation prompt */\n force: z.boolean().default(false),\n})\n\nexport type DropOptions = z.infer<typeof DropOptionsSchema>\n\n/**\n * Options for `pstash clean` command (Phase 3).\n */\nexport const CleanOptionsSchema = z.object({\n /**\n * Delete stashes older than this timespec.\n * @example \"30d\", \"2w\", \"1m\"\n */\n olderThan: z.string().optional(),\n /** Keep only N most recent stashes per project */\n keep: z.number().int().positive().optional(),\n /** Delete only stashes with this tag */\n tag: z.string().optional(),\n /** Preview what would be deleted without actually deleting */\n dryRun: z.boolean().default(false),\n})\n\nexport type CleanOptions = z.infer<typeof CleanOptionsSchema>\n","/**\n * @module config/loader\n *\n * Load, save, and validate the global pstash config (`~/.pstashrc`).\n *\n * The config file is a JSON document validated against\n * {@link GlobalConfigSchema}. All paths support `~/` notation, which\n * is expanded against `os.homedir()` for cross-platform compatibility.\n *\n * @example\n * ```ts\n * import { loadConfig, updateConfig } from \"pstash/config/loader\"\n *\n * const config = await loadConfig()\n * await updateConfig({ autoSync: false })\n * ```\n */\n\nimport { readFile, writeFile, access } from \"node:fs/promises\"\nimport { join, resolve } from \"node:path\"\nimport { homedir } from \"node:os\"\nimport { GlobalConfigSchema } from \"../schemas.js\"\nimport type { GlobalConfig } from \"../schemas.js\"\n\n/** Absolute path to the global config file */\nexport const CONFIG_PATH = join(homedir(), \".pstashrc\")\n\n/**\n * Resolves the stash repo local path from config.\n * Handles \"~/.pstash\" notation → expands to absolute path.\n */\nexport function resolveLocalPath(localPath: string): string {\n if (localPath.startsWith(\"~/\")) {\n return join(homedir(), localPath.slice(2))\n }\n if (localPath.startsWith(\"~\")) {\n return join(homedir(), localPath.slice(1))\n }\n return resolve(localPath)\n}\n\n/**\n * Checks whether the config file exists.\n */\nexport async function configExists(): Promise<boolean> {\n try {\n await access(CONFIG_PATH)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Loads and validates ~/.pstashrc.\n * Throws if file not found or invalid.\n */\nexport async function loadConfig(): Promise<GlobalConfig> {\n let raw: string\n try {\n raw = await readFile(CONFIG_PATH, \"utf-8\")\n } catch {\n throw new Error(\n `Config file not found: ${CONFIG_PATH}\\n` +\n `Run \"pstash init\" to set up your personal stash.`,\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch {\n throw new Error(`Config file is not valid JSON: ${CONFIG_PATH}`)\n }\n\n const result = GlobalConfigSchema.safeParse(parsed)\n if (!result.success) {\n const issues = result.error.issues.map(i => ` - ${i.path.join(\".\")}: ${i.message}`).join(\"\\n\")\n throw new Error(`Config validation failed:\\n${issues}`)\n }\n\n return result.data\n}\n\n/**\n * Saves config to ~/.pstashrc.\n */\nexport async function saveConfig(config: GlobalConfig): Promise<void> {\n const validated = GlobalConfigSchema.parse(config)\n await writeFile(CONFIG_PATH, JSON.stringify(validated, null, 2) + \"\\n\", \"utf-8\")\n}\n\n/**\n * Updates a specific key in the config.\n */\nexport async function updateConfig(updates: Partial<GlobalConfig>): Promise<GlobalConfig> {\n const current = await loadConfig()\n const merged = { ...current, ...updates }\n const validated = GlobalConfigSchema.parse(merged)\n await saveConfig(validated)\n return validated\n}\n","/**\n * @module config/templates\n *\n * Default config templates and seed files for fresh pstash installs.\n *\n * Provides:\n * - {@link createDefaultConfig} — builds a {@link GlobalConfig} from defaults\n * - {@link DATA_REPO_README} — initial README for the data repo\n * - {@link DATA_REPO_GITIGNORE} — initial `.gitignore` for the data repo\n */\n\nimport type { GlobalConfig } from \"../schemas.js\"\n\n/**\n * Creates a default GlobalConfig for new pstash installations.\n */\nexport function createDefaultConfig(remote: string, localPath = \"~/.pstash\"): GlobalConfig {\n return {\n version: \"1.0.0\",\n remote,\n localPath,\n autoSync: true,\n projects: {},\n defaults: {\n keepOnPop: false,\n compression: false,\n removeAfterSave: false,\n },\n }\n}\n\n/**\n * README template for the data repo (my-personal-stash).\n */\nexport const DATA_REPO_README = `# My Personal Stash\n\n> Managed by [pstash](https://github.com/the-coded/pstash-cli) — Git-backed personal file stash.\n\n## Structure\n\n\\`\\`\\`\nmy-personal-stash/\n├── <project-name>/\n│ ├── .project.json # Project metadata\n│ └── YYYY-MM-DD_HH-mm_XXXX/ # Stash entries\n│ ├── .stash.json # Stash metadata\n│ └── <files...>\n\\`\\`\\`\n\n## Usage\n\nThis repo is managed automatically by \\`pstash\\`. Do not edit manually.\n\n\\`\\`\\`bash\npstash save \"message\" *.md # Save files\npstash list # List stashes\npstash pop 0 # Restore latest\npstash sync # Sync with remote\n\\`\\`\\`\n`\n\n/**\n * .gitignore template for the data repo.\n */\nexport const DATA_REPO_GITIGNORE = `.DS_Store\nThumbs.db\n*.log\n.env\n.env.*\n!.env.example\n`\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "@the-coded/pstash",
3
+ "version": "0.1.0",
4
+ "description": "Git-backed personal file stash — persistent, project-categorized, multi-machine",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/the-coded/pstash.git"
9
+ },
10
+ "homepage": "https://github.com/the-coded/pstash#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/the-coded/pstash/issues"
13
+ },
14
+ "bin": {
15
+ "pstash": "./dist/bin/pstash.js"
16
+ },
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "files": [
20
+ "dist",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "dev": "tsx watch bin/pstash.ts",
27
+ "start": "tsx bin/pstash.ts",
28
+ "typecheck": "tsc --noEmit",
29
+ "lint": "eslint .",
30
+ "lint:fix": "eslint . --fix",
31
+ "format": "prettier --write .",
32
+ "format:check": "prettier --check .",
33
+ "test": "vitest run",
34
+ "test:watch": "vitest",
35
+ "test:coverage": "vitest run --coverage",
36
+ "prepublishOnly": "npm run build && npm run typecheck"
37
+ },
38
+ "engines": {
39
+ "node": ">=20.0.0"
40
+ },
41
+ "keywords": [
42
+ "stash",
43
+ "git",
44
+ "cli",
45
+ "files",
46
+ "backup",
47
+ "sync",
48
+ "personal"
49
+ ],
50
+ "publishConfig": {
51
+ "access": "public"
52
+ },
53
+ "author": "gabemule",
54
+ "license": "MIT",
55
+ "dependencies": {
56
+ "@inquirer/prompts": "^8.3.2",
57
+ "chalk": "^5.6.2",
58
+ "commander": "^14.0.3",
59
+ "date-fns": "^4.1.0",
60
+ "globby": "^16.2.0",
61
+ "micromatch": "^4.0.8",
62
+ "nanoid": "^5.1.7",
63
+ "ora": "^9.3.0",
64
+ "pretty-bytes": "^7.1.0",
65
+ "simple-git": "^3.33.0",
66
+ "tar": "^7.5.13",
67
+ "zod": "^4.3.6"
68
+ },
69
+ "devDependencies": {
70
+ "@eslint/js": "^10.0.1",
71
+ "@types/micromatch": "^4.0.10",
72
+ "@types/node": "^25.5.0",
73
+ "@types/tar": "^6.1.13",
74
+ "@typescript-eslint/eslint-plugin": "^8.58.0",
75
+ "@typescript-eslint/parser": "^8.58.0",
76
+ "@vitest/coverage-v8": "^4.1.5",
77
+ "eslint": "^10.1.0",
78
+ "eslint-config-prettier": "^10.1.8",
79
+ "globals": "^17.5.0",
80
+ "jiti": "^2.6.1",
81
+ "prettier": "^3.8.1",
82
+ "tsup": "^8.5.1",
83
+ "tsx": "^4.21.0",
84
+ "typescript": "^6.0.2",
85
+ "vitest": "^4.1.2"
86
+ }
87
+ }