@pmelab/gtd 52.1.0 → 53.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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +91 -129
- package/claude/hooks/drive.ts +0 -3
- package/claude/hooks/register.tsx +3 -68
- package/claude/hooks/ship.ts +85 -1595
- package/dist/chunks/{reserved-DxB5CROj.mjs → reserved-CQ7amcKG.mjs} +45 -132
- package/dist/chunks/{review-CKMSdvgW.mjs → review-CNgN_V8T.mjs} +2 -2
- package/dist/chunks/{tail-QZc7Uz2U.mjs → tail-JZ9PAOwp.mjs} +2 -14
- package/dist/chunks/{wire-BJuZWR3C.mjs → wire-C12dahrH.mjs} +2 -130
- package/dist/flows.mjs +1 -16
- package/dist/gtd.bundle.mjs +476 -982
- package/dist/testkit.mjs +58 -417
- package/dist/wire.mjs +2 -2
- package/dist/workflows/cleanup.mjs +2 -2
- package/dist/workflows/feature.mjs +112 -363
- package/dist/workflows/fix.mjs +2 -2
- package/dist/workflows/review.mjs +3 -3
- package/dist/workflows/rollout.mjs +1 -1
- package/dist/workflows.mjs +311 -674
- package/package.json +1 -1
- package/src/cli/Cli.test.ts +1 -86
- package/src/cli/Cli.ts +13 -133
- package/src/cli/Metrics.test.ts +21 -31
- package/src/cli/Metrics.ts +25 -36
- package/src/cli/Program.test.ts +1 -35
- package/src/cli/Program.ts +5 -64
- package/src/cli/ToolCommands.ts +0 -3
- package/src/edge/Edge.test.ts +20 -322
- package/src/edge/Edge.ts +10 -181
- package/src/edge/index.ts +0 -1
- package/src/flows/runtime.test.ts +0 -27
- package/src/flows/runtime.ts +1 -49
- package/src/install/Install.ts +15 -24
- package/src/replay/Replay.test.ts +0 -169
- package/src/replay/Replay.ts +4 -93
- package/src/replay/index.ts +0 -1
- package/src/testkit/Layers.ts +0 -1
- package/src/wire/BeatDocument.ts +2 -12
- package/src/wire/BeatStatus.ts +0 -3
- package/src/wire/WireSchema.test.ts +0 -18
- package/src/wire/index.ts +0 -2
- package/src/wire/types.ts +0 -7
- package/src/workflow/Workflow.ts +1 -9
- package/src/workflows/feature/criteria.test.ts +1 -14
- package/src/workflows/feature/criteria.ts +6 -38
- package/src/workflows/feature/feature.ts +34 -118
- package/src/workflows/feature/hands-on-skip.feature +2 -70
- package/src/workflows/feature/index.ts +0 -1
- package/src/workflows/feature/one-step-land.feature +0 -4
- package/src/workflows/feature/prototype-rounds.feature +1 -1
- package/src/workflows/feature/prototype.feature +3 -3
- package/src/workflows/feature/prototype.ts +18 -29
- package/src/workflows/feature/refactor-absorbs.feature +3 -8
- package/src/workflows/feature/refactor-first.feature +0 -5
- package/src/workflows/feature/refactor-rebuild.feature +0 -6
- package/src/workflows/feature/refactor-review.feature +11 -11
- package/src/workflows/feature/refactor.ts +30 -64
- package/src/workflows/feature/review-gate.feature +1 -1
- package/src/workflows/feature/seam-rejection.feature +0 -4
- package/src/workflows/feature/seam.test.ts +7 -26
- package/src/workflows/feature/seam.ts +52 -165
- package/src/workflows/index.ts +0 -1
- package/src/workflows/shared/lenses.ts +0 -13
- package/src/workflows/shared/options.test.ts +2 -11
- package/src/workflows/shared/options.ts +4 -21
- package/src/workflows/shared/review.test.ts +3 -3
- package/src/workflows/shared/steps.ts +0 -2
- package/src/workflows/shared/text.test.ts +10 -80
- package/src/workflows/shared/text.ts +35 -211
- package/src/workflows/shared/vars.ts +2 -8
- package/src/workflows/shared/workflows.steps.ts +0 -13
- package/claude/hooks/context.ts +0 -66
- package/src/replay/Slices.test.ts +0 -73
- package/src/replay/Slices.ts +0 -45
- package/src/wire/ContextLimit.fixture.ts +0 -30
- package/src/wire/ContextLimit.test.ts +0 -96
- package/src/wire/ContextLimit.ts +0 -169
- package/src/wire/Slices.ts +0 -25
- package/src/wire/golden/1/slices.json +0 -11
- package/src/workflows/feature/context-limit.feature +0 -37
- package/src/workflows/feature/continuation-cap.feature +0 -69
- package/src/workflows/feature/continuation-rebuild.feature +0 -92
- package/src/workflows/feature/continuation-slices.feature +0 -68
- package/src/workflows/feature/continuation.feature +0 -209
- package/src/workflows/feature/slice-findings.feature +0 -256
- package/src/workflows/feature/slicing-cap.feature +0 -23
- package/src/workflows/feature/slicing.feature +0 -297
- package/src/workflows/feature/slicing.test.ts +0 -49
- package/src/workflows/feature/slicing.ts +0 -105
- package/src/workflows/shared/continuation.ts +0 -92
package/dist/gtd.bundle.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { isIP } from "node:net";
|
|
|
7
7
|
import * as Crypto from "node:crypto";
|
|
8
8
|
import { createHash, randomUUID } from "node:crypto";
|
|
9
9
|
import * as NFS from "node:fs";
|
|
10
|
-
import {
|
|
10
|
+
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
11
11
|
import * as OS from "node:os";
|
|
12
12
|
import { homedir, networkInterfaces, tmpdir } from "node:os";
|
|
13
13
|
import * as Path from "node:path";
|
|
@@ -6766,7 +6766,7 @@ var init_label = __esmMin((() => {
|
|
|
6766
6766
|
}));
|
|
6767
6767
|
//#endregion
|
|
6768
6768
|
//#region node_modules/effect/dist/esm/internal/core-effect.js
|
|
6769
|
-
var annotateLogs$1, asSome, try_$1,
|
|
6769
|
+
var annotateLogs$1, asSome, try_$1, catchTag$1, diffFiberRefs, bind, bindTo, match$4, fiberRefs, ignore$1, logWithLevel, logWarning$1, logError$1, mapErrorCause, negate$1, orElseSucceed$1, patchFiberRefs, promise$1, provideService$1, provideServiceEffect, sandbox$1, sleep$1, succeedNone, summarized, tapErrorCause$1, tryPromise$1, tryMap$1, updateFiberRefs, when$1, serviceOption$1;
|
|
6770
6770
|
var init_core_effect = __esmMin((() => {
|
|
6771
6771
|
init_Clock();
|
|
6772
6772
|
init_Context();
|
|
@@ -6801,13 +6801,6 @@ var init_core_effect = __esmMin((() => {
|
|
|
6801
6801
|
}
|
|
6802
6802
|
});
|
|
6803
6803
|
};
|
|
6804
|
-
catchAllDefect$1 = /*#__PURE__*/ dual(2, (self, f) => catchAllCause$1(self, (cause) => {
|
|
6805
|
-
const option = find(cause, (_) => isDieType$1(_) ? some(_) : none$4());
|
|
6806
|
-
switch (option._tag) {
|
|
6807
|
-
case "None": return failCause$6(cause);
|
|
6808
|
-
case "Some": return f(option.value.defect);
|
|
6809
|
-
}
|
|
6810
|
-
}));
|
|
6811
6804
|
catchTag$1 = /*#__PURE__*/ dual((args) => isEffect$1(args[0]), (self, ...args) => {
|
|
6812
6805
|
const f = args[args.length - 1];
|
|
6813
6806
|
let predicate;
|
|
@@ -7656,7 +7649,7 @@ _A: (_) => _ };
|
|
|
7656
7649
|
//#endregion
|
|
7657
7650
|
//#region node_modules/effect/dist/esm/internal/metric/hook.js
|
|
7658
7651
|
var MetricHookSymbolKey, MetricHookTypeId, metricHookVariance, make$25, bigint0$1, counter$1, frequency, gauge, histogram$1, summary, calculateQuantiles, resolveQuantile;
|
|
7659
|
-
var init_hook
|
|
7652
|
+
var init_hook = __esmMin((() => {
|
|
7660
7653
|
init_Array();
|
|
7661
7654
|
init_Duration();
|
|
7662
7655
|
init_Function();
|
|
@@ -7953,7 +7946,7 @@ var init_registry = __esmMin((() => {
|
|
|
7953
7946
|
init_Function();
|
|
7954
7947
|
init_MutableHashMap();
|
|
7955
7948
|
init_Option();
|
|
7956
|
-
init_hook
|
|
7949
|
+
init_hook();
|
|
7957
7950
|
init_keyType();
|
|
7958
7951
|
init_pair();
|
|
7959
7952
|
MetricRegistrySymbolKey = "effect/MetricRegistry";
|
|
@@ -11382,7 +11375,7 @@ var init_MutableQueue = __esmMin((() => {
|
|
|
11382
11375
|
}));
|
|
11383
11376
|
//#endregion
|
|
11384
11377
|
//#region node_modules/effect/dist/esm/Effect.js
|
|
11385
|
-
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,
|
|
11378
|
+
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;
|
|
11386
11379
|
var init_Effect = __esmMin((() => {
|
|
11387
11380
|
init_core_effect();
|
|
11388
11381
|
init_core();
|
|
@@ -11408,7 +11401,6 @@ var init_Effect = __esmMin((() => {
|
|
|
11408
11401
|
_void = void_$4;
|
|
11409
11402
|
catchAll = catchAll$1;
|
|
11410
11403
|
catchAllCause = catchAllCause$1;
|
|
11411
|
-
catchAllDefect = catchAllDefect$1;
|
|
11412
11404
|
catchIf = catchIf$1;
|
|
11413
11405
|
catchTag = catchTag$1;
|
|
11414
11406
|
ignore = ignore$1;
|
|
@@ -14226,7 +14218,7 @@ function getBrands(ast) {
|
|
|
14226
14218
|
onSome: (brands) => brands.map((brand) => ` & Brand<${formatUnknown(brand)}>`).join("")
|
|
14227
14219
|
});
|
|
14228
14220
|
}
|
|
14229
|
-
var TypeConstructorAnnotationId, BrandAnnotationId, SchemaIdAnnotationId, MessageAnnotationId, MissingMessageAnnotationId, IdentifierAnnotationId, TitleAnnotationId, AutoTitleAnnotationId, DescriptionAnnotationId, ExamplesAnnotationId, DefaultAnnotationId, JSONSchemaAnnotationId, ArbitraryAnnotationId, PrettyAnnotationId, EquivalenceAnnotationId, DocumentationAnnotationId, ConcurrencyAnnotationId, BatchingAnnotationId, ParseIssueTitleAnnotationId, ParseOptionsAnnotationId, DecodingFallbackAnnotationId, SurrogateAnnotationId, StableFilterAnnotationId, getAnnotation, getBrandAnnotation, getMessageAnnotation, getMissingMessageAnnotation, getTitleAnnotation, getAutoTitleAnnotation, getIdentifierAnnotation, getDescriptionAnnotation, getConcurrencyAnnotation, getBatchingAnnotation, getParseIssueTitleAnnotation$1, getParseOptionsAnnotation, getDecodingFallbackAnnotation, getSurrogateAnnotation, getStableFilterAnnotation, hasStableFilter, JSONIdentifierAnnotationId, getJSONIdentifierAnnotation, getJSONIdentifier, Declaration, createASTGuard, Literal$1, isLiteral, UniqueSymbol, UndefinedKeyword, undefinedKeyword, NeverKeyword, neverKeyword, UnknownKeyword, unknownKeyword, AnyKeyword, anyKeyword, StringKeyword, stringKeyword, isStringKeyword, NumberKeyword, numberKeyword, isNumberKeyword, BooleanKeyword, booleanKeyword, isBooleanKeyword, BigIntKeyword, bigIntKeyword, SymbolKeyword, symbolKeyword, isSymbolKeyword, Type$1, OptionalType, getRestASTs, TupleType, formatTuple, PropertySignature, isParameter, IndexSignature, TypeLiteral, formatIndexSignatures, formatTypeLiteral, isTypeLiteral, sortCandidates, literalMap, flatten$2, unify, Union$1, mapMembers, isMembers, isUnion, toJSONMemoMap, Suspend, Refinement$1, isRefinement$1, defaultParseOption, Transformation$1, isTransformation$1, FinalTransformation, createTransformationGuard, ComposeTransformation, composeTransformation, PropertySignatureTransformation$1, TypeLiteralTransformation, isTypeLiteralTransformation, annotations, STRING_KEYWORD_PATTERN, NUMBER_KEYWORD_PATTERN, getTemplateLiteralSpanTypePattern, handleTemplateLiteralSpanTypeParens, getTemplateLiteralPattern, getTemplateLiteralRegExp, record$
|
|
14221
|
+
var TypeConstructorAnnotationId, BrandAnnotationId, SchemaIdAnnotationId, MessageAnnotationId, MissingMessageAnnotationId, IdentifierAnnotationId, TitleAnnotationId, AutoTitleAnnotationId, DescriptionAnnotationId, ExamplesAnnotationId, DefaultAnnotationId, JSONSchemaAnnotationId, ArbitraryAnnotationId, PrettyAnnotationId, EquivalenceAnnotationId, DocumentationAnnotationId, ConcurrencyAnnotationId, BatchingAnnotationId, ParseIssueTitleAnnotationId, ParseOptionsAnnotationId, DecodingFallbackAnnotationId, SurrogateAnnotationId, StableFilterAnnotationId, getAnnotation, getBrandAnnotation, getMessageAnnotation, getMissingMessageAnnotation, getTitleAnnotation, getAutoTitleAnnotation, getIdentifierAnnotation, getDescriptionAnnotation, getConcurrencyAnnotation, getBatchingAnnotation, getParseIssueTitleAnnotation$1, getParseOptionsAnnotation, getDecodingFallbackAnnotation, getSurrogateAnnotation, getStableFilterAnnotation, hasStableFilter, JSONIdentifierAnnotationId, getJSONIdentifierAnnotation, getJSONIdentifier, Declaration, createASTGuard, Literal$1, isLiteral, UniqueSymbol, UndefinedKeyword, undefinedKeyword, NeverKeyword, neverKeyword, UnknownKeyword, unknownKeyword, AnyKeyword, anyKeyword, StringKeyword, stringKeyword, isStringKeyword, NumberKeyword, numberKeyword, isNumberKeyword, BooleanKeyword, booleanKeyword, isBooleanKeyword, BigIntKeyword, bigIntKeyword, SymbolKeyword, symbolKeyword, isSymbolKeyword, Type$1, OptionalType, getRestASTs, TupleType, formatTuple, PropertySignature, isParameter, IndexSignature, TypeLiteral, formatIndexSignatures, formatTypeLiteral, isTypeLiteral, sortCandidates, literalMap, flatten$2, unify, Union$1, mapMembers, isMembers, isUnion, toJSONMemoMap, Suspend, Refinement$1, isRefinement$1, defaultParseOption, Transformation$1, isTransformation$1, FinalTransformation, createTransformationGuard, ComposeTransformation, composeTransformation, PropertySignatureTransformation$1, TypeLiteralTransformation, isTypeLiteralTransformation, annotations, STRING_KEYWORD_PATTERN, NUMBER_KEYWORD_PATTERN, getTemplateLiteralSpanTypePattern, handleTemplateLiteralSpanTypeParens, getTemplateLiteralPattern, getTemplateLiteralRegExp, record$1, pickAnnotations, omitAnnotations, preserveTransformationAnnotations, typeAST, getTransformationFrom, encodedAST_, encodedAST, toJSONAnnotations, getEncodedParameter, formatKeyword, getOrElseExpected, getExpected;
|
|
14230
14222
|
var init_SchemaAST = __esmMin((() => {
|
|
14231
14223
|
init_Array();
|
|
14232
14224
|
init_Function();
|
|
@@ -15189,7 +15181,7 @@ var init_SchemaAST = __esmMin((() => {
|
|
|
15189
15181
|
return pattern;
|
|
15190
15182
|
};
|
|
15191
15183
|
getTemplateLiteralRegExp = (ast) => new RegExp(`^${getTemplateLiteralPattern(ast, false, true)}$`);
|
|
15192
|
-
record$
|
|
15184
|
+
record$1 = (key, value) => {
|
|
15193
15185
|
const propertySignatures = [];
|
|
15194
15186
|
const indexSignatures = [];
|
|
15195
15187
|
const go = (key) => {
|
|
@@ -16774,7 +16766,7 @@ var init_Schema = __esmMin((() => {
|
|
|
16774
16766
|
const issFrom = [];
|
|
16775
16767
|
const issTo = [];
|
|
16776
16768
|
for (const r of records) {
|
|
16777
|
-
const { indexSignatures, propertySignatures } = record$
|
|
16769
|
+
const { indexSignatures, propertySignatures } = record$1(r.key.ast, r.value.ast);
|
|
16778
16770
|
propertySignatures.forEach((ps) => {
|
|
16779
16771
|
from.push(ps);
|
|
16780
16772
|
to.push(new PropertySignature(ps.name, typeAST(ps.type), ps.isOptional, ps.isReadonly, ps.annotations));
|
|
@@ -16789,7 +16781,7 @@ var init_Schema = __esmMin((() => {
|
|
|
16789
16781
|
}
|
|
16790
16782
|
const iss = [];
|
|
16791
16783
|
for (const r of records) {
|
|
16792
|
-
const { indexSignatures, propertySignatures } = record$
|
|
16784
|
+
const { indexSignatures, propertySignatures } = record$1(r.key.ast, r.value.ast);
|
|
16793
16785
|
propertySignatures.forEach((ps) => pss.push(ps));
|
|
16794
16786
|
indexSignatures.forEach((is) => iss.push(is));
|
|
16795
16787
|
}
|
|
@@ -19962,7 +19954,7 @@ var init_esm = __esmMin((() => {
|
|
|
19962
19954
|
init_mergeStrategy();
|
|
19963
19955
|
init_metric();
|
|
19964
19956
|
init_boundaries();
|
|
19965
|
-
init_hook
|
|
19957
|
+
init_hook();
|
|
19966
19958
|
init_key();
|
|
19967
19959
|
init_keyType();
|
|
19968
19960
|
init_label();
|
|
@@ -21023,10 +21015,7 @@ while :; do
|
|
|
21023
21015
|
model="$(gtd next --json=model)"
|
|
21024
21016
|
system="$(gtd next --json=system)"
|
|
21025
21017
|
validate="$(gtd next --json=validate)"
|
|
21026
|
-
context="$(gtd next --json=context)"
|
|
21027
21018
|
log="$(gtd next --json=log)"
|
|
21028
|
-
# Absolute, since the hook runs wherever the agent last cd'ed to.
|
|
21029
|
-
case "$log" in /*) ;; *) log="$(git rev-parse --show-toplevel)/$log" ;; esac
|
|
21030
21019
|
# \`model\` is a hint, not a model name — the bundled workflow ships
|
|
21031
21020
|
# \`smart\` and \`base\`, which \`claude --model\` rejects. Resolve them
|
|
21032
21021
|
# from your environment; unset, the hint is dropped and the agent runs
|
|
@@ -21035,18 +21024,6 @@ while :; do
|
|
|
21035
21024
|
smart) model="\${GTD_LOOP_MODEL_SMART:-}" ;;
|
|
21036
21025
|
base) model="\${GTD_LOOP_MODEL_BASE:-}" ;;
|
|
21037
21026
|
esac
|
|
21038
|
-
# The step's context limit, as a Claude Code hook after every tool
|
|
21039
|
-
# call and before compacting. Past \`hard\` it stops the turn and
|
|
21040
|
-
# appends why to the stop file, so we land nothing and stop too.
|
|
21041
|
-
soft="$(printf '%s' "$context" | sed -n 's/.*"soft":\\([0-9.]*\\).*/\\1/p')"
|
|
21042
|
-
hard="$(printf '%s' "$context" | sed -n 's/.*"hard":\\([0-9.]*\\).*/\\1/p')"
|
|
21043
|
-
window="\${GTD_LOOP_CONTEXT_WINDOW:-}"
|
|
21044
|
-
case "$model" in *'[1m]'* | *'[1M]'*) window="\${window:-1000000}" ;; esac
|
|
21045
|
-
hook="gtd context-hook\${soft:+ --soft $soft}\${hard:+ --hard $hard}\${window:+ --window $window}"
|
|
21046
|
-
hooks="[{\\"hooks\\":[{\\"type\\":\\"command\\",\\"command\\":\\"$hook\\"}]}]"
|
|
21047
|
-
settings="{\\"hooks\\":{\\"PostToolUse\\":$hooks,\\"PreCompact\\":$hooks}}"
|
|
21048
|
-
stop="$log.context-stop"
|
|
21049
|
-
rm -f "$stop"
|
|
21050
21027
|
# One turn, its prompt on stdin. The prompt embeds a full diff, so it
|
|
21051
21028
|
# never goes on argv — argv is capped (~1 MB on macOS, and POSIX
|
|
21052
21029
|
# guarantees only 4 KB, ARG_MAX), and a diff crosses that far sooner
|
|
@@ -21054,15 +21031,15 @@ while :; do
|
|
|
21054
21031
|
# whether \`claude --resume\` re-applies the original session's is a
|
|
21055
21032
|
# harness detail gtd cannot verify from outside. Its JSON envelope
|
|
21056
21033
|
# goes to the log, and its \`total_cost_usd\` adds up into $cost. An
|
|
21057
|
-
# \`is_error\` envelope (max turns, an error mid-turn) or one
|
|
21058
|
-
#
|
|
21059
|
-
#
|
|
21034
|
+
# \`is_error\` envelope (max turns, an error mid-turn) or one a hook
|
|
21035
|
+
# stopped fails the turn even at exit 0: landing it would land the
|
|
21036
|
+
# failure as work. Only a turn that never ran (no envelope: the
|
|
21060
21037
|
# session flag was refused) is worth retrying under the other flag.
|
|
21061
21038
|
turn() {
|
|
21062
|
-
out="$(
|
|
21039
|
+
out="$(claude -p "$@" \\
|
|
21063
21040
|
\${model:+--model "$model"} \\
|
|
21064
21041
|
\${system:+--system-prompt "$system"} \\
|
|
21065
|
-
--
|
|
21042
|
+
--output-format json \\
|
|
21066
21043
|
--dangerously-skip-permissions 2>>"$log")" && ok=0 || ok=$?
|
|
21067
21044
|
printf '%s\\n' "$out" >>"$log"
|
|
21068
21045
|
case "$out" in *'"type":"result"'*) ran=true ;; *) ran=false ;; esac
|
|
@@ -21072,10 +21049,6 @@ while :; do
|
|
|
21072
21049
|
usd="$(printf '%s\\n' "$out" | sed -n 's/.*"total_cost_usd":\\([0-9.eE+-]*\\).*/\\1/p' | tail -n 1)"
|
|
21073
21050
|
[ -z "$usd" ] || cost="$(awk -v a="\${cost:-0}" -v b="$usd" 'BEGIN { printf "%.6f", a + b }')"
|
|
21074
21051
|
[ "\${cost:-}" != 0.000000 ] || cost=
|
|
21075
|
-
if [ -s "$stop" ]; then
|
|
21076
|
-
cat "$stop" >&2
|
|
21077
|
-
exit 1
|
|
21078
|
-
fi
|
|
21079
21052
|
return "$ok"
|
|
21080
21053
|
}
|
|
21081
21054
|
# Plain \`gtd next\` (not --json=content) renders the diff once.
|
|
@@ -21544,6 +21517,16 @@ way — an existing \`gtd-loop\` survives untouched beside the new
|
|
|
21544
21517
|
\`gtd-door review [base]\`). When either is still present beside the suite,
|
|
21545
21518
|
name it to the user as superseded and ask before removing it — never remove
|
|
21546
21519
|
one unasked.
|
|
21520
|
+
|
|
21521
|
+
An older \`gtd-build\` passed a \`gtd context-hook\` hook to every turn, and
|
|
21522
|
+
an older setup guide had it registered in Claude Code's own settings
|
|
21523
|
+
(\`PostToolUse\`/\`PreCompact\`). That command no longer exists: a hook
|
|
21524
|
+
still calling it fails on every tool call. Replace such a \`gtd-build\` as a
|
|
21525
|
+
differing file above, and look for a \`gtd context-hook\` entry in the user's
|
|
21526
|
+
Claude Code settings files (\`~/.claude/settings.json\`, the repository's
|
|
21527
|
+
\`.claude/settings.json\` and \`.claude/settings.local.json\`): name each file
|
|
21528
|
+
to the user, then remove that entry alone, leaving every other key
|
|
21529
|
+
byte-identical.
|
|
21547
21530
|
`;
|
|
21548
21531
|
const EDITOR_INTEGRATION = `
|
|
21549
21532
|
## Editor integration: offer it, don't bury it
|
|
@@ -21705,20 +21688,6 @@ const scope = async (scope, fn) => {
|
|
|
21705
21688
|
context.popScope();
|
|
21706
21689
|
}
|
|
21707
21690
|
};
|
|
21708
|
-
/**
|
|
21709
|
-
* Mark the span `fn` covers as one named slice of the process. Lands no
|
|
21710
|
-
* commit, prefixes no step name, opens no memory scope.
|
|
21711
|
-
*/
|
|
21712
|
-
const slice = async (name, fn) => {
|
|
21713
|
-
const context = ctx();
|
|
21714
|
-
if (typeof context.enterSlice !== "function") return fn();
|
|
21715
|
-
context.enterSlice(name);
|
|
21716
|
-
try {
|
|
21717
|
-
return await fn();
|
|
21718
|
-
} finally {
|
|
21719
|
-
context.leaveSlice?.();
|
|
21720
|
-
}
|
|
21721
|
-
};
|
|
21722
21691
|
const read = (path) => ctx().read(path);
|
|
21723
21692
|
/** Every path in the tree matching `pattern` (`*` stays within a segment, `**` crosses them). */
|
|
21724
21693
|
const glob = (pattern) => ctx().glob(pattern);
|
|
@@ -22299,7 +22268,6 @@ var flows_exports = /* @__PURE__ */ __exportAll({
|
|
|
22299
22268
|
sections: () => sections,
|
|
22300
22269
|
showScript: () => showScript,
|
|
22301
22270
|
skillsFor: () => skillsFor,
|
|
22302
|
-
slice: () => slice,
|
|
22303
22271
|
start: () => start,
|
|
22304
22272
|
threads: () => threads,
|
|
22305
22273
|
vars: () => vars,
|
|
@@ -37502,470 +37470,440 @@ const accessShapeFault = (value) => {
|
|
|
37502
37470
|
};
|
|
37503
37471
|
const sameGlobs = (a, b) => a === void 0 || b === void 0 ? a === b : a.length === b.length && a.every((g, i) => g === b[i]);
|
|
37504
37472
|
const sameAccess = (a, b) => sameGlobs(a?.read, b?.read) && sameGlobs(a?.write, b?.write);
|
|
37505
|
-
|
|
37506
|
-
|
|
37507
|
-
|
|
37508
|
-
|
|
37509
|
-
|
|
37510
|
-
|
|
37511
|
-
|
|
37512
|
-
};
|
|
37513
|
-
ACCESS_REFUSAL = "gtd land: this turn wrote outside its write access — revert these paths, then run `gtd land` again:";
|
|
37514
|
-
}));
|
|
37515
|
-
//#endregion
|
|
37516
|
-
//#region src/wire/ExitCodes.ts
|
|
37517
|
-
var init_ExitCodes = __esmMin((() => {}));
|
|
37518
|
-
//#endregion
|
|
37519
|
-
//#region src/wire/types.ts
|
|
37520
|
-
var init_types = __esmMin((() => {}));
|
|
37473
|
+
/** The plain-text twin of `OutcomeScript.ts`'s `printfLine`. Lives here so `src/wire/`'s encoders reach it without importing out of the leaf. */
|
|
37474
|
+
const renderFormat = (fmt, ...args) => {
|
|
37475
|
+
let i = 0;
|
|
37476
|
+
return fmt.replace(/%s/g, () => args[i++] ?? "");
|
|
37477
|
+
};
|
|
37478
|
+
/** The first line of the landing refusal for a turn that wrote outside its `write` access — a stable marker drivers and tests grep for. */
|
|
37479
|
+
const ACCESS_REFUSAL = "gtd land: this turn wrote outside its write access — revert these paths, then run `gtd land` again:";
|
|
37521
37480
|
//#endregion
|
|
37522
37481
|
//#region src/wire/Demand.ts
|
|
37523
|
-
|
|
37524
|
-
|
|
37525
|
-
|
|
37526
|
-
|
|
37527
|
-
|
|
37528
|
-
|
|
37529
|
-
|
|
37530
|
-
|
|
37531
|
-
|
|
37532
|
-
|
|
37533
|
-
|
|
37534
|
-
|
|
37535
|
-
|
|
37536
|
-
|
|
37537
|
-
|
|
37538
|
-
|
|
37539
|
-
|
|
37540
|
-
|
|
37541
|
-
|
|
37542
|
-
|
|
37543
|
-
|
|
37544
|
-
|
|
37545
|
-
|
|
37546
|
-
|
|
37547
|
-
|
|
37548
|
-
|
|
37549
|
-
|
|
37482
|
+
/**
|
|
37483
|
+
* The beat kind for a rest, in precedence order:
|
|
37484
|
+
*
|
|
37485
|
+
* 1. `stalled` — `Edge.ts`'s `stalledAt` verdict, passed in as a plain
|
|
37486
|
+
* boolean (this module never resolves it itself). `stalledAt` already
|
|
37487
|
+
* requires a clean tree, so this can never collide with `capture` below.
|
|
37488
|
+
* 2. `capture` — a `message` rest with a DIRTY tree: the human already
|
|
37489
|
+
* acted; a driver lands it immediately.
|
|
37490
|
+
* 3. Otherwise, the content kind verbatim (`script`/`prompt`, or `message`
|
|
37491
|
+
* on a clean tree).
|
|
37492
|
+
*/
|
|
37493
|
+
const beatKindOf = (input) => {
|
|
37494
|
+
if (input.stalled) return "stalled";
|
|
37495
|
+
if (input.contentKind === "message" && input.dirty) return "capture";
|
|
37496
|
+
return input.contentKind;
|
|
37497
|
+
};
|
|
37498
|
+
/**
|
|
37499
|
+
* The `stalled` beat's own content: a diagnosis naming the stuck step and
|
|
37500
|
+
* the ways out. The first line is shaped so a stderr grep for
|
|
37501
|
+
* `stalled at "<state>"` stays a stable substring (see
|
|
37502
|
+
* `tests/integration/features/driver-doc.feature`).
|
|
37503
|
+
*/
|
|
37504
|
+
const stallDiagnosis = (state, actor) => `stalled at "${state}": the last gtd(${actor}): ${state} turn landed an empty attempt, the tree is clean, and another dispatch would repeat it.\n\nTwo ways out:\n - sharpen the step's prompt so the turn has something concrete to author\n - pass allowEmpty: true to the step, so an empty turn completes it and the\n flow decides what follows — finishing, or an escalation step after N\n empty turns\n`;
|
|
37505
|
+
/**
|
|
37506
|
+
* Assemble one `Demand` — the ONLY place a rest's resolved content becomes
|
|
37507
|
+
* `Demand.content` (`stallDiagnosis` at `kind === "stalled"`, the rendered
|
|
37508
|
+
* content otherwise), and the only place `session`/`validate`/`skills`/`access` are
|
|
37509
|
+
* attached, which the `prompt` variant's own type makes impossible to do at
|
|
37510
|
+
* any other kind.
|
|
37511
|
+
*/
|
|
37512
|
+
const demandOf = (input) => {
|
|
37513
|
+
const { rendered, kind, session, validate } = input;
|
|
37514
|
+
const content = kind === "stalled" ? stallDiagnosis(rendered.state, rendered.actor) : rendered.content;
|
|
37515
|
+
const skills = rendered.skills !== void 0 && rendered.skills.length > 0 ? rendered.skills : void 0;
|
|
37516
|
+
const access = rendered.access ?? {
|
|
37517
|
+
read: null,
|
|
37518
|
+
write: null
|
|
37550
37519
|
};
|
|
37551
|
-
|
|
37520
|
+
return kind === "prompt" ? {
|
|
37521
|
+
kind,
|
|
37522
|
+
content,
|
|
37523
|
+
session,
|
|
37524
|
+
validate,
|
|
37525
|
+
skills,
|
|
37526
|
+
access
|
|
37527
|
+
} : {
|
|
37528
|
+
kind,
|
|
37529
|
+
content
|
|
37530
|
+
};
|
|
37531
|
+
};
|
|
37552
37532
|
//#endregion
|
|
37553
37533
|
//#region src/wire/BeatStatus.ts
|
|
37554
|
-
|
|
37555
|
-
|
|
37556
|
-
|
|
37557
|
-
|
|
37558
|
-
|
|
37559
|
-
|
|
37560
|
-
|
|
37561
|
-
|
|
37562
|
-
|
|
37563
|
-
|
|
37564
|
-
|
|
37565
|
-
|
|
37566
|
-
|
|
37567
|
-
|
|
37568
|
-
|
|
37569
|
-
|
|
37570
|
-
|
|
37571
|
-
|
|
37572
|
-
|
|
37573
|
-
|
|
37574
|
-
|
|
37575
|
-
|
|
37576
|
-
|
|
37577
|
-
|
|
37578
|
-
|
|
37534
|
+
/**
|
|
37535
|
+
* Assemble one `BeatStatus`. `system` is omitted (not just falsy) when its
|
|
37536
|
+
* rendered value is the empty string, unlike `model`: an empty
|
|
37537
|
+
* `--system-prompt ""` would silently delete the harness's own default
|
|
37538
|
+
* instead of failing loudly.
|
|
37539
|
+
*/
|
|
37540
|
+
const statusOf = (input) => {
|
|
37541
|
+
const { rendered, idle, initial, workflow, log, changes, next, cost, costByModel, approvals } = input;
|
|
37542
|
+
return {
|
|
37543
|
+
idle,
|
|
37544
|
+
initial,
|
|
37545
|
+
model: rendered.model,
|
|
37546
|
+
system: rendered.system !== void 0 && rendered.system !== "" ? rendered.system : void 0,
|
|
37547
|
+
log,
|
|
37548
|
+
state: rendered.state,
|
|
37549
|
+
actor: rendered.actor,
|
|
37550
|
+
workflow,
|
|
37551
|
+
label: rendered.label,
|
|
37552
|
+
memory: rendered.memory,
|
|
37553
|
+
file: rendered.file,
|
|
37554
|
+
mode: rendered.mode,
|
|
37555
|
+
changes,
|
|
37556
|
+
next,
|
|
37557
|
+
cost,
|
|
37558
|
+
costByModel,
|
|
37559
|
+
judge: rendered.judge,
|
|
37560
|
+
approvals
|
|
37579
37561
|
};
|
|
37580
|
-
}
|
|
37562
|
+
};
|
|
37581
37563
|
//#endregion
|
|
37582
37564
|
//#region src/wire/BeatDocument.ts
|
|
37583
|
-
|
|
37584
|
-
|
|
37585
|
-
|
|
37586
|
-
|
|
37587
|
-
|
|
37588
|
-
|
|
37589
|
-
|
|
37590
|
-
|
|
37591
|
-
|
|
37592
|
-
|
|
37593
|
-
|
|
37594
|
-
|
|
37595
|
-
|
|
37596
|
-
|
|
37597
|
-
|
|
37598
|
-
|
|
37599
|
-
|
|
37600
|
-
|
|
37601
|
-
|
|
37602
|
-
|
|
37603
|
-
|
|
37604
|
-
|
|
37605
|
-
|
|
37606
|
-
|
|
37607
|
-
|
|
37608
|
-
|
|
37609
|
-
|
|
37610
|
-
|
|
37611
|
-
|
|
37612
|
-
|
|
37613
|
-
|
|
37614
|
-
|
|
37615
|
-
|
|
37616
|
-
|
|
37617
|
-
|
|
37618
|
-
|
|
37619
|
-
|
|
37620
|
-
|
|
37621
|
-
|
|
37622
|
-
|
|
37623
|
-
|
|
37624
|
-
|
|
37625
|
-
|
|
37626
|
-
|
|
37627
|
-
|
|
37628
|
-
|
|
37629
|
-
|
|
37630
|
-
|
|
37631
|
-
|
|
37632
|
-
|
|
37633
|
-
|
|
37634
|
-
|
|
37635
|
-
|
|
37636
|
-
|
|
37637
|
-
|
|
37638
|
-
|
|
37639
|
-
|
|
37640
|
-
|
|
37565
|
+
/** `gtd next --json`'s `next` key — `null` when landing would commit nothing. */
|
|
37566
|
+
const nextField = (next) => next === null ? null : { target: next.target };
|
|
37567
|
+
/** Unreachable in a well-typed call — used as the `default` arm of an exhaustive switch over `Demand["kind"]`, so a future `BeatKind` member that isn't handled fails `tsc`, not just review. */
|
|
37568
|
+
const assertNeverDemand = (demand) => {
|
|
37569
|
+
throw new Error(`unreachable demand: ${JSON.stringify(demand)}`);
|
|
37570
|
+
};
|
|
37571
|
+
/**
|
|
37572
|
+
* The ONE exhaustive switch translating a `Demand` into its wire
|
|
37573
|
+
* `session`/`validate`/`skills`/`access` set — only the `prompt` variant carries
|
|
37574
|
+
* any of them, and every other member is listed explicitly (not a
|
|
37575
|
+
* `default:` fallthrough) so adding a sixth `BeatKind` without adding its
|
|
37576
|
+
* case here fails `tsc` at the `assertNeverDemand` call, not silently
|
|
37577
|
+
* falling through to "no session."
|
|
37578
|
+
*/
|
|
37579
|
+
const dispatchFieldsOf = (demand) => {
|
|
37580
|
+
switch (demand.kind) {
|
|
37581
|
+
case "prompt": return {
|
|
37582
|
+
session: demand.session,
|
|
37583
|
+
validate: demand.validate,
|
|
37584
|
+
skills: demand.skills,
|
|
37585
|
+
access: demand.access
|
|
37586
|
+
};
|
|
37587
|
+
case "capture":
|
|
37588
|
+
case "message":
|
|
37589
|
+
case "script":
|
|
37590
|
+
case "stalled": return {
|
|
37591
|
+
session: void 0,
|
|
37592
|
+
validate: void 0,
|
|
37593
|
+
skills: void 0,
|
|
37594
|
+
access: void 0
|
|
37595
|
+
};
|
|
37596
|
+
default: return assertNeverDemand(demand);
|
|
37597
|
+
}
|
|
37598
|
+
};
|
|
37599
|
+
/**
|
|
37600
|
+
* Flatten a `Demand` plus its `BeatStatus` into the single 25-key document
|
|
37601
|
+
* `gtd next --json` emits — the ONLY place the two are joined. `cost`/
|
|
37602
|
+
* `costByModel` are omitted together, exactly when no cost was recorded
|
|
37603
|
+
* (`cost <= 0`).
|
|
37604
|
+
*/
|
|
37605
|
+
const beatDocument = (demand, status) => {
|
|
37606
|
+
const { session, validate, skills, access } = dispatchFieldsOf(demand);
|
|
37607
|
+
const hasCost = status.cost > 0;
|
|
37608
|
+
return {
|
|
37609
|
+
schema: 1,
|
|
37610
|
+
kind: demand.kind,
|
|
37611
|
+
content: demand.content,
|
|
37612
|
+
idle: status.idle,
|
|
37613
|
+
initial: status.initial,
|
|
37614
|
+
session,
|
|
37615
|
+
model: status.model,
|
|
37616
|
+
system: status.system,
|
|
37617
|
+
validate,
|
|
37618
|
+
log: status.log,
|
|
37619
|
+
state: status.state,
|
|
37620
|
+
actor: status.actor,
|
|
37621
|
+
workflow: status.workflow,
|
|
37622
|
+
label: status.label,
|
|
37623
|
+
memory: status.memory,
|
|
37624
|
+
file: status.file,
|
|
37625
|
+
mode: status.mode,
|
|
37626
|
+
changes: status.changes,
|
|
37627
|
+
next: nextField(status.next),
|
|
37628
|
+
cost: hasCost ? status.cost : void 0,
|
|
37629
|
+
costByModel: hasCost ? status.costByModel : void 0,
|
|
37630
|
+
judge: status.judge,
|
|
37631
|
+
skills,
|
|
37632
|
+
access,
|
|
37633
|
+
approvals: status.approvals
|
|
37641
37634
|
};
|
|
37642
|
-
|
|
37643
|
-
|
|
37635
|
+
};
|
|
37636
|
+
const renderBeatJson = (document) => JSON.stringify(document) + "\n";
|
|
37644
37637
|
//#endregion
|
|
37645
37638
|
//#region src/wire/BeatPlain.ts
|
|
37646
|
-
|
|
37647
|
-
|
|
37648
|
-
|
|
37649
|
-
|
|
37650
|
-
|
|
37651
|
-
|
|
37652
|
-
|
|
37653
|
-
|
|
37654
|
-
|
|
37655
|
-
};
|
|
37656
|
-
|
|
37657
|
-
|
|
37658
|
-
|
|
37659
|
-
|
|
37660
|
-
|
|
37661
|
-
|
|
37662
|
-
|
|
37663
|
-
|
|
37664
|
-
|
|
37665
|
-
|
|
37666
|
-
|
|
37667
|
-
|
|
37668
|
-
|
|
37669
|
-
|
|
37670
|
-
|
|
37671
|
-
|
|
37672
|
-
|
|
37673
|
-
|
|
37674
|
-
|
|
37675
|
-
|
|
37676
|
-
|
|
37677
|
-
|
|
37678
|
-
|
|
37679
|
-
|
|
37680
|
-
|
|
37681
|
-
|
|
37682
|
-
|
|
37683
|
-
|
|
37684
|
-
|
|
37685
|
-
|
|
37686
|
-
|
|
37687
|
-
|
|
37688
|
-
|
|
37689
|
-
|
|
37690
|
-
|
|
37691
|
-
|
|
37692
|
-
|
|
37693
|
-
|
|
37694
|
-
|
|
37695
|
-
|
|
37696
|
-
|
|
37697
|
-
|
|
37698
|
-
|
|
37699
|
-
|
|
37700
|
-
|
|
37701
|
-
|
|
37702
|
-
|
|
37703
|
-
|
|
37704
|
-
}
|
|
37705
|
-
|
|
37706
|
-
|
|
37707
|
-
|
|
37708
|
-
|
|
37709
|
-
|
|
37710
|
-
|
|
37711
|
-
|
|
37712
|
-
}
|
|
37713
|
-
|
|
37639
|
+
/**
|
|
37640
|
+
* True when the per-model breakdown adds information beyond the `Cost:`
|
|
37641
|
+
* total: more than one model, or a single model that carries an actual
|
|
37642
|
+
* `--model` tag (a lone `unspecified` bucket just restates the total, so
|
|
37643
|
+
* it's suppressed).
|
|
37644
|
+
*/
|
|
37645
|
+
const breakdownIsInformative = (byModel) => byModel.length > 1 || byModel.length === 1 && byModel[0].model !== "unspecified";
|
|
37646
|
+
const costStatusLines = (cost, byModel) => {
|
|
37647
|
+
if (cost <= 0) return [];
|
|
37648
|
+
const lines = [`Cost: ${cost}`];
|
|
37649
|
+
if (breakdownIsInformative(byModel)) for (const m of byModel) lines.push(` ${m.model}: ${m.cost}`);
|
|
37650
|
+
return lines;
|
|
37651
|
+
};
|
|
37652
|
+
const pendingStatusLines = (statusChanges) => statusChanges.length === 0 ? ["Pending: (clean)"] : ["Pending:", ...statusChanges.map((c) => ` ${c.status} ${c.path}`)];
|
|
37653
|
+
const nextStatusLine = (next) => next === null ? "Next: (nothing would land)" : `Next: → ${next.target}`;
|
|
37654
|
+
/** Builds `{[key]: value}` for each entry whose value isn't `undefined` — the shared "omit absent optional fields" shape the header uses. */
|
|
37655
|
+
const definedFields = (entries) => {
|
|
37656
|
+
const result = {};
|
|
37657
|
+
for (const [key, value] of entries) if (value !== void 0) result[key] = value;
|
|
37658
|
+
return result;
|
|
37659
|
+
};
|
|
37660
|
+
/**
|
|
37661
|
+
* The header block's own lines — `State:`/`Awaits:`/`Workflow:`/optional `Label:`/
|
|
37662
|
+
* `Model:`/`Memory:`/`File:`/`Mode:`/`Cost:`(+breakdown)/`Pending:`/`Next:`,
|
|
37663
|
+
* in that order. Used by `renderBeatPlain`'s header-showing kinds below.
|
|
37664
|
+
*/
|
|
37665
|
+
const beatHeaderLines = (document) => {
|
|
37666
|
+
const optional = definedFields([
|
|
37667
|
+
["Label", document.label],
|
|
37668
|
+
["Model", document.model],
|
|
37669
|
+
["Memory", document.memory],
|
|
37670
|
+
["File", document.file],
|
|
37671
|
+
["Mode", document.mode]
|
|
37672
|
+
]);
|
|
37673
|
+
return [
|
|
37674
|
+
`State: ${document.state}`,
|
|
37675
|
+
`Awaits: ${document.actor}`,
|
|
37676
|
+
`Workflow: ${document.workflow}`,
|
|
37677
|
+
...Object.entries(optional).map(([key, value]) => `${key}: ${value}`),
|
|
37678
|
+
...costStatusLines(document.cost ?? 0, document.costByModel ?? []),
|
|
37679
|
+
...pendingStatusLines(document.changes),
|
|
37680
|
+
nextStatusLine(document.next)
|
|
37681
|
+
];
|
|
37682
|
+
};
|
|
37683
|
+
/**
|
|
37684
|
+
* One entry per `BeatKind`, typed as `Record<BeatKind, DemandBriefing>` so
|
|
37685
|
+
* the object literal itself is exhaustive: adding or removing a `BeatKind`
|
|
37686
|
+
* member without updating this map fails `tsc`, the same guarantee
|
|
37687
|
+
* `dispatchFieldsOf`'s switch (`BeatDocument.ts`) gives the JSON side. Exported
|
|
37688
|
+
* so the one cross-cutting exhaustiveness test (`BeatDocument.test.ts`) can
|
|
37689
|
+
* assert against it directly, rather than re-deriving it from
|
|
37690
|
+
* `renderBeatPlain`'s output. Replaces the old `renderBeatPlain`'s hand-written
|
|
37691
|
+
* `if (kind === "script") … if (kind === "capture") …` pair.
|
|
37692
|
+
*/
|
|
37693
|
+
const DEMAND_BRIEFING = {
|
|
37694
|
+
capture: {
|
|
37695
|
+
instructionLine: "The edit is already made — run `gtd land` to land it.",
|
|
37696
|
+
suppressHeader: false
|
|
37697
|
+
},
|
|
37698
|
+
message: {
|
|
37699
|
+
instructionLine: void 0,
|
|
37700
|
+
suppressHeader: false
|
|
37701
|
+
},
|
|
37702
|
+
script: {
|
|
37703
|
+
instructionLine: "Run this script:",
|
|
37704
|
+
suppressHeader: false
|
|
37705
|
+
},
|
|
37706
|
+
stalled: {
|
|
37707
|
+
instructionLine: void 0,
|
|
37708
|
+
suppressHeader: false
|
|
37709
|
+
},
|
|
37710
|
+
prompt: {
|
|
37711
|
+
instructionLine: void 0,
|
|
37712
|
+
suppressHeader: true
|
|
37713
|
+
}
|
|
37714
|
+
};
|
|
37715
|
+
/**
|
|
37716
|
+
* The self-validation instruction appended to a `prompt` beat's plain output
|
|
37717
|
+
* only — a structured (`--json`) driver runs the `validate` field
|
|
37718
|
+
* itself instead. Advisory either way: `gtd land` refuses a turn whose
|
|
37719
|
+
* steering file is invalid regardless of whether this was followed.
|
|
37720
|
+
*/
|
|
37721
|
+
const selfValidateInstruction = (command, file) => `\nBefore finishing your turn, run \`${command}\` — it checks ${file} — and fix every violation it reports until it exits cleanly. Do not finish while it still reports violations.\n`;
|
|
37722
|
+
/**
|
|
37723
|
+
* `gtd next`'s plain-text encoding, driven by `DEMAND_BRIEFING`. At
|
|
37724
|
+
* `kind === "prompt"` the status header is suppressed and only `content`
|
|
37725
|
+
* plus the self-validation instruction is emitted — those bytes ARE the
|
|
37726
|
+
* agent's input, so gtd's own bookkeeping (state name, edges, pending
|
|
37727
|
+
* changes) must not be prefixed onto it. Every other kind gets the header
|
|
37728
|
+
* plus its instruction line, since its plain output is read by a human or
|
|
37729
|
+
* driver, never an agent. `selfValidateCommand` is already resolved; this
|
|
37730
|
+
* function never resolves it itself.
|
|
37731
|
+
*/
|
|
37732
|
+
const renderBeatPlain = (document, selfValidateCommand) => {
|
|
37733
|
+
const content = document.content.endsWith("\n") ? document.content : document.content + "\n";
|
|
37734
|
+
const briefing = DEMAND_BRIEFING[document.kind];
|
|
37735
|
+
if (briefing.suppressHeader) return selfValidateCommand !== void 0 && document.file !== void 0 ? content + selfValidateInstruction(selfValidateCommand, document.file) : content;
|
|
37736
|
+
const body = beatHeaderLines(document).join("\n") + "\n\n" + content;
|
|
37737
|
+
return briefing.instructionLine !== void 0 ? `${briefing.instructionLine}\n` + body : body;
|
|
37738
|
+
};
|
|
37714
37739
|
//#endregion
|
|
37715
37740
|
//#region src/wire/Land.ts
|
|
37716
|
-
|
|
37717
|
-
|
|
37718
|
-
|
|
37719
|
-
|
|
37720
|
-
|
|
37721
|
-
|
|
37722
|
-
|
|
37723
|
-
|
|
37724
|
-
|
|
37725
|
-
|
|
37726
|
-
|
|
37727
|
-
|
|
37728
|
-
|
|
37729
|
-
|
|
37730
|
-
|
|
37731
|
-
|
|
37732
|
-
|
|
37733
|
-
|
|
37734
|
-
|
|
37735
|
-
|
|
37741
|
+
/** Assembles one `gtd land` result's fields in `LandFields`' declared order — the ONLY place `gtd land --json`'s wire shape is built. */
|
|
37742
|
+
const landFields = (input) => ({
|
|
37743
|
+
schema: 1,
|
|
37744
|
+
script: input.script,
|
|
37745
|
+
settled: input.settled,
|
|
37746
|
+
idle: input.idle,
|
|
37747
|
+
state: input.state,
|
|
37748
|
+
subject: input.subject,
|
|
37749
|
+
cost: input.cost,
|
|
37750
|
+
model: input.model
|
|
37751
|
+
});
|
|
37752
|
+
const renderLandJson = (fields) => JSON.stringify(fields) + "\n";
|
|
37753
|
+
const FMT_NOOP = "nothing to do at \"%s\"\n";
|
|
37754
|
+
const FMT_LAND_PROSE = "commit everything with this message: %s\n";
|
|
37755
|
+
/** `nothing to do at "<state>"` — a no-op step's plain-text line, and the text a print-only script's own `printf` carries. */
|
|
37756
|
+
const noopText = (state) => renderFormat(FMT_NOOP, state);
|
|
37757
|
+
/** `commit everything with this message: <subject>` — plain `gtd land`'s own stdout at a pending diff (no script); `--json` keeps emitting the script itself, unaffected. */
|
|
37758
|
+
const landProseText = (subject) => renderFormat(FMT_LAND_PROSE, subject);
|
|
37759
|
+
/**
|
|
37760
|
+
* `gtd land`'s plain-text encoding — names the commit subject at a real
|
|
37761
|
+
* landing and points at `--json=script`, since the script itself is
|
|
37762
|
+
* unreachable from plain output; prints the existing no-op note otherwise.
|
|
37763
|
+
* `command` names the invocation to point at — `"land"` by default, but
|
|
37764
|
+
* `gtd judge answer` (the only other caller of `planLanding`/`landFields`)
|
|
37765
|
+
* passes its own two-word name so the hint stays runnable verbatim.
|
|
37766
|
+
*/
|
|
37767
|
+
const renderLandPlain = (fields, command = "land") => fields.subject !== null ? `${landProseText(fields.subject).trimEnd()}\n(run \`gtd ${command} --json=script | sh\` to get the landing script)\n` : noopText(fields.state);
|
|
37736
37768
|
//#endregion
|
|
37737
37769
|
//#region src/wire/Judge.ts
|
|
37738
|
-
|
|
37739
|
-
|
|
37740
|
-
|
|
37741
|
-
|
|
37742
|
-
|
|
37743
|
-
state,
|
|
37744
|
-
questions
|
|
37745
|
-
});
|
|
37746
|
-
}));
|
|
37770
|
+
const judgeJson = (state, questions) => JSON.stringify({
|
|
37771
|
+
schema: 1,
|
|
37772
|
+
state,
|
|
37773
|
+
questions
|
|
37774
|
+
});
|
|
37747
37775
|
//#endregion
|
|
37748
37776
|
//#region src/wire/Select.ts
|
|
37749
|
-
|
|
37750
|
-
|
|
37751
|
-
|
|
37752
|
-
|
|
37753
|
-
|
|
37754
|
-
|
|
37755
|
-
|
|
37756
|
-
|
|
37757
|
-
|
|
37758
|
-
|
|
37759
|
-
};
|
|
37760
|
-
return {
|
|
37761
|
-
kind: "value",
|
|
37762
|
-
text: String(value)
|
|
37763
|
-
};
|
|
37777
|
+
/**
|
|
37778
|
+
* Renders a fully-walked leaf value: scalars/booleans stringify directly,
|
|
37779
|
+
* arrays become one `JSON.stringify` per entry, newline-joined.
|
|
37780
|
+
* `selectPath`'s loop already returns `absent` for `undefined`/`null` after
|
|
37781
|
+
* every segment (including the last), so neither ever reaches here.
|
|
37782
|
+
*/
|
|
37783
|
+
const toSelection = (value) => {
|
|
37784
|
+
if (Array.isArray(value)) return {
|
|
37785
|
+
kind: "value",
|
|
37786
|
+
text: value.map((entry) => JSON.stringify(entry)).join("\n")
|
|
37764
37787
|
};
|
|
37765
|
-
|
|
37766
|
-
|
|
37767
|
-
|
|
37768
|
-
if (Array.isArray(current)) return ARRAY_INDEX.test(segment) && Number(segment) < current.length ? current[Number(segment)] : NOT_FOUND;
|
|
37769
|
-
if (typeof current === "object" && current !== null) {
|
|
37770
|
-
const record = current;
|
|
37771
|
-
return Object.prototype.hasOwnProperty.call(record, segment) ? record[segment] : NOT_FOUND;
|
|
37772
|
-
}
|
|
37773
|
-
return NOT_FOUND;
|
|
37788
|
+
if (typeof value === "object") return {
|
|
37789
|
+
kind: "value",
|
|
37790
|
+
text: JSON.stringify(value)
|
|
37774
37791
|
};
|
|
37775
|
-
|
|
37776
|
-
|
|
37777
|
-
|
|
37778
|
-
|
|
37779
|
-
|
|
37780
|
-
|
|
37781
|
-
|
|
37782
|
-
|
|
37783
|
-
|
|
37784
|
-
|
|
37785
|
-
|
|
37786
|
-
|
|
37787
|
-
|
|
37788
|
-
|
|
37792
|
+
return {
|
|
37793
|
+
kind: "value",
|
|
37794
|
+
text: String(value)
|
|
37795
|
+
};
|
|
37796
|
+
};
|
|
37797
|
+
/** A segment that resolves to no key at all (missing from an object, out-of-range or non-numeric against an array) — distinct from a present key holding `undefined`. */
|
|
37798
|
+
const NOT_FOUND = Symbol("not-found");
|
|
37799
|
+
/** An all-digits segment (`"0"`, `"12"`) — the only shape an array segment resolves against. */
|
|
37800
|
+
const ARRAY_INDEX = /^\d+$/;
|
|
37801
|
+
/**
|
|
37802
|
+
* One step of the walk: looks `segment` up on `current`, or reports
|
|
37803
|
+
* `NOT_FOUND` for a key that was never there (a primitive has no keys).
|
|
37804
|
+
* Against an array, only an all-digits segment resolves — an in-range index
|
|
37805
|
+
* (`skills.0` reads the first declared skill), never a non-index own key
|
|
37806
|
+
* (`length`/`map`/every other inherited array member stays `NOT_FOUND`).
|
|
37807
|
+
* Against an object, presence is an OWN-property test
|
|
37808
|
+
* (`Object.prototype.hasOwnProperty`), never the `in` operator — `in` walks
|
|
37809
|
+
* the prototype chain, so it would resolve inherited members
|
|
37810
|
+
* (`constructor`, `toString`, `hasOwnProperty`, `valueOf`, ...) as real
|
|
37811
|
+
* document fields.
|
|
37812
|
+
*/
|
|
37813
|
+
const resolveSegment = (current, segment) => {
|
|
37814
|
+
if (Array.isArray(current)) return ARRAY_INDEX.test(segment) && Number(segment) < current.length ? current[Number(segment)] : NOT_FOUND;
|
|
37815
|
+
if (typeof current === "object" && current !== null) {
|
|
37816
|
+
const record = current;
|
|
37817
|
+
return Object.prototype.hasOwnProperty.call(record, segment) ? record[segment] : NOT_FOUND;
|
|
37818
|
+
}
|
|
37819
|
+
return NOT_FOUND;
|
|
37820
|
+
};
|
|
37821
|
+
/**
|
|
37822
|
+
* Walks `fields` by dotted key path. Never throws — any unwalkable shape
|
|
37823
|
+
* (a primitive mid-path, an out-of-range or non-numeric array segment)
|
|
37824
|
+
* degrades to `unknown`, and a present-but-`undefined` value anywhere along
|
|
37825
|
+
* the path short-circuits the whole remaining path to `absent` rather than
|
|
37826
|
+
* reporting a key that was never actually missing.
|
|
37827
|
+
*/
|
|
37828
|
+
const selectPath = (fields, path) => {
|
|
37829
|
+
try {
|
|
37830
|
+
let current = fields;
|
|
37831
|
+
for (const segment of path.split(".")) {
|
|
37832
|
+
current = resolveSegment(current, segment);
|
|
37833
|
+
if (current === NOT_FOUND) return {
|
|
37789
37834
|
kind: "unknown",
|
|
37790
37835
|
path
|
|
37791
37836
|
};
|
|
37837
|
+
if (current === void 0 || current === null) return { kind: "absent" };
|
|
37792
37838
|
}
|
|
37793
|
-
|
|
37794
|
-
}
|
|
37839
|
+
return toSelection(current);
|
|
37840
|
+
} catch {
|
|
37841
|
+
return {
|
|
37842
|
+
kind: "unknown",
|
|
37843
|
+
path
|
|
37844
|
+
};
|
|
37845
|
+
}
|
|
37846
|
+
};
|
|
37795
37847
|
//#endregion
|
|
37796
37848
|
//#region src/wire/Sessions.ts
|
|
37797
|
-
|
|
37798
|
-
|
|
37799
|
-
|
|
37800
|
-
|
|
37801
|
-
|
|
37802
|
-
|
|
37803
|
-
|
|
37804
|
-
|
|
37805
|
-
|
|
37806
|
-
|
|
37807
|
-
|
|
37808
|
-
|
|
37809
|
-
|
|
37810
|
-
|
|
37811
|
-
|
|
37812
|
-
|
|
37813
|
-
|
|
37814
|
-
|
|
37815
|
-
|
|
37816
|
-
|
|
37817
|
-
|
|
37818
|
-
|
|
37849
|
+
/** Fixed forever — every derived session id descends from this one constant. */
|
|
37850
|
+
const GTD_SESSION_NAMESPACE = "ca4be249-805e-41c1-8b6a-75a11c011e25";
|
|
37851
|
+
const toBytes = (uuid) => Buffer.from(uuid.replace(/-/g, ""), "hex");
|
|
37852
|
+
const toUuidString = (bytes) => {
|
|
37853
|
+
const hex = bytes.toString("hex");
|
|
37854
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
37855
|
+
};
|
|
37856
|
+
/**
|
|
37857
|
+
* UUIDv5 (RFC 4122 §4.3), hand-rolled over `node:crypto`'s SHA-1 rather than
|
|
37858
|
+
* a dependency — correctness is checked against the RFC's own DNS-namespace
|
|
37859
|
+
* test vector in `Sessions.test.ts`.
|
|
37860
|
+
*/
|
|
37861
|
+
const uuidv5 = (namespace, name) => {
|
|
37862
|
+
const bytes = createHash("sha1").update(toBytes(namespace)).update(name, "utf8").digest().subarray(0, 16);
|
|
37863
|
+
bytes[6] = bytes[6] & 15 | 80;
|
|
37864
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
37865
|
+
return toUuidString(bytes);
|
|
37866
|
+
};
|
|
37867
|
+
/**
|
|
37868
|
+
* Resolves a `prompt` rest's session id: with no `memoryKey` there's no
|
|
37869
|
+
* history to derive a stable id from, so it mints an ephemeral one and forces
|
|
37870
|
+
* `resume: false`; otherwise the id is deterministic (`uuidv5`) and `resume`
|
|
37871
|
+
* is passed through as given.
|
|
37872
|
+
*/
|
|
37873
|
+
const resolveSession = (memoryKey, resume, mint = randomUUID) => memoryKey === void 0 ? {
|
|
37874
|
+
sessionId: mint(),
|
|
37875
|
+
resume: false
|
|
37876
|
+
} : {
|
|
37877
|
+
sessionId: uuidv5(GTD_SESSION_NAMESPACE, memoryKey),
|
|
37878
|
+
resume
|
|
37879
|
+
};
|
|
37819
37880
|
//#endregion
|
|
37820
37881
|
//#region src/wire/Glob.ts
|
|
37821
|
-
|
|
37822
|
-
|
|
37823
|
-
|
|
37824
|
-
|
|
37825
|
-
|
|
37826
|
-
|
|
37827
|
-
|
|
37828
|
-
|
|
37829
|
-
|
|
37830
|
-
|
|
37831
|
-
|
|
37832
|
-
|
|
37833
|
-
|
|
37834
|
-
|
|
37835
|
-
|
|
37836
|
-
|
|
37837
|
-
|
|
37838
|
-
|
|
37839
|
-
pattern += ".*";
|
|
37840
|
-
i += 2;
|
|
37841
|
-
}
|
|
37842
|
-
}
|
|
37843
|
-
return new RegExp(`${pattern}$`, "s");
|
|
37844
|
-
};
|
|
37845
|
-
globMatches = (path, glob) => compile(glob).test(path);
|
|
37846
|
-
}));
|
|
37847
|
-
//#endregion
|
|
37848
|
-
//#region src/wire/Slices.ts
|
|
37849
|
-
var slicesDocument, renderSlicesJson;
|
|
37850
|
-
var init_Slices = __esmMin((() => {
|
|
37851
|
-
init_constants();
|
|
37852
|
-
slicesDocument = (slices) => ({
|
|
37853
|
-
schema: 1,
|
|
37854
|
-
slices: slices.map(({ name, base, tip, closed }) => ({
|
|
37855
|
-
name,
|
|
37856
|
-
base,
|
|
37857
|
-
tip,
|
|
37858
|
-
closed
|
|
37859
|
-
}))
|
|
37860
|
-
});
|
|
37861
|
-
renderSlicesJson = (document) => JSON.stringify(document) + "\n";
|
|
37862
|
-
}));
|
|
37863
|
-
//#endregion
|
|
37864
|
-
//#region src/wire/ContextLimit.ts
|
|
37865
|
-
var DEFAULT_CONTEXT_LIMIT, DEFAULT_WINDOW, LONG_WINDOW, contextWindowFor, parseContextWindow, CONTEXT_STOP_FILE_ENV, contextShapeFault, contextLimitFault, record$1, count, usageTokens, transcriptTokens, CONTEXT_LIMIT_MARKER, percent, contextHook, hookOutput;
|
|
37866
|
-
var init_ContextLimit = __esmMin((() => {
|
|
37867
|
-
DEFAULT_CONTEXT_LIMIT = {
|
|
37868
|
-
soft: .6,
|
|
37869
|
-
hard: .7
|
|
37870
|
-
};
|
|
37871
|
-
DEFAULT_WINDOW = 2e5;
|
|
37872
|
-
LONG_WINDOW = 1e6;
|
|
37873
|
-
contextWindowFor = (model) => model !== void 0 && /\[1m\]/i.test(model) ? LONG_WINDOW : DEFAULT_WINDOW;
|
|
37874
|
-
parseContextWindow = (raw) => {
|
|
37875
|
-
const n = Number(raw);
|
|
37876
|
-
return raw !== void 0 && raw.trim() !== "" && Number.isInteger(n) && n > 0 ? n : void 0;
|
|
37877
|
-
};
|
|
37878
|
-
CONTEXT_STOP_FILE_ENV = "GTD_LOOP_CONTEXT_STOP_FILE";
|
|
37879
|
-
contextShapeFault = (limit) => {
|
|
37880
|
-
if (typeof limit !== "object" || limit === null || Array.isArray(limit)) return `context must be an object { soft, hard }, got ${JSON.stringify(limit)}`;
|
|
37881
|
-
const unknown = Object.keys(limit).filter((key) => key !== "soft" && key !== "hard");
|
|
37882
|
-
if (unknown.length > 0) return `unknown key(s) ${unknown.join(", ")} in context`;
|
|
37883
|
-
const shares = limit;
|
|
37884
|
-
for (const key of ["soft", "hard"]) {
|
|
37885
|
-
const value = shares[key];
|
|
37886
|
-
if (value === void 0) continue;
|
|
37887
|
-
if (typeof value !== "number" || !(value > 0 && value <= 1)) return `context.${key} must be a share of the context window in (0, 1], got ${JSON.stringify(value)}`;
|
|
37888
|
-
}
|
|
37889
|
-
};
|
|
37890
|
-
contextLimitFault = (limit) => {
|
|
37891
|
-
const shape = contextShapeFault(limit);
|
|
37892
|
-
if (shape !== void 0) return shape;
|
|
37893
|
-
const shares = limit;
|
|
37894
|
-
const soft = shares.soft ?? DEFAULT_CONTEXT_LIMIT.soft;
|
|
37895
|
-
const hard = shares.hard ?? DEFAULT_CONTEXT_LIMIT.hard;
|
|
37896
|
-
return soft < hard ? void 0 : `context.soft (${soft}) must be below context.hard (${hard})`;
|
|
37897
|
-
};
|
|
37898
|
-
record$1 = (value) => typeof value === "object" && value !== null ? value : void 0;
|
|
37899
|
-
count = (value) => typeof value === "number" && value > 0 ? value : 0;
|
|
37900
|
-
usageTokens = (usage) => {
|
|
37901
|
-
const u = record$1(usage);
|
|
37902
|
-
if (u === void 0) return void 0;
|
|
37903
|
-
return count(u["input_tokens"]) + count(u["cache_read_input_tokens"]) + count(u["cache_creation_input_tokens"]);
|
|
37904
|
-
};
|
|
37905
|
-
transcriptTokens = (transcript) => {
|
|
37906
|
-
const lines = transcript.split("\n");
|
|
37907
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
37908
|
-
const line = lines[i].trim();
|
|
37909
|
-
if (line === "") continue;
|
|
37910
|
-
let entry;
|
|
37911
|
-
try {
|
|
37912
|
-
entry = JSON.parse(line);
|
|
37913
|
-
} catch {
|
|
37914
|
-
continue;
|
|
37915
|
-
}
|
|
37916
|
-
if (record$1(entry)?.["type"] !== "assistant") continue;
|
|
37917
|
-
const tokens = usageTokens(record$1(record$1(entry)?.["message"])?.["usage"]);
|
|
37918
|
-
if (tokens !== void 0) return tokens;
|
|
37882
|
+
const ESCAPE_RE = /[.+^${}()|[\]\\?]/g;
|
|
37883
|
+
const compile = (glob) => {
|
|
37884
|
+
let pattern = "^";
|
|
37885
|
+
let i = 0;
|
|
37886
|
+
while (i < glob.length) {
|
|
37887
|
+
const char = glob[i];
|
|
37888
|
+
if (char !== "*") {
|
|
37889
|
+
pattern += char.replace(ESCAPE_RE, "\\$&");
|
|
37890
|
+
i += 1;
|
|
37891
|
+
} else if (glob[i + 1] !== "*") {
|
|
37892
|
+
pattern += "[^/]*";
|
|
37893
|
+
i += 1;
|
|
37894
|
+
} else if (glob[i + 2] === "/") {
|
|
37895
|
+
pattern += "(?:.*/)?";
|
|
37896
|
+
i += 3;
|
|
37897
|
+
} else {
|
|
37898
|
+
pattern += ".*";
|
|
37899
|
+
i += 2;
|
|
37919
37900
|
}
|
|
37920
|
-
}
|
|
37921
|
-
|
|
37922
|
-
|
|
37923
|
-
|
|
37924
|
-
const { event, tokens, window, limit } = input;
|
|
37925
|
-
if (event === "PreCompact") return {
|
|
37926
|
-
kind: "hard",
|
|
37927
|
-
stopReason: `${CONTEXT_LIMIT_MARKER}: the context is about to be compacted — the run is too big for one turn`
|
|
37928
|
-
};
|
|
37929
|
-
if (tokens === void 0) return void 0;
|
|
37930
|
-
const used = `${tokens} of ${window} tokens`;
|
|
37931
|
-
if (tokens >= limit.hard * window) return {
|
|
37932
|
-
kind: "hard",
|
|
37933
|
-
stopReason: `${CONTEXT_LIMIT_MARKER}: ${used}, past the ${percent(limit.hard)} hard limit`
|
|
37934
|
-
};
|
|
37935
|
-
if (tokens >= limit.soft * window) return {
|
|
37936
|
-
kind: "soft",
|
|
37937
|
-
reason: `Context limit: ${used} used, past the ${percent(limit.soft)} soft limit. Stop working now. Do not start anything new; end your turn with a short handoff note as your answer: what you touched, and what was next. Where your instructions name a file for that note, write it there too.`
|
|
37938
|
-
};
|
|
37939
|
-
};
|
|
37940
|
-
hookOutput = (decision) => decision.kind === "soft" ? {
|
|
37941
|
-
decision: "block",
|
|
37942
|
-
reason: decision.reason
|
|
37943
|
-
} : {
|
|
37944
|
-
continue: false,
|
|
37945
|
-
stopReason: decision.stopReason
|
|
37946
|
-
};
|
|
37947
|
-
}));
|
|
37948
|
-
//#endregion
|
|
37949
|
-
//#region src/wire/index.ts
|
|
37950
|
-
var init_wire = __esmMin((() => {
|
|
37951
|
-
init_constants();
|
|
37952
|
-
init_ExitCodes();
|
|
37953
|
-
init_types();
|
|
37954
|
-
init_Demand();
|
|
37955
|
-
init_BeatStatus();
|
|
37956
|
-
init_BeatDocument();
|
|
37957
|
-
init_BeatPlain();
|
|
37958
|
-
init_Land();
|
|
37959
|
-
init_Judge();
|
|
37960
|
-
init_Select();
|
|
37961
|
-
init_Sessions();
|
|
37962
|
-
init_Glob();
|
|
37963
|
-
init_Slices();
|
|
37964
|
-
init_ContextLimit();
|
|
37965
|
-
}));
|
|
37901
|
+
}
|
|
37902
|
+
return new RegExp(`${pattern}$`, "s");
|
|
37903
|
+
};
|
|
37904
|
+
const globMatches = (path, glob) => compile(glob).test(path);
|
|
37966
37905
|
//#endregion
|
|
37967
37906
|
//#region src/replay/RefName.ts
|
|
37968
|
-
init_wire();
|
|
37969
37907
|
const FORBIDDEN = /[\u0000- \u007f~^:?*[\\]/;
|
|
37970
37908
|
/** Why `name` cannot be (part of) a git ref name, or `undefined` when it can. */
|
|
37971
37909
|
const refNameFault = (name) => {
|
|
@@ -37981,32 +37919,6 @@ const refNameFault = (name) => {
|
|
|
37981
37919
|
}
|
|
37982
37920
|
};
|
|
37983
37921
|
//#endregion
|
|
37984
|
-
//#region src/replay/Slices.ts
|
|
37985
|
-
const IMPLICIT_SLICE = "main";
|
|
37986
|
-
/** Same shape as mark()'s name rule; kept local so @pmelab/gtd/flows publishes no regex. */
|
|
37987
|
-
const SLICE_NAME = /^[a-z][a-z0-9-]*$/;
|
|
37988
|
-
/**
|
|
37989
|
-
* Folds boundaries into ranges: each kept span's base is the previous kept
|
|
37990
|
-
* span's `leftAt`, so a gap before a span belongs to it, and the last span
|
|
37991
|
-
* runs to the episode tip. A closed span that landed nothing is dropped.
|
|
37992
|
-
*/
|
|
37993
|
-
const sliceRanges = (spans, episode) => {
|
|
37994
|
-
if (spans.some((s) => s.enteredAt === "" || s.leftAt === "")) throw new Error("sliceRanges: a pending turn's \"\" hash is not a commit");
|
|
37995
|
-
const kept = spans.filter((s) => s.leftAt === void 0 || s.enteredAt !== s.leftAt);
|
|
37996
|
-
if (kept.length === 0) return [{
|
|
37997
|
-
name: IMPLICIT_SLICE,
|
|
37998
|
-
base: episode.base,
|
|
37999
|
-
tip: episode.tip,
|
|
38000
|
-
open: true
|
|
38001
|
-
}];
|
|
38002
|
-
return kept.map((s, i) => ({
|
|
38003
|
-
name: s.name,
|
|
38004
|
-
base: i === 0 ? episode.base : kept[i - 1].leftAt ?? episode.base,
|
|
38005
|
-
tip: i === kept.length - 1 ? episode.tip : s.leftAt ?? episode.tip,
|
|
38006
|
-
open: i === kept.length - 1
|
|
38007
|
-
}));
|
|
38008
|
-
};
|
|
38009
|
-
//#endregion
|
|
38010
37922
|
//#region src/replay/Tree.ts
|
|
38011
37923
|
const treeFromRecord = (files) => ({
|
|
38012
37924
|
paths: () => Object.keys(files).sort(),
|
|
@@ -38313,15 +38225,6 @@ const budgeted = (evidence, budgetBytes) => {
|
|
|
38313
38225
|
truncated
|
|
38314
38226
|
};
|
|
38315
38227
|
};
|
|
38316
|
-
const scopeLabel = (chain) => {
|
|
38317
|
-
const path = chain.flatMap((o) => o.name === void 0 ? [] : [o.name]).join(".");
|
|
38318
|
-
if (chain.at(-1)?.name !== void 0) return `scope "${path}"`;
|
|
38319
|
-
return path === "" ? "an unnamed scope" : `an unnamed scope inside "${path}"`;
|
|
38320
|
-
};
|
|
38321
|
-
const inheritedContext = (layers) => layers.reduce((merged, layer) => layer === void 0 ? merged : {
|
|
38322
|
-
...merged,
|
|
38323
|
-
...Object.fromEntries(Object.entries(layer).filter(([, value]) => value !== void 0))
|
|
38324
|
-
}, void 0);
|
|
38325
38228
|
const sameSkills = (a, b) => (a?.length ?? 0) === (b?.length ?? 0) && (a ?? []).every((skill, i) => skill === b?.[i]);
|
|
38326
38229
|
const samePersona = (a, b) => a.model === b.model && a.system === b.system && sameSkills(a.skills, b.skills) && sameAccess(a.access, b.access);
|
|
38327
38230
|
/**
|
|
@@ -38341,7 +38244,6 @@ const KNOWN_OPTIONS = {
|
|
|
38341
38244
|
...STEERING_OPTIONS,
|
|
38342
38245
|
"model",
|
|
38343
38246
|
"system",
|
|
38344
|
-
"context",
|
|
38345
38247
|
"allowEmpty",
|
|
38346
38248
|
"keep"
|
|
38347
38249
|
]),
|
|
@@ -38386,7 +38288,6 @@ const replay = async (input) => {
|
|
|
38386
38288
|
const keptTips = /* @__PURE__ */ new Map();
|
|
38387
38289
|
let landed;
|
|
38388
38290
|
const marks = [];
|
|
38389
|
-
const slices = [];
|
|
38390
38291
|
let outcome;
|
|
38391
38292
|
let signal;
|
|
38392
38293
|
const settled = new Promise((resolve) => {
|
|
@@ -38456,13 +38357,11 @@ const replay = async (input) => {
|
|
|
38456
38357
|
if (s.model !== void 0) persona.model = s.model;
|
|
38457
38358
|
if (s.system !== void 0) persona.system = s.system;
|
|
38458
38359
|
}
|
|
38459
|
-
const context = inheritedContext([...scopes.map((s) => s.context), request.options.context]);
|
|
38460
38360
|
return {
|
|
38461
38361
|
...request,
|
|
38462
38362
|
options: {
|
|
38463
38363
|
...persona,
|
|
38464
|
-
...request.options
|
|
38465
|
-
...context === void 0 ? {} : { context }
|
|
38364
|
+
...request.options
|
|
38466
38365
|
}
|
|
38467
38366
|
};
|
|
38468
38367
|
}
|
|
@@ -38596,31 +38495,14 @@ const replay = async (input) => {
|
|
|
38596
38495
|
rest: step,
|
|
38597
38496
|
trace,
|
|
38598
38497
|
landed,
|
|
38599
|
-
marks
|
|
38600
|
-
slices: slices.map((s) => ({ ...s }))
|
|
38498
|
+
marks
|
|
38601
38499
|
});
|
|
38602
38500
|
return landPending(step, input.pending);
|
|
38603
38501
|
};
|
|
38604
|
-
const scopeContextFault = () => {
|
|
38605
|
-
for (const [i, s] of scopes.entries()) {
|
|
38606
|
-
const fault = s.context === void 0 ? void 0 : contextShapeFault(s.context);
|
|
38607
|
-
if (fault !== void 0) return `the context of ${scopeLabel(scopes.slice(0, i + 1))}: ${fault}`;
|
|
38608
|
-
}
|
|
38609
|
-
};
|
|
38610
|
-
const resolvedContextFault = (request) => {
|
|
38611
|
-
const own = request.options.context;
|
|
38612
|
-
const layered = scopeContextFault() ?? (own === void 0 ? void 0 : contextShapeFault(own));
|
|
38613
|
-
if (layered !== void 0) return layered;
|
|
38614
|
-
const resolved = resolve(request);
|
|
38615
|
-
const context = resolved.kind === "agent" ? resolved.options.context : void 0;
|
|
38616
|
-
return context === void 0 ? void 0 : contextLimitFault(context);
|
|
38617
|
-
};
|
|
38618
38502
|
const requestFault = (request) => {
|
|
38619
38503
|
if (request.kind === "run" && typeof request.body !== "string") return `gtd: step "${scoped(request.name)}": a run() body is a shell script string — decide in flow code, then render the script (see checkScript and friends)`;
|
|
38620
38504
|
if (request.kind !== "agent") return void 0;
|
|
38621
38505
|
const name = scoped(request.name);
|
|
38622
|
-
const contextFault = resolvedContextFault(request);
|
|
38623
|
-
if (contextFault !== void 0) return `gtd: step "${name}": ${contextFault}`;
|
|
38624
38506
|
if (request.options.keep === true) {
|
|
38625
38507
|
if (request.options.allowEmpty === true) return `gtd: step "${name}": keep: true cannot be combined with allowEmpty — a turn that changes nothing has nothing to keep`;
|
|
38626
38508
|
const refFault = name.includes("/") ? "it contains \"/\"" : refNameFault(name);
|
|
@@ -38652,8 +38534,7 @@ const replay = async (input) => {
|
|
|
38652
38534
|
via,
|
|
38653
38535
|
trace,
|
|
38654
38536
|
landed,
|
|
38655
|
-
marks
|
|
38656
|
-
slices: slices.map((s) => ({ ...s }))
|
|
38537
|
+
marks
|
|
38657
38538
|
};
|
|
38658
38539
|
const changesSince = (hash) => {
|
|
38659
38540
|
const tree = treeAt(hash);
|
|
@@ -38678,31 +38559,6 @@ const replay = async (input) => {
|
|
|
38678
38559
|
scopes.pop();
|
|
38679
38560
|
scopeCallIds.pop();
|
|
38680
38561
|
},
|
|
38681
|
-
enterSlice: (name) => {
|
|
38682
|
-
if (outcome !== void 0) return;
|
|
38683
|
-
const fail = (message) => {
|
|
38684
|
-
finish({
|
|
38685
|
-
kind: "failed",
|
|
38686
|
-
message
|
|
38687
|
-
});
|
|
38688
|
-
throw new Stop();
|
|
38689
|
-
};
|
|
38690
|
-
if (typeof name !== "string" || !SLICE_NAME.test(name)) fail(`gtd: slice "${name}": name must match ${SLICE_NAME}`);
|
|
38691
|
-
if (name === "main") fail(`gtd: slice "${IMPLICIT_SLICE}" is reserved for the implicit slice`);
|
|
38692
|
-
const open = slices.find((s) => s.leftAt === void 0);
|
|
38693
|
-
if (open !== void 0) fail(`gtd: slice "${name}" is entered inside slice "${open.name}" — slices do not nest`);
|
|
38694
|
-
if (slices.some((s) => s.name === name)) fail(`gtd: slice "${name}" is entered twice in this episode — a loop must sit inside its slice`);
|
|
38695
|
-
slices.push({
|
|
38696
|
-
name,
|
|
38697
|
-
enteredAt: position.hash,
|
|
38698
|
-
leftAt: void 0
|
|
38699
|
-
});
|
|
38700
|
-
},
|
|
38701
|
-
leaveSlice: () => {
|
|
38702
|
-
if (outcome !== void 0) return;
|
|
38703
|
-
const open = slices.find((s) => s.leftAt === void 0);
|
|
38704
|
-
if (open !== void 0) open.leftAt = position.hash;
|
|
38705
|
-
},
|
|
38706
38562
|
read: (path) => position.tree.read(path),
|
|
38707
38563
|
glob: (pattern) => position.tree.paths().filter((path) => globMatches(path, pattern)),
|
|
38708
38564
|
changes: () => changesBetween(previousPosition.tree, position.tree),
|
|
@@ -39781,7 +39637,6 @@ const planStep = (snapshot, opts = {}) => {
|
|
|
39781
39637
|
};
|
|
39782
39638
|
//#endregion
|
|
39783
39639
|
//#region src/step/accessRefusal.ts
|
|
39784
|
-
init_wire();
|
|
39785
39640
|
/**
|
|
39786
39641
|
* The landing refusal for changes outside `write`, or `undefined` when every
|
|
39787
39642
|
* path is allowed. `null` is unrestricted. The message is the fix prompt: gtd
|
|
@@ -39881,14 +39736,12 @@ const resolveBase = (commandLabel, value) => gen(function* () {
|
|
|
39881
39736
|
});
|
|
39882
39737
|
//#endregion
|
|
39883
39738
|
//#region src/step/strayPaths.ts
|
|
39884
|
-
init_wire();
|
|
39885
39739
|
/** Globs use the `access.write` dialect (`globMatches`). */
|
|
39886
39740
|
const strayPaths = (touched, allow) => touched.filter((path) => !allow.some((glob) => globMatches(path, glob))).sort();
|
|
39887
39741
|
//#endregion
|
|
39888
39742
|
//#region src/edge/Edge.ts
|
|
39889
39743
|
init_esm();
|
|
39890
39744
|
init_steering();
|
|
39891
|
-
init_wire();
|
|
39892
39745
|
const EMPTY_TREE$1 = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
39893
39746
|
const ACTORS = /* @__PURE__ */ new Set([
|
|
39894
39747
|
"agent",
|
|
@@ -40241,10 +40094,6 @@ const commonOf = (step) => {
|
|
|
40241
40094
|
...optional("mode", options.mode)
|
|
40242
40095
|
};
|
|
40243
40096
|
};
|
|
40244
|
-
const stepContext = (context) => context === void 0 ? void 0 : {
|
|
40245
|
-
...optional("soft", context.soft),
|
|
40246
|
-
...optional("hard", context.hard)
|
|
40247
|
-
};
|
|
40248
40097
|
const promptDef = (common, request, skills, access) => ({
|
|
40249
40098
|
...common,
|
|
40250
40099
|
kind: "prompt",
|
|
@@ -40253,7 +40102,6 @@ const promptDef = (common, request, skills, access) => ({
|
|
|
40253
40102
|
...optional("system", request.options.system),
|
|
40254
40103
|
...optional("allowEmpty", request.options.allowEmpty),
|
|
40255
40104
|
...optional("keep", request.options.keep),
|
|
40256
|
-
...optional("context", stepContext(request.options.context)),
|
|
40257
40105
|
...optional("skills", skills),
|
|
40258
40106
|
...optional("access", access)
|
|
40259
40107
|
});
|
|
@@ -40317,8 +40165,7 @@ const hintsOf = (def) => ({
|
|
|
40317
40165
|
...optional("system", def.system),
|
|
40318
40166
|
...optional("mode", def.mode),
|
|
40319
40167
|
...optional("skills", def.skills),
|
|
40320
|
-
...optional("access", def.access)
|
|
40321
|
-
...optional("context", def.context)
|
|
40168
|
+
...optional("access", def.access)
|
|
40322
40169
|
});
|
|
40323
40170
|
const normalizeStatus = (raw) => raw === "A" ? "A" : raw === "D" ? "D" : "M";
|
|
40324
40171
|
/** Per-model token totals, highest-cost first (ties broken by model name). */
|
|
@@ -40395,10 +40242,6 @@ const pendingChanges = (git, withIndex) => gen(function* () {
|
|
|
40395
40242
|
const seen = new Set(worktree.map((e) => e.path));
|
|
40396
40243
|
return [...worktree, ...(yield* git.stagedPaths()).filter((e) => !seen.has(e.path))];
|
|
40397
40244
|
});
|
|
40398
|
-
const sliceRangesOf = (slices, run) => sliceRanges(slices, {
|
|
40399
|
-
base: run.startParentHash,
|
|
40400
|
-
tip: run.trace.at(-1)?.hash ?? run.startParentHash
|
|
40401
|
-
});
|
|
40402
40245
|
/** Resolve the rest at `ref`, or at HEAD when `ref` is `undefined`. */
|
|
40403
40246
|
const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
40404
40247
|
const git = yield* GitService;
|
|
@@ -40442,64 +40285,6 @@ const restAt = (ref, startOverrides = {}) => gen(function* () {
|
|
|
40442
40285
|
};
|
|
40443
40286
|
});
|
|
40444
40287
|
const currentRest = restAt(void 0);
|
|
40445
|
-
/** Every slice refusal reads `<command>: refused — <reason>`; replay's own `gtd: ` lead is dropped. */
|
|
40446
|
-
const slicesDivergence = (command, run, outcome) => {
|
|
40447
|
-
const reason = outcome.kind === "rest" && run.closingHash !== void 0 ? `${run.closingHash.slice(0, 7)} closes a process that does not end under today's config` : replayError(outcome)?.message.replace(/^gtd: /, "") ?? "replay failed";
|
|
40448
|
-
return /* @__PURE__ */ new Error(`${command}: refused — ${reason}`);
|
|
40449
|
-
};
|
|
40450
|
-
/**
|
|
40451
|
-
* The replay `gtd slices` and `gtd summary --slice` share: the slice ranges of
|
|
40452
|
-
* `run`, plus whether HEAD closes it. Reads like `historyTrail`, never
|
|
40453
|
-
* `restAt`: it narrates and writes nothing.
|
|
40454
|
-
*/
|
|
40455
|
-
const replayedSlices = (config, run, how) => gen(function* () {
|
|
40456
|
-
const workspace = yield* Workspace;
|
|
40457
|
-
const host = yield* Host;
|
|
40458
|
-
if (run.trace.length === 0) return yield* fail$7(/* @__PURE__ */ new Error(`${how.command}: refused — HEAD neither closes nor sits inside a process`));
|
|
40459
|
-
const closing = run.closingHash;
|
|
40460
|
-
let outcome;
|
|
40461
|
-
let versionWarning;
|
|
40462
|
-
if (closing !== void 0) {
|
|
40463
|
-
const replayed = yield* replayFinishedProcess(config, run, closing, {
|
|
40464
|
-
command: how.command,
|
|
40465
|
-
configReason: how.configReason,
|
|
40466
|
-
notEnded: (o) => slicesDivergence(how.command, run, o)
|
|
40467
|
-
});
|
|
40468
|
-
outcome = replayed.outcome;
|
|
40469
|
-
versionWarning = replayed.versionWarning;
|
|
40470
|
-
} else {
|
|
40471
|
-
const { setup } = yield* setupFor(config, run, workspace, host);
|
|
40472
|
-
outcome = yield* promise(() => replayFor(setup));
|
|
40473
|
-
if (outcome.kind !== "rest") return yield* fail$7(slicesDivergence(how.command, run, outcome));
|
|
40474
|
-
}
|
|
40475
|
-
return {
|
|
40476
|
-
ranges: sliceRangesOf(outcome.slices, run),
|
|
40477
|
-
closed: closing !== void 0,
|
|
40478
|
-
...versionWarning !== void 0 ? { versionWarning } : {}
|
|
40479
|
-
};
|
|
40480
|
-
});
|
|
40481
|
-
/**
|
|
40482
|
-
* The slices of the process HEAD closes or sits inside, current episode only,
|
|
40483
|
-
* stack order. Returns a version warning for a closed run.
|
|
40484
|
-
*/
|
|
40485
|
-
const processSlices = gen(function* () {
|
|
40486
|
-
const git = yield* GitService;
|
|
40487
|
-
const config = yield* (yield* ConfigService).load;
|
|
40488
|
-
const run = yield* runAtHead(git, config.workflow);
|
|
40489
|
-
const { ranges, closed, versionWarning } = yield* replayedSlices(config, run, {
|
|
40490
|
-
command: "gtd slices",
|
|
40491
|
-
configReason: "HEAD's process replays under today's config; revert or stash the edit to list its slices"
|
|
40492
|
-
});
|
|
40493
|
-
return {
|
|
40494
|
-
slices: ranges.map(({ name, base, tip, open }) => ({
|
|
40495
|
-
name,
|
|
40496
|
-
base,
|
|
40497
|
-
tip,
|
|
40498
|
-
closed: closed || !open
|
|
40499
|
-
})),
|
|
40500
|
-
...versionWarning !== void 0 ? { versionWarning } : {}
|
|
40501
|
-
};
|
|
40502
|
-
});
|
|
40503
40288
|
/**
|
|
40504
40289
|
* Whether `gtd --workflow <name>` can start a process here: the workflow must
|
|
40505
40290
|
* reach a step from the tree the opening commit would capture. That step is
|
|
@@ -40706,85 +40491,31 @@ const snapshotFromRest = (rest, verdicts) => gen(function* () {
|
|
|
40706
40491
|
landing
|
|
40707
40492
|
};
|
|
40708
40493
|
});
|
|
40709
|
-
const narrowToSlice = (run, slice) => {
|
|
40710
|
-
if (slice === void 0) return {
|
|
40711
|
-
trace: run.trace,
|
|
40712
|
-
costEntries: run.costEntries
|
|
40713
|
-
};
|
|
40714
|
-
const from = slice.base === run.startParentHash ? -1 : run.trace.findIndex((e) => e.hash === slice.base);
|
|
40715
|
-
const to = run.trace.findIndex((e) => e.hash === slice.tip);
|
|
40716
|
-
const trace = run.trace.slice(from + 1, to + 1);
|
|
40717
|
-
const hashes = new Set(trace.map((e) => e.hash));
|
|
40718
|
-
return {
|
|
40719
|
-
trace,
|
|
40720
|
-
costEntries: run.costEntries.filter((c) => hashes.has(c.hash))
|
|
40721
|
-
};
|
|
40722
|
-
};
|
|
40723
40494
|
const humanCommitsOf = (trace, entryCommit) => trace.filter((entry) => entry.actor === "human" && entry.hash !== entryCommit).map((entry) => ({
|
|
40724
40495
|
hash: entry.hash,
|
|
40725
40496
|
state: entry.state
|
|
40726
40497
|
}));
|
|
40727
|
-
const summaryInput = (run,
|
|
40498
|
+
const summaryInput = (run, settings) => {
|
|
40728
40499
|
const entryCommit = run.trace[0].hash;
|
|
40729
|
-
const {
|
|
40730
|
-
const { total, byModel } = costTotals(costEntries);
|
|
40500
|
+
const { total, byModel } = costTotals(run.costEntries);
|
|
40731
40501
|
return {
|
|
40732
40502
|
entryCommit,
|
|
40733
|
-
processBase:
|
|
40734
|
-
processTip:
|
|
40735
|
-
humanCommits: humanCommitsOf(trace, entryCommit),
|
|
40503
|
+
processBase: run.startParentHash,
|
|
40504
|
+
processTip: run.trace[run.trace.length - 1].hash,
|
|
40505
|
+
humanCommits: humanCommitsOf(run.trace, entryCommit),
|
|
40736
40506
|
processCost: total,
|
|
40737
40507
|
processCostByModel: byModel,
|
|
40738
40508
|
marks: run.marks,
|
|
40739
|
-
...settings
|
|
40740
|
-
...slice !== void 0 ? { slice } : {}
|
|
40509
|
+
...settings
|
|
40741
40510
|
};
|
|
40742
40511
|
};
|
|
40743
|
-
const
|
|
40744
|
-
if (sliceName === void 0) return {};
|
|
40745
|
-
const { ranges, versionWarning } = yield* replayedSlices(config, run, {
|
|
40746
|
-
command: "gtd summary",
|
|
40747
|
-
configReason: "HEAD's process replays under today's config; revert or stash the edit to summarize its slices"
|
|
40748
|
-
});
|
|
40749
|
-
const warn = versionWarning !== void 0 ? { versionWarning } : {};
|
|
40750
|
-
const at = ranges.findIndex((r) => r.name === sliceName);
|
|
40751
|
-
if (at < 0) return {
|
|
40752
|
-
unknownSlice: `gtd summary: refused — no slice named "${sliceName}"; the slices, in stack order: ${ranges.map((r) => r.name).join(", ")}`,
|
|
40753
|
-
...warn
|
|
40754
|
-
};
|
|
40755
|
-
const { base, tip } = ranges[at];
|
|
40756
|
-
return {
|
|
40757
|
-
slice: {
|
|
40758
|
-
name: sliceName,
|
|
40759
|
-
base,
|
|
40760
|
-
tip,
|
|
40761
|
-
index: at + 1,
|
|
40762
|
-
count: ranges.length
|
|
40763
|
-
},
|
|
40764
|
-
...warn
|
|
40765
|
-
};
|
|
40766
|
-
});
|
|
40767
|
-
/**
|
|
40768
|
-
* A plain call never replays; under a slice, `entryCommit` and `marks` stay
|
|
40769
|
-
* whole-process.
|
|
40770
|
-
*/
|
|
40771
|
-
const summaryFor = (run, sliceName) => gen(function* () {
|
|
40512
|
+
const summaryFor = (run) => gen(function* () {
|
|
40772
40513
|
const config = yield* (yield* ConfigService).load;
|
|
40773
40514
|
const host = yield* Host;
|
|
40774
40515
|
const resolved = yield* workflowOf(config, run);
|
|
40775
40516
|
const summary = resolved.def.summary;
|
|
40776
|
-
if (summary === void 0) return { prompt: void 0 };
|
|
40777
|
-
|
|
40778
|
-
const located = yield* locateSlice(config, run, sliceName);
|
|
40779
|
-
if ("unknownSlice" in located) return {
|
|
40780
|
-
prompt: void 0,
|
|
40781
|
-
...located
|
|
40782
|
-
};
|
|
40783
|
-
const { slice, versionWarning } = located;
|
|
40784
|
-
return {
|
|
40785
|
-
prompt: summary(summaryInput(run, slice, settingsFor(resolved, run.pinnedVars, host.env, {}, {}))),
|
|
40786
|
-
...versionWarning !== void 0 ? { versionWarning } : {}
|
|
40787
|
-
};
|
|
40517
|
+
if (summary === void 0 || run.trace.length === 0) return { prompt: void 0 };
|
|
40518
|
+
return { prompt: summary(summaryInput(run, settingsFor(resolved, run.pinnedVars, host.env, {}, {}))) };
|
|
40788
40519
|
});
|
|
40789
40520
|
/** Repository-relative paths of the config files today's config and the process's own record name; one outside the repository cannot be read at a ref, so it is left out. */
|
|
40790
40521
|
const inRepoConfigPaths = (root, files, recorded) => {
|
|
@@ -41088,7 +40819,6 @@ const combinedScript = (required, optional) => {
|
|
|
41088
40819
|
};
|
|
41089
40820
|
//#endregion
|
|
41090
40821
|
//#region src/emit/OutcomeScript.ts
|
|
41091
|
-
init_wire();
|
|
41092
40822
|
/**
|
|
41093
40823
|
* A `printf '<fmt>' <args...>` bash statement — `args` are already-valid bash
|
|
41094
40824
|
* tokens (a `shellQuote`d literal, a `"$(git …)"` substitution), never
|
|
@@ -41343,7 +41073,6 @@ var ToolCommands = class extends Tag("ToolCommands")() {};
|
|
|
41343
41073
|
//#endregion
|
|
41344
41074
|
//#region src/cli/Output.ts
|
|
41345
41075
|
init_esm();
|
|
41346
|
-
init_wire();
|
|
41347
41076
|
/**
|
|
41348
41077
|
* A command-level failure that should exit like a CLI usage error (2), not a
|
|
41349
41078
|
* generic runtime failure (1): an unknown `--json=<path>` selector, or a
|
|
@@ -41369,7 +41098,6 @@ const writeJson = (json, out, document) => gen(function* () {
|
|
|
41369
41098
|
//#endregion
|
|
41370
41099
|
//#region src/cli/Strays.ts
|
|
41371
41100
|
init_esm();
|
|
41372
|
-
init_wire();
|
|
41373
41101
|
const resolveSide = (git, side) => git.resolveRef(`${side}^{commit}`).pipe(mapError$1(() => new GtdError(`gtd strays: unknown revision '${side}'`)));
|
|
41374
41102
|
/** 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. */
|
|
41375
41103
|
const runStraysCommand = (command, json, out) => gen(function* () {
|
|
@@ -41469,10 +41197,8 @@ const runHistoryFetchCommand = (remote, out) => sync$1(() => {
|
|
|
41469
41197
|
//#endregion
|
|
41470
41198
|
//#region src/cli/Metrics.ts
|
|
41471
41199
|
init_esm();
|
|
41472
|
-
init_wire();
|
|
41473
41200
|
const QUALITY$1 = ".gtd/QUALITY.md";
|
|
41474
41201
|
const LENS = /^((?:.*\.)?quality\.[^.]+)\.reviewing$/;
|
|
41475
|
-
const CONTINUE = /(^|\.)continue\./;
|
|
41476
41202
|
const inSeam = (step) => step.startsWith("seam.");
|
|
41477
41203
|
const phaseOf = (step) => {
|
|
41478
41204
|
const scope = step.split(".")[0];
|
|
@@ -41491,13 +41217,12 @@ const add = (into, key, n) => {
|
|
|
41491
41217
|
};
|
|
41492
41218
|
const roundAll = (record) => Object.fromEntries(Object.entries(record).map(([k, v]) => [k, round(v)]));
|
|
41493
41219
|
const emptyTally = () => ({
|
|
41494
|
-
|
|
41220
|
+
builds: {
|
|
41495
41221
|
decided: 0,
|
|
41496
41222
|
landed: 0,
|
|
41497
41223
|
rebuilt: 0,
|
|
41498
41224
|
refactored: 0
|
|
41499
41225
|
},
|
|
41500
|
-
continuations: record(),
|
|
41501
41226
|
byModel: record(),
|
|
41502
41227
|
byPhase: {
|
|
41503
41228
|
naive: 0,
|
|
@@ -41514,9 +41239,8 @@ const emptyTally = () => ({
|
|
|
41514
41239
|
});
|
|
41515
41240
|
const sum = (record) => Object.values(record).reduce((a, b) => a + b, 0);
|
|
41516
41241
|
const finish = (t) => ({
|
|
41517
|
-
|
|
41518
|
-
refactorFirstRate: t.
|
|
41519
|
-
continuations: t.continuations,
|
|
41242
|
+
builds: t.builds,
|
|
41243
|
+
refactorFirstRate: t.builds.decided === 0 ? null : round(t.builds.rebuilt / t.builds.decided, 3),
|
|
41520
41244
|
cost: {
|
|
41521
41245
|
total: round(sum(t.byModel)),
|
|
41522
41246
|
byModel: roundAll(t.byModel),
|
|
@@ -41544,33 +41268,33 @@ const tallyCost = (t, open, costs, phase) => {
|
|
|
41544
41268
|
if (phase === "implement" && open.rebuilding) t.doubleWork.rebuild += cost;
|
|
41545
41269
|
}
|
|
41546
41270
|
};
|
|
41547
|
-
/** A
|
|
41271
|
+
/** A build is decided where its turns leave the seam: for `implement.applying` the naive pass landed — unless it fails to apply and is built afresh — anywhere else it is rebuilt. */
|
|
41548
41272
|
const tallyDecision = (t, open, at, to) => {
|
|
41549
41273
|
if (at === "implement.closing") open.rebuilding = false;
|
|
41550
41274
|
if (open.applying !== void 0 && at === "implement.applying") {
|
|
41551
41275
|
const naive = open.applying;
|
|
41552
41276
|
open.applying = void 0;
|
|
41553
41277
|
if (to !== "implement.building") return;
|
|
41554
|
-
t.
|
|
41278
|
+
t.builds.landed--;
|
|
41555
41279
|
rebuild(t, open, naive);
|
|
41556
41280
|
return;
|
|
41557
41281
|
}
|
|
41558
41282
|
if (!inSeam(at) || inSeam(to)) return;
|
|
41559
|
-
t.
|
|
41283
|
+
t.builds.decided++;
|
|
41560
41284
|
const naive = open.naive;
|
|
41561
41285
|
open.naive = 0;
|
|
41562
41286
|
if (to === "implement.applying") {
|
|
41563
41287
|
open.applying = naive;
|
|
41564
41288
|
open.rebuilding = false;
|
|
41565
|
-
t.
|
|
41289
|
+
t.builds.landed++;
|
|
41566
41290
|
return;
|
|
41567
41291
|
}
|
|
41568
41292
|
rebuild(t, open, naive);
|
|
41569
|
-
if (at === "seam.note") t.
|
|
41293
|
+
if (at === "seam.note") t.builds.refactored++;
|
|
41570
41294
|
};
|
|
41571
41295
|
const rebuild = (t, open, naive) => {
|
|
41572
41296
|
open.rebuilding = true;
|
|
41573
|
-
t.
|
|
41297
|
+
t.builds.rebuilt++;
|
|
41574
41298
|
t.doubleWork.naive += naive;
|
|
41575
41299
|
};
|
|
41576
41300
|
/** Oldest first; a commit gtd did not write is skipped. */
|
|
@@ -41587,7 +41311,6 @@ const processMetrics = (commits) => {
|
|
|
41587
41311
|
if (subject === void 0) continue;
|
|
41588
41312
|
const at = subject.from ?? subject.to;
|
|
41589
41313
|
tallyCost(t, open, message.cost, phaseOf(at));
|
|
41590
|
-
if (subject.actor === "agent" && CONTINUE.test(at)) add(t.continuations, at.replace(CONTINUE, "$1"), 1);
|
|
41591
41314
|
const lens = LENS.exec(at)?.[1];
|
|
41592
41315
|
if (lens !== void 0 && commit.findings > 0) add(t.byLens, lens, commit.findings);
|
|
41593
41316
|
if (message.marks.some((m) => m.name === "flaky")) add(t.flaky, at, 1);
|
|
@@ -41598,8 +41321,7 @@ const processMetrics = (commits) => {
|
|
|
41598
41321
|
const totalMetrics = (all) => {
|
|
41599
41322
|
const t = emptyTally();
|
|
41600
41323
|
for (const m of all) {
|
|
41601
|
-
for (const key of Object.keys(t.
|
|
41602
|
-
for (const [k, v] of Object.entries(m.continuations)) add(t.continuations, k, v);
|
|
41324
|
+
for (const key of Object.keys(t.builds)) t.builds[key] += m.builds[key];
|
|
41603
41325
|
for (const [k, v] of Object.entries(m.cost.byModel)) add(t.byModel, k, v);
|
|
41604
41326
|
for (const key of Object.keys(t.byPhase)) t.byPhase[key] += m.cost.byPhase[key];
|
|
41605
41327
|
t.doubleWork.naive += m.cost.doubleWork.naive;
|
|
@@ -41636,12 +41358,10 @@ const readProcessMetrics = (entry) => gen(function* () {
|
|
|
41636
41358
|
const printable = (s) => s.replace(/[\x00-\x1f\x7f-\x9f\u2028\u2029]/g, "?");
|
|
41637
41359
|
const listed = (record) => Object.entries(record).map(([k, v]) => `${printable(k)} ${v}`).join(", ");
|
|
41638
41360
|
const line = (label, m) => {
|
|
41639
|
-
const {
|
|
41361
|
+
const { builds, cost } = m;
|
|
41640
41362
|
const percent = m.refactorFirstRate === null ? "-" : `${Math.round(m.refactorFirstRate * 100)}%`;
|
|
41641
|
-
const continued = sum(m.continuations);
|
|
41642
41363
|
return [
|
|
41643
|
-
`${label}: ${
|
|
41644
|
-
`${continued} continuations${continued > 0 ? ` (${listed(m.continuations)})` : ""}`,
|
|
41364
|
+
`${label}: ${builds.decided} builds, ${builds.rebuilt} rebuilt (refactor-first ${percent}), ${builds.refactored} refactored`,
|
|
41645
41365
|
`cost ${cost.total} (${listed(cost.byPhase)}; by model ${listed(cost.byModel) || "-"}), double work ${cost.doubleWork.total}`,
|
|
41646
41366
|
`${m.findings.total} findings${m.findings.total > 0 ? ` (${listed(m.findings.byLens)})` : ""}`,
|
|
41647
41367
|
`${m.flaky.total} flaky${m.flaky.total > 0 ? ` (${listed(m.flaky.byStep)})` : ""}`
|
|
@@ -41838,7 +41558,7 @@ const processJudgments = (commits) => {
|
|
|
41838
41558
|
});
|
|
41839
41559
|
};
|
|
41840
41560
|
const NOT_COMPARABLE = { tailsNotComparable: "true" };
|
|
41841
|
-
const field
|
|
41561
|
+
const field = (key, value) => value === void 0 ? {} : { [key]: value };
|
|
41842
41562
|
const JUDGES = {
|
|
41843
41563
|
retry: ({ reads, i, scope }, v) => {
|
|
41844
41564
|
const reports = reportsBefore(reads, i, scope);
|
|
@@ -41848,8 +41568,8 @@ const JUDGES = {
|
|
|
41848
41568
|
const known = previous !== void 0 && current !== void 0;
|
|
41849
41569
|
return {
|
|
41850
41570
|
state: comparable ? {
|
|
41851
|
-
...field
|
|
41852
|
-
...field
|
|
41571
|
+
...field("previous", previous),
|
|
41572
|
+
...field("current", current)
|
|
41853
41573
|
} : NOT_COMPARABLE,
|
|
41854
41574
|
outcome: known && comparable && answerOf(v.answer) === "identical" ? retryOutcome(reads, i, scope) : null
|
|
41855
41575
|
};
|
|
@@ -41857,12 +41577,12 @@ const JUDGES = {
|
|
|
41857
41577
|
triage: ({ reads, i }, v) => {
|
|
41858
41578
|
const finding = reads[i].commit.findings?.find((f) => f.id === v.id);
|
|
41859
41579
|
return {
|
|
41860
|
-
state: field
|
|
41580
|
+
state: field(v.id, finding && `## ${finding.title}\n\n${finding.body}`),
|
|
41861
41581
|
outcome: triageOutcome(reads, i, answerOf(v.answer), finding)
|
|
41862
41582
|
};
|
|
41863
41583
|
},
|
|
41864
41584
|
clear: ({ reads, i, sketch }, v) => ({
|
|
41865
|
-
state: field
|
|
41585
|
+
state: field("sketch", sketch),
|
|
41866
41586
|
outcome: clearOutcome(reads, i, v)
|
|
41867
41587
|
})
|
|
41868
41588
|
};
|
|
@@ -42049,7 +41769,6 @@ const runHistoryJudgesCommand = (refs, vars, jsonl, json, out) => gen(function*
|
|
|
42049
41769
|
//#endregion
|
|
42050
41770
|
//#region src/cli/Program.ts
|
|
42051
41771
|
init_esm();
|
|
42052
|
-
init_wire();
|
|
42053
41772
|
init_steering();
|
|
42054
41773
|
const selfGtd = () => {
|
|
42055
41774
|
const script = process.argv[1];
|
|
@@ -42131,16 +41850,10 @@ const planLanding = (opts = {}, current) => gen(function* () {
|
|
|
42131
41850
|
* closes or sits inside its own closing message. Writes nothing — no git, no
|
|
42132
41851
|
* state transition, no file. Refuses (throws, mapped to the runtime-error
|
|
42133
41852
|
* exit code) when the workflow declares no `summary` prompt, or the resolved
|
|
42134
|
-
* run has no commits to name.
|
|
42135
|
-
|
|
42136
|
-
|
|
42137
|
-
|
|
42138
|
-
* it in its message.
|
|
42139
|
-
*/
|
|
42140
|
-
const runSummaryCommand = (out, sliceName) => gen(function* () {
|
|
42141
|
-
const { prompt, versionWarning, unknownSlice } = yield* summaryFor(yield* summaryRun, sliceName);
|
|
42142
|
-
if (versionWarning !== void 0) yield* (yield* Narrator).warn(versionWarning);
|
|
42143
|
-
if (unknownSlice !== void 0) return yield* fail$7(new Error(unknownSlice));
|
|
41853
|
+
* run has no commits to name.
|
|
41854
|
+
*/
|
|
41855
|
+
const runSummaryCommand = (out) => gen(function* () {
|
|
41856
|
+
const { prompt } = yield* summaryFor(yield* summaryRun);
|
|
42144
41857
|
if (prompt === void 0) return yield* fail$7(/* @__PURE__ */ new Error("gtd summary: refused — either this workflow declares no \"summary:\" template, or there is no finished/in-flight process to summarize at HEAD"));
|
|
42145
41858
|
out.write(prompt.endsWith("\n") ? prompt : prompt + "\n");
|
|
42146
41859
|
});
|
|
@@ -42185,17 +41898,6 @@ const readStdin = () => tryPromise({
|
|
|
42185
41898
|
},
|
|
42186
41899
|
catch: (e) => e instanceof Error ? e : new Error(String(e))
|
|
42187
41900
|
});
|
|
42188
|
-
/** Stdout is written once: a hook reads one JSON document, or none. */
|
|
42189
|
-
const runContextHookCommand = (command, out) => gen(function* () {
|
|
42190
|
-
const tools = yield* ToolCommands;
|
|
42191
|
-
const input = yield* readStdin();
|
|
42192
|
-
out.write(yield* tools.contextHook({
|
|
42193
|
-
input,
|
|
42194
|
-
...command.soft !== void 0 ? { soft: command.soft } : {},
|
|
42195
|
-
...command.hard !== void 0 ? { hard: command.hard } : {},
|
|
42196
|
-
...command.window !== void 0 ? { window: command.window } : {}
|
|
42197
|
-
}));
|
|
42198
|
-
});
|
|
42199
41901
|
/** Stdout is written once, after the answerer resolves — a rejection leaves it empty. */
|
|
42200
41902
|
const runJudgeRunCommand = (command, out) => gen(function* () {
|
|
42201
41903
|
const host = yield* Host;
|
|
@@ -42337,17 +42039,6 @@ const runDoorsCommand = (json, out) => gen(function* () {
|
|
|
42337
42039
|
if (json.kind === "document") out.write(`${JSON.stringify(document)}\n`);
|
|
42338
42040
|
else yield* writeSelection(out, document, json.path);
|
|
42339
42041
|
});
|
|
42340
|
-
const runSlicesCommand = (json, out) => gen(function* () {
|
|
42341
|
-
const { slices, versionWarning } = yield* processSlices;
|
|
42342
|
-
if (versionWarning !== void 0) yield* (yield* Narrator).warn(versionWarning);
|
|
42343
|
-
if (json.kind === "off") {
|
|
42344
|
-
out.write(slices.map((s) => `${s.name} ${s.base}..${s.tip}\n`).join(""));
|
|
42345
|
-
return;
|
|
42346
|
-
}
|
|
42347
|
-
const document = slicesDocument(slices);
|
|
42348
|
-
if (json.kind === "document") out.write(renderSlicesJson(document));
|
|
42349
|
-
else yield* writeSelection(out, document, json.path);
|
|
42350
|
-
});
|
|
42351
42042
|
/**
|
|
42352
42043
|
* Ends the process underway without completing it. Nothing is discarded: a
|
|
42353
42044
|
* `git reset --mixed` to the process's start commit leaves every turn
|
|
@@ -42654,7 +42345,6 @@ const needsOf = (kind) => {
|
|
|
42654
42345
|
switch (kind) {
|
|
42655
42346
|
case "lsp":
|
|
42656
42347
|
case "install":
|
|
42657
|
-
case "contextHook":
|
|
42658
42348
|
case "judgeRun":
|
|
42659
42349
|
case "strays": return "none";
|
|
42660
42350
|
case "check":
|
|
@@ -42683,7 +42373,6 @@ const dispatchVoidCommand = (command, json, out) => {
|
|
|
42683
42373
|
case "start": return runStartCommand(command.actor, command.workflow, command.vars, out, command.label);
|
|
42684
42374
|
case "door": return runDoorCommand(command.name, command.args, out);
|
|
42685
42375
|
case "doors": return runDoorsCommand(json, out);
|
|
42686
|
-
case "slices": return runSlicesCommand(json, out);
|
|
42687
42376
|
case "abandon": return runAbandonCommand(out);
|
|
42688
42377
|
case "restore": return runRestoreCommand(out);
|
|
42689
42378
|
case "next": return runNextCommand(json, out);
|
|
@@ -42693,8 +42382,7 @@ const dispatchVoidCommand = (command, json, out) => {
|
|
|
42693
42382
|
return runCheckCommand(command.mode, command.file, command.openQuestions ?? false, command.openThreads ?? false);
|
|
42694
42383
|
case "uncheck": return runUncheckCommand(command.file);
|
|
42695
42384
|
case "install": return runInstallCommand(out);
|
|
42696
|
-
case "
|
|
42697
|
-
case "summary": return runSummaryCommand(out, command.slice);
|
|
42385
|
+
case "summary": return runSummaryCommand(out);
|
|
42698
42386
|
case "base": return runBaseCommand(out);
|
|
42699
42387
|
case "judge": return runJudgeCommand(json, out);
|
|
42700
42388
|
case "judgeAnswer": return runJudgeAnswerCommand(json, out);
|
|
@@ -42733,23 +42421,17 @@ const runCommand = (command, json, out) => {
|
|
|
42733
42421
|
//#endregion
|
|
42734
42422
|
//#region src/cli/Cli.ts
|
|
42735
42423
|
init_esm();
|
|
42736
|
-
init_wire();
|
|
42737
42424
|
const nonNegativeNumber = (raw, flag) => {
|
|
42738
42425
|
const n = Number(raw);
|
|
42739
42426
|
if (raw.trim() === "" || !Number.isFinite(n) || n < 0) return left(`gtd: ${flag} must be a non-negative number — got "${raw}"`);
|
|
42740
42427
|
return right(n);
|
|
42741
42428
|
};
|
|
42742
|
-
const share = (raw, flag) => {
|
|
42743
|
-
const n = Number(raw);
|
|
42744
|
-
return raw.trim() !== "" && n > 0 && n <= 1 ? right(n) : left(`gtd: ${flag} must be a share of the context window in (0, 1] — got "${raw}"`);
|
|
42745
|
-
};
|
|
42746
42429
|
const JSON_KINDS = /* @__PURE__ */ new Set([
|
|
42747
42430
|
"next",
|
|
42748
42431
|
"land",
|
|
42749
42432
|
"judge",
|
|
42750
42433
|
"judgeAnswer",
|
|
42751
42434
|
"doors",
|
|
42752
|
-
"slices",
|
|
42753
42435
|
"historyList",
|
|
42754
42436
|
"historyShow",
|
|
42755
42437
|
"historyMetrics",
|
|
@@ -42763,10 +42445,10 @@ const FLAGS = [
|
|
|
42763
42445
|
repeatable: false,
|
|
42764
42446
|
scope: (kind) => JSON_KINDS.has(kind),
|
|
42765
42447
|
decode: ([raw]) => right(raw ?? ""),
|
|
42766
|
-
scopeError: "gtd: --json is only valid for `gtd next`/`gtd land`/`gtd judge`/`gtd judge answer`/`gtd doors`/`gtd
|
|
42448
|
+
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 history metrics`/`gtd history judges`/`gtd strays` — every other command prints plain text; see `gtd install` for the driver protocol briefing",
|
|
42767
42449
|
valueHint: "<path>",
|
|
42768
42450
|
help: [
|
|
42769
|
-
"(gtd next/gtd land/gtd judge/gtd judge answer/gtd doors/gtd
|
|
42451
|
+
"(gtd next/gtd land/gtd judge/gtd judge answer/gtd doors/gtd history",
|
|
42770
42452
|
"list/gtd history show/gtd history metrics/gtd history judges/gtd strays only) output structured JSON. Bare --json prints the whole document;",
|
|
42771
42453
|
"--json=<path> (a dotted key path into that document, e.g.",
|
|
42772
42454
|
"kind, content, session.id) prints just that value: a",
|
|
@@ -42883,54 +42565,6 @@ const FLAGS = [
|
|
|
42883
42565
|
"GTD_JUDGE_MODEL, then judge.json, then sonnet"
|
|
42884
42566
|
]
|
|
42885
42567
|
},
|
|
42886
|
-
{
|
|
42887
|
-
name: "--soft",
|
|
42888
|
-
arity: 1,
|
|
42889
|
-
repeatable: false,
|
|
42890
|
-
scope: (kind) => kind === "contextHook",
|
|
42891
|
-
decode: ([raw]) => share(raw ?? "", "--soft"),
|
|
42892
|
-
scopeError: "gtd: --soft is only valid for `gtd context-hook`",
|
|
42893
|
-
valueHint: "<share>",
|
|
42894
|
-
help: ["(gtd context-hook only) the soft limit; default 0.6"]
|
|
42895
|
-
},
|
|
42896
|
-
{
|
|
42897
|
-
name: "--hard",
|
|
42898
|
-
arity: 1,
|
|
42899
|
-
repeatable: false,
|
|
42900
|
-
scope: (kind) => kind === "contextHook",
|
|
42901
|
-
decode: ([raw]) => share(raw ?? "", "--hard"),
|
|
42902
|
-
scopeError: "gtd: --hard is only valid for `gtd context-hook`",
|
|
42903
|
-
valueHint: "<share>",
|
|
42904
|
-
help: ["(gtd context-hook only) the hard limit; default 0.7"]
|
|
42905
|
-
},
|
|
42906
|
-
{
|
|
42907
|
-
name: "--window",
|
|
42908
|
-
arity: 1,
|
|
42909
|
-
repeatable: false,
|
|
42910
|
-
scope: (kind) => kind === "contextHook",
|
|
42911
|
-
decode: ([raw]) => {
|
|
42912
|
-
const window = parseContextWindow(raw);
|
|
42913
|
-
return window !== void 0 ? right(window) : left(`gtd: --window must be a positive integer — got "${raw ?? ""}"`);
|
|
42914
|
-
},
|
|
42915
|
-
scopeError: "gtd: --window is only valid for `gtd context-hook`",
|
|
42916
|
-
valueHint: "<tokens>",
|
|
42917
|
-
help: ["(gtd context-hook only) the model's context window in", "tokens; default 200000"]
|
|
42918
|
-
},
|
|
42919
|
-
{
|
|
42920
|
-
name: "--slice",
|
|
42921
|
-
arity: 1,
|
|
42922
|
-
repeatable: false,
|
|
42923
|
-
scope: (kind) => kind === "summary",
|
|
42924
|
-
decode: ([raw]) => right(raw),
|
|
42925
|
-
scopeError: "gtd: --slice is only valid for `gtd summary`",
|
|
42926
|
-
valueHint: "<name>",
|
|
42927
|
-
help: [
|
|
42928
|
-
"(gtd summary only) the closing-message prompt for one",
|
|
42929
|
-
"slice, named as `gtd slices` lists it: its own range, human",
|
|
42930
|
-
"commits and cost. An unknown name is refused (exit 1) with",
|
|
42931
|
-
"every valid slice listed"
|
|
42932
|
-
]
|
|
42933
|
-
},
|
|
42934
42568
|
{
|
|
42935
42569
|
name: "--workflow",
|
|
42936
42570
|
arity: 1,
|
|
@@ -43221,22 +42855,6 @@ const COMMAND_ROWS = [
|
|
|
43221
42855
|
"else it writes nothing."
|
|
43222
42856
|
]
|
|
43223
42857
|
},
|
|
43224
|
-
{
|
|
43225
|
-
token: "context-hook",
|
|
43226
|
-
kind: "contextHook",
|
|
43227
|
-
arity: "none",
|
|
43228
|
-
details: [
|
|
43229
|
-
"A Claude Code PostToolUse and PreCompact hook that stops an",
|
|
43230
|
-
"agent turn at a share of its context window. Reads the",
|
|
43231
|
-
"hook's input on stdin and the context the transcript's",
|
|
43232
|
-
"latest assistant message used. Past --soft it blocks with a",
|
|
43233
|
-
"request to end the turn with a handoff note; past --hard,",
|
|
43234
|
-
"or at PreCompact, it stops the turn with a stopReason",
|
|
43235
|
-
"starting \"gtd: context limit\". Prints nothing otherwise,",
|
|
43236
|
-
"and for a subagent's tool call. Always exits 0 on valid",
|
|
43237
|
-
"input; see docs/driver.md"
|
|
43238
|
-
]
|
|
43239
|
-
},
|
|
43240
42858
|
{
|
|
43241
42859
|
token: "summary",
|
|
43242
42860
|
kind: "summary",
|
|
@@ -43250,15 +42868,10 @@ const COMMAND_ROWS = [
|
|
|
43250
42868
|
"no state transition, no file, no session identity — the",
|
|
43251
42869
|
"driver pipes the output to a cold agent and does what it",
|
|
43252
42870
|
"wants with the result (a squash, an amend, a PR body).",
|
|
43253
|
-
"
|
|
43254
|
-
"
|
|
43255
|
-
"
|
|
43256
|
-
"
|
|
43257
|
-
"replay diverged; an open last slice is provisional. Refuses",
|
|
43258
|
-
"(exit 1) when the workflow declares no summary: template,",
|
|
43259
|
-
"or when the resolved run has no commits to name — plain",
|
|
43260
|
-
"gtd summary is runnable any time before the next thing",
|
|
43261
|
-
"lands on the branch"
|
|
42871
|
+
"Refuses (exit 1) when the workflow declares no summary:",
|
|
42872
|
+
"template, or when the resolved run has no commits to name —",
|
|
42873
|
+
"it is runnable any time before the next thing lands on the",
|
|
42874
|
+
"branch"
|
|
43262
42875
|
]
|
|
43263
42876
|
},
|
|
43264
42877
|
{
|
|
@@ -43304,25 +42917,6 @@ const COMMAND_ROWS = [
|
|
|
43304
42917
|
"value off it. Writes nothing"
|
|
43305
42918
|
]
|
|
43306
42919
|
},
|
|
43307
|
-
{
|
|
43308
|
-
token: "slices",
|
|
43309
|
-
kind: "slices",
|
|
43310
|
-
arity: "none",
|
|
43311
|
-
details: [
|
|
43312
|
-
"List the slices of the process HEAD closes or sits inside,",
|
|
43313
|
-
"first slice first: one `name base..tip` line each (full",
|
|
43314
|
-
"hashes; plain output carries no promise). --json prints",
|
|
43315
|
-
"{ schema: 1, slices: [{ name, base, tip, closed }] };",
|
|
43316
|
-
"--json=<path> reads one value off it. Writes nothing: no",
|
|
43317
|
-
"git, no state transition, no session identity. When HEAD",
|
|
43318
|
-
"closes a process, every slice is listed as closed; a gtd",
|
|
43319
|
-
"version mismatch only warns on stderr. Refuses (exit 1)",
|
|
43320
|
-
"when HEAD neither closes nor sits inside a process, or when",
|
|
43321
|
-
"today's config disagrees with history about the process:",
|
|
43322
|
-
"HEAD closes it and its config files differ from the working",
|
|
43323
|
-
"tree, or the replay diverged"
|
|
43324
|
-
]
|
|
43325
|
-
},
|
|
43326
42920
|
{
|
|
43327
42921
|
token: "judge",
|
|
43328
42922
|
kind: "judge",
|
|
@@ -43424,11 +43018,11 @@ const COMMAND_ROWS = [
|
|
|
43424
43018
|
many: true
|
|
43425
43019
|
},
|
|
43426
43020
|
details: [
|
|
43427
|
-
"Report how the kept processes went:
|
|
43428
|
-
"landed or was rebuilt (the refactor-first rate),
|
|
43429
|
-
"
|
|
43430
|
-
"
|
|
43431
|
-
"
|
|
43021
|
+
"Report how the kept processes went: builds whose naive pass",
|
|
43022
|
+
"landed or was rebuilt (the refactor-first rate), Gtd-Cost by",
|
|
43023
|
+
"model and phase with the double work of a rebuild, quality-lap",
|
|
43024
|
+
"findings per lens, and red checks that passed on their",
|
|
43025
|
+
"re-check — per process and in total. Each <ref>",
|
|
43432
43026
|
"is read as by history show, but a branch name means every",
|
|
43433
43027
|
"process kept from it; none means every ref.",
|
|
43434
43028
|
"Reads commit subjects and trailers, never replays, so a",
|
|
@@ -43757,23 +43351,9 @@ const parseArgv = (argv) => {
|
|
|
43757
43351
|
verbose
|
|
43758
43352
|
};
|
|
43759
43353
|
}
|
|
43760
|
-
if (kind === "contextHook") return {
|
|
43761
|
-
kind: "command",
|
|
43762
|
-
command: {
|
|
43763
|
-
kind: "contextHook",
|
|
43764
|
-
...bag["--soft"] !== void 0 ? { soft: bag["--soft"] } : {},
|
|
43765
|
-
...bag["--hard"] !== void 0 ? { hard: bag["--hard"] } : {},
|
|
43766
|
-
...bag["--window"] !== void 0 ? { window: bag["--window"] } : {}
|
|
43767
|
-
},
|
|
43768
|
-
json,
|
|
43769
|
-
verbose
|
|
43770
|
-
};
|
|
43771
43354
|
if (kind === "summary") return {
|
|
43772
43355
|
kind: "command",
|
|
43773
|
-
command: {
|
|
43774
|
-
kind: "summary",
|
|
43775
|
-
...bag["--slice"] !== void 0 ? { slice: bag["--slice"] } : {}
|
|
43776
|
-
},
|
|
43356
|
+
command: { kind: "summary" },
|
|
43777
43357
|
json,
|
|
43778
43358
|
verbose
|
|
43779
43359
|
};
|
|
@@ -59472,88 +59052,6 @@ var init_lsp = __esmMin((() => {
|
|
|
59472
59052
|
init_Lsp();
|
|
59473
59053
|
}));
|
|
59474
59054
|
//#endregion
|
|
59475
|
-
//#region src/hook/HookHost.ts
|
|
59476
|
-
var HookHost, liveHookHost;
|
|
59477
|
-
var init_HookHost = __esmMin((() => {
|
|
59478
|
-
init_esm();
|
|
59479
|
-
HookHost = class extends Tag("HookHost")() {};
|
|
59480
|
-
liveHookHost = succeed$2(HookHost, {
|
|
59481
|
-
env: process.env,
|
|
59482
|
-
readFile: (path) => try_({
|
|
59483
|
-
try: () => {
|
|
59484
|
-
try {
|
|
59485
|
-
return readFileSync(path, "utf-8");
|
|
59486
|
-
} catch (error) {
|
|
59487
|
-
if (error.code === "ENOENT") return void 0;
|
|
59488
|
-
throw error;
|
|
59489
|
-
}
|
|
59490
|
-
},
|
|
59491
|
-
catch: (error) => /* @__PURE__ */ new Error(`could not read ${path}: ${String(error)}`)
|
|
59492
|
-
}),
|
|
59493
|
-
appendFile: (path, text) => sync$1(() => appendFileSync(path, text))
|
|
59494
|
-
});
|
|
59495
|
-
}));
|
|
59496
|
-
//#endregion
|
|
59497
|
-
//#region src/hook/ContextHook.ts
|
|
59498
|
-
var field, limitOf, parseInput, tokensOf, recordStop, runContextHook;
|
|
59499
|
-
var init_ContextHook = __esmMin((() => {
|
|
59500
|
-
init_esm();
|
|
59501
|
-
init_wire();
|
|
59502
|
-
init_HookHost();
|
|
59503
|
-
field = (value, key) => typeof value === "object" && value !== null ? value[key] : void 0;
|
|
59504
|
-
limitOf = (request) => {
|
|
59505
|
-
const limit = {
|
|
59506
|
-
soft: request.soft ?? DEFAULT_CONTEXT_LIMIT.soft,
|
|
59507
|
-
hard: request.hard ?? DEFAULT_CONTEXT_LIMIT.hard
|
|
59508
|
-
};
|
|
59509
|
-
const fault = contextLimitFault(limit);
|
|
59510
|
-
return fault === void 0 ? succeed$3(limit) : fail$7(/* @__PURE__ */ new Error(`gtd context-hook: ${fault}`));
|
|
59511
|
-
};
|
|
59512
|
-
parseInput = (text) => try_({
|
|
59513
|
-
try: () => JSON.parse(text),
|
|
59514
|
-
catch: () => /* @__PURE__ */ new Error("gtd context-hook: stdin is not a hook's JSON input")
|
|
59515
|
-
}).pipe(map$4((input) => {
|
|
59516
|
-
const event = field(input, "hook_event_name");
|
|
59517
|
-
if (event !== "PostToolUse" && event !== "PreCompact") return void 0;
|
|
59518
|
-
if (field(input, "agent_id") !== void 0) return void 0;
|
|
59519
|
-
const path = field(input, "transcript_path");
|
|
59520
|
-
return {
|
|
59521
|
-
event,
|
|
59522
|
-
transcript: typeof path === "string" && path !== "" ? path : void 0
|
|
59523
|
-
};
|
|
59524
|
-
}));
|
|
59525
|
-
tokensOf = (input) => input.event === "PostToolUse" && input.transcript !== void 0 ? flatMap$4(HookHost, (host) => host.readFile(input.transcript)).pipe(map$4((text) => text === void 0 ? void 0 : transcriptTokens(text))) : succeed$3(void 0);
|
|
59526
|
-
recordStop = (stopReason) => flatMap$4(HookHost, (host) => {
|
|
59527
|
-
const file = host.env[CONTEXT_STOP_FILE_ENV];
|
|
59528
|
-
return file === void 0 || file === "" ? _void : host.appendFile(file, `${stopReason}\n`).pipe(catchAllDefect(() => _void));
|
|
59529
|
-
});
|
|
59530
|
-
runContextHook = (request) => gen(function* () {
|
|
59531
|
-
const limit = yield* limitOf(request);
|
|
59532
|
-
const input = yield* parseInput(request.input);
|
|
59533
|
-
if (input === void 0) return "";
|
|
59534
|
-
const decision = contextHook({
|
|
59535
|
-
event: input.event,
|
|
59536
|
-
tokens: yield* tokensOf(input),
|
|
59537
|
-
window: request.window ?? contextWindowFor(void 0),
|
|
59538
|
-
limit
|
|
59539
|
-
});
|
|
59540
|
-
if (decision === void 0) return "";
|
|
59541
|
-
if (decision.kind === "hard") yield* recordStop(decision.stopReason);
|
|
59542
|
-
return `${JSON.stringify(hookOutput(decision))}\n`;
|
|
59543
|
-
});
|
|
59544
|
-
}));
|
|
59545
|
-
//#endregion
|
|
59546
|
-
//#region src/hook/index.ts
|
|
59547
|
-
var hook_exports = /* @__PURE__ */ __exportAll({
|
|
59548
|
-
HookHost: () => HookHost,
|
|
59549
|
-
liveHookHost: () => liveHookHost,
|
|
59550
|
-
runContextHook: () => runContextHook
|
|
59551
|
-
});
|
|
59552
|
-
var init_hook = __esmMin((() => {
|
|
59553
|
-
init_HookHost();
|
|
59554
|
-
init_ContextHook();
|
|
59555
|
-
}));
|
|
59556
|
-
//#endregion
|
|
59557
59055
|
//#region src/judges/providers/io.ts
|
|
59558
59056
|
var liveJudgeIo;
|
|
59559
59057
|
var init_io = __esmMin((() => {
|
|
@@ -60036,10 +59534,6 @@ const liveToolCommands = effect$1(ToolCommands, gen(function* () {
|
|
|
60036
59534
|
const { startLspServer } = await Promise.resolve().then(() => (init_lsp(), lsp_exports));
|
|
60037
59535
|
return () => startLspServer({ gtd });
|
|
60038
59536
|
}),
|
|
60039
|
-
contextHook: (request) => lazy("context-hook", async () => {
|
|
60040
|
-
const { runContextHook, liveHookHost } = await Promise.resolve().then(() => (init_hook(), hook_exports));
|
|
60041
|
-
return () => runContextHook(request).pipe(provide$1(liveHookHost));
|
|
60042
|
-
}),
|
|
60043
59537
|
judge: async (request) => {
|
|
60044
59538
|
const { runJudge } = await Promise.resolve().then(() => (init_judges(), judges_exports)).catch((error) => {
|
|
60045
59539
|
throw loadFailure("judge")(error);
|