@pmelab/gtd 26.1.0 → 27.0.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtd",
3
- "version": "26.1.0",
3
+ "version": "27.0.0",
4
4
  "description": "Drive the gtd loop inside Claude Code: agent turns run as subagents, gtd decides every beat",
5
5
  "author": {
6
6
  "name": "Philipp Melab"
package/README.md CHANGED
@@ -552,6 +552,19 @@ unknown key. `gtd.config.ts` is the only repository config: gtd 23 removed the
552
552
  home directory — fails every command until you delete it. See
553
553
  [Migrating from `.gtdrc`](https://github.com/pmelab/gtd/blob/main/docs/configuration.md#migrating-from-gtdrc).
554
554
 
555
+ ## Checking a range for stray files
556
+
557
+ ```sh
558
+ gtd strays main..HEAD 'src/**' '*.md' # files the range touched outside the globs
559
+ ```
560
+
561
+ Prints a sorted JSON array of every file the range's committed diff touched that
562
+ matches none of the globs, and exits `10` when there are any (`0` when none).
563
+ stderr gets a summary; `--verbose` adds one `stray:` line per path. `--json=<n>`
564
+ picks one entry (nothing when past the end). Symlinks count as paths and are
565
+ never followed; a submodule counts as its path, whatever `diff.ignoreSubmodules`
566
+ or `.gitmodules` `ignore` say. Works in any git repository, no process needed.
567
+
555
568
  ## Process history
556
569
 
557
570
  When a process finishes, the landing that closes it also keeps the whole run
@@ -602,9 +615,9 @@ install for any of these but `./testkit`: gtd resolves a `gtd.config.ts`'s
602
615
 
603
616
  The `gtd` bin's commands — `next`, `land`, `abandon`, `restore`, `validate`,
604
617
  `check`, `uncheck`, `install`, `summary`, `base`, `door`, `doors`, `version`,
605
- `help`, `history list`, `history show`, `history fetch`, and the tools `ui`,
606
- `lsp`, `loop` and `judge` (`judge answer`, `judge run`) — are listed in the
607
- [CLI reference](https://github.com/pmelab/gtd/blob/main/docs/cli.md).
618
+ `help`, `history list`, `history show`, `history fetch`, `strays`, and the tools
619
+ `ui`, `lsp`, `loop` and `judge` (`judge answer`, `judge run`) — are listed in
620
+ the [CLI reference](https://github.com/pmelab/gtd/blob/main/docs/cli.md).
608
621
 
609
622
  ## License
610
623
 
@@ -10197,7 +10197,7 @@ const catchAll$1 = /*#__PURE__*/ dual(2, (self, f) => matchEffect$1(self, {
10197
10197
  onFailure: f,
10198
10198
  onSuccess: succeed$5
10199
10199
  }));
10200
- const catchIf = /*#__PURE__*/ dual(3, (self, predicate, f) => catchAllCause$1(self, (cause) => {
10200
+ const catchIf$1 = /*#__PURE__*/ dual(3, (self, predicate, f) => catchAllCause$1(self, (cause) => {
10201
10201
  const either = failureOrCause$1(cause);
10202
10202
  switch (either._tag) {
10203
10203
  case "Left": return predicate(either.left) ? f(either.left) : failCause$3(cause);
@@ -12202,7 +12202,7 @@ const catchTag$1 = /*#__PURE__*/ dual((args) => isEffect$1(args[0]), (self, ...a
12202
12202
  for (let i = 0; i < args.length - 1; i++) if (args[i] === tag) return true;
12203
12203
  return false;
12204
12204
  };
12205
- return catchIf(self, predicate, f);
12205
+ return catchIf$1(self, predicate, f);
12206
12206
  });
12207
12207
  const diffFiberRefs = (self) => summarized(self, fiberRefs, diff$2);
12208
12208
  const bind = /*#__PURE__*/ bind$1(map$5, flatMap$3);
@@ -18757,6 +18757,59 @@ const catchAll = catchAll$1;
18757
18757
  */
18758
18758
  const catchAllCause = catchAllCause$1;
18759
18759
  /**
18760
+ * Recovers from specific errors based on a predicate.
18761
+ *
18762
+ * **When to Use**
18763
+ *
18764
+ * `catchIf` works similarly to {@link catchSome}, but it allows you to
18765
+ * recover from errors by providing a predicate function. If the predicate
18766
+ * matches the error, the recovery effect is applied. This function doesn't
18767
+ * alter the error type, so the resulting effect still carries the original
18768
+ * error type unless a user-defined type guard is used to narrow the type.
18769
+ *
18770
+ * **Example** (Catching Specific Errors with a Predicate)
18771
+ *
18772
+ * ```ts
18773
+ * import { Effect, Random } from "effect"
18774
+ *
18775
+ * class HttpError {
18776
+ * readonly _tag = "HttpError"
18777
+ * }
18778
+ *
18779
+ * class ValidationError {
18780
+ * readonly _tag = "ValidationError"
18781
+ * }
18782
+ *
18783
+ * // ┌─── Effect<string, HttpError | ValidationError, never>
18784
+ * // ▼
18785
+ * const program = Effect.gen(function* () {
18786
+ * const n1 = yield* Random.next
18787
+ * const n2 = yield* Random.next
18788
+ * if (n1 < 0.5) {
18789
+ * yield* Effect.fail(new HttpError())
18790
+ * }
18791
+ * if (n2 < 0.5) {
18792
+ * yield* Effect.fail(new ValidationError())
18793
+ * }
18794
+ * return "some result"
18795
+ * })
18796
+ *
18797
+ * // ┌─── Effect<string, ValidationError, never>
18798
+ * // ▼
18799
+ * const recovered = program.pipe(
18800
+ * Effect.catchIf(
18801
+ * // Only handle HttpError errors
18802
+ * (error) => error._tag === "HttpError",
18803
+ * () => Effect.succeed("Recovering from HttpError")
18804
+ * )
18805
+ * )
18806
+ * ```
18807
+ *
18808
+ * @since 2.0.0
18809
+ * @category Error handling
18810
+ */
18811
+ const catchIf = catchIf$1;
18812
+ /**
18760
18813
  * Catches and handles specific errors by their `_tag` field, which is used as a
18761
18814
  * discriminator.
18762
18815
  *
@@ -25396,4 +25449,4 @@ Struct({
25396
25449
  description: "Represents an issue returned by the ArrayFormatter formatter"
25397
25450
  });
25398
25451
  //#endregion
25399
- export { makeSemaphore as $, toJSON as $i, mapBoth$2 as $n, none$2 as $r, provideMerge as $t, catchTag as A, getOption as Ai, scopeTag as An, interrupt$2 as Ar, sync as At, flatten as B, isSome as Bi, make$12 as Bn, tap$1 as Br, unsafeMakeSemaphore as Bt, all as C, unsafeFromArray as Ci, isInterruptedOnly as Cn, exitFailCause$1 as Cr, scope as Ct, async as D, Tag as Di, ensuring$1 as Dn, fiberId as Dr, serviceOption as Dt, asVoid as E, GenericTag as Ei, squash as En, exitZipRight as Er, scopedWith as Et, exit as F, filter$3 as Fi, sequential$1 as Fn, onInterrupt as Fr, tryPromise as Ft, forkScoped as G, isLeft as Gi, Class as Gn, withFiberRuntime$1 as Gr, zipRight as Gt, forEach as H, match$6 as Hi, set as Hn, uninterruptibleMask$2 as Hr, withFiberRuntime as Ht, fail$1 as I, flatMap$6 as Ii, bind as In, scopeClose as Ir, try_ as It, interrupt as J, right as Ji, die$2 as Jn, empty$15 as Jr, TaggedError$1 as Jt, gen as K, left as Ki, spanTag as Kn, zipRight$2 as Kr, zipWith as Kt, failCause as L, fromNullable as Li, bindTo as Ln, succeed$5 as Lr, uninterruptible as Lt, dieMessage as M, empty$22 as Mi, inheritAll as Mn, interruptFiber as Mr, tapErrorCause as Mt, either as N, fromIterable$6 as Ni, interruptAllAs as Nn, interruptible$2 as Nr, timeout as Nt, catchAll as O, add$2 as Oi, forEachConcurrentDiscard as On, fiberRefLocally as Or, succeed$1 as Ot, ensuring as P, splitAt as Pi, join as Pn, map$5 as Pr, tryMap as Pt, logWarning as Q, format$4 as Qi, map$4 as Qn, make$20 as Qr, provide$1 as Qt, fiberIdWith as R, getOrElse as Ri, sleep as Rn, suspend$3 as Rr, uninterruptibleMask as Rt, addFinalizer as S, toArray$1 as Si, isInterrupted as Sn, exitFail as Sr, sandbox as St, as as T, unsafeHead as Ti, reduceWithContext as Tn, exitVoid$1 as Tr, scoped as Tt, forkDaemon as U, none$4 as Ui, update$1 as Un, void_$2 as Ur, zip as Ut, fnUntraced as V, map$11 as Vi, modify as Vn, uninterruptible$1 as Vr, when as Vt, forkIn as W, some as Wi, TypeId$4 as Wn, whenEffect as Wr, zipLeft as Wt, intoDeferred as X, BaseProto as Xi, failCause$2 as Xn, combineAll as Xr, fromEffect as Xt, interruptible as Y, pipeArguments as Yi, fail$4 as Yn, combine$5 as Yr, tagged as Yt, isEffect as Z, NodeInspectSymbol as Zi, isFailure as Zn, ids as Zr, mergeAll as Zt, decodeSync as _, prepend$1 as _i, failureOrCause as _n, deferredMake as _r, repeat as _t, Literal as a, constVoid as aa, decode as ai, forkIn$1 as an, as$1 as ar, matchCauseEffect as at, acquireRelease as b, take as bi, interruptors as bn, deferredUnsafeMake as br, runPromiseExit as bt, String$ as c, pipe as ca, appendAll$1 as ci, addFinalizer$1 as cn, catchAll$1 as cr, onError as ct, Union as d, filter$1 as di, extend$1 as dn, deferredComplete as dr, orElseSucceed as dt, equals$2 as ea, get$4 as ei, scoped$1 as en, match$3 as er, map$1 as et, between as f, fromIterable$5 as fi, fork as fn, deferredFail as fr, promise as ft, standardSchemaV1 as g, of$1 as gi, fail$2 as gn, deferredIsDone as gr, raceWith as gt, optionalWith as h, map$9 as hi, die$1 as hn, deferredInterruptWith as hr, race as ht, Defect as i, constUndefined as ia, size$2 as ii, updateContext as in, zipRight$1 as ir, matchCause as it, die as j, omit$1 as ji, _await as jn, interruptAsFiber as jr, tap as jt, catchAllCause as k, get$7 as ki, scopeMake as kn, flatMap$3 as kr, suspend$1 as kt, Struct as l, drop as li, addFinalizerExit as ln, currentContext as lr, orDie as lt, optional as m, isEmpty$5 as mi, NoSuchElementException as mn, deferredInterrupt as mr, provideService as mt, Boolean$ as n, constFalse as na, set$3 as ni, unsafeFork as nn, void_$1 as nr, mapError$1 as nt, Never as o, dual as oa, isFinite as oi, unsafeMakeSemaphore$1 as on, asVoid$1 as or, matchEffect as ot, decodeUnknown as p, isChunk as pi, make$2 as pn, deferredFailCause as pr, provide as pt, ignore as q, match$7 as qi, all$2 as qn, DeferredTypeId as qr, spaced as qt, Cause as r, constTrue as ra, has$1 as ri, unsafeRunPromiseExit as rn, zip$1 as rr, match as rt, Number$ as s, identity as sa, append as si, Scope as sn, asyncInterrupt as sr, negate as st, Array$ as t, hasProperty as ta, make$22 as ti, succeed$2 as tn, succeed$4 as tr, mapBoth$1 as tt, TaggedError as u, empty$19 as ui, close as un, deferredAwait as ur, orElse$1 as ut, encodeSync as v, reduce$6 as vi, flipCauseOption as vn, deferredSucceed as vr, retry as vt, annotateLogs as w, unsafeGet as wi, map$3 as wn, exitSucceed$1 as wr, scopeWith as wt, acquireUseRelease as x, takeRight as xi, isDieType as xn, exitAs as xr, runtime as xt, _void as y, reverse$1 as yi, interrupt$1 as yn, deferredUnsafeDone as yr, runPromise as yt, flatMap$1 as z, isNone as zi, get as zn, sync$1 as zr, unsafeMakeLatch as zt };
25452
+ export { logWarning as $, format$4 as $i, map$4 as $n, make$20 as $r, provide$1 as $t, catchIf as A, get$7 as Ai, scopeMake as An, flatMap$3 as Ar, suspend$1 as At, flatMap$1 as B, isNone as Bi, get as Bn, sync$1 as Br, unsafeMakeLatch as Bt, all as C, toArray$1 as Ci, isInterrupted as Cn, exitFail as Cr, sandbox as Ct, async as D, GenericTag as Di, squash as Dn, exitZipRight as Dr, scopedWith as Dt, asVoid as E, unsafeHead as Ei, reduceWithContext as En, exitVoid$1 as Er, scoped as Et, ensuring as F, splitAt as Fi, join as Fn, map$5 as Fr, tryMap as Ft, forkIn as G, some as Gi, TypeId$4 as Gn, whenEffect as Gr, zipLeft as Gt, fnUntraced as H, map$11 as Hi, modify as Hn, uninterruptible$1 as Hr, when as Ht, exit as I, filter$3 as Ii, sequential$1 as In, onInterrupt as Ir, tryPromise as It, ignore as J, match$7 as Ji, all$2 as Jn, DeferredTypeId as Jr, spaced as Jt, forkScoped as K, isLeft as Ki, Class as Kn, withFiberRuntime$1 as Kr, zipRight as Kt, fail$1 as L, flatMap$6 as Li, bind as Ln, scopeClose as Lr, try_ as Lt, die as M, omit$1 as Mi, _await as Mn, interruptAsFiber as Mr, tap as Mt, dieMessage as N, empty$22 as Ni, inheritAll as Nn, interruptFiber as Nr, tapErrorCause as Nt, catchAll as O, Tag as Oi, ensuring$1 as On, fiberId as Or, serviceOption as Ot, either as P, fromIterable$6 as Pi, interruptAllAs as Pn, interruptible$2 as Pr, timeout as Pt, isEffect as Q, NodeInspectSymbol as Qi, isFailure as Qn, ids as Qr, mergeAll as Qt, failCause as R, fromNullable as Ri, bindTo as Rn, succeed$5 as Rr, uninterruptible as Rt, addFinalizer as S, takeRight as Si, isDieType as Sn, exitAs as Sr, runtime as St, as as T, unsafeGet as Ti, map$3 as Tn, exitSucceed$1 as Tr, scopeWith as Tt, forEach as U, match$6 as Ui, set as Un, uninterruptibleMask$2 as Ur, withFiberRuntime as Ut, flatten as V, isSome as Vi, make$12 as Vn, tap$1 as Vr, unsafeMakeSemaphore as Vt, forkDaemon as W, none$4 as Wi, update$1 as Wn, void_$2 as Wr, zip as Wt, interruptible as X, pipeArguments as Xi, fail$4 as Xn, combine$5 as Xr, tagged as Xt, interrupt as Y, right as Yi, die$2 as Yn, empty$15 as Yr, TaggedError$1 as Yt, intoDeferred as Z, BaseProto as Zi, failCause$2 as Zn, combineAll as Zr, fromEffect as Zt, decodeSync as _, of$1 as _i, fail$2 as _n, deferredIsDone as _r, raceWith as _t, Literal as a, constUndefined as aa, size$2 as ai, updateContext as an, zipRight$1 as ar, matchCause as at, acquireRelease as b, reverse$1 as bi, interrupt$1 as bn, deferredUnsafeDone as br, runPromise as bt, String$ as c, identity as ca, append as ci, Scope as cn, asyncInterrupt as cr, negate as ct, Union as d, empty$19 as di, close as dn, deferredAwait as dr, orElse$1 as dt, toJSON as ea, none$2 as ei, provideMerge as en, mapBoth$2 as er, makeSemaphore as et, between as f, filter$1 as fi, extend$1 as fn, deferredComplete as fr, orElseSucceed as ft, standardSchemaV1 as g, map$9 as gi, die$1 as gn, deferredInterruptWith as gr, race as gt, optionalWith as h, isEmpty$5 as hi, NoSuchElementException as hn, deferredInterrupt as hr, provideService as ht, Defect as i, constTrue as ia, has$1 as ii, unsafeRunPromiseExit as in, zip$1 as ir, match as it, catchTag as j, getOption as ji, scopeTag as jn, interrupt$2 as jr, sync as jt, catchAllCause as k, add$2 as ki, forEachConcurrentDiscard as kn, fiberRefLocally as kr, succeed$1 as kt, Struct as l, pipe as la, appendAll$1 as li, addFinalizer$1 as ln, catchAll$1 as lr, onError as lt, optional as m, isChunk as mi, make$2 as mn, deferredFailCause as mr, provide as mt, Boolean$ as n, hasProperty as na, make$22 as ni, succeed$2 as nn, succeed$4 as nr, mapBoth$1 as nt, Never as o, constVoid as oa, decode as oi, forkIn$1 as on, as$1 as or, matchCauseEffect as ot, decodeUnknown as p, fromIterable$5 as pi, fork as pn, deferredFail as pr, promise as pt, gen as q, left as qi, spanTag as qn, zipRight$2 as qr, zipWith as qt, Cause as r, constFalse as ra, set$3 as ri, unsafeFork as rn, void_$1 as rr, mapError$1 as rt, Number$ as s, dual as sa, isFinite as si, unsafeMakeSemaphore$1 as sn, asVoid$1 as sr, matchEffect as st, Array$ as t, equals$2 as ta, get$4 as ti, scoped$1 as tn, match$3 as tr, map$1 as tt, TaggedError as u, drop as ui, addFinalizerExit as un, currentContext as ur, orDie as ut, encodeSync as v, prepend$1 as vi, failureOrCause as vn, deferredMake as vr, repeat as vt, annotateLogs as w, unsafeFromArray as wi, isInterruptedOnly as wn, exitFailCause$1 as wr, scope as wt, acquireUseRelease as x, take as xi, interruptors as xn, deferredUnsafeMake as xr, runPromiseExit as xt, _void as y, reduce$6 as yi, flipCauseOption as yn, deferredSucceed as yr, retry as yt, fiberIdWith as z, getOrElse as zi, sleep as zn, suspend$3 as zr, uninterruptibleMask as zt };
@@ -1,6 +1,6 @@
1
1
  import { accessFor, agent, answered, changesSince, check, codeThreads, defineWorkflow, env, head, human, judge, numeric, quote, read, refuse, removeScript, run, scope, showScript, skillsFor, start, vars } from "../flows.mjs";
2
2
  import { escalation as escalation$1, fixFindings, gate as gate$1, healthy as healthy$1, lensScopes, lensesOf, qualityLap as qualityLap$1, removedAssertions, reviewToHandOff } from "../workflow-kit.mjs";
3
- import { c as String$, g as standardSchemaV1, h as optionalWith, l as Struct } from "./Schema-B5yK7QrT.mjs";
3
+ import { c as String$, g as standardSchemaV1, h as optionalWith, l as Struct } from "./Schema-CyImoHTj.mjs";
4
4
  //#region src/workflows/shared/prose.ts
5
5
  const skillsPreamble = `- Load whatever's listed here that your harness actually has installed,
6
6
  skip anything it doesn't — silently, never stopping or asking about a
@@ -22,6 +22,8 @@ const EXIT_OK = 0;
22
22
  const EXIT_RUNTIME_ERROR = 1;
23
23
  /** gtd was invoked wrong — an unknown option/command, bad arity, a scope violation, a decode failure. */
24
24
  const EXIT_USAGE_ERROR = 2;
25
+ /** `gtd strays` found touched paths outside the allowlist — that command's only, and it carries no other meaning. */
26
+ const EXIT_STRAYS_FOUND = 10;
25
27
  const EXIT_SIGINT = 130;
26
28
  const EXIT_SIGTERM = 143;
27
29
  /** Frozen: adding a code is a driver-protocol break, the same as removing a wire field. */
@@ -29,6 +31,7 @@ const EXIT_CODES = /* @__PURE__ */ new Set([
29
31
  0,
30
32
  1,
31
33
  2,
34
+ 10,
32
35
  130,
33
36
  143
34
37
  ]);
@@ -466,8 +469,8 @@ const compile = (glob) => {
466
469
  i += 2;
467
470
  }
468
471
  }
469
- return new RegExp(`${pattern}$`);
472
+ return new RegExp(`${pattern}$`, "s");
470
473
  };
471
474
  const globMatches = (path, glob) => compile(glob).test(path);
472
475
  //#endregion
473
- export { EXIT_SIGTERM as C, WIRE_SCHEMA as D, UNATTRIBUTED_MODEL as E, renderFormat as O, EXIT_SIGINT as S, ACCESS_REFUSAL as T, demandOf as _, landFields as a, EXIT_OK as b, renderLandJson as c, renderBeatPlain as d, beatDocument as f, beatKindOf as g, BEAT_KINDS as h, judgeJson as i, renderLandPlain as l, statusOf as m, resolveSession as n, landProseText as o, renderBeatJson as p, selectPath as r, noopText as s, globMatches as t, DEMAND_BRIEFING as u, stallDiagnosis as v, EXIT_USAGE_ERROR as w, EXIT_RUNTIME_ERROR as x, EXIT_CODES as y };
476
+ export { EXIT_SIGTERM as C, UNATTRIBUTED_MODEL as D, ACCESS_REFUSAL as E, WIRE_SCHEMA as O, EXIT_SIGINT as S, EXIT_USAGE_ERROR as T, demandOf as _, landFields as a, EXIT_OK as b, renderLandJson as c, renderBeatPlain as d, beatDocument as f, beatKindOf as g, BEAT_KINDS as h, judgeJson as i, renderFormat as k, renderLandPlain as l, statusOf as m, resolveSession as n, landProseText as o, renderBeatJson as p, selectPath as r, noopText as s, globMatches as t, DEMAND_BRIEFING as u, stallDiagnosis as v, EXIT_STRAYS_FOUND as w, EXIT_RUNTIME_ERROR as x, EXIT_CODES as y };
@@ -4126,7 +4126,7 @@ var DeferredSymbolKey, DeferredTypeId$1, deferredVariance, pending, done$5, init
4126
4126
  return new SingleShotGen(this.self);
4127
4127
  }
4128
4128
  };
4129
- })), blocked, runRequestBlock, EffectTypeId, RevertFlags, EffectPrimitive, EffectPrimitiveFailure, EffectPrimitiveSuccess, isEffect$1, withFiberRuntime$1, acquireUseRelease$3, as$1, asVoid$1, custom, unsafeAsync, asyncInterrupt, async_, catchAllCause$1, catchAll$1, catchIf, originalSymbol, capture, die$3, dieMessage$1, either$1, exit$2, fail$12, failSync, failCause$6, failCauseSync$1, fiberId, fiberIdWith$1, flatMap$6, andThen$1, step, flatten$6, matchCause$1, matchCauseEffect$2, matchEffect$1, forEachSequential, forEachSequentialDiscard, interrupt$4, interruptWith, interruptible$2, intoDeferred$1, map$8, mapBoth$3, mapError$2, onError$1, onExit$1, onInterrupt, orElse$2, orDie$1, orDieWith, succeed$8, suspend$7, sync$2, tap$1, transplant, attemptOrElse, uninterruptible$1, uninterruptibleMask$2, void_$4, updateRuntimeFlags, whenEffect, whileLoop, fromIterator, gen$1, fnUntraced$1, withRuntimeFlags, yieldNow$2, zip$3, zipLeft$1, zipRight$3, zipWith$1, interruptFiber, interruptAsFiber, logLevelAll, logLevelFatal, logLevelError, logLevelWarning, logLevelInfo, logLevelDebug, logLevelTrace, logLevelNone, FiberRefSymbolKey, FiberRefTypeId, fiberRefVariance, fiberRefGet, fiberRefGetWith, fiberRefSet, fiberRefModify, fiberRefLocally, fiberRefLocallyWith, fiberRefUnsafeMake, fiberRefUnsafeMakeHashSet, fiberRefUnsafeMakeReadonlyArray, fiberRefUnsafeMakeContext, fiberRefUnsafeMakePatch, fiberRefUnsafeMakeRuntimeFlags, currentContext$1, currentSchedulingPriority, currentMaxOpsBeforeYield, currentLogAnnotations, currentLogLevel, currentLogSpan, withSchedulingPriority, currentConcurrency, currentRequestBatching, currentUnhandledErrorLogLevel, currentVersionMismatchErrorLogLevel, currentMetricLabels, currentForkScopeOverride, currentInterruptedCause, ScopeTypeId, CloseableScopeTypeId, scopeAddFinalizer, scopeAddFinalizerExit, scopeClose, scopeFork, causeSquash, causeSquashWith, YieldableError$1, makeException, RuntimeExceptionTypeId, RuntimeException, InterruptedExceptionTypeId, InterruptedException, isInterruptedException, IllegalArgumentExceptionTypeId, IllegalArgumentException$1, NoSuchElementExceptionTypeId, NoSuchElementException$2, TimeoutExceptionTypeId, TimeoutException$1, timeoutExceptionFromDuration, UnknownExceptionTypeId, UnknownException, exitIsExit, exitIsFailure, exitIsSuccess, exitAs, exitAsVoid, exitCollectAll, exitDie$1, exitFail, exitFailCause$1, exitInterrupt$1, exitMap, exitMapBoth, exitMatch, exitMatchEffect, exitSucceed$1, exitVoid$1, exitZip, exitZipRight, exitZipWith, exitCollectAllInternal, deferredUnsafeMake, deferredMake, deferredMakeAs, deferredAwait, deferredComplete, deferredCompleteWith, deferredDone, deferredFail, deferredFailCause, deferredInterrupt, deferredInterruptWith, deferredIsDone, deferredSucceed, deferredUnsafeDone, deferredInterruptJoiner, constContext, context$1, contextWithEffect, provideContext$1, provideSomeContext, mapInputContext, currentSpanFromFiber;
4129
+ })), blocked, runRequestBlock, EffectTypeId, RevertFlags, EffectPrimitive, EffectPrimitiveFailure, EffectPrimitiveSuccess, isEffect$1, withFiberRuntime$1, acquireUseRelease$3, as$1, asVoid$1, custom, unsafeAsync, asyncInterrupt, async_, catchAllCause$1, catchAll$1, catchIf$1, originalSymbol, capture, die$3, dieMessage$1, either$1, exit$2, fail$12, failSync, failCause$6, failCauseSync$1, fiberId, fiberIdWith$1, flatMap$6, andThen$1, step, flatten$6, matchCause$1, matchCauseEffect$2, matchEffect$1, forEachSequential, forEachSequentialDiscard, interrupt$4, interruptWith, interruptible$2, intoDeferred$1, map$8, mapBoth$3, mapError$2, onError$1, onExit$1, onInterrupt, orElse$2, orDie$1, orDieWith, succeed$8, suspend$7, sync$2, tap$1, transplant, attemptOrElse, uninterruptible$1, uninterruptibleMask$2, void_$4, updateRuntimeFlags, whenEffect, whileLoop, fromIterator, gen$1, fnUntraced$1, withRuntimeFlags, yieldNow$2, zip$3, zipLeft$1, zipRight$3, zipWith$1, interruptFiber, interruptAsFiber, logLevelAll, logLevelFatal, logLevelError, logLevelWarning, logLevelInfo, logLevelDebug, logLevelTrace, logLevelNone, FiberRefSymbolKey, FiberRefTypeId, fiberRefVariance, fiberRefGet, fiberRefGetWith, fiberRefSet, fiberRefModify, fiberRefLocally, fiberRefLocallyWith, fiberRefUnsafeMake, fiberRefUnsafeMakeHashSet, fiberRefUnsafeMakeReadonlyArray, fiberRefUnsafeMakeContext, fiberRefUnsafeMakePatch, fiberRefUnsafeMakeRuntimeFlags, currentContext$1, currentSchedulingPriority, currentMaxOpsBeforeYield, currentLogAnnotations, currentLogLevel, currentLogSpan, withSchedulingPriority, currentConcurrency, currentRequestBatching, currentUnhandledErrorLogLevel, currentVersionMismatchErrorLogLevel, currentMetricLabels, currentForkScopeOverride, currentInterruptedCause, ScopeTypeId, CloseableScopeTypeId, scopeAddFinalizer, scopeAddFinalizerExit, scopeClose, scopeFork, causeSquash, causeSquashWith, YieldableError$1, makeException, RuntimeExceptionTypeId, RuntimeException, InterruptedExceptionTypeId, InterruptedException, isInterruptedException, IllegalArgumentExceptionTypeId, IllegalArgumentException$1, NoSuchElementExceptionTypeId, NoSuchElementException$2, TimeoutExceptionTypeId, TimeoutException$1, timeoutExceptionFromDuration, UnknownExceptionTypeId, UnknownException, exitIsExit, exitIsFailure, exitIsSuccess, exitAs, exitAsVoid, exitCollectAll, exitDie$1, exitFail, exitFailCause$1, exitInterrupt$1, exitMap, exitMapBoth, exitMatch, exitMatchEffect, exitSucceed$1, exitVoid$1, exitZip, exitZipRight, exitZipWith, exitCollectAllInternal, deferredUnsafeMake, deferredMake, deferredMakeAs, deferredAwait, deferredComplete, deferredCompleteWith, deferredDone, deferredFail, deferredFailCause, deferredInterrupt, deferredInterruptWith, deferredIsDone, deferredSucceed, deferredUnsafeDone, deferredInterruptJoiner, constContext, context$1, contextWithEffect, provideContext$1, provideSomeContext, mapInputContext, currentSpanFromFiber;
4130
4130
  var init_core = __esmMin((() => {
4131
4131
  init_Array();
4132
4132
  init_Chunk();
@@ -4382,7 +4382,7 @@ var init_core = __esmMin((() => {
4382
4382
  onFailure: f,
4383
4383
  onSuccess: succeed$8
4384
4384
  }));
4385
- catchIf = /*#__PURE__*/ dual(3, (self, predicate, f) => catchAllCause$1(self, (cause) => {
4385
+ catchIf$1 = /*#__PURE__*/ dual(3, (self, predicate, f) => catchAllCause$1(self, (cause) => {
4386
4386
  const either = failureOrCause$1(cause);
4387
4387
  switch (either._tag) {
4388
4388
  case "Left": return predicate(either.left) ? f(either.left) : failCause$6(cause);
@@ -6871,7 +6871,7 @@ var init_core_effect = __esmMin((() => {
6871
6871
  for (let i = 0; i < args.length - 1; i++) if (args[i] === tag) return true;
6872
6872
  return false;
6873
6873
  };
6874
- return catchIf(self, predicate, f);
6874
+ return catchIf$1(self, predicate, f);
6875
6875
  });
6876
6876
  diffFiberRefs = (self) => summarized(self, fiberRefs, diff$2);
6877
6877
  bind = /*#__PURE__*/ bind$1(map$8, flatMap$6);
@@ -11435,7 +11435,7 @@ var init_MutableQueue = __esmMin((() => {
11435
11435
  }));
11436
11436
  //#endregion
11437
11437
  //#region node_modules/effect/dist/esm/Effect.js
11438
- var isEffect, all$1, forEach$2, async, withFiberRuntime, fail$7, failCause$2, die, dieMessage, gen, promise, succeed$3, suspend$5, sync$1, _void, catchAll, catchAllCause, catchTag, ignore, sandbox, retry, try_, tryMap, tryPromise, interrupt, interruptible, uninterruptible, uninterruptibleMask, as, asVoid, map$4, mapBoth$1, mapError$1, negate, acquireRelease, acquireUseRelease$2, addFinalizer, ensuring$2, onError, scope$1, scopeWith, scopedWith$1, scoped$3, fiberIdWith, forkDaemon, forkIn, forkScoped, timeout, provide$1, provideService, serviceOption, either, exit$1, intoDeferred, when, flatMap$4, andThen, flatten$5, race, raceWith, tap, tapErrorCause, repeat, match$2, matchCause, matchCauseEffect, matchEffect, logWarning, logError, annotateLogs, orDie, orElse$1, orElseSucceed, runtime, unsafeMakeSemaphore, makeSemaphore, unsafeMakeLatch, runFork$1, runPromise, runSync, zip$1, zipLeft, zipRight$1, zipWith, fnUntraced;
11438
+ var isEffect, all$1, forEach$2, async, withFiberRuntime, fail$7, failCause$2, die, dieMessage, gen, promise, succeed$3, suspend$5, sync$1, _void, catchAll, catchAllCause, catchIf, catchTag, ignore, sandbox, retry, try_, tryMap, tryPromise, interrupt, interruptible, uninterruptible, uninterruptibleMask, as, asVoid, map$4, mapBoth$1, mapError$1, negate, acquireRelease, acquireUseRelease$2, addFinalizer, ensuring$2, onError, scope$1, scopeWith, scopedWith$1, scoped$3, fiberIdWith, forkDaemon, forkIn, forkScoped, timeout, provide$1, provideService, serviceOption, either, exit$1, intoDeferred, when, flatMap$4, andThen, flatten$5, race, raceWith, tap, tapErrorCause, repeat, match$2, matchCause, matchCauseEffect, matchEffect, logWarning, logError, annotateLogs, orDie, orElse$1, orElseSucceed, runtime, unsafeMakeSemaphore, makeSemaphore, unsafeMakeLatch, runFork$1, runPromise, runSync, zip$1, zipLeft, zipRight$1, zipWith, fnUntraced;
11439
11439
  var init_Effect = __esmMin((() => {
11440
11440
  init_core_effect();
11441
11441
  init_core();
@@ -11461,6 +11461,7 @@ var init_Effect = __esmMin((() => {
11461
11461
  _void = void_$4;
11462
11462
  catchAll = catchAll$1;
11463
11463
  catchAllCause = catchAllCause$1;
11464
+ catchIf = catchIf$1;
11464
11465
  catchTag = catchTag$1;
11465
11466
  ignore = ignore$1;
11466
11467
  sandbox = sandbox$1;
@@ -20267,6 +20268,7 @@ const makeGitImpl = (executor, root) => {
20267
20268
  }))),
20268
20269
  hasCommits: () => exec("git", "rev-parse", "--verify", "HEAD").pipe(map$4(() => true), catchAll(() => succeed$3(false))),
20269
20270
  readFileAtRef: (ref, path) => exec("git", "show", `${ref}:${path}`),
20271
+ diffPaths: (base, tip) => exec("git", "diff", "--name-only", "-z", "--no-renames", "--no-relative", "--ignore-submodules=none", "--end-of-options", base, tip, "--").pipe(map$4(splitNul)),
20270
20272
  changedPaths: (base) => gen(function* () {
20271
20273
  const ref = base ?? "HEAD";
20272
20274
  const nameStatusOut = yield* exec("git", "diff", "--name-status", "-z", ref).pipe(catchAll(() => succeed$3("")));
@@ -37486,7 +37488,7 @@ var init_Glob = __esmMin((() => {
37486
37488
  i += 2;
37487
37489
  }
37488
37490
  }
37489
- return new RegExp(`${pattern}$`);
37491
+ return new RegExp(`${pattern}$`, "s");
37490
37492
  };
37491
37493
  globMatches = (path, glob) => compile(glob).test(path);
37492
37494
  }));
@@ -39209,6 +39211,11 @@ const resolveBase = (commandLabel, value) => gen(function* () {
39209
39211
  return mergeBase.value;
39210
39212
  });
39211
39213
  //#endregion
39214
+ //#region src/step/strayPaths.ts
39215
+ init_wire();
39216
+ /** Globs use the `access.write` dialect (`globMatches`). */
39217
+ const strayPaths = (touched, allow) => touched.filter((path) => !allow.some((glob) => globMatches(path, glob))).sort();
39218
+ //#endregion
39212
39219
  //#region src/edge/Edge.ts
39213
39220
  init_esm();
39214
39221
  init_steering();
@@ -40502,6 +40509,26 @@ const writeSelection = (out, fields, path) => gen(function* () {
40502
40509
  else if (selection.kind === "unknown") return yield* fail$7(new SelectorUsageError(`gtd: unknown --json selector "${selection.path}" — see \`gtd --help\``));
40503
40510
  });
40504
40511
  //#endregion
40512
+ //#region src/cli/Strays.ts
40513
+ init_esm();
40514
+ init_wire();
40515
+ const resolveSide = (git, side) => git.resolveRef(`${side}^{commit}`).pipe(mapError$1(() => new GtdError(`gtd strays: unknown revision '${side}'`)));
40516
+ /** stdout carries the array alone; the summary is ungated stderr so a quiet run still explains its exit code, the per-path lines are narration. */
40517
+ const runStraysCommand = (command, json, out) => gen(function* () {
40518
+ const git = yield* GitService;
40519
+ const narrator = yield* Narrator;
40520
+ const base = yield* resolveSide(git, command.base);
40521
+ const tip = yield* resolveSide(git, command.tip);
40522
+ const touched = yield* git.diffPaths(base, tip);
40523
+ const strays = strayPaths(touched, command.globs);
40524
+ const total = touched.length;
40525
+ if (json.kind === "select") yield* writeSelection(out, strays, json.path).pipe(catchIf((error) => error instanceof SelectorUsageError && /^\d+$/.test(json.path), () => _void));
40526
+ else out.write(`${JSON.stringify(strays)}\n`);
40527
+ for (const path of strays) yield* narrator.narrate(`stray: ${JSON.stringify(path)}`);
40528
+ yield* narrator.warn(strays.length === 0 ? `gtd strays: no strays — ${total} touched files, all inside the allowlist` : `gtd strays: ${strays.length} of ${total} touched files outside the allowlist`);
40529
+ return strays.length === 0 ? 0 : 10;
40530
+ });
40531
+ //#endregion
40505
40532
  //#region src/cli/History.ts
40506
40533
  init_esm();
40507
40534
  /** Newest tip first. */
@@ -41162,7 +41189,8 @@ const needsOf = (kind) => {
41162
41189
  case "lsp":
41163
41190
  case "install":
41164
41191
  case "loop":
41165
- case "judgeRun": return "none";
41192
+ case "judgeRun":
41193
+ case "strays": return "none";
41166
41194
  case "check":
41167
41195
  case "uncheck": return "fs";
41168
41196
  default: return "state";
@@ -41171,10 +41199,13 @@ const needsOf = (kind) => {
41171
41199
  /**
41172
41200
  * Dispatches to the named `run*Command` handler for every `Command.kind` —
41173
41201
  * the counterpart of `Cli.ts`'s `parseArgv`, which has already validated
41174
- * every field a handler receives. A command choosing its own exit code is
41175
- * unrepresentable: every branch returns `Effect<void>`, and `Cli.ts`'s
41176
- * `runCli` supplies `EXIT_OK` once, after this Effect succeeds.
41202
+ * every field a handler receives. Only `strays` chooses its exit code; every
41203
+ * other branch returns `Effect<void>` and resolves to `EXIT_OK`.
41177
41204
  */
41205
+ const dispatchCommand = (command, json, out) => {
41206
+ if (command.kind === "strays") return runStraysCommand(command, json, out);
41207
+ return as(dispatchVoidCommand(command, json, out), 0);
41208
+ };
41178
41209
  const dispatchVoidCommand = (command, json, out) => {
41179
41210
  switch (command.kind) {
41180
41211
  case "lsp": return runLspCommand();
@@ -41214,7 +41245,7 @@ const dispatchVoidCommand = (command, json, out) => {
41214
41245
  * so a refusal writes nothing and emits no script by construction.
41215
41246
  */
41216
41247
  const runCommand = (command, json, out) => {
41217
- const dispatch = dispatchVoidCommand(command, json, out);
41248
+ const dispatch = dispatchCommand(command, json, out);
41218
41249
  if (needsOf(command.kind) !== "state") return dispatch;
41219
41250
  return gen(function* () {
41220
41251
  const git = yield* GitService;
@@ -41227,7 +41258,7 @@ const runCommand = (command, json, out) => {
41227
41258
  warn: () => _void
41228
41259
  }));
41229
41260
  for (const warning of config.warnings) yield* narrator.warn(`gtd: warning: ${formatDiagnostic(warning)}`);
41230
- yield* dispatch;
41261
+ return yield* dispatch;
41231
41262
  });
41232
41263
  };
41233
41264
  //#endregion
@@ -41244,13 +41275,13 @@ const FLAGS = [
41244
41275
  name: "--json",
41245
41276
  arity: "optional",
41246
41277
  repeatable: false,
41247
- scope: (kind) => kind === "next" || kind === "land" || kind === "judge" || kind === "judgeAnswer" || kind === "doors" || kind === "historyList" || kind === "historyShow",
41278
+ scope: (kind) => kind === "next" || kind === "land" || kind === "judge" || kind === "judgeAnswer" || kind === "doors" || kind === "historyList" || kind === "historyShow" || kind === "strays",
41248
41279
  decode: ([raw]) => right(raw ?? ""),
41249
- scopeError: "gtd: --json is only valid for `gtd next`/`gtd land`/`gtd judge`/`gtd judge answer`/`gtd doors`/`gtd history list`/`gtd history show` — every other command prints plain text; see `gtd install` for the driver protocol briefing",
41280
+ scopeError: "gtd: --json is only valid for `gtd next`/`gtd land`/`gtd judge`/`gtd judge answer`/`gtd doors`/`gtd history list`/`gtd history show`/`gtd strays` — every other command prints plain text; see `gtd install` for the driver protocol briefing",
41250
41281
  valueHint: "<path>",
41251
41282
  help: [
41252
41283
  "(gtd next/gtd land/gtd judge/gtd judge answer/gtd doors/gtd history",
41253
- "list/gtd history show only) output structured JSON. Bare --json prints the whole document;",
41284
+ "list/gtd history show/gtd strays only) output structured JSON. Bare --json prints the whole document;",
41254
41285
  "--json=<path> (a dotted key path into that document, e.g.",
41255
41286
  "kind, content, session.id) prints just that value: a",
41256
41287
  "scalar raw and unquoted, a boolean as true/false, a list",
@@ -41759,6 +41790,28 @@ const COMMAND_ROWS = [
41759
41790
  "every question, with nothing on stdout"
41760
41791
  ]
41761
41792
  },
41793
+ {
41794
+ token: "strays",
41795
+ kind: "strays",
41796
+ arity: {
41797
+ name: "range",
41798
+ variadic: "glob"
41799
+ },
41800
+ details: [
41801
+ "List the files <range> (<base>..<tip>, an empty side is HEAD)",
41802
+ "touched in its committed net diff that match none of the",
41803
+ "globs. Anchored at the repo root: * stays within one path",
41804
+ "segment, ** crosses them, everything else is literal — quote",
41805
+ "globs in the shell. stdout: a sorted JSON array, [] when",
41806
+ "clean; stderr: a summary, plus one stray: line per path with",
41807
+ "--verbose. --json=<n> picks one entry, nothing past the end;",
41808
+ "a symlink is a path like any other, never followed; a",
41809
+ "submodule counts as its path, whatever diff.ignoreSubmodules",
41810
+ "or .gitmodules ignore say. Runs in any git repository, no",
41811
+ "process needed. Exits 10 when",
41812
+ "strays were found, 1 on an unknown revision, 2 on a bad range"
41813
+ ]
41814
+ },
41762
41815
  {
41763
41816
  token: "history list",
41764
41817
  kind: "historyList",
@@ -42101,6 +42154,22 @@ const parseArgv = (argv) => {
42101
42154
  json,
42102
42155
  verbose
42103
42156
  };
42157
+ if (kind === "strays") {
42158
+ const [range, ...globs] = restPositionals;
42159
+ const at = range.indexOf("..");
42160
+ if (at < 0 || range.includes("...") || range.indexOf("..", at + 1) >= 0) return usagePlan(`gtd strays: <range> must be <base>..<tip> (an empty side is HEAD) — got '${range}'`, jsonSeen);
42161
+ return {
42162
+ kind: "command",
42163
+ command: {
42164
+ kind: "strays",
42165
+ base: range.slice(0, at) || "HEAD",
42166
+ tip: range.slice(at + 2) || "HEAD",
42167
+ globs
42168
+ },
42169
+ json,
42170
+ verbose
42171
+ };
42172
+ }
42104
42173
  if (kind === "uncheck") return {
42105
42174
  kind: "command",
42106
42175
  command: {
@@ -42192,9 +42261,9 @@ const runCli = (argv, io) => {
42192
42261
  return flatMap$4(Host, ({ env }) => try_({
42193
42262
  try: () => resolveFlags(env, io.flags),
42194
42263
  catch: (e) => new GtdError(e.message)
42195
- })).pipe(flatMap$4(() => runCommand(plan.command, plan.json, out)), map$4(() => {
42264
+ })).pipe(flatMap$4(() => runCommand(plan.command, plan.json, out)), map$4((code) => {
42196
42265
  out.flush();
42197
- io.exit(0);
42266
+ io.exit(code);
42198
42267
  }), provide$1(io.layers(plan.verbose)), sandbox, catchAll((cause) => isInterruptedOnly(cause) ? failCause$2(cause) : report(io, plan.json.kind !== "off")(cause)));
42199
42268
  };
42200
42269
  //#endregion