{"_id":"@a5omic/kill-tree","_rev":"2-038959a047081845ddedd23773ce879c","name":"@a5omic/kill-tree","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@a5omic/kill-tree","version":"0.1.0","keywords":["process","process-tree","tree-kill","kill","signal","child-process","subprocess","orphan-process","cross-platform","cli"],"author":{"name":"Atomics Hub"},"license":"MIT","_id":"@a5omic/kill-tree@0.1.0","maintainers":[{"name":"a5omic","email":"therekishianime@gmail.com"}],"homepage":"https://github.com/Atomics-hub/kill-tree#readme","bugs":{"url":"https://github.com/Atomics-hub/kill-tree/issues"},"c8":{"all":true,"lines":80,"exclude":["src/cli.ts","src/types.ts"],"include":["src/**/*.ts"],"branches":70,"reporter":["text"],"functions":80,"statements":80,"check-coverage":true},"bin":{"kill-tree":"dist/cli.js"},"dist":{"shasum":"16e7715caf5c64d418fb193e464a8829ba3e518f","tarball":"https://registry.npmjs.org/@a5omic/kill-tree/-/kill-tree-0.1.0.tgz","fileCount":14,"integrity":"sha512-MeHffUb0i/9jQ1NirMR6RGnxdF0eYfD7MiWrD1NBhnHmegOdVtZtuci7CvjGFMaV/tyUgqq/LE75wOvSxVXoNA==","signatures":[{"sig":"MEYCIQCLDaqtWB5+Kjc+DCdesnEMzU0S8yFY/vFitNB3O9kNpgIhAOAntiV/FRPtOr4vIb7uVlYycuPCEPmr6oWOT3sBkt5R","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":184276},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"gitHead":"04d515aa29cc159c41e719f0132decafae8bcbf0","scripts":{"test":"tsx --test test/unit/*.test.ts","build":"tsup src/index.ts --format esm,cjs --dts --clean --sourcemap --splitting false && node scripts/build-cjs-shim.mjs && tsup src/cli.ts --format esm --sourcemap --splitting false","check":"tsc --noEmit","verify":"npm run check && npm run test:coverage && npm run build && npm run test:integration && npm run test:cli && npm run test:package && npm pack --dry-run","test:cli":"node test/cli-smoke.mjs","test:stress":"KILL_TREE_STRESS_RUNS=20 tsx --test --test-concurrency=1 test/integration/stress.test.ts","test:package":"node test/package-smoke.mjs","release:check":"node scripts/check-release.mjs","test:coverage":"c8 tsx --test --test-concurrency=1 test/unit/*.test.ts test/integration/*.test.ts","prepublishOnly":"npm run verify","test:integration":"tsx --test --test-concurrency=1 test/integration/*.test.ts"},"_npmUser":{"name":"a5omic","email":"therekishianime@gmail.com"},"overrides":{"esbuild":"^0.28.2"},"repository":{"url":"git+https://github.com/Atomics-hub/kill-tree.git","type":"git"},"_npmVersion":"11.6.0","description":"Reliable cross-platform process-tree termination for Node.js, with graceful escalation, structured reports, and a tree-kill compatible API.","directories":{},"sideEffects":false,"_nodeVersion":"24.8.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"c8":"^10.1.3","tsx":"^4.20.0","tsup":"^8.5.0","typescript":"^5.8.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/kill-tree_0.1.0_1787895455061_0.41709213841128423","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@a5omic/kill-tree","version":"0.1.1","description":"Reliable cross-platform process-tree termination for Node.js, with graceful escalation, structured reports, and a tree-kill compatible API.","type":"module","sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"bin":{"kill-tree":"dist/cli.js"},"scripts":{"build":"tsup src/index.ts --format esm,cjs --dts --clean --sourcemap --splitting false && node scripts/build-cjs-shim.mjs && tsup src/cli.ts --format esm --sourcemap --splitting false","check":"tsc --noEmit","test":"tsx --test test/unit/*.test.ts","test:coverage":"c8 tsx --test --test-concurrency=1 test/unit/*.test.ts test/integration/*.test.ts","test:integration":"tsx --test --test-concurrency=1 test/integration/*.test.ts","test:stress":"KILL_TREE_STRESS_RUNS=20 tsx --test --test-concurrency=1 test/integration/stress.test.ts","test:cli":"node test/cli-smoke.mjs","test:package":"node test/package-smoke.mjs","verify":"npm run check && npm run test:coverage && npm run build && npm run test:integration && npm run test:cli && npm run test:package && npm pack --dry-run","prepublishOnly":"npm run verify","release:check":"node scripts/check-release.mjs"},"keywords":["process","process-tree","tree-kill","kill","signal","child-process","subprocess","orphan-process","cross-platform","cli"],"author":{"name":"Atomics Hub"},"repository":{"type":"git","url":"git+https://github.com/Atomics-hub/kill-tree.git"},"bugs":{"url":"https://github.com/Atomics-hub/kill-tree/issues"},"homepage":"https://github.com/Atomics-hub/kill-tree#readme","license":"MIT","engines":{"node":">=18"},"publishConfig":{"access":"public","provenance":true},"c8":{"all":true,"include":["src/**/*.ts"],"exclude":["src/cli.ts","src/types.ts"],"check-coverage":true,"lines":80,"statements":80,"functions":80,"branches":70,"reporter":["text"]},"devDependencies":{"@types/node":"^22.0.0","c8":"^10.1.3","tsup":"^8.5.0","tsx":"^4.20.0","typescript":"^5.8.0"},"overrides":{"esbuild":"^0.28.2"},"_id":"@a5omic/kill-tree@0.1.1","gitHead":"0b6b3926eb45b93dd3bc5437e435efa3b96c5dd5","_nodeVersion":"24.8.0","_npmVersion":"11.6.0","dist":{"integrity":"sha512-qs8qy5Wjengfq/MfSUkJ76Jhr9yRcQkU0MNbDQyA8fLXVadg6+m02tyV2wG7hnKWL9MPSnrB16AV63s9/QpE/g==","shasum":"754764870481aad8330fd7758426f3a65eedac29","tarball":"https://registry.npmjs.org/@a5omic/kill-tree/-/kill-tree-0.1.1.tgz","fileCount":14,"unpackedSize":184669,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC8TwjWSakVRpl6N6T5hhuqPZ0zsStOJNjrOCr004dl3wIgPiSCsjB+QIUNFjysNTT84KQkgZ+bxyju6ng5b+Rdm5U="}]},"_npmUser":{"name":"a5omic","email":"therekishianime@gmail.com"},"directories":{},"maintainers":[{"name":"a5omic","email":"therekishianime@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/kill-tree_0.1.1_1787935518858_0.06625594497423282"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-28T05:37:34.814Z","modified":"2026-08-28T16:45:19.186Z","0.1.0":"2026-08-28T05:37:35.211Z","0.1.1":"2026-08-28T16:45:18.997Z"},"bugs":{"url":"https://github.com/Atomics-hub/kill-tree/issues"},"author":{"name":"Atomics Hub"},"license":"MIT","homepage":"https://github.com/Atomics-hub/kill-tree#readme","keywords":["process","process-tree","tree-kill","kill","signal","child-process","subprocess","orphan-process","cross-platform","cli"],"repository":{"type":"git","url":"git+https://github.com/Atomics-hub/kill-tree.git"},"description":"Reliable cross-platform process-tree termination for Node.js, with graceful escalation, structured reports, and a tree-kill compatible API.","maintainers":[{"name":"a5omic","email":"therekishianime@gmail.com"}],"readme":"# kill-tree\n\n[![npm version](https://img.shields.io/npm/v/%40a5omic%2Fkill-tree.svg)](https://www.npmjs.com/package/@a5omic/kill-tree)\n[![CI](https://github.com/Atomics-hub/kill-tree/actions/workflows/ci.yml/badge.svg)](https://github.com/Atomics-hub/kill-tree/actions/workflows/ci.yml)\n[![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nTerminate a process tree without pretending one signal is enough.\n\n```ts\nimport { terminateTree } from '@a5omic/kill-tree';\n\nconst report = await terminateTree(server.pid!, {\n  gracePeriodMs: 2_000,\n});\n\nif (report.survivors.length) {\n  console.error('still alive:', report.survivors);\n}\n```\n\n`kill-tree` snapshots descendants, signals leaves before parents, keeps discovering during shutdown, checks process identity before every signal, escalates after a grace period, and returns a report you can act on.\n\n## Install\n\n```sh\nnpm install @a5omic/kill-tree\n```\n\n- Zero runtime dependencies\n- No native addon or install script\n- ESM and CommonJS\n- TypeScript declarations included\n- Node.js 18+\n- Linux, macOS, and Windows CI\n\n## Replace `tree-kill` without changing imports\n\nUse an npm alias:\n\n```sh\nnpm install tree-kill@npm:@a5omic/kill-tree\n```\n\nThe default export keeps the familiar callback API, including numeric-string PIDs:\n\n```ts\nimport kill from 'tree-kill';\n\nkill(pid, 'SIGTERM', (error) => {\n  if (error) console.error(error);\n});\n```\n\nThe compatibility API is intentionally one-shot. Adopt `terminateTree` when you want escalation, repeated discovery, PID identity checks across passes, and a structured result.\n\n## Modern API\n\n### `terminateTree(pid, options?)`\n\n```ts\nconst report = await terminateTree(pid, {\n  signal: 'SIGTERM',\n  forceSignal: 'SIGKILL',\n  gracePeriodMs: 5_000,\n  pollIntervalMs: 50,\n  maxPasses: 256,\n  includeRoot: true,\n  ignoreMissing: true,\n  abortSignal,\n});\n```\n\nThe result is evidence, not just absence of an exception:\n\n```ts\ninterface TerminationReport {\n  rootPid: number;\n  platform: NodeJS.Platform;\n  strategies: ('procfs' | 'ps' | 'powershell')[];\n  startedAt: string;\n  endedAt: string;\n  durationMs: number;\n  passes: number;\n  discovered: number[];\n  signaled: number[];\n  exited: number[];\n  survivors: number[];\n  skipped: Array<{\n    pid: number;\n    reason: 'identity-changed' | 'not-found';\n  }>;\n  failures: Array<{\n    pid: number;\n    signal: NodeJS.Signals | number;\n    code?: string;\n    message: string;\n  }>;\n  escalated: boolean;\n  aborted: boolean;\n}\n```\n\n`ignoreMissing` defaults to `true`, making cleanup idempotent. Set it to `false` when a missing root should reject with `code === 'ESRCH'`.\n\nPID 1 is rejected unless `allowPid1: true` is explicit.\n\n### `signalTree(pid, options?)`\n\nTake one tree snapshot and send one signal, leaves first:\n\n```ts\nimport { signalTree } from '@a5omic/kill-tree';\n\nconst report = await signalTree(pid, { signal: 'SIGINT' });\n```\n\nUse this for compatibility or non-termination signals. For reliable shutdown, prefer `terminateTree`.\n\n### `spawnGuarded(command, args?, options?)`\n\nThe strongest cleanup starts when you create the child:\n\n```ts\nimport { spawnGuarded } from '@a5omic/kill-tree';\n\nconst worker = spawnGuarded('node', ['worker.js'], {\n  stdio: 'inherit',\n  termination: { gracePeriodMs: 1_000 },\n});\n\nconsole.log(worker.pid, worker.containment);\nawait worker.terminate();\n```\n\nOn POSIX, owned children start in a separate process group and final cleanup targets that group. On Windows, `containment` is honestly reported as `snapshot`; there is no hidden native Job Object addon.\n\n## CLI\n\n```sh\nnpx @a5omic/kill-tree <pid> [signal]\nnpx @a5omic/kill-tree 4321 --force-after 2000 --json\n```\n\nOptions:\n\n```text\n--force-after <ms>  grace period before escalation\n--poll <ms>         liveness polling interval\n--no-root           signal descendants but leave the root alive\n--allow-pid-1       explicitly permit PID 1\n--json              print the structured report\n```\n\nExit code `0` means no tracked survivors or signal failures. Exit code `1` means cleanup was incomplete or the process was missing in strict CLI mode. Usage errors return `2`.\n\n## What it can and cannot guarantee\n\n| Target | Mechanism | Boundary |\n| --- | --- | --- |\n| Existing PID on Linux | `/proc` snapshots and start-time identity | A process that daemonizes and is reparented between snapshots can escape discovery. |\n| Existing PID on macOS/other POSIX | `ps` snapshots and creation-time identity | Snapshot identity is only as precise as the platform data. |\n| Existing PID on Windows | PowerShell/CIM snapshots and creation identity | Windows signals have Node's platform semantics; graceful signals are not POSIX-equivalent. |\n| `spawnGuarded` on POSIX | Repeated snapshots plus a dedicated process group | A child can deliberately create a new session/process group and escape. |\n| `spawnGuarded` on Windows | Repeated snapshots | This release does not claim kernel Job Object containment. |\n\nNo user-space package can prove it killed a descendant it never observed. `survivors` covers tracked process identities; it cannot list a process that escaped the ancestry graph before a snapshot. This package reports that boundary instead of marketing around it.\n\n## Why snapshots instead of recursive shell commands?\n\n- The full table is captured once per pass, avoiding one subprocess per descendant.\n- Signals go through `process.kill`; PID and signal values are never interpolated into a shell command.\n- Descendants are ordered leaf-first, reducing orphaning during ordinary shutdown.\n- Repeated passes catch children created by signal handlers or shutdown races.\n- Creation identity reduces the risk of signaling a reused PID.\n\nLinux reads `/proc` directly. Other POSIX systems invoke `ps` with `execFile` (no shell). Windows invokes the built-in PowerShell CIM interface with a fixed command.\n\n## Development\n\n```sh\nnpm ci\nnpm run verify\nnpm run test:stress\n```\n\nThe suite uses real isolated Node process trees, including graceful, signal-ignoring, branching, late-spawning, abort, CLI, ESM, CommonJS, and packed-tarball cases. See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).\n\n## License\n\nMIT © Atomics Hub\n","readmeFilename":"README.md"}