@cleocode/contracts 2026.5.78 → 2026.5.79

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.
@@ -349,3 +349,277 @@ export interface PruneWorktreesResult {
349
349
  /** Whether `git worktree prune` was run. */
350
350
  gitPruneRan: boolean;
351
351
  }
352
+
353
+ // ---------------------------------------------------------------------------
354
+ // Structured listing (T9546 — worktree-lifecycle 2/5)
355
+ // ---------------------------------------------------------------------------
356
+
357
+ /**
358
+ * Mutually-exclusive worktree status category assigned by orphan-detection
359
+ * heuristics in {@link WorktreeInfo}.
360
+ *
361
+ * Resolution precedence (first match wins):
362
+ * 1. `locked` — git porcelain reports the worktree is locked.
363
+ * 2. `orphan` — owning task is cancelled OR the branch has been deleted.
364
+ * 3. `merged` — the branch is reachable from `main` (already integrated).
365
+ * 4. `stale` — no commits in N days AND (task=done OR no live owner).
366
+ * 5. `active` — everything else (default).
367
+ *
368
+ * @task T9546
369
+ */
370
+ export type WorktreeStatusCategory = 'active' | 'stale' | 'merged' | 'orphan' | 'locked';
371
+
372
+ /**
373
+ * A single structured worktree entry with full status classification.
374
+ *
375
+ * Returned by `cleo worktree list` and the `worktree.list` dispatch operation.
376
+ * Each entry combines filesystem state, git state, and owning-task state into
377
+ * a single JSON envelope payload that downstream consumers (prune, dashboard,
378
+ * sentient daemon) can act on without re-querying git.
379
+ *
380
+ * @task T9546
381
+ */
382
+ export interface WorktreeInfo {
383
+ /** Absolute path to the worktree directory. */
384
+ path: string;
385
+ /** Branch checked out in this worktree. */
386
+ branch: string;
387
+ /** Task ID derived from the branch name (`task/T####`), or null. */
388
+ taskId: string | null;
389
+ /** Agent identifier that owns this worktree (from audit log / metadata), or null. */
390
+ owningAgent: string | null;
391
+ /** ISO-8601 timestamp of last activity (newest commit OR mtime of working tree). */
392
+ lastActivity: string;
393
+ /** Whether `git worktree list --porcelain` reports the worktree as locked. */
394
+ isLocked: boolean;
395
+ /** Whether the worktree is stale: no activity > N days AND (task done/cancelled OR branch merged). */
396
+ isStale: boolean;
397
+ /** Whether the branch is reachable from `main` (already integrated). */
398
+ isMerged: boolean;
399
+ /** Status of the owning task (if {@link taskId} is present and resolvable), or null. */
400
+ owningTaskStatus: string | null;
401
+ /** Mutually-exclusive status category — see {@link WorktreeStatusCategory}. */
402
+ statusCategory: WorktreeStatusCategory;
403
+ }
404
+
405
+ /**
406
+ * Options for the structured worktree listing operation.
407
+ *
408
+ * @task T9546
409
+ */
410
+ export interface ListWorktreesOpts {
411
+ /**
412
+ * Absolute path to the project root (used to compute the project hash
413
+ * and resolve the canonical worktrees directory).
414
+ */
415
+ projectRoot?: string;
416
+ /**
417
+ * Filter results to entries with one of these status categories.
418
+ * When omitted, all entries are returned.
419
+ */
420
+ statusFilter?: WorktreeStatusCategory[];
421
+ /**
422
+ * Staleness threshold in days. An entry is marked stale when its last
423
+ * activity is older than this AND either the owning task is done/cancelled
424
+ * or no owning task can be resolved while the branch is merged.
425
+ *
426
+ * @default 7
427
+ */
428
+ staleDays?: number;
429
+ }
430
+
431
+ /**
432
+ * Result of the structured worktree listing operation.
433
+ *
434
+ * @task T9546
435
+ */
436
+ export interface ListWorktreesResult {
437
+ /** All matched worktree entries (post-filter). */
438
+ worktrees: WorktreeInfo[];
439
+ }
440
+
441
+ // ---------------------------------------------------------------------------
442
+ // Lifecycle prune + force-unlock (T9547 — worktree-lifecycle 3/5)
443
+ // ---------------------------------------------------------------------------
444
+
445
+ /**
446
+ * Canonical action recorded in `.cleo/audit/worktree-lifecycle.jsonl`.
447
+ *
448
+ * - `prune` — orphaned/merged worktree was removed.
449
+ * - `prune-skip` — orphan was detected but skipped (user said N, or had uncommitted changes).
450
+ * - `force-unlock` — git index.lock removed + `git worktree unlock` ran.
451
+ * - `complete` — worktree was merged (`--no-ff`) into the default branch and pruned (T9548).
452
+ * - `complete-skip` — idempotent no-op (worktree already integrated or branch absent) (T9548).
453
+ * - `complete-manual` — operator marked the worktree as manually-handled via
454
+ * `--resolve manual`; no automatic merge attempted (T9548).
455
+ * - `complete-conflict` — auto-merge attempted but failed (e.g. rebase/merge conflict);
456
+ * worktree was preserved for manual resolution (T9548).
457
+ *
458
+ * @task T9547
459
+ * @task T9548
460
+ */
461
+ export type WorktreeLifecycleAction =
462
+ | 'prune'
463
+ | 'prune-skip'
464
+ | 'force-unlock'
465
+ | 'complete'
466
+ | 'complete-skip'
467
+ | 'complete-manual'
468
+ | 'complete-conflict';
469
+
470
+ /**
471
+ * One append-only entry written to `.cleo/audit/worktree-lifecycle.jsonl` by
472
+ * the prune + force-unlock commands.
473
+ *
474
+ * The shape intentionally mirrors the existing audit-jsonl pattern used by
475
+ * `worktree-prune.jsonl` (single-task path) so downstream log shippers can
476
+ * unify both streams. Optional fields are omitted (not null) when absent,
477
+ * keeping the JSONL surface compact and grep-friendly.
478
+ *
479
+ * @task T9547
480
+ */
481
+ export interface WorktreeLifecycleAuditEntry {
482
+ /** ISO-8601 timestamp when the action was attempted. */
483
+ timestamp: string;
484
+ /** Agent / actor that initiated the action (env `CLEO_AGENT_ID` ?? `'cleo'`). */
485
+ actor: string;
486
+ /** The action performed — see {@link WorktreeLifecycleAction}. */
487
+ action: WorktreeLifecycleAction;
488
+ /** Absolute path to the worktree directory the action targeted. */
489
+ target: string;
490
+ /** Branch name (e.g. `task/T9547`) when known. */
491
+ branch?: string;
492
+ /** Task ID parsed from the branch name when known. */
493
+ taskId?: string;
494
+ /** Free-form reason — e.g. `orphaned-merged`, `dirty-skip`, `index-lock`. */
495
+ reason?: string;
496
+ /** Whether the action completed without error. */
497
+ success: boolean;
498
+ /** Error message when {@link success} is false. */
499
+ error?: string;
500
+ }
501
+
502
+ /**
503
+ * Options for {@link pruneOrphanedWorktreesByStatus} — the SDK primitive behind
504
+ * `cleo worktree prune --orphaned`.
505
+ *
506
+ * Per-orphan Y/N confirmation is the responsibility of the CLI layer; the SDK
507
+ * primitive itself is non-interactive and acts on the input set wholesale.
508
+ *
509
+ * @task T9547
510
+ */
511
+ export interface PruneOrphanedWorktreesOpts {
512
+ /** Absolute path to the project root used for git invocations + audit log. */
513
+ projectRoot: string;
514
+ /**
515
+ * When true, do not actually remove worktrees — return the set that WOULD
516
+ * be pruned with `success: true` and the appropriate `reason`. The audit
517
+ * log is NOT written under `--dry-run`.
518
+ *
519
+ * @default false
520
+ */
521
+ dryRun?: boolean;
522
+ /**
523
+ * Staleness threshold in days passed through to {@link listWorktrees} when
524
+ * the caller wants a non-default `isStale` window.
525
+ *
526
+ * @default 7
527
+ */
528
+ staleDays?: number;
529
+ /**
530
+ * Optional subset of paths to prune. When supplied, only worktrees whose
531
+ * absolute `path` matches one of these strings are removed. The CLI passes
532
+ * this set after the user has confirmed per-orphan; in pure SDK use, omit
533
+ * the field to prune every orphan/merged entry the listing surfaces.
534
+ */
535
+ paths?: readonly string[];
536
+ /**
537
+ * Override actor name written to the audit log. Defaults to
538
+ * `process.env.CLEO_AGENT_ID ?? 'cleo'`.
539
+ */
540
+ actor?: string;
541
+ /**
542
+ * Optional override for the audit-log file path (testing). When omitted,
543
+ * writes to `<projectRoot>/.cleo/audit/worktree-lifecycle.jsonl`.
544
+ */
545
+ auditLogPath?: string;
546
+ }
547
+
548
+ /**
549
+ * Per-worktree outcome from a prune attempt.
550
+ *
551
+ * @task T9547
552
+ */
553
+ export interface PrunedWorktreeOutcome {
554
+ /** Absolute path of the worktree. */
555
+ path: string;
556
+ /** Branch name (when known) — used by callers to render audit summaries. */
557
+ branch: string;
558
+ /** Task ID derived from the branch (when known). */
559
+ taskId: string | null;
560
+ /** Why this worktree was selected — e.g. `orphaned-merged`, `orphan-cancelled`. */
561
+ reason: string;
562
+ /** Whether the worktree was actually removed (false under --dry-run or on error). */
563
+ pruned: boolean;
564
+ /** Whether the task branch was deleted (only true when it was safe to do so). */
565
+ branchDeleted: boolean;
566
+ /** Error message when prune failed (set only on `pruned=false` + error path). */
567
+ error?: string;
568
+ }
569
+
570
+ /**
571
+ * Result of {@link pruneOrphanedWorktreesByStatus}.
572
+ *
573
+ * @task T9547
574
+ */
575
+ export interface PruneOrphanedWorktreesResult {
576
+ /** Number of worktrees successfully pruned. */
577
+ prunedCount: number;
578
+ /** Number of orphans detected but NOT pruned (filtered by `paths`, dry-run, or errored). */
579
+ skippedCount: number;
580
+ /** Per-worktree outcomes, one entry per orphan that was considered. */
581
+ outcomes: PrunedWorktreeOutcome[];
582
+ /** Per-worktree errors raised during prune (subset of {@link outcomes} where `error` is set). */
583
+ errors: Array<{ path: string; error: string }>;
584
+ /** Whether {@link PruneOrphanedWorktreesOpts.dryRun} was set. */
585
+ dryRun: boolean;
586
+ }
587
+
588
+ /**
589
+ * Options for {@link forceUnlockWorktree} — the SDK primitive behind
590
+ * `cleo worktree force-unlock <taskId>`.
591
+ *
592
+ * @task T9547
593
+ */
594
+ export interface ForceUnlockWorktreeOpts {
595
+ /** Absolute path to the project root used for git invocations. */
596
+ projectRoot: string;
597
+ /** Task ID whose worktree should be force-unlocked. */
598
+ taskId: string;
599
+ /** Override actor name written to the audit log. */
600
+ actor?: string;
601
+ /** Optional override for the audit-log file path (testing). */
602
+ auditLogPath?: string;
603
+ }
604
+
605
+ /**
606
+ * Result of {@link forceUnlockWorktree}.
607
+ *
608
+ * @task T9547
609
+ */
610
+ export interface ForceUnlockWorktreeResult {
611
+ /** Task ID whose worktree was located. */
612
+ taskId: string;
613
+ /** Absolute path to the worktree (when located). */
614
+ path: string | null;
615
+ /** Whether `.git/index.lock` was present and removed. */
616
+ indexLockRemoved: boolean;
617
+ /** Whether `git worktree unlock` was executed (because porcelain reported `locked`). */
618
+ worktreeUnlocked: boolean;
619
+ /** Whether the worktree had uncommitted changes at the time of unlock (warn-only). */
620
+ hadUncommittedChanges: boolean;
621
+ /** Aggregate success — true when at least one unlock action ran without error. */
622
+ success: boolean;
623
+ /** Error message when no worktree could be located or all actions failed. */
624
+ error?: string;
625
+ }