@icntswm/skillcheck 1.0.1 → 1.0.2

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.
@@ -202,22 +202,45 @@ function spawnErrorMessage(e, bin) {
202
202
  }
203
203
  /** Live runs by process group id, with their tmp dirs; see abortActiveRuns. */
204
204
  const active = new Map();
205
+ function groupAlive(pid) {
206
+ try {
207
+ process.kill(-pid, 0);
208
+ return true;
209
+ }
210
+ catch {
211
+ return false;
212
+ }
213
+ }
205
214
  /**
206
215
  * Kill every running claude group and remove its tmp dir. Runs are detached
207
216
  * process groups, so Ctrl+C on skillcheck does not reach them: without this
208
- * they keep running (and billing) after skillcheck exits.
217
+ * they keep running (and billing) after skillcheck exits. Groups still alive
218
+ * after graceMs get SIGKILL, so a process that ignores SIGTERM dies too.
209
219
  */
210
- export function abortActiveRuns() {
211
- for (const [pid, workdir] of active) {
220
+ export async function abortActiveRuns(graceMs = KILL_GRACE_MS) {
221
+ const runs = [...active];
222
+ active.clear();
223
+ for (const [pid] of runs) {
212
224
  try {
213
225
  process.kill(-pid, "SIGTERM");
214
226
  }
215
227
  catch {
216
228
  // already exited
217
229
  }
230
+ }
231
+ const deadline = Date.now() + graceMs;
232
+ while (runs.some(([pid]) => groupAlive(pid)) && Date.now() < deadline) {
233
+ await new Promise((r) => setTimeout(r, 50));
234
+ }
235
+ for (const [pid, workdir] of runs) {
236
+ try {
237
+ process.kill(-pid, "SIGKILL");
238
+ }
239
+ catch {
240
+ // already exited
241
+ }
218
242
  rmSync(workdir, { recursive: true, force: true });
219
243
  }
220
- active.clear();
221
244
  }
222
245
  const LOGIN_HINT = " — with --config-dir, auth comes from ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN (claude setup-token)";
223
246
  /** A fresh config dir has no credentials; point the user at the env vars. */
package/dist/cli.js CHANGED
@@ -670,8 +670,9 @@ if (invokedDirectly()) {
670
670
  // agent runs are detached process groups that Ctrl+C does not reach
671
671
  for (const [signal, code] of [["SIGINT", 130], ["SIGTERM", 143]]) {
672
672
  process.once(signal, () => {
673
- abortActiveRuns();
674
- process.exit(code);
673
+ // a second Ctrl+C during the grace period exits at once
674
+ process.once(signal, () => process.exit(code));
675
+ void abortActiveRuns().finally(() => process.exit(code));
675
676
  });
676
677
  }
677
678
  main(process.argv.slice(2)).then((code) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icntswm/skillcheck",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Regression tests for Claude Code skills: check that every request still loads the right skill",
5
5
  "type": "module",
6
6
  "bin": {