dorfl 0.10.0 → 0.11.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.
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +74 -4
- package/dist/cli.js.map +1 -1
- package/dist/gc.d.ts +59 -0
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +145 -2
- package/dist/gc.js.map +1 -1
- package/dist/git.d.ts +20 -0
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +155 -8
- package/dist/git.js.map +1 -1
- package/dist/skills/drive-tasks/SKILL.md +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +64 -21
- package/dist/workspace.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +94 -5
- package/src/gc.ts +202 -2
- package/src/git.ts +180 -8
- package/src/workspace.ts +74 -20
package/src/workspace.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from 'node:fs';
|
|
2
8
|
import {basename, dirname, join} from 'node:path';
|
|
3
9
|
import {git} from './git.js';
|
|
4
10
|
import {
|
|
@@ -440,21 +446,80 @@ export function updateJobRecord(
|
|
|
440
446
|
return next;
|
|
441
447
|
}
|
|
442
448
|
|
|
443
|
-
/**
|
|
444
|
-
|
|
449
|
+
/**
|
|
450
|
+
* Clear a stale worktree PATH the contract-safe way, SELF-HEALING a half-set-up
|
|
451
|
+
* claim. `git worktree remove --force` is the normal path, but it REFUSES with
|
|
452
|
+
* "is not a working tree" when `dir` is a leftover that git never registered — a
|
|
453
|
+
* dangling `~/.dorfl/work/<id>` SYMLINK, or a bare directory from a run that
|
|
454
|
+
* crashed AFTER the path appeared but BEFORE `git worktree add` registered it
|
|
455
|
+
* (an early `spawn git ENOENT`, exactly the failure this whole change targets).
|
|
456
|
+
* In that case git's own removal + `worktree prune` both leave the path in place,
|
|
457
|
+
* so the NEXT `git worktree add <dir>` fails "already exists" and the claim is
|
|
458
|
+
* wedged until a human `rm`s it by hand.
|
|
459
|
+
*
|
|
460
|
+
* So: if after the git removal the path STILL EXISTS and is NOT a live
|
|
461
|
+
* registered worktree, fall back to a BOUNDED `rmSync` of that one path. This is
|
|
462
|
+
* ADR §4-safe — we only ever `rm` a path git ITSELF refused to manage and that
|
|
463
|
+
* carries no registered worktree (no durable work to lose): a broken symlink, or
|
|
464
|
+
* an orphaned dir git does not know about. A genuinely registered worktree is
|
|
465
|
+
* removed by git and never reaches the `rmSync`.
|
|
466
|
+
*/
|
|
467
|
+
function forceClearWorktreePath(
|
|
445
468
|
mirrorPath: string,
|
|
446
469
|
dir: string,
|
|
447
|
-
branch: string,
|
|
448
470
|
env: NodeJS.ProcessEnv | undefined,
|
|
449
471
|
): void {
|
|
450
|
-
if (
|
|
451
|
-
|
|
472
|
+
if (!pathPresent(dir)) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
// Soft-remove: ignore errors (e.g. the dir is not a registered worktree).
|
|
476
|
+
try {
|
|
477
|
+
git(['worktree', 'remove', '--force', dir], mirrorPath, {env});
|
|
478
|
+
} catch {
|
|
479
|
+
// fall through to the prune + orphan-rm below
|
|
480
|
+
}
|
|
481
|
+
try {
|
|
482
|
+
git(['worktree', 'prune'], mirrorPath, {env});
|
|
483
|
+
} catch {
|
|
484
|
+
// best-effort
|
|
485
|
+
}
|
|
486
|
+
// If the path survived git's removal + prune it is an ORPHAN git will not
|
|
487
|
+
// manage (a dangling symlink / an un-registered dir). Remove that one path so
|
|
488
|
+
// the re-create's `worktree add` is not blocked. Never recurse into a
|
|
489
|
+
// registered worktree (git already handled those).
|
|
490
|
+
if (pathPresent(dir)) {
|
|
452
491
|
try {
|
|
453
|
-
|
|
492
|
+
rmSync(dir, {recursive: true, force: true});
|
|
454
493
|
} catch {
|
|
455
|
-
//
|
|
494
|
+
// best-effort: a genuine permission error surfaces on the next add
|
|
456
495
|
}
|
|
457
496
|
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* True iff `dir` EXISTS as a path entry — including a DANGLING symlink (whose
|
|
501
|
+
* target is gone). `existsSync` follows symlinks so it returns `false` for a
|
|
502
|
+
* broken one, which would let an orphaned `~/.dorfl/work/<id>` symlink slip past
|
|
503
|
+
* the cleanup; `lstatSync` inspects the LINK itself, so a dangling symlink is
|
|
504
|
+
* still seen (and removed).
|
|
505
|
+
*/
|
|
506
|
+
function pathPresent(dir: string): boolean {
|
|
507
|
+
try {
|
|
508
|
+
lstatSync(dir);
|
|
509
|
+
return true;
|
|
510
|
+
} catch {
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/** Remove a stale worktree dir / branch registration before re-creating. */
|
|
516
|
+
function clearStale(
|
|
517
|
+
mirrorPath: string,
|
|
518
|
+
dir: string,
|
|
519
|
+
branch: string,
|
|
520
|
+
env: NodeJS.ProcessEnv | undefined,
|
|
521
|
+
): void {
|
|
522
|
+
forceClearWorktreePath(mirrorPath, dir, env);
|
|
458
523
|
pruneAndDropBranch(mirrorPath, branch, env);
|
|
459
524
|
}
|
|
460
525
|
|
|
@@ -469,18 +534,7 @@ function clearStaleWorktreeOnly(
|
|
|
469
534
|
dir: string,
|
|
470
535
|
env: NodeJS.ProcessEnv | undefined,
|
|
471
536
|
): void {
|
|
472
|
-
|
|
473
|
-
try {
|
|
474
|
-
git(['worktree', 'remove', '--force', dir], mirrorPath, {env});
|
|
475
|
-
} catch {
|
|
476
|
-
// fall through to prune below
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
try {
|
|
480
|
-
git(['worktree', 'prune'], mirrorPath, {env});
|
|
481
|
-
} catch {
|
|
482
|
-
// best-effort
|
|
483
|
-
}
|
|
537
|
+
forceClearWorktreePath(mirrorPath, dir, env);
|
|
484
538
|
}
|
|
485
539
|
|
|
486
540
|
/** Prune dangling worktree registrations + delete the work branch if present. */
|