Tags: kriasoft/srcpack
Tags
feat: bundle Linear issues, harden path boundaries (#10) Adds Linear as a bundle source, and fixes a class of path-handling defects where a run could read or delete the wrong thing. Linear: a bundle may declare `linear` — a team key, optionally scoped by project or widened with `includeClosed`. Each issue becomes a virtual file at `linear/issues/<ID>.md`, preceded by a roster at `linear/issues.md`. Both are ordinary entries: indexed, line-ranged, droppable with `!`. Auth reads `LINEAR_API_KEY` from the environment, never config. Pagination orders by `createdAt` because `updatedAt` is mutable and corrupts a cursor walk. Invariants to preserve when changing this code: - Only the conventional `.srcpack` is emptied automatically; any other `outDir` requires `emptyOutDir: true`. - Ownership — what may be deleted — compares physical paths by exact match. Never case-fold it; folding can only widen deletion. - Every other path comparison (write collisions, own-output exclusion) folds NFC then case, on all platforms, and carries both the lexical and physical spelling. - Globs never follow symlinks at any level; `.gitignore` resolves per directory, deepest rule first, no re-inclusion under an ignored directory. - Unknown CLI flags and unknown config keys are errors, never silent no-ops. - Bundles are written via temp file + rename, so a symlink at the output path is replaced rather than written through. - `linear/issues/` and `linear/issues.md` are reserved once a bundle pulls issues; a real file there is an error. - Prose is never hard-wrapped — one paragraph per line in Markdown, commit bodies and PR descriptions. Enforced for Markdown by `proseWrap: "never"` in package.json plus `prettier --check .`. BREAKING CHANGE: a custom `outDir` is no longer emptied automatically. Set `emptyOutDir: true` for the previous behaviour. Version 0.3.0. Rationale: docs/adr/003-linear-issues-as-virtual-files.md, docs/adr/004-path-boundaries.md
feat: bundle git change sets with `git:` source tokens (#9) A bundle pattern may now be a git source instead of a glob: `git:staged`, `git:unstaged`, `git:untracked`, `git:dirty`, or `git:<rev>`. CLI flags `--staged`, `--dirty` and `--since <rev>` build the same bundle ad hoc with no config file. Resolution lives in `src/git.ts` (`resolveGitSource`), called from `resolvePatterns()` in `src/bundle.ts`. Tokens resolve lazily rather than as a spread of pre-resolved paths (`[...$staged]`, rejected): a spread runs git at config import time even for unrelated commands, rules out `package.json` config, and drops literal paths into an array that is later matched as globs, so a staged `src/[id].tsx` silently matches nothing. See docs/adr/001-git-source-tokens.md. Invariants to preserve: a git source selects which files, never their content, which always comes from the worktree so line citations match disk; `.gitignore` does not apply to git sources; deletions, binaries, submodules and symlinks are skipped; `!git:` and `+git:` are errors; ad-hoc CLI bundles are never uploaded, because `upload.exclude` cannot name a bundle that exists for one run. Node floor raised to `^22.18.0 || >=24` so cosmiconfig 10 loads `srcpack.config.ts` via Node type stripping instead of a bundled TypeScript compiler, cutting the published CLI from 10.8 MB to 2.4 MB. Consequence: Node derives a `.ts` file's module format from the nearest package.json, so a `.ts` config's `import` line is a syntax error in a CommonJS project; `srcpack.config.mts` is therefore in `searchPlaces` and `init` writes it when the project is not `"type": "module"` (`configFileName`). The Bun test suite loads either extension regardless of package type and cannot catch this, so CI installs the packed tarball into a CommonJS project and runs the CLI under Node. See docs/adr/002-minimum-node-version.md. Fixes, each with a regression test: an `outDir` resolving to the project root is refused instead of emptied, which previously deleted the entire project and still exited 0; srcpack never bundles its own output (`isOwnOutput`), which previously nested the prior run's bundle one level deeper on every run; `outDir` is emptied only on a full run, so a named subset no longer destroys bundles it cannot rebuild; symlinks are stat'd with `lstat` so a tracked link cannot pull a file in from outside the project; Drive queries escape the file name (`escapeQueryValue`) because an unescaped quote resolved to a different file that the upload then overwrote; credentials are written `0600`; `init` escapes generated values so hyphenated bundle names and backslash patterns produce valid TypeScript; failed uploads set a non-zero exit code; subcommands match only in first position, so `--since init` diffs against the `init` branch. Docs deploy from GitHub Actions (.github/workflows/docs.yml) on release instead of the `gh-pages` branch. The Pages source must be switched to `build_type=workflow` once, by hand.