Package is a dependency-free TypeScript CLI for creating project source snapshots, generating update archives, and safely applying them to another copy of a project.
package init now creates a concise starter configuration; use
package config --json to inspect resolved defaults or package init --full
for the expanded template.depends_on declarations. Every project keeps its own file rules and hooks.forceInclude and forceIgnore can override ordinary file-selection and
ignore rules without weakening Package’s internal safety exclusions.packageManager, {packageManager}, and PACKAGE_MANAGER.├─ rows.| Guide | Use it when you need to… |
|---|---|
| AI agent guide | Give coding agents an exact, safe workflow for reading, modifying, validating, and returning package archives. |
| Configuration schema | Configure file selection, ZIP output, metadata, and apply behavior in .packagerc. |
.packageshift format |
Understand or author removals, moves, copies, replacements, mode changes, and source snapshot metadata. |
Create a project snapshot:
npx @streetraceing/package zip
To use the package command directly, install it globally:
npm install --global @streetraceing/package
package zip
After changing the project, create an update package from that snapshot and preview it before writing anything:
npx @streetraceing/package shift base.zip --output update.zip
npx @streetraceing/package diff update.zip
npx @streetraceing/package apply update.zip --dry-run
Apply an approved update non-interactively:
npx @streetraceing/package apply update.zip --yes
Package writes only added, modified, or mode-changed payload files by default. Unchanged files are not opened for writing and keep their timestamps. To restore the former full-overlay behavior intentionally, pass:
package apply snapshot.zip --rewrite-all
Package checks whether the archive appears to belong to the target project. A
likely mismatch requires a second interactive confirmation; --yes alone does
not bypass it. For an intentional automated cross-project apply, review with
--dry-run and pass --allow-project-mismatch explicitly.
Run npx @streetraceing/package --help for the complete command reference.
Create the small configuration most projects need:
package init
The generated .packagerc contains the schema URL, archive name, collection
strategy, and Git-ignore choice. Other settings remain active through validated
defaults rather than being copied into every new project. Inspect them at any
time:
package config
package config --json
Use package init --full for an expanded reference template,
package init --force to intentionally replace an existing config, and
package init --no-gitignore to skip both Git-ignore selection and the generated
*.zip block.
depends_onUse explicit composition when separately configured projects must be packaged and
updated together. The project where the command is launched is the entry
project. It declares local dependencies in its own .packagerc, and every
dependent project loads its own .packagerc recursively.
codeissue/
├─ website/
│ ├─ .packagerc
│ └─ package.json
└─ backend/
├─ .packagerc
└─ package.json
codeissue/website/.packagerc:
{
"depends_on": [
{
"path": "../backend",
"name": "@codeissue/backend"
}
],
"afterApply": ["npm install", "npm run build"]
}
codeissue/backend/.packagerc:
{
"ignore": ["coverage/**"],
"afterApply": ["npm install", "npm run migrate"]
}
Run the ordinary workflow from website:
cd codeissue/website
package projects
package zip
package shift website.zip --output update.zip
package apply update.zip --dry-run
package apply update.zip --yes
The snapshot and update archive contain both website/** and backend/**, with
paths rooted at their shared codeissue directory. package projects prints the
resolved graph before packaging. depends_on may be recursive; cycles, missing
directories, duplicate names, and colliding archive paths are rejected.
Each project independently controls include, ignore, force rules, Git and
package-manager ignore handling, sensitive-file policy, mode/mtime preservation,
and lifecycle hooks. A discovered local .packagerc is always carried with its
project, even when an ordinary ignore rule would omit it. Hooks run dependency-first in the directory of the project
that owns them. The entry project controls archive-level settings such as output,
compression, backup, conflict, and cleanup policies.
A dependency may also use the short form:
{
"depends_on": ["../backend"]
}
Its name is inferred from package.json, then from the directory name. Run
apply from the entry project; Package automatically targets the shared root, so
both sibling directories are updated while entry-level apply policy is preserved.
Each dependency path is relative to the .packagerc that declares it, even when
that configuration sets a different project root.
Workspace discovery supports package.json#workspaces, pnpm-workspace.yaml,
lerna.json, and rush.json. Use it only when a package-manager workspace scope
is the desired delivery unit rather than an explicit project relationship.
package workspaces
package zip -w @acme/api --with-dependencies
package zip -w packages/ui --with-dependents
package zip --all-workspaces --no-root-files
Selectors accept package names, root-relative paths, basenames, and globs. Local
dependency and dependent expansion is recursive. Scoped payload paths remain
relative to the monorepo root, and monorepo.shared controls root lockfiles and
shared configuration.
The manifest records the workspace scope. shift and metadata inherit it from
the base snapshot and reject a conflicting explicit scope with
WORKSPACE_SCOPE_MISMATCH. depends_on composition and workspace selection
cannot be combined in one archive.
forceInclude and forceIgnore are repeatable project-relative glob lists for
exceptions to normal selection. They override include, ignore, dotfile
filtering, .gitignore, and the configured package-manager ignore file.
forceIgnore wins if both lists match. Package still never collects .git,
node_modules, reserved metadata files, or the archive currently being written.
Use packageManager to select the command exposed to lifecycle hooks. It is
npm by default but accepts pnpm, yarn, bun, or any build runner. In hook
commands, {packageManager} expands to that value and PACKAGE_MANAGER exposes
it to child processes. Enable packageManagerIgnore to read
packageManagerIgnoreFile (default .npmignore); npmignore remains a legacy
alias.
{
"packageManager": "pnpm",
"packageManagerIgnore": true,
"packageManagerIgnoreFile": ".npmignore",
"forceInclude": [".env.example"],
"forceIgnore": ["secrets/**"],
"beforePackage": ["{packageManager} run build"],
"afterPackage": ["node scripts/report-package.mjs"],
"beforeApply": [],
"afterApply": ["{packageManager} run prepare"],
"deletePackageOnApply": false,
"deleteSourcePackageOnApply": false,
"saveDeletedCache": true
}
beforePackage runs before file collection for zip and shift.afterPackage runs after the archive is written.beforeApply runs after validation and confirmation, before files change.afterApply runs after a successful apply as a best-effort hook. A failed
command prints a warning; later hooks and cleanup still continue.deletePackageOnApply removes the applied archive after project changes are
written, even if an afterApply command fails. It is false by default.deleteSourcePackageOnApply removes one safely identified source snapshot. It
first uses the exact filename and SHA-256 recorded by package shift; when that
reference is unavailable, it accepts exactly one snapshot beside the update
archive or in the project root whose manifest matches the project state before
apply. Ambiguous or changed archives are preserved. It is false by default.For one project, hook commands run sequentially from its root. In a
depends_on composition, they run dependency-first from each owning project’s
root. Hook processes receive PACKAGE_HOOK, PACKAGE_COMMAND, PACKAGE_ROOT,
PACKAGE_ARCHIVE, PACKAGE_MANAGER, PACKAGE_PROJECT_NAME,
PACKAGE_PROJECT_PATH, and PACKAGE_COMPOSITION_ROOT.
saveDeletedCache is true by default. Before a CLI-managed deletion or
replacement, Package copies the previous regular file into
~/streetraceing/.package/cache/<project-id>/<operation-id>; Windows resolves
that path below %USERPROFILE%. A .packagecache.json file records original and
cached paths, reasons, modes, sizes, and SHA-256 values.
The cache covers destructive apply and backup restore changes, replacement of
an existing ZIP output, init --force, and package/source-package cleanup.
User-defined hook scripts are separate processes, so their own deletions cannot be
captured. Files over 10 MiB are still saved and produce an explicit warning.
Use --no-save-deleted-cache for one command or set saveDeletedCache to false
to disable the feature.
Interactive output separates reference text from operational status. Help output
is intentionally white-and-gray only. Operational sections start with ┌─, use
a neutral ├─ for every row, and end only at the muted └──── divider. Semantic
meaning belongs to the adjacent glyph (◆, ●, ▲, ×, or a change marker),
so connector colors stay stable from top to bottom. Redirected output stays
plain; NO_COLOR=1 disables colors explicitly.
Apply backups are stored per project under
~/streetraceing/.package/backups/<project-id>; on Windows the same path is
resolved below %USERPROFILE%. Legacy .package-backups archives are still
listed and restorable.
package backup list
package backup inspect 1
package backup restore latest
package backup restore 3 --yes
A restore creates a recovery backup first, then restores the selected version and
every newer delta. The recovery version can be selected later to undo the rollback.
STREETRACEING_PACKAGE_HOME can override the data directory.
Use metadata when project files are already prepared and you need ready
metadata files in the project root without creating a ZIP:
package metadata base.zip --message "Describe the update"
package metadata --message "Use the current .packagemanifest.json as baseline"
package meta
The command recalculates the complete snapshot .packagemanifest.json and
creates .packageshift structural instructions. Baseline selection is:
.packagemanifest.json;.packageshift.When a ZIP baseline is supplied, its filename and SHA-256 are recorded so source package cleanup can identify it safely. The command preserves application files and replaces only the two reserved metadata files.
.packageshift instructions for operations ZIP entries cannot express.
.packageshift remains reserved CLI metadata and is never written into the
target project, even when a malformed legacy manifest lists it as payload.--rewrite-all only for an intentional full payload rewrite.diff or apply --dry-run to review
changes, then select abort, overwrite, or skip conflict handling when
needed.Manually assembled .packageshift archives without an embedded manifest are
supported, but cannot verify that the target matches the base project.
Return to the project README.