@pmelab/gtd 5.0.0 → 6.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/README.md +32 -19
- package/dist/gtd.bundle.mjs +281 -153
- package/docs/cli.md +85 -12
- package/docs/configuration.md +116 -51
- package/docs/development.md +7 -5
- package/docs/examples/advanced-workflow.md +33 -23
- package/docs/upgrading.md +49 -32
- package/package.json +1 -1
- package/schema.json +4 -0
package/dist/gtd.bundle.mjs
CHANGED
|
@@ -1058,15 +1058,15 @@ const hash = (self) => {
|
|
|
1058
1058
|
if (structuralRegionState.enabled === true) return 0;
|
|
1059
1059
|
switch (typeof self) {
|
|
1060
1060
|
case "number": return number$1(self);
|
|
1061
|
-
case "bigint": return string
|
|
1062
|
-
case "boolean": return string
|
|
1063
|
-
case "symbol": return string
|
|
1064
|
-
case "string": return string
|
|
1065
|
-
case "undefined": return string
|
|
1061
|
+
case "bigint": return string(self.toString(10));
|
|
1062
|
+
case "boolean": return string(String(self));
|
|
1063
|
+
case "symbol": return string(String(self));
|
|
1064
|
+
case "string": return string(self);
|
|
1065
|
+
case "undefined": return string("undefined");
|
|
1066
1066
|
case "function":
|
|
1067
|
-
case "object": if (self === null) return string
|
|
1067
|
+
case "object": if (self === null) return string("null");
|
|
1068
1068
|
else if (self instanceof Date) {
|
|
1069
|
-
if (Number.isNaN(self.getTime())) return string
|
|
1069
|
+
if (Number.isNaN(self.getTime())) return string("Invalid Date");
|
|
1070
1070
|
return hash(self.toISOString());
|
|
1071
1071
|
} else if (self instanceof URL) return hash(self.href);
|
|
1072
1072
|
else if (isHash(self)) return self[symbol$1]();
|
|
@@ -1112,7 +1112,7 @@ const number$1 = (n) => {
|
|
|
1112
1112
|
* @since 2.0.0
|
|
1113
1113
|
* @category hashing
|
|
1114
1114
|
*/
|
|
1115
|
-
const string
|
|
1115
|
+
const string = (str) => {
|
|
1116
1116
|
let h = 5381, i = str.length;
|
|
1117
1117
|
while (i) h = h * 33 ^ str.charCodeAt(--i);
|
|
1118
1118
|
return optimize(h);
|
|
@@ -1123,7 +1123,7 @@ const string$2 = (str) => {
|
|
|
1123
1123
|
*/
|
|
1124
1124
|
const structureKeys = (o, keys) => {
|
|
1125
1125
|
let h = 12289;
|
|
1126
|
-
for (let i = 0; i < keys.length; i++) h ^= pipe(string
|
|
1126
|
+
for (let i = 0; i < keys.length; i++) h ^= pipe(string(keys[i]), combine$7(hash(o[keys[i]])));
|
|
1127
1127
|
return optimize(h);
|
|
1128
1128
|
};
|
|
1129
1129
|
/**
|
|
@@ -6665,7 +6665,7 @@ const OP_NONE = "None";
|
|
|
6665
6665
|
const OP_RUNTIME = "Runtime";
|
|
6666
6666
|
/** @internal */
|
|
6667
6667
|
const OP_COMPOSITE = "Composite";
|
|
6668
|
-
const emptyHash = /*#__PURE__*/ string
|
|
6668
|
+
const emptyHash = /*#__PURE__*/ string(`${FiberIdSymbolKey}-${OP_NONE}`);
|
|
6669
6669
|
/** @internal */
|
|
6670
6670
|
var None$2 = class {
|
|
6671
6671
|
[FiberIdTypeId] = FiberIdTypeId;
|
|
@@ -6702,7 +6702,7 @@ var Runtime = class {
|
|
|
6702
6702
|
this.startTimeMillis = startTimeMillis;
|
|
6703
6703
|
}
|
|
6704
6704
|
[symbol$1]() {
|
|
6705
|
-
return cached(this, string
|
|
6705
|
+
return cached(this, string(`${FiberIdSymbolKey}-${this._tag}-${this.id}-${this.startTimeMillis}`));
|
|
6706
6706
|
}
|
|
6707
6707
|
[symbol](that) {
|
|
6708
6708
|
return isFiberId$1(that) && that._tag === OP_RUNTIME && this.id === that.id && this.startTimeMillis === that.startTimeMillis;
|
|
@@ -6734,7 +6734,7 @@ var Composite$1 = class {
|
|
|
6734
6734
|
}
|
|
6735
6735
|
_hash;
|
|
6736
6736
|
[symbol$1]() {
|
|
6737
|
-
return pipe(string
|
|
6737
|
+
return pipe(string(`${FiberIdSymbolKey}-${this._tag}`), combine$7(hash(this.left)), combine$7(hash(this.right)), cached(this));
|
|
6738
6738
|
}
|
|
6739
6739
|
[symbol](that) {
|
|
6740
6740
|
return isFiberId$1(that) && that._tag === OP_COMPOSITE && equals$2(this.left, that.left) && equals$2(this.right, that.right);
|
|
@@ -7047,7 +7047,7 @@ const makeCons = (head, tail) => {
|
|
|
7047
7047
|
cons.tail = tail;
|
|
7048
7048
|
return cons;
|
|
7049
7049
|
};
|
|
7050
|
-
const NilHash = /*#__PURE__*/ string
|
|
7050
|
+
const NilHash = /*#__PURE__*/ string("Nil");
|
|
7051
7051
|
const _Nil = /*#__PURE__*/ Object.create({
|
|
7052
7052
|
[TypeId$18]: TypeId$18,
|
|
7053
7053
|
_tag: "Nil",
|
|
@@ -7957,7 +7957,7 @@ var EffectPrimitiveFailure = class {
|
|
|
7957
7957
|
return exitIsExit(that) && that._op === "Failure" && equals$2(this.effect_instruction_i0, that.effect_instruction_i0);
|
|
7958
7958
|
}
|
|
7959
7959
|
[symbol$1]() {
|
|
7960
|
-
return pipe(string
|
|
7960
|
+
return pipe(string(this._tag), combine$7(hash(this.effect_instruction_i0)), cached(this));
|
|
7961
7961
|
}
|
|
7962
7962
|
get cause() {
|
|
7963
7963
|
return this.effect_instruction_i0;
|
|
@@ -7998,7 +7998,7 @@ var EffectPrimitiveSuccess = class {
|
|
|
7998
7998
|
return exitIsExit(that) && that._op === "Success" && equals$2(this.effect_instruction_i0, that.effect_instruction_i0);
|
|
7999
7999
|
}
|
|
8000
8000
|
[symbol$1]() {
|
|
8001
|
-
return pipe(string
|
|
8001
|
+
return pipe(string(this._tag), combine$7(hash(this.effect_instruction_i0)), cached(this));
|
|
8002
8002
|
}
|
|
8003
8003
|
get value() {
|
|
8004
8004
|
return this.effect_instruction_i0;
|
|
@@ -10127,7 +10127,7 @@ const OP_DONE$3 = "Done";
|
|
|
10127
10127
|
const OP_RUNNING = "Running";
|
|
10128
10128
|
/** @internal */
|
|
10129
10129
|
const OP_SUSPENDED = "Suspended";
|
|
10130
|
-
const DoneHash = /*#__PURE__*/ string
|
|
10130
|
+
const DoneHash = /*#__PURE__*/ string(`${FiberStatusSymbolKey}-${OP_DONE$3}`);
|
|
10131
10131
|
/** @internal */
|
|
10132
10132
|
var Done$1 = class {
|
|
10133
10133
|
[FiberStatusTypeId] = FiberStatusTypeId;
|
|
@@ -10551,7 +10551,7 @@ const makeExit = (options) => {
|
|
|
10551
10551
|
return isMicroExit(that) && that._tag === options.op && equals$2(this[args], that[args]);
|
|
10552
10552
|
},
|
|
10553
10553
|
[symbol$1]() {
|
|
10554
|
-
return cached(this, combine$7(string
|
|
10554
|
+
return cached(this, combine$7(string(options.op))(hash(this[args])));
|
|
10555
10555
|
}
|
|
10556
10556
|
};
|
|
10557
10557
|
return function(value) {
|
|
@@ -11203,7 +11203,7 @@ var MetricLabelImpl = class {
|
|
|
11203
11203
|
constructor(key, value) {
|
|
11204
11204
|
this.key = key;
|
|
11205
11205
|
this.value = value;
|
|
11206
|
-
this._hash = string
|
|
11206
|
+
this._hash = string(MetricLabelSymbolKey + this.key + this.value);
|
|
11207
11207
|
}
|
|
11208
11208
|
[symbol$1]() {
|
|
11209
11209
|
return this._hash;
|
|
@@ -11734,7 +11734,7 @@ var MetricBoundariesImpl = class {
|
|
|
11734
11734
|
[MetricBoundariesTypeId] = MetricBoundariesTypeId;
|
|
11735
11735
|
constructor(values) {
|
|
11736
11736
|
this.values = values;
|
|
11737
|
-
this._hash = pipe(string
|
|
11737
|
+
this._hash = pipe(string(MetricBoundariesSymbolKey), combine$7(array(this.values)));
|
|
11738
11738
|
}
|
|
11739
11739
|
_hash;
|
|
11740
11740
|
[symbol$1]() {
|
|
@@ -11786,7 +11786,7 @@ var CounterKeyType = class {
|
|
|
11786
11786
|
constructor(incremental, bigint) {
|
|
11787
11787
|
this.incremental = incremental;
|
|
11788
11788
|
this.bigint = bigint;
|
|
11789
|
-
this._hash = string
|
|
11789
|
+
this._hash = string(CounterKeyTypeSymbolKey);
|
|
11790
11790
|
}
|
|
11791
11791
|
_hash;
|
|
11792
11792
|
[symbol$1]() {
|
|
@@ -11806,7 +11806,7 @@ var HistogramKeyType = class {
|
|
|
11806
11806
|
[HistogramKeyTypeTypeId] = HistogramKeyTypeTypeId;
|
|
11807
11807
|
constructor(boundaries) {
|
|
11808
11808
|
this.boundaries = boundaries;
|
|
11809
|
-
this._hash = pipe(string
|
|
11809
|
+
this._hash = pipe(string(HistogramKeyTypeSymbolKey), combine$7(hash(this.boundaries)));
|
|
11810
11810
|
}
|
|
11811
11811
|
_hash;
|
|
11812
11812
|
[symbol$1]() {
|
|
@@ -11853,7 +11853,7 @@ var MetricKeyImpl = class {
|
|
|
11853
11853
|
this.keyType = keyType;
|
|
11854
11854
|
this.description = description;
|
|
11855
11855
|
this.tags = tags;
|
|
11856
|
-
this._hash = pipe(string
|
|
11856
|
+
this._hash = pipe(string(this.name + this.description), combine$7(hash(this.keyType)), combine$7(array(this.tags)));
|
|
11857
11857
|
}
|
|
11858
11858
|
_hash;
|
|
11859
11859
|
[symbol$1]() {
|
|
@@ -12058,7 +12058,7 @@ var FrequencyState = class {
|
|
|
12058
12058
|
}
|
|
12059
12059
|
_hash;
|
|
12060
12060
|
[symbol$1]() {
|
|
12061
|
-
return pipe(string
|
|
12061
|
+
return pipe(string(FrequencyStateSymbolKey), combine$7(array(fromIterable$6(this.occurrences.entries()))), cached(this));
|
|
12062
12062
|
}
|
|
12063
12063
|
[symbol](that) {
|
|
12064
12064
|
return isFrequencyState(that) && arrayEquals(fromIterable$6(this.occurrences.entries()), fromIterable$6(that.occurrences.entries()));
|
|
@@ -15357,7 +15357,7 @@ const ProtoTimeZoneNamed = {
|
|
|
15357
15357
|
...ProtoTimeZone,
|
|
15358
15358
|
_tag: "Named",
|
|
15359
15359
|
[symbol$1]() {
|
|
15360
|
-
return cached(this, string
|
|
15360
|
+
return cached(this, string(`Named:${this.id}`));
|
|
15361
15361
|
},
|
|
15362
15362
|
[symbol](that) {
|
|
15363
15363
|
return isTimeZone(that) && that._tag === "Named" && this.id === that.id;
|
|
@@ -15377,7 +15377,7 @@ const ProtoTimeZoneOffset = {
|
|
|
15377
15377
|
...ProtoTimeZone,
|
|
15378
15378
|
_tag: "Offset",
|
|
15379
15379
|
[symbol$1]() {
|
|
15380
|
-
return cached(this, string
|
|
15380
|
+
return cached(this, string(`Offset:${this.offset}`));
|
|
15381
15381
|
},
|
|
15382
15382
|
[symbol](that) {
|
|
15383
15383
|
return isTimeZone(that) && that._tag === "Offset" && this.offset === that.offset;
|
|
@@ -29906,8 +29906,6 @@ const collectUint8Array = /*#__PURE__*/ foldLeftChunks(/*#__PURE__*/ new Uint8Ar
|
|
|
29906
29906
|
/** @internal */
|
|
29907
29907
|
const CommandTypeId = /*#__PURE__*/ Symbol.for("@effect/platform/Command");
|
|
29908
29908
|
/** @internal */
|
|
29909
|
-
const isCommand = (u) => typeof u === "object" && u != null && CommandTypeId in u;
|
|
29910
|
-
/** @internal */
|
|
29911
29909
|
const flatten$1 = (self) => Array.from(flattenLoop(self));
|
|
29912
29910
|
/** @internal */
|
|
29913
29911
|
const flattenLoop = (self) => {
|
|
@@ -30012,8 +30010,6 @@ const stdout$1 = /*#__PURE__*/ dual(2, (self, output) => {
|
|
|
30012
30010
|
}
|
|
30013
30011
|
});
|
|
30014
30012
|
/** @internal */
|
|
30015
|
-
const string$1 = /*#__PURE__*/ dual((args) => isCommand(args[0]), (command, encoding) => flatMap$4(CommandExecutor$1, (executor) => executor.string(command, encoding)));
|
|
30016
|
-
/** @internal */
|
|
30017
30013
|
const workingDirectory$1 = /*#__PURE__*/ dual(2, (self, cwd) => {
|
|
30018
30014
|
switch (self._tag) {
|
|
30019
30015
|
case "StandardCommand": return makeStandard({
|
|
@@ -30045,16 +30041,6 @@ const flatten = flatten$1;
|
|
|
30045
30041
|
*/
|
|
30046
30042
|
const make$1 = make$2;
|
|
30047
30043
|
/**
|
|
30048
|
-
* Runs the command returning the entire output as a string with the
|
|
30049
|
-
* specified encoding.
|
|
30050
|
-
*
|
|
30051
|
-
* If an encoding is not specified, the encoding will default to `utf-8`.
|
|
30052
|
-
*
|
|
30053
|
-
* @since 1.0.0
|
|
30054
|
-
* @category execution
|
|
30055
|
-
*/
|
|
30056
|
-
const string = string$1;
|
|
30057
|
-
/**
|
|
30058
30044
|
* Specify the standard error stream for a command.
|
|
30059
30045
|
*
|
|
30060
30046
|
* @since 1.0.0
|
|
@@ -189117,6 +189103,10 @@ const isCommitState = (state) => state.commit !== void 0;
|
|
|
189117
189103
|
const isReviewWindowState = (def, state) => def.states[state]?.reviewWindow === true;
|
|
189118
189104
|
/** True when `state` anchors the review window's diff base (see `StateDef.reviewBase`). Safe for an unknown state name (returns `false`). */
|
|
189119
189105
|
const isReviewBaseState = (def, state) => def.states[state]?.reviewBase === true;
|
|
189106
|
+
/** The workflow's declared review-entry state name (see `StateDef.reviewEntry`) — `gtd review <commitish>` (`src/program.ts`) enters this state to start a new review process. `undefined` when no state declares one; `validateDefinition` guarantees at most one does, so the first (only) match wins. */
|
|
189107
|
+
const reviewEntryStateOf = (def) => {
|
|
189108
|
+
for (const [name, state] of Object.entries(def.states)) if (state.reviewEntry === true) return name;
|
|
189109
|
+
};
|
|
189120
189110
|
/**
|
|
189121
189111
|
* `gtd(<actor>): <state>` — the subject a step commit carries. Per decision
|
|
189122
189112
|
* 2, `<actor>` is WHO AUTHORED THE STEP (the invoker), and `<state>` is the
|
|
@@ -189445,6 +189435,29 @@ const validateReviewWindow = (name, state) => {
|
|
|
189445
189435
|
if (state.reviewBase !== void 0) errors.push(`state "${name}": a commit state cannot declare "reviewBase"`);
|
|
189446
189436
|
return errors;
|
|
189447
189437
|
};
|
|
189438
|
+
/**
|
|
189439
|
+
* `reviewEntry`, when present, is forbidden on a commit state (never at
|
|
189440
|
+
* rest — same rule family as `reviewWindow`/`reviewBase`) and on the initial
|
|
189441
|
+
* state: a review entry is a deliberate, distinct starting point from the
|
|
189442
|
+
* workflow's ordinary "no active cycle" rest (`gtd review` itself REQUIRES
|
|
189443
|
+
* resting at the initial state before it will act — see `src/program.ts` —
|
|
189444
|
+
* so the two must stay distinguishable rather than collapsing into one).
|
|
189445
|
+
* The at-most-one-state rule is checked separately, over the whole
|
|
189446
|
+
* definition (see `validateReviewEntryUniqueness`), since it isn't a
|
|
189447
|
+
* per-state finding.
|
|
189448
|
+
*/
|
|
189449
|
+
const validateReviewEntry = (name, state) => {
|
|
189450
|
+
if (state.reviewEntry === void 0) return [];
|
|
189451
|
+
const errors = [];
|
|
189452
|
+
if (isCommitState(state)) errors.push(`state "${name}": a commit state cannot declare "reviewEntry"`);
|
|
189453
|
+
if (state.initial === true) errors.push(`state "${name}": the initial state cannot declare "reviewEntry"`);
|
|
189454
|
+
return errors;
|
|
189455
|
+
};
|
|
189456
|
+
/** At most one state may declare `reviewEntry: true` — `gtd review <commitish>` (`src/program.ts`) needs a single, unambiguous state name to enter (see `reviewEntryStateOf`). */
|
|
189457
|
+
const validateReviewEntryUniqueness = (def, names) => {
|
|
189458
|
+
const entryNames = names.filter((name) => def.states[name].reviewEntry === true);
|
|
189459
|
+
return entryNames.length > 1 ? [`at most one state may declare "reviewEntry" (found ${entryNames.length}: ${entryNames.join(", ")})`] : [];
|
|
189460
|
+
};
|
|
189448
189461
|
/** Every `on` row parses, and its target names a defined state. */
|
|
189449
189462
|
const validateOnEdges = (name, state, names) => {
|
|
189450
189463
|
const errors = [];
|
|
@@ -189505,7 +189518,8 @@ const validateState = (def, name, names) => {
|
|
|
189505
189518
|
...validateMemory(name, state),
|
|
189506
189519
|
...validateFile(name, state),
|
|
189507
189520
|
...validateMode(def, name, state),
|
|
189508
|
-
...validateReviewWindow(name, state)
|
|
189521
|
+
...validateReviewWindow(name, state),
|
|
189522
|
+
...validateReviewEntry(name, state)
|
|
189509
189523
|
];
|
|
189510
189524
|
};
|
|
189511
189525
|
/**
|
|
@@ -189527,9 +189541,11 @@ const validateState = (def, name, names) => {
|
|
|
189527
189541
|
* `knownModes`), requires a sibling `file`, and is never declared on a commit
|
|
189528
189542
|
* state; every `modes:` entry declares at least one non-blank
|
|
189529
189543
|
* `format`/`validate` command; `reviewWindow`/`reviewBase`, when
|
|
189530
|
-
* present, are never declared on a commit state;
|
|
189531
|
-
*
|
|
189532
|
-
*
|
|
189544
|
+
* present, are never declared on a commit state; `reviewEntry`, when present,
|
|
189545
|
+
* is never declared on a commit state or the initial state, and at most one
|
|
189546
|
+
* state across the whole workflow may declare it; every state is reachable
|
|
189547
|
+
* from the initial state by walking `on` targets and `retry.otherwise`
|
|
189548
|
+
* redirects (checked only when the initial-state rule itself passed — see
|
|
189533
189549
|
* `validateReachability`).
|
|
189534
189550
|
*/
|
|
189535
189551
|
const validateDefinition = (def) => {
|
|
@@ -189540,6 +189556,7 @@ const validateDefinition = (def) => {
|
|
|
189540
189556
|
...initialErrors,
|
|
189541
189557
|
...validateModes(def),
|
|
189542
189558
|
...names.flatMap((name) => validateState(def, name, names)),
|
|
189559
|
+
...validateReviewEntryUniqueness(def, names),
|
|
189543
189560
|
...initialErrors.length === 0 ? validateReachability(def, names) : []
|
|
189544
189561
|
];
|
|
189545
189562
|
};
|
|
@@ -189749,7 +189766,8 @@ const KNOWN_STATE_KEYS = /* @__PURE__ */ new Set([
|
|
|
189749
189766
|
"file",
|
|
189750
189767
|
"mode",
|
|
189751
189768
|
"reviewWindow",
|
|
189752
|
-
"reviewBase"
|
|
189769
|
+
"reviewBase",
|
|
189770
|
+
"reviewEntry"
|
|
189753
189771
|
]);
|
|
189754
189772
|
const KNOWN_TOP_KEYS = /* @__PURE__ */ new Set([
|
|
189755
189773
|
"vars",
|
|
@@ -189909,7 +189927,7 @@ const compileMode = (raw, name, errors) => {
|
|
|
189909
189927
|
/** The `initial` field: `true` only when the raw value is the literal boolean `true`. */
|
|
189910
189928
|
const compileInitial = (raw, name, errors) => compileBooleanFlag(raw, "initial", name, errors);
|
|
189911
189929
|
/**
|
|
189912
|
-
* A boolean state flag (`initial`/`reviewWindow`/`reviewBase`): `true` only
|
|
189930
|
+
* A boolean state flag (`initial`/`reviewWindow`/`reviewBase`/`reviewEntry`): `true` only
|
|
189913
189931
|
* when the raw value is the literal `true`; a non-boolean is a config error;
|
|
189914
189932
|
* `false` (or absent) compiles away to `undefined` so it never lands in the
|
|
189915
189933
|
* `StateDef` — `false` and "unset" mean the same thing for every such flag.
|
|
@@ -189948,7 +189966,8 @@ const assembleStateDef = (parts) => ({
|
|
|
189948
189966
|
file: parts.file,
|
|
189949
189967
|
mode: parts.mode,
|
|
189950
189968
|
reviewWindow: parts.reviewWindow,
|
|
189951
|
-
reviewBase: parts.reviewBase
|
|
189969
|
+
reviewBase: parts.reviewBase,
|
|
189970
|
+
reviewEntry: parts.reviewEntry
|
|
189952
189971
|
}),
|
|
189953
189972
|
...assembleContentFields(parts.content)
|
|
189954
189973
|
});
|
|
@@ -189971,7 +189990,8 @@ const compileState = (name, raw, configDir, errors) => {
|
|
|
189971
189990
|
file: compileFile(raw, name, errors),
|
|
189972
189991
|
mode: compileMode(raw, name, errors),
|
|
189973
189992
|
reviewWindow: compileBooleanFlag(raw, "reviewWindow", name, errors),
|
|
189974
|
-
reviewBase: compileBooleanFlag(raw, "reviewBase", name, errors)
|
|
189993
|
+
reviewBase: compileBooleanFlag(raw, "reviewBase", name, errors),
|
|
189994
|
+
reviewEntry: compileBooleanFlag(raw, "reviewEntry", name, errors)
|
|
189975
189995
|
});
|
|
189976
189996
|
};
|
|
189977
189997
|
/**
|
|
@@ -190012,27 +190032,6 @@ const compileWorkflowConfig = (raw, configDir, rcModes) => {
|
|
|
190012
190032
|
};
|
|
190013
190033
|
};
|
|
190014
190034
|
//#endregion
|
|
190015
|
-
//#region src/workflows/default.ts
|
|
190016
|
-
/**
|
|
190017
|
-
* The bundled default workflow, compiled through the exact same
|
|
190018
|
-
* `compileWorkflowConfig` a user's `.gtdrc` `workflow:` key goes through — no
|
|
190019
|
-
* privileged code path (see default.yaml's header comment for why every
|
|
190020
|
-
* content string there is inline rather than a `./`-relative file
|
|
190021
|
-
* reference). `default.yaml` is imported as raw text (tsdown's `.yaml` text
|
|
190022
|
-
* loader / the vitest `rawMd` transform — see tsdown.config.ts /
|
|
190023
|
-
* tests/vitest.rawMd.ts), so this module never touches the filesystem: it
|
|
190024
|
-
* works identically in the dev checkout, under `vitest`, and inside the
|
|
190025
|
-
* single-file `dist/gtd.bundle.mjs` build.
|
|
190026
|
-
*
|
|
190027
|
-
* `configDir` is passed as `"."` and never actually consulted: none of
|
|
190028
|
-
* default.yaml's content values start with `./`/`../`, so
|
|
190029
|
-
* `compileWorkflowConfig` never resolves a file reference against it.
|
|
190030
|
-
*/
|
|
190031
|
-
const compiled = compileWorkflowConfig((0, import_dist$1.parse)("# The bundled default gtd workflow (v3 \"pattern machine\" — see\n# docs/design/pattern-machine-plan.md, and\n# docs/design/steering-file-validation-command.md for the `gtd validate`\n# self-validation model that replaced the old in-machine validation loops).\n# Compiled through the exact same `compileWorkflowConfig` a user's `.gtdrc`\n# `workflow:` key goes through — no privileged code path\n# (src/workflows/default.ts does the compiling).\n#\n# Every content string below is INLINE (no `./`-relative file references):\n# the bundled default ships inside a single-file build\n# (`dist/gtd.bundle.mjs`), so its content must already be resolved at BUILD\n# time rather than read from disk at RUNTIME — the same reason\n# `src/prompts/*.md` are imported through tsdown's `.md`-as-text loader\n# instead of read via `fs` at runtime. `default.yaml` itself is imported the\n# same way (tsdown/vitest both treat `.yaml` as text — see\n# tsdown.config.ts / tests/vitest.rawMd.ts), so this file never needs a\n# sibling prompts/ directory to exist on disk once installed.\n#\n# States, in cycle order — 10 states, no squash:\n# idle (initial, human, message)\n# -> grilling (agent, prompt): reads .gtd/TODO.md's sketch (or a prior\n# lap's feedback), explores the codebase, and develops it into a concrete\n# implementation plan, stating any open question it can't settle itself\n# under a `## Open Questions` heading rather than guessing silently. Its\n# `file:`/`mode:` (TODO.md/`qa`) makes the plan's format checkable — it\n# self-validates before finishing (see \"Steering-file validation\" below).\n# grilling -> grilling-answer (human, message): tick off suggested\n# defaults by replacing them with `Answer:` lines; a clean step accepts\n# every remaining default -> building; any edit loops back to grilling.\n# grilling-answer -> building (agent, prompt): implements the plan in\n# .gtd/TODO.md directly (no task decomposition, no per-task queue) and\n# deletes .gtd/TODO.md once the work is complete and verified.\n# building -> checking (check, script): runs the test command inline;\n# A/M .gtd/FEEDBACK.md -> fixing (capped, redirects to escalate); D\n# .gtd/FEEDBACK.md (a green run that just cleaned up prior feedback) or C\n# (green, nothing to clean) -> reviewing.\n# fixing (agent, prompt) <-> checking\n# escalate (human, message): manual intervention, then back to checking\n# checking (green) -> reviewing (agent, prompt): writes .gtd/REVIEW.md in\n# the checkbox review format, grouping the cycle's full diff into\n# reviewable chunks; its `file:`/`mode:` (REVIEW.md/`review`) makes that\n# format checkable, so it self-validates before finishing.\n# reviewing -> await-review (human, message): tick a pointer's `- [\n# ]` to `- [x]` to approve that item; deleting .gtd/REVIEW.md outright is\n# the power-user approve-everything shortcut; anything else pending\n# (code edits with REVIEW.md untouched) is feedback straight to grilling.\n# await-review -> review-deciding (check, script), entered by any `M\n# .gtd/REVIEW.md` step: if no unticked `- [ ]` pointer remains, the cycle\n# is approved (rm .gtd/REVIEW.md -> idle); otherwise the still-unticked\n# pointers (with their notes) are extracted into a fresh .gtd/TODO.md and\n# .gtd/REVIEW.md is removed (-> grilling, feedback lap).\n#\n# The cycle ends at human approval, resting back at idle — there is no\n# squash state. The cycle's commits stay in history; whether/how to squash\n# them (amend, interactive rebase, a PR's squash-merge) is entirely the\n# human's business once they're done reviewing — gtd makes no assumption.\n# That squash-flavored finale (a `squashing` prompt state + a `done` commit\n# state) still exists as an ENGINE capability (see STATES.md §8) and is kept\n# as part of the fuller machine at docs/examples/advanced-workflow.md.\n#\n# A `gtd(<actor>): idle` commit — entering the workflow's own initial state —\n# is therefore a PROCESS BOUNDARY (see `computeProcessRun` in src/Edge.ts):\n# the next cycle's `retry` counts, `startCommit`, and diffs never reach back\n# across an approved cycle's own idle-entering commit.\n#\n# HYGIENE INVARIANT (asserted in e2e — see default-workflow.feature): an\n# approved cycle leaves `.gtd/` empty. FEEDBACK.md is cleaned up by a green\n# `checking` run; REVIEW.md by the review-deciding approval branch; TODO.md by\n# `building`.\n#\n# Steering-file validation. `grilling` and `reviewing` each declare a\n# `file:`/`mode:` pair (TODO.md as `qa`, REVIEW.md as `review`), so their\n# output has a checkable format. src/OpenQuestions.ts and src/ReviewDoc.ts are\n# those two formats' EXECUTABLE SPECS (their unit tests are the format spec\n# tests), and `gtd validate` FORMATS the resolved state's rendered `file:` in\n# place then runs the SAME parser over it, exiting non-zero with findings when\n# it violates the format. There is no in-machine validation state and no bash\n# port to keep in sync — the producing agent self-validates before finishing\n# (plain `gtd next` appends the \"run `gtd validate` and fix all violations\"\n# instruction; under `--json` the driving loop runs it after the turn and\n# re-prompts on findings — see skills/loop/SKILL.md), AND `gtd step` runs the\n# same format-and-validate gate on capture, refusing a turn whose steering file\n# is invalid. So the check runs after a human edits too — at the human gates\n# `grilling-answer` (TODO.md) and `await-review` (REVIEW.md) — and a human gate\n# is only ever handed a tidy, well-formed file. The same parsers back the LSP's\n# live diagnostics (src/Lsp.ts).\n#\n# `grilling` and `reviewing` both declare `model: smart` — an opaque harness\n# hint (see the comment at `grilling` below) for their heavier one-shot\n# planning/reviewing turns. Every other state is unset (harness default).\n#\n# The four agent states each declare a `memory:` scope label (`plan`,\n# `build`, `fix`, `review` — see the comment at `grilling` below) — another\n# opaque hint a memory-aware driver reads to decide when an agent turn keeps\n# the previous turn's memory and when it starts fresh: a loop that re-enters\n# one state (grilling around its answer loop, fixing around checking) keeps\n# the same label and retains memory across its laps; crossing to the next\n# phase's differently-labelled state clears it. gtd never interprets the\n# label (see STATES.md §1 and skills/loop/SKILL.md's driver contract).\n#\n# `vars.testCommand` is this workflow's own declared default for `checking`'s\n# script (see it.vars in PatternTemplates.ts) — override it per-repo with a\n# top-level `.gtdrc` `vars: { testCommand: ... }` key, or per-invocation with\n# a `GTD_VAR_testCommand` environment variable (highest precedence).\n#\n# `vars.todoFile`/`reviewFile`/`feedbackFile` are this workflow's ONE source\n# of truth for the three steering-file paths every prompt/script below reads\n# or writes — each appears in templates as `<%~ it.vars.todoFile %>` etc\n# (see docs/design/state-file-association.md §2). Every state that rests on\n# one of these files also declares a `file:`/`mode:` pair (see STATES.md's\n# property table) so `gtd status`/`gtd next --json` and the LSP (`gtd lsp`)\n# know which file a human/editor should look at, and in what format\n# (`qa` for TODO.md's open-questions format, `review` for REVIEW.md's\n# checkbox format) — `fixing`/`escalate` declare `file:` alone (FEEDBACK.md\n# is plain text, no LSP format). KNOWN LIMITATION: `on` pattern keys below are\n# NOT Eta templates — they keep the LITERAL `.gtd/...` paths matching these\n# vars' DEFAULT values. Repointing `todoFile`/`reviewFile`/`feedbackFile` via\n# `.gtdrc`'s top-level `vars:` or a `GTD_VAR_` override changes what `file:`\n# renders to (and what a template reads/writes) WITHOUT changing what the\n# `on` patterns match against — desyncing the machine. See\n# docs/configuration.md's `file:`/`mode:` section for the full caveat.\n#\n# A fuller machine — two-phase Q&A planning, an architecture phase, task\n# decomposition, a deterministic per-task builder loop, agent-prepared\n# review, and the squash finale (`squashing` + `done`) — is preserved as a\n# copy-paste `.gtdrc` example at docs/examples/advanced-workflow.md.\n\nvars:\n testCommand: npm test\n todoFile: .gtd/TODO.md\n reviewFile: .gtd/REVIEW.md\n feedbackFile: .gtd/FEEDBACK.md\n\nstates:\n idle:\n actor: human\n initial: true\n # Every human-gate `message:` below closes with the SAME rendered block —\n # a \"What each change does next\" list built from `it.edges`, the resting\n # state's own `on` edges (see PatternTemplates.TemplateContext.edges). Each\n # edge's human-readable route lives in its `describe:` (the single source\n # of truth), so the list can never drift from the routing it documents.\n # The block skips edges without a `describe`, and each `<%~ ... \"\\n\" %>`\n # keeps the bullet's own newline (Eta's autoTrim strips the template's own\n # line breaks around `<% %>` tags, not text inside an interpolation).\n message: |\n No active gtd cycle.\n\n To start one, write `.gtd/TODO.md` with a short sketch of what you want\n built — a few sentences is enough.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: grilling\n describe: >-\n Save your sketch to `.gtd/TODO.md` (any working-tree change) to start\n the cycle — the agent reads it, explores the codebase, and develops it\n into a concrete implementation plan (**grilling**).\n\n grilling:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n # `model` is an opaque harness hint — gtd never interprets this string, it\n # only passes it through to `gtd next --json`/`gtd status --json` so the\n # driving loop can map it onto whatever models its agent harness\n # provides. \"smart\" here just names the harness-chosen tier for the\n # heavier planning turn; states without a `model` use the harness's\n # default.\n model: smart\n # `memory` is an opaque memory-scope label — gtd never interprets it, it\n # only passes it through to `gtd next --json`/`gtd status --json` so a\n # memory-aware driving loop can decide when an agent turn continues from\n # the previous turn's memory and when it starts fresh: consecutive agent\n # turns that emit the SAME label share memory, a change (or the first\n # turn) is where the driver starts fresh. `grilling` re-enters itself\n # around the grilling-answer loop, so labelling every lap\n # `plan` keeps the planner's accumulated exploration across the loop; the\n # next phase (`building`) carries a different label, clearing it.\n memory: plan\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.todoFile %>`, which this prompt tells you to develop.\n\n `<%= it.vars.todoFile %>` holds a short sketch of what to build (or\n feedback from a prior review lap). Read it, explore the codebase, and\n develop it into a concrete implementation plan.\n\n For every question you cannot settle yourself, add it under a\n `## Open Questions` heading, one `### <question>` sub-heading each,\n whose body's FIRST non-blank line is exactly\n `Suggested default: <your answer>` — the human can accept it as-is or\n override it. Omit the section entirely once there are no open\n questions. If `<%= it.vars.todoFile %>` already carries\n `## Open Questions` entries from a previous lap, fold any\n `Answer: <text>` line into the plan; every remaining entry's body must\n still start with either `Suggested default: ...` or `Answer: ...` —\n never anything else.\n\n Leave `<%= it.vars.todoFile %>` uncommitted and finish your turn — do\n not commit, and do not run `gtd step agent` yourself; the harness does\n that.\n on:\n \"* **\": grilling-answer\n\n grilling-answer:\n actor: human\n file: <%= it.vars.todoFile %>\n mode: qa\n message: |\n `<%= it.vars.todoFile %>` holds the plan under development, with any\n open questions under `## Open Questions` — each carrying a suggested\n default. Answer a question by editing its entry in place: replace its\n `Suggested default: ...` line with `Answer: ...`, or add further detail.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: building\n describe: >-\n Change nothing to accept every suggested default as-is and move on to\n implementation (**building**).\n \"* **\":\n to: grilling\n describe: >-\n Edit the plan — answer a question, add detail, or raise a new one — to\n send it back for another round of grilling (**grilling**).\n\n building:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n # A distinct memory scope from `plan`: implementation starts fresh, with\n # the plan already settled in `.gtd/TODO.md` (which this prompt reads\n # below) rather than carried in the planner's working memory.\n memory: build\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create or edit anything under `.gtd/` except deleting\n `<%= it.vars.todoFile %>`, which this prompt tells you to delete once\n its work is complete.\n\n The plan to implement is: <%~ it.read(it.vars.todoFile) %>\n\n Read that plan and implement it in full. Use TDD discipline: one test,\n then the implementation that passes it, then the next — never write\n all the tests first.\n\n Delete `<%= it.vars.todoFile %>` once the work it describes is complete\n and verified. Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": checking\n\n checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — the loop driver executes this verbatim, then\n # steps the check actor. Mechanics only: run the test suite and record a\n # red run's output as .gtd/FEEDBACK.md. What that output MEANS (another\n # fix round vs escalation) is decided by the workflow's own `on`/`retry`\n # rules at capture time — never here.\n #\n # `it.vars.testCommand` defaults to \"npm test\" (this workflow's own\n # `vars:` above) — override it with a top-level `.gtdrc`\n # `vars: { testCommand: ... }` key, or a `GTD_VAR_testCommand`\n # environment variable (highest precedence).\n #\n # A green run also removes any `<%= it.vars.feedbackFile %>` left over\n # from an earlier red run — there is no squash to discard it for us\n # anymore, so the script cleans it up itself before moving on to\n # review.\n set +e\n mkdir -p .gtd\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output <%~ it.vars.feedbackFile %>\n else\n rm -f .gtd/.check-output\n printf 'npm test failed with exit code %s and produced no output.' \"$code\" > <%~ it.vars.feedbackFile %>\n fi\n else\n rm -f .gtd/.check-output\n rm -f <%~ it.vars.feedbackFile %>\n fi\n on:\n \"A .gtd/FEEDBACK.md\": fixing\n \"M .gtd/FEEDBACK.md\": fixing\n \"D .gtd/FEEDBACK.md\": reviewing\n \"C\": reviewing\n\n fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n # Its own memory scope: the fix loop re-enters `fixing` around `checking`\n # (up to the retry cap), so labelling every attempt `fix` lets the agent\n # remember what it already tried across the loop; a green check moves on\n # to the differently-labelled `reviewing`, clearing it.\n memory: fix\n retry:\n max: 3\n otherwise: escalate\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.feedbackFile %>`, which this prompt tells you to address.\n\n Read `<%= it.vars.feedbackFile %>` (the failing test output) and fix\n the code so the suite passes. Keep the change focused — do not\n refactor unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue; the\n machine picks the dispute up either way.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": checking\n\n escalate:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The agent could not get the check to pass after repeated attempts.\n `<%= it.vars.feedbackFile %>` holds the last failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: checking\n describe: >-\n Fix the failure yourself — edit the code and/or `.gtd/FEEDBACK.md` —\n to re-run the check (**checking**).\n\n reviewing:\n actor: agent\n file: <%= it.vars.reviewFile %>\n mode: review\n model: smart\n # A distinct memory scope again: the reviewer looks at the cycle's full\n # diff (inlined into its prompt) with a fresh perspective, not the\n # builder's or fixer's working memory.\n memory: review\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.reviewFile %>`, which this prompt tells you to write.\n\n Write `<%= it.vars.reviewFile %>` to help a human review the cycle's\n full diff (inlined below) in this EXACT format:\n\n - First non-blank line: `# Review: <%= it.currentCommit.slice(0, 7) %>`\n - Somewhere in the document: `<!-- base: <%= it.startCommit %> -->`\n - At least one `## <Chunk Title>` heading, grouping hunks semantically\n (same feature/refactor/fix, even across files) — a short explanation\n of what changed and why, then one\n `- [ ] ./path/to/file.ts#42 — note` file pointer per hunk (checkboxes\n are for the human to tick, not you; every path is `./`-relative, with\n an optional `#line` and an optional ` — note`).\n\n Leave `<%= it.vars.reviewFile %>` uncommitted and finish your turn.\n <% if (it.processDiff.trim()) { %>\n\n ## Full cycle diff\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"* **\": await-review\n\n await-review:\n actor: human\n file: <%= it.vars.reviewFile %>\n mode: review\n # While the cycle rests here for human review, gtd opens a \"review checkout\n # window\": HEAD and the index are temporarily rewound to this cycle's start\n # (the process boundary — no `reviewBase` state is declared, so the base\n # defaults to it) with the working tree untouched, so the whole cycle diff\n # surfaces as ordinary uncommitted changes in the editor's git integration.\n # The window closes automatically on the next invocation, once the cycle\n # rests anywhere else (see src/ReviewWindow.ts, STATES.md §11).\n reviewWindow: true\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the completed\n cycle — one `- [ ]` checkbox per reviewable item, grouped into chunks.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"D .gtd/REVIEW.md\":\n to: idle\n describe: >-\n Delete `.gtd/REVIEW.md` outright to approve the whole cycle at once\n and rest back at the start (**idle**).\n \"M .gtd/REVIEW.md\":\n to: review-deciding\n describe: >-\n Edit `.gtd/REVIEW.md` — tick a `- [ ]` box to `- [x]` to approve that\n item, or add notes — to hand it to a deterministic check\n (**review-deciding**) that closes the cycle out when every box is\n ticked, or extracts the still-unticked items into a fresh\n `.gtd/TODO.md` and loops back through **grilling** with that feedback.\n \"* **\":\n to: grilling\n describe: >-\n Change only code, leaving `.gtd/REVIEW.md` untouched, to send that as\n feedback straight back to **grilling**.\n\n review-deciding:\n actor: check\n file: <%= it.vars.reviewFile %>\n mode: review\n script: |\n #!/usr/bin/env bash\n # gtd check turn — the loop driver executes this verbatim, then\n # steps the check actor. Mechanics only: if .gtd/REVIEW.md still has any\n # unticked \"- [ ]\" pointer, extract those pointers (with their notes)\n # into a fresh .gtd/TODO.md; either way, remove .gtd/REVIEW.md. What\n # the resulting diff MEANS (feedback lap vs. full approval) is decided\n # by this state's own `on` rules at capture time — never here. The\n # A/M .gtd/TODO.md row is declared BEFORE the D .gtd/REVIEW.md row (see\n # this workflow's `on` map below) so a feedback lap — which always also\n # deletes .gtd/REVIEW.md — is captured as feedback, not mistaken for an\n # approval.\n set +e\n mkdir -p .gtd\n if grep -qE '^[[:space:]]*-[[:space:]]*\\[[[:space:]]*\\]' <%~ it.vars.reviewFile %> 2>/dev/null; then\n {\n echo \"Feedback from review — address these before continuing:\"\n echo\n grep -E '^[[:space:]]*-[[:space:]]*\\[[[:space:]]*\\]' <%~ it.vars.reviewFile %>\n } > <%~ it.vars.todoFile %>\n fi\n rm -f <%~ it.vars.reviewFile %>\n on:\n \"A .gtd/TODO.md\": grilling\n \"M .gtd/TODO.md\": grilling\n \"D .gtd/REVIEW.md\": idle\n \"C\": await-review\n"), ".");
|
|
190032
|
-
const defaultWorkflowDefinition = compiled.definition;
|
|
190033
|
-
/** The default workflow's own declared `vars:` defaults — `{ testCommand: "npm test" }`, overridable via a top-level `.gtdrc` `vars:` key or a `GTD_VAR_testCommand` environment variable (see `src/Edge.ts`'s `resolveVars`). */
|
|
190034
|
-
const defaultWorkflowVars = compiled.vars;
|
|
190035
|
-
//#endregion
|
|
190036
190035
|
//#region src/Cwd.ts
|
|
190037
190036
|
var Cwd = class Cwd extends Tag("Cwd")() {
|
|
190038
190037
|
static layer = (root) => succeed$2(Cwd, { root });
|
|
@@ -190070,10 +190069,11 @@ var Cwd = class Cwd extends Tag("Cwd")() {
|
|
|
190070
190069
|
* Kept in its own module, separate from `./Config.js`, so `scripts/generate-
|
|
190071
190070
|
* schema.ts` (run via `jiti`, a plain TS-via-Babel loader with no bundler-
|
|
190072
190071
|
* style pluggable per-extension loaders) can import JUST the schema without
|
|
190073
|
-
*
|
|
190074
|
-
* imports `
|
|
190075
|
-
* loader
|
|
190076
|
-
*
|
|
190072
|
+
* risking a transitive pull into the bundled workflow templates
|
|
190073
|
+
* (`./workflows/templates.js`, which imports `simple.yaml`/`advanced.yaml` as
|
|
190074
|
+
* raw text via tsdown's/vitest's `.yaml`-as-text loader — something `jiti` has
|
|
190075
|
+
* no equivalent for and doesn't need here): the schema shape never depends on
|
|
190076
|
+
* any workflow's content.
|
|
190077
190077
|
*/
|
|
190078
190078
|
/** The `vars:` shape (top-level AND inside `workflow:`): a flat name -> scalar map (`compileVarsMap` coerces every scalar to a string). */
|
|
190079
190079
|
const varsJsonSchema = {
|
|
@@ -190210,6 +190210,10 @@ const workflowJsonSchema = {
|
|
|
190210
190210
|
reviewBase: {
|
|
190211
190211
|
type: "boolean",
|
|
190212
190212
|
description: "Marks the state whose most-recent in-process commit anchors the review window's diff base; absent any, the base is the process start. Forbidden on a commit state."
|
|
190213
|
+
},
|
|
190214
|
+
reviewEntry: {
|
|
190215
|
+
type: "boolean",
|
|
190216
|
+
description: "Marks the (at most one) state `gtd review <commitish>` enters to start a brand new process reviewing <commitish>..HEAD. Forbidden on a commit state and on the initial state."
|
|
190213
190217
|
}
|
|
190214
190218
|
}
|
|
190215
190219
|
}
|
|
@@ -190317,11 +190321,11 @@ const loadMerged = (root) => tryPromise({
|
|
|
190317
190321
|
},
|
|
190318
190322
|
catch: (e) => e instanceof Error ? e : new Error(String(e))
|
|
190319
190323
|
});
|
|
190320
|
-
const SCHEMA_STUB = `${JSON.stringify({ $schema: "https://raw.githubusercontent.com/pmelab/gtd/main/schema.json" }, null, 2)}\n`;
|
|
190321
190324
|
/**
|
|
190322
190325
|
* Reuses the same `walkUp` + `searchStrategy: "none"` cosmiconfig explorer as
|
|
190323
190326
|
* `loadMerged` to detect whether ANY level of the cwd→root walk carries a gtd
|
|
190324
|
-
* config. Returns `true` on the first non-empty `search(dir)` result.
|
|
190327
|
+
* config. Returns `true` on the first non-empty `search(dir)` result. Exported
|
|
190328
|
+
* so `gtd init` (src/program.ts) can refuse to overwrite an existing config.
|
|
190325
190329
|
*/
|
|
190326
190330
|
const anyConfigPresent = (root) => tryPromise({
|
|
190327
190331
|
try: async () => {
|
|
@@ -190374,29 +190378,25 @@ const compileRcVars = (raw) => {
|
|
|
190374
190378
|
return vars;
|
|
190375
190379
|
};
|
|
190376
190380
|
/**
|
|
190377
|
-
*
|
|
190378
|
-
*
|
|
190379
|
-
*
|
|
190380
|
-
*
|
|
190381
|
-
|
|
190382
|
-
|
|
190383
|
-
|
|
190384
|
-
*
|
|
190381
|
+
* The error every config-reading command fails with when no `workflow:` key is
|
|
190382
|
+
* configured anywhere in the cwd→home chain. gtd ships no default workflow, so
|
|
190383
|
+
* there is nothing to fall back to — the user must scaffold one with
|
|
190384
|
+
* `gtd init`. Exported so the CLI layer and tests can assert on it verbatim.
|
|
190385
|
+
*/
|
|
190386
|
+
const NO_WORKFLOW_MESSAGE = "gtd: no workflow configured — run `gtd init <simple|advanced>` to create .gtdrc.json";
|
|
190387
|
+
/**
|
|
190388
|
+
* Compile the decoded config's `workflow:` key plus its top-level
|
|
190389
|
+
* `vars:`/`modes:` keys into `ConfigOperations`. `root` is used as the
|
|
190390
|
+
* workflow compiler's `configDir` — the directory a custom workflow's
|
|
190391
|
+
* `./`-relative content references resolve against. Throws `NO_WORKFLOW_MESSAGE`
|
|
190392
|
+
* when no `workflow:` key is present (there is no bundled default to fall back
|
|
190393
|
+
* to — see `gtd init`), or (via `compileWorkflowConfig`/`compileRcVars`) on any
|
|
190394
|
+
* invalid workflow/vars.
|
|
190385
190395
|
*/
|
|
190386
190396
|
const toOperations = (decoded, root) => {
|
|
190387
190397
|
const rcVars = compileRcVars(decoded.vars);
|
|
190388
190398
|
const rcModes = compileRcModes(decoded.modes);
|
|
190389
|
-
if (decoded.workflow === void 0)
|
|
190390
|
-
const modes = mergeModes(defaultWorkflowDefinition.modes, rcModes);
|
|
190391
|
-
return {
|
|
190392
|
-
workflow: modes !== void 0 ? {
|
|
190393
|
-
...defaultWorkflowDefinition,
|
|
190394
|
-
modes
|
|
190395
|
-
} : defaultWorkflowDefinition,
|
|
190396
|
-
workflowVars: defaultWorkflowVars,
|
|
190397
|
-
rcVars
|
|
190398
|
-
};
|
|
190399
|
-
}
|
|
190399
|
+
if (decoded.workflow === void 0) throw new Error(NO_WORKFLOW_MESSAGE);
|
|
190400
190400
|
const { definition, vars: workflowVars } = compileWorkflowConfig(decoded.workflow, root, rcModes);
|
|
190401
190401
|
return {
|
|
190402
190402
|
workflow: definition,
|
|
@@ -190407,43 +190407,17 @@ const toOperations = (decoded, root) => {
|
|
|
190407
190407
|
const formatSchemaError = (e) => {
|
|
190408
190408
|
return `Invalid gtd config: ${ArrayFormatter.formatErrorSync(e).map((i) => (i.path.length > 0 ? i.path.join(".") + ": " : "") + i.message).join("; ")}`;
|
|
190409
190409
|
};
|
|
190410
|
-
/**
|
|
190411
|
-
* Auto-init as an explicit capability, separate from config LOADING: the stub
|
|
190412
|
-
* write mutates the repository (a file write plus a commit or amend), so it
|
|
190413
|
-
* must only ever run for a state command that has already passed the
|
|
190414
|
-
* repo-root guard — never at layer-construction time, where it would fire for
|
|
190415
|
-
* `--version`/`--help`, `format`, bare/unknown commands, and root-guard
|
|
190416
|
-
* refusals alike (and from a subdirectory would drop the stub into the wrong
|
|
190417
|
-
* directory). `makeProgram` calls `ensure` right after the repo-root guard;
|
|
190418
|
-
* the in-memory test world provides `ConfigInit.Noop`.
|
|
190419
|
-
*/
|
|
190420
|
-
var ConfigInit = class ConfigInit extends Tag("ConfigInit")() {
|
|
190421
|
-
static Noop = succeed$2(ConfigInit, { ensure: _void });
|
|
190422
|
-
static Live = effect$1(ConfigInit, gen(function* () {
|
|
190423
|
-
const { root } = yield* Cwd;
|
|
190424
|
-
const executor = yield* CommandExecutor;
|
|
190425
|
-
const fs = yield* FileSystem;
|
|
190426
|
-
return { ensure: gen(function* () {
|
|
190427
|
-
if (yield* anyConfigPresent(root)) return;
|
|
190428
|
-
const git = (...args) => executor.exitCode(make$1("git", ...args).pipe(workingDirectory(root)));
|
|
190429
|
-
const writeStub = fs.writeFileString(join(root, ".gtdrc.json"), SCHEMA_STUB).pipe(mapError$1((e) => e instanceof Error ? e : new Error(String(e))));
|
|
190430
|
-
const headSubject = yield* make$1("git", "log", "-1", "--pretty=%s").pipe(workingDirectory(root), string).pipe(map$4((s) => s.trim())).pipe(catchAll(() => succeed$3(void 0)));
|
|
190431
|
-
yield* writeStub;
|
|
190432
|
-
yield* git("add", ".gtdrc.json");
|
|
190433
|
-
if (headSubject !== void 0 && parseStateSubject(headSubject) !== void 0) yield* git("commit", "--amend", "--no-edit");
|
|
190434
|
-
else yield* git("commit", "-m", "chore: add .gtdrc.json");
|
|
190435
|
-
}).pipe(provideService(CommandExecutor, executor), mapError$1((e) => e instanceof Error ? e : new Error(String(e)))) };
|
|
190436
|
-
}));
|
|
190437
|
-
};
|
|
190438
190410
|
var ConfigService = class ConfigService extends Tag("ConfigService")() {
|
|
190439
190411
|
static Live = effect$1(ConfigService, gen(function* () {
|
|
190440
190412
|
const { root } = yield* Cwd;
|
|
190441
|
-
|
|
190442
|
-
|
|
190443
|
-
|
|
190444
|
-
|
|
190445
|
-
|
|
190446
|
-
|
|
190413
|
+
return { load: gen(function* () {
|
|
190414
|
+
const { $schema: _schema, ...cleaned } = yield* loadMerged(root);
|
|
190415
|
+
const decoded = yield* decodeUnknown(ConfigSchema)(cleaned, { onExcessProperty: "error" }).pipe(mapError$1(formatSchemaError)).pipe(mapError$1((msg) => new Error(msg)));
|
|
190416
|
+
return yield* try_({
|
|
190417
|
+
try: () => toOperations(decoded, root),
|
|
190418
|
+
catch: (e) => e instanceof Error ? e : new Error(String(e))
|
|
190419
|
+
});
|
|
190420
|
+
}) };
|
|
190447
190421
|
}));
|
|
190448
190422
|
};
|
|
190449
190423
|
//#endregion
|
|
@@ -191401,6 +191375,49 @@ var WorktreeReader = class WorktreeReader extends Tag("WorktreeReader")() {
|
|
|
191401
191375
|
}));
|
|
191402
191376
|
};
|
|
191403
191377
|
//#endregion
|
|
191378
|
+
//#region src/workflows/advanced.yaml
|
|
191379
|
+
var advanced_default = "# The \"advanced\" gtd workflow template — written verbatim into `.gtdrc.json`\n# (nested under a `workflow:` key) by `gtd init advanced`. See\n# docs/examples/advanced-workflow.md for the full state-by-state walkthrough\n# and STATES.md §10 for how it relates to the \"simple\" template.\n#\n# This is the fuller machine: two-phase human-in-the-loop planning\n# (grilling ⇄ grilling-answer for the product plan, architecting ⇄\n# architecting-answer for the technical plan), task decomposition\n# (decompose → .gtd/tasks/), a deterministic per-task builder queue (picking →\n# building → checking/fixing), agent-prepared review (reviewing → await-review),\n# and a squash finale (squashing → done, a `commit:` state that collapses the\n# whole cycle into one commit whose message the agent authored in\n# .gtd/COMMIT_MSG.md).\n#\n# Every content string below is INLINE (no `./`-relative file references): the\n# bundled templates ship inside a single-file build (`dist/gtd.bundle.mjs`), so\n# their content must already be resolved at BUILD time rather than read from\n# disk at RUNTIME. `advanced.yaml` is imported as raw text (tsdown's `.yaml`\n# text loader / the vitest rawMd transform), so this module never touches the\n# filesystem.\n\nvars:\n testCommand: npm test\n todoFile: .gtd/TODO.md\n architectureFile: .gtd/ARCHITECTURE.md\n reviewFile: .gtd/REVIEW.md\n feedbackFile: .gtd/FEEDBACK.md\n\nstates:\n idle:\n actor: human\n initial: true\n message: |\n No active gtd cycle.\n\n To start one: write `.gtd/TODO.md` with a short sketch of what you want\n built — a few sentences is enough — then run `gtd step human`.\n on:\n \"* **\": grilling\n\n grilling:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n # `model` is an opaque harness hint — gtd never interprets this string, it\n # only passes it through to `gtd next --json`/`gtd status --json` so the\n # driving loop can map it onto whatever models its agent harness\n # provides. \"smart\" here just names the harness-chosen tier for the\n # heavier planning/reviewing turns; states without a `model` use the\n # harness's default.\n model: smart\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state (plans, task specs, review records) — never create, edit, or\n delete anything under `.gtd/` except `<%= it.vars.todoFile %>`, which\n this prompt tells you to write.\n\n `<%= it.vars.todoFile %>` holds the plan under development. Read it,\n then develop it into a concrete, product-level plan in this one turn —\n explore the codebase before asking anything, so every open question is\n one the codebase genuinely cannot answer.\n\n Scope: product and user-facing decisions only. Leave implementation\n details (file/module structure, data models, tech-stack choices) for\n the next phase (`.gtd/ARCHITECTURE.md`).\n\n For every remaining open question, add it under a `## Open Questions`\n heading, one `### <question>` sub-heading each, with a\n `Suggested default: <answer>` line the human can accept as-is. Omit the\n section entirely once there are none.\n\n Leave `.gtd/TODO.md` uncommitted and finish your turn — do not commit,\n and do not run `gtd step agent` yourself; the harness does that.\n on:\n \"* **\": grilling-answer\n\n grilling-answer:\n actor: human\n file: <%= it.vars.todoFile %>\n mode: qa\n message: |\n `<%= it.vars.todoFile %>` holds the plan under development, with any\n open questions under `## Open Questions` — each carrying a suggested\n default.\n\n To answer a question, edit its entry in place (replace\n `Suggested default: ...` with `Answer: ...`, or annotate further), then\n run `gtd step human`.\n\n To accept all suggested defaults as-is and move on to technical\n architecting, run `gtd step human` with no edits.\n on:\n \"C\": architecting\n \"* **\": grilling\n\n architecting:\n actor: agent\n file: <%= it.vars.architectureFile %>\n mode: qa\n model: smart\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.architectureFile %>` and `<%= it.vars.todoFile %>`, which\n this prompt tells you to write and delete respectively.\n\n Read `<%= it.vars.todoFile %>` (the converged product plan) and\n develop `<%= it.vars.architectureFile %>` from it in this one turn:\n file/module structure, data models, library/tech-stack choices,\n error-handling strategy — the *how*, building on the *what* already\n settled. Do not re-open product-level decisions from\n `<%= it.vars.todoFile %>`; treat them as settled.\n\n For every remaining open question, add it under a `## Open Questions`\n heading the same way as the grilling phase (one `### <question>`\n sub-heading, a `Suggested default: <answer>` line). Omit the section\n once there are none.\n\n Once `<%= it.vars.architectureFile %>` is written, delete\n `<%= it.vars.todoFile %>` — its content has been folded in and it must\n not linger.\n\n Leave everything uncommitted and finish your turn.\n on:\n \"* **\": architecting-answer\n\n architecting-answer:\n actor: human\n file: <%= it.vars.architectureFile %>\n mode: qa\n message: |\n `<%= it.vars.architectureFile %>` holds the technical plan under\n development, with any open questions under `## Open Questions`.\n\n To answer a question, edit its entry in place, then run\n `gtd step human`.\n\n To accept all suggested defaults as-is and move on to decomposing the\n work, run `gtd step human` with no edits.\n on:\n \"C\": decompose\n \"* **\": architecting\n\n decompose:\n actor: agent\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except the\n task-spec files this prompt tells you to write (under `.gtd/tasks/`),\n and `.gtd/ARCHITECTURE.md`, which it tells you to delete.\n\n Read `.gtd/ARCHITECTURE.md` (the converged technical plan) and\n decompose it into an ordered set of self-contained task files under\n `.gtd/tasks/` (e.g. `.gtd/tasks/01-short-name.md`,\n `.gtd/tasks/02-short-name.md`, ...). Order them so the test suite can\n pass again after each one lands; each file is self-contained\n (description, acceptance criteria as `- [ ]` checkboxes, relevant\n paths) since a building turn receives only that one file as context.\n\n No task file may reference any other `.gtd/` file, or a requirement to\n preserve/update workflow state — `.gtd/` is machine-managed and gets\n cleaned up as the cycle proceeds.\n\n Once the task files are written, delete `.gtd/ARCHITECTURE.md`. Leave\n everything uncommitted and finish your turn.\n on:\n \"* .gtd/tasks/**\": picking\n\n picking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn (the queue arbiter) — the loop driver executes this\n # verbatim, then steps the check actor. Mechanics only: take the\n # first task file (by name) under .gtd/tasks/ into .gtd/NEXT.md, or\n # remove .gtd/NEXT.md when the queue is empty. What NEXT.md's\n # presence/absence MEANS (another task to build vs. the queue closing\n # out to review) is decided by this state's own `on` rules at capture\n # time — never here.\n set +e\n next=$(ls .gtd/tasks/*.md 2>/dev/null | head -n 1)\n if [ -n \"$next\" ]; then\n printf '%s' \"$next\" > .gtd/NEXT.md\n else\n rm -f .gtd/NEXT.md\n fi\n on:\n \"D .gtd/NEXT.md\": reviewing\n \"* .gtd/NEXT.md\": building\n \"C\": reviewing\n\n building:\n actor: agent\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create or edit anything under `.gtd/` except deleting the\n one task file this prompt tells you to delete; never touch\n `.gtd/NEXT.md` yourself — the `picking` state owns it.\n\n The task file to implement is: <%~ it.read(\".gtd/NEXT.md\") %>\n\n Read that file and implement EXACTLY the one task it describes — no\n more, no less; leave every other task file under `.gtd/tasks/`\n untouched. Use TDD discipline: one test, then the implementation that\n passes it, then the next — never write all the tests first.\n\n Delete the task file once its work is complete and verified. Leave\n everything else uncommitted and finish your turn.\n on:\n \"* **\": checking\n\n checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — the loop driver executes this verbatim, then steps\n # the check actor. Mechanics only: run the test suite and record a\n # red run's output as .gtd/FEEDBACK.md. What that output MEANS (another\n # fix round vs escalation) is decided by the workflow's own `on`/`retry`\n # rules at capture time — never here.\n #\n # `it.vars.testCommand` defaults to \"npm test\" (this workflow's own\n # `vars:` above) — override it with a top-level `.gtdrc`\n # `vars: { testCommand: ... }` key, or a `GTD_VAR_testCommand`\n # environment variable (highest precedence).\n set +e\n mkdir -p .gtd\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output .gtd/FEEDBACK.md\n else\n rm -f .gtd/.check-output\n printf 'npm test failed with exit code %s and produced no output.' \"$code\" > .gtd/FEEDBACK.md\n fi\n else\n rm -f .gtd/.check-output\n fi\n on:\n \"A .gtd/FEEDBACK.md\": fixing\n \"M .gtd/FEEDBACK.md\": fixing\n \"C\": picking\n\n fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n retry:\n max: 3\n otherwise: escalate\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.feedbackFile %>`, which this prompt tells you to address.\n\n Read `<%= it.vars.feedbackFile %>` (the failing test output) and fix\n the code so the suite passes. Keep the change focused — do not\n refactor unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue; the\n machine picks the dispute up either way.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": checking\n\n escalate:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The agent could not get the check to pass after repeated attempts.\n `<%= it.vars.feedbackFile %>` holds the last failing output.\n\n Investigate and fix it yourself (editing code and/or\n `<%= it.vars.feedbackFile %>`), then run `gtd step human` to try the\n check again.\n on:\n \"* **\": checking\n\n reviewing:\n actor: agent\n file: <%= it.vars.reviewFile %>\n mode: review\n model: smart\n # A direct entry point: `gtd review <commitish>` starts a fresh review\n # process here over `<commitish>..HEAD` (e.g. a colleague's PR branch).\n reviewEntry: true\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.reviewFile %>`, which this prompt tells you to write.\n\n Write `<%= it.vars.reviewFile %>` to help a human review the cycle's\n full diff (inlined below): group hunks semantically (same\n feature/refactor/fix, even across files), and for each group write a\n short heading, an explanation of what changed and why, and\n `- [ ] ./path/to/file.ts#42` style file pointers (checkboxes are for\n the human to tick, not you).\n\n Leave `<%= it.vars.reviewFile %>` uncommitted and finish your turn.\n <% if (it.processDiff.trim()) { %>\n\n ## Full cycle diff\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"* .gtd/REVIEW.md\": await-review\n\n await-review:\n actor: human\n file: <%= it.vars.reviewFile %>\n mode: review\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the completed\n cycle.\n\n To approve: delete `<%= it.vars.reviewFile %>` and run\n `gtd step human`.\n\n To request changes: edit `<%= it.vars.reviewFile %>` (or the code)\n with what's wrong — including just ticking its checkboxes — then run\n `gtd step human`; this sends the cycle back through grilling with your\n feedback as the new input.\n on:\n \"D .gtd/REVIEW.md\": squashing\n \"* **\": grilling\n\n squashing:\n actor: agent\n prompt: |\n You are an autonomous coding agent. The cycle is approved and done.\n `.gtd/` holds this workflow's own state — never create, edit, or delete\n anything under `.gtd/` except `.gtd/COMMIT_MSG.md`, which this prompt\n tells you to write.\n\n Write `.gtd/COMMIT_MSG.md` with ONE conventional-commits message for\n the entire cycle (subject line `type(scope): subject`, imperative mood,\n <= 72 characters; a body explaining the why, trade-offs, and key\n decisions). Plain text, no markdown wrapper.\n\n Leave `.gtd/COMMIT_MSG.md` uncommitted and finish your turn — entering\n this file squashes the whole cycle into one commit using its content as\n the message.\n <% if (it.processDiff.trim()) { %>\n\n ## Full cycle diff\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"A .gtd/COMMIT_MSG.md\": done\n \"M .gtd/COMMIT_MSG.md\": done\n\n done:\n commit: |\n <%~ it.read(\".gtd/COMMIT_MSG.md\") %>\n\n Token cost: <%= it.processCost %>\n <% it.processCostByModel.forEach(function(m){ %>\n - <%= m.model %>: <%= m.cost %>\n <% }) %>\n";
|
|
191380
|
+
//#endregion
|
|
191381
|
+
//#region src/workflows/simple.yaml
|
|
191382
|
+
var simple_default = "# The \"simple\" gtd workflow template — written verbatim into `.gtdrc.json`\n# (nested under a `workflow:` key) by `gtd init simple`. gtd ships no default\n# workflow: a repo picks one of the bundled templates with `gtd init`, and a\n# state command run with no workflow configured fails (see src/Config.ts).\n# See STATES.md §10 for the walkthrough and docs/examples/advanced-workflow.md\n# for the fuller `advanced` template.\n#\n# Compiled through the exact same `compileWorkflowConfig` a user's `.gtdrc`\n# `workflow:` key goes through — no privileged code path\n# (src/workflows/templates.ts does the compiling).\n#\n# Every content string below is INLINE (no `./`-relative file references):\n# the bundled templates ship inside a single-file build\n# (`dist/gtd.bundle.mjs`), so their content must already be resolved at BUILD\n# time rather than read from disk at RUNTIME — the same reason\n# `src/prompts/*.md` are imported through tsdown's `.md`-as-text loader\n# instead of read via `fs` at runtime. `simple.yaml` itself is imported the\n# same way (tsdown/vitest both treat `.yaml` as text — see\n# tsdown.config.ts / tests/vitest.rawMd.ts), so this file never needs a\n# sibling prompts/ directory to exist on disk once installed.\n#\n# States, in cycle order — 10 states, no squash:\n# idle (initial, human, message)\n# -> grilling (agent, prompt): reads .gtd/TODO.md's sketch (or a prior\n# lap's feedback), explores the codebase, and develops it into a concrete\n# implementation plan, stating any open question it can't settle itself\n# under a `## Open Questions` heading rather than guessing silently. Its\n# `file:`/`mode:` (TODO.md/`qa`) makes the plan's format checkable — it\n# self-validates before finishing (see \"Steering-file validation\" below).\n# grilling -> grilling-answer (human, message): tick off suggested\n# defaults by replacing them with `Answer:` lines; a clean step accepts\n# every remaining default -> building; any edit loops back to grilling.\n# grilling-answer -> building (agent, prompt): implements the plan in\n# .gtd/TODO.md directly (no task decomposition, no per-task queue) and\n# deletes .gtd/TODO.md once the work is complete and verified.\n# building -> checking (check, script): runs the test command inline;\n# A/M .gtd/FEEDBACK.md -> fixing (capped, redirects to escalate); D\n# .gtd/FEEDBACK.md (a green run that just cleaned up prior feedback) or C\n# (green, nothing to clean) -> reviewing.\n# fixing (agent, prompt) <-> checking\n# escalate (human, message): manual intervention, then back to checking\n# checking (green) -> reviewing (agent, prompt): writes .gtd/REVIEW.md in\n# the checkbox review format, grouping the cycle's full diff into\n# reviewable chunks; its `file:`/`mode:` (REVIEW.md/`review`) makes that\n# format checkable, so it self-validates before finishing. `reviewing`\n# ALSO declares `reviewEntry: true` — a second way in: `gtd review\n# <commitish>` (a clean tree resting at idle) writes an empty\n# `gtd(human): reviewing` entry commit carrying <commitish>'s hash as a\n# `Gtd-Review-Base:` trailer, starting a brand new process that reviews\n# <commitish>..HEAD (e.g. a colleague's PR branch) through this exact same\n# reviewing/await-review/feedback-lap machinery.\n# reviewing -> await-review (human, message): tick a pointer's `- [\n# ]` to `- [x]` to approve that item; deleting .gtd/REVIEW.md outright is\n# the power-user approve-everything shortcut; anything else pending\n# (code edits with REVIEW.md untouched) is feedback straight to grilling.\n# await-review -> review-deciding (check, script), entered by any `M\n# .gtd/REVIEW.md` step: if no unticked `- [ ]` pointer remains, the cycle\n# is approved (rm .gtd/REVIEW.md -> idle); otherwise the still-unticked\n# pointers (with their notes) are extracted into a fresh .gtd/TODO.md and\n# .gtd/REVIEW.md is removed (-> grilling, feedback lap).\n#\n# The cycle ends at human approval, resting back at idle — there is no\n# squash state. The cycle's commits stay in history; whether/how to squash\n# them (amend, interactive rebase, a PR's squash-merge) is entirely the\n# human's business once they're done reviewing — gtd makes no assumption.\n# That squash-flavored finale (a `squashing` prompt state + a `done` commit\n# state) still exists as an ENGINE capability (see STATES.md §8) and is kept\n# as part of the fuller machine at docs/examples/advanced-workflow.md.\n#\n# A `gtd(<actor>): idle` commit — entering the workflow's own initial state —\n# is therefore a PROCESS BOUNDARY (see `computeProcessRun` in src/Edge.ts):\n# the next cycle's `retry` counts, `startCommit`, and diffs never reach back\n# across an approved cycle's own idle-entering commit.\n#\n# HYGIENE INVARIANT (asserted in e2e — see default-workflow.feature): an\n# approved cycle leaves `.gtd/` empty. FEEDBACK.md is cleaned up by a green\n# `checking` run; REVIEW.md by the review-deciding approval branch; TODO.md by\n# `building`.\n#\n# Steering-file validation. `grilling` and `reviewing` each declare a\n# `file:`/`mode:` pair (TODO.md as `qa`, REVIEW.md as `review`), so their\n# output has a checkable format. src/OpenQuestions.ts and src/ReviewDoc.ts are\n# those two formats' EXECUTABLE SPECS (their unit tests are the format spec\n# tests), and `gtd validate` FORMATS the resolved state's rendered `file:` in\n# place then runs the SAME parser over it, exiting non-zero with findings when\n# it violates the format. There is no in-machine validation state and no bash\n# port to keep in sync — the producing agent self-validates before finishing\n# (plain `gtd next` appends the \"run `gtd validate` and fix all violations\"\n# instruction; under `--json` the driving loop runs it after the turn and\n# re-prompts on findings — see skills/loop/SKILL.md), AND `gtd step` runs the\n# same format-and-validate gate on capture, refusing a turn whose steering file\n# is invalid. So the check runs after a human edits too — at the human gates\n# `grilling-answer` (TODO.md) and `await-review` (REVIEW.md) — and a human gate\n# is only ever handed a tidy, well-formed file. The same parsers back the LSP's\n# live diagnostics (src/Lsp.ts).\n#\n# `grilling` and `reviewing` both declare `model: smart` — an opaque harness\n# hint (see the comment at `grilling` below) for their heavier one-shot\n# planning/reviewing turns. Every other state is unset (harness default).\n#\n# The four agent states each declare a `memory:` scope label (`plan`,\n# `build`, `fix`, `review` — see the comment at `grilling` below) — another\n# opaque hint a memory-aware driver reads to decide when an agent turn keeps\n# the previous turn's memory and when it starts fresh: a loop that re-enters\n# one state (grilling around its answer loop, fixing around checking) keeps\n# the same label and retains memory across its laps; crossing to the next\n# phase's differently-labelled state clears it. gtd never interprets the\n# label (see STATES.md §1 and skills/loop/SKILL.md's driver contract).\n#\n# `vars.testCommand` is this workflow's own declared default for `checking`'s\n# script (see it.vars in PatternTemplates.ts) — override it per-repo with a\n# top-level `.gtdrc` `vars: { testCommand: ... }` key, or per-invocation with\n# a `GTD_VAR_testCommand` environment variable (highest precedence).\n#\n# `vars.todoFile`/`reviewFile`/`feedbackFile` are this workflow's ONE source\n# of truth for the three steering-file paths every prompt/script below reads\n# or writes — each appears in templates as `<%~ it.vars.todoFile %>` etc\n# (see docs/design/state-file-association.md §2). Every state that rests on\n# one of these files also declares a `file:`/`mode:` pair (see STATES.md's\n# property table) so `gtd status`/`gtd next --json` and the LSP (`gtd lsp`)\n# know which file a human/editor should look at, and in what format\n# (`qa` for TODO.md's open-questions format, `review` for REVIEW.md's\n# checkbox format) — `fixing`/`escalate` declare `file:` alone (FEEDBACK.md\n# is plain text, no LSP format). KNOWN LIMITATION: `on` pattern keys below are\n# NOT Eta templates — they keep the LITERAL `.gtd/...` paths matching these\n# vars' DEFAULT values. Repointing `todoFile`/`reviewFile`/`feedbackFile` via\n# `.gtdrc`'s top-level `vars:` or a `GTD_VAR_` override changes what `file:`\n# renders to (and what a template reads/writes) WITHOUT changing what the\n# `on` patterns match against — desyncing the machine. See\n# docs/configuration.md's `file:`/`mode:` section for the full caveat.\n#\n# A fuller machine — two-phase Q&A planning, an architecture phase, task\n# decomposition, a deterministic per-task builder loop, agent-prepared\n# review, and the squash finale (`squashing` + `done`) — is preserved as a\n# copy-paste `.gtdrc` example at docs/examples/advanced-workflow.md.\n\nvars:\n testCommand: npm test\n todoFile: .gtd/TODO.md\n reviewFile: .gtd/REVIEW.md\n feedbackFile: .gtd/FEEDBACK.md\n\nstates:\n idle:\n actor: human\n initial: true\n # Every human-gate `message:` below closes with the SAME rendered block —\n # a \"What each change does next\" list built from `it.edges`, the resting\n # state's own `on` edges (see PatternTemplates.TemplateContext.edges). Each\n # edge's human-readable route lives in its `describe:` (the single source\n # of truth), so the list can never drift from the routing it documents.\n # The block skips edges without a `describe`, and each `<%~ ... \"\\n\" %>`\n # keeps the bullet's own newline (Eta's autoTrim strips the template's own\n # line breaks around `<% %>` tags, not text inside an interpolation).\n message: |\n No active gtd cycle.\n\n To start one, write `.gtd/TODO.md` with a short sketch of what you want\n built — a few sentences is enough.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: grilling\n describe: >-\n Save your sketch to `.gtd/TODO.md` (any working-tree change) to start\n the cycle — the agent reads it, explores the codebase, and develops it\n into a concrete implementation plan (**grilling**).\n\n grilling:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n # `model` is an opaque harness hint — gtd never interprets this string, it\n # only passes it through to `gtd next --json`/`gtd status --json` so the\n # driving loop can map it onto whatever models its agent harness\n # provides. \"smart\" here just names the harness-chosen tier for the\n # heavier planning turn; states without a `model` use the harness's\n # default.\n model: smart\n # `memory` is an opaque memory-scope label — gtd never interprets it, it\n # only passes it through to `gtd next --json`/`gtd status --json` so a\n # memory-aware driving loop can decide when an agent turn continues from\n # the previous turn's memory and when it starts fresh: consecutive agent\n # turns that emit the SAME label share memory, a change (or the first\n # turn) is where the driver starts fresh. `grilling` re-enters itself\n # around the grilling-answer loop, so labelling every lap\n # `plan` keeps the planner's accumulated exploration across the loop; the\n # next phase (`building`) carries a different label, clearing it.\n memory: plan\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.todoFile %>`, which this prompt tells you to develop.\n\n `<%= it.vars.todoFile %>` holds a short sketch of what to build (or\n feedback from a prior review lap). Read it, explore the codebase, and\n develop it into a concrete implementation plan.\n\n For every question you cannot settle yourself, add it under a\n `## Open Questions` heading, one `### <question>` sub-heading each,\n whose body's FIRST non-blank line is exactly\n `Suggested default: <your answer>` — the human can accept it as-is or\n override it. Omit the section entirely once there are no open\n questions. If `<%= it.vars.todoFile %>` already carries\n `## Open Questions` entries from a previous lap, fold any\n `Answer: <text>` line into the plan; every remaining entry's body must\n still start with either `Suggested default: ...` or `Answer: ...` —\n never anything else.\n\n Leave `<%= it.vars.todoFile %>` uncommitted and finish your turn — do\n not commit, and do not run `gtd step agent` yourself; the harness does\n that.\n on:\n \"* **\": grilling-answer\n\n grilling-answer:\n actor: human\n file: <%= it.vars.todoFile %>\n mode: qa\n message: |\n `<%= it.vars.todoFile %>` holds the plan under development, with any\n open questions under `## Open Questions` — each carrying a suggested\n default. Answer a question by editing its entry in place: replace its\n `Suggested default: ...` line with `Answer: ...`, or add further detail.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"C\":\n to: building\n describe: >-\n Change nothing to accept every suggested default as-is and move on to\n implementation (**building**).\n \"* **\":\n to: grilling\n describe: >-\n Edit the plan — answer a question, add detail, or raise a new one — to\n send it back for another round of grilling (**grilling**).\n\n building:\n actor: agent\n file: <%= it.vars.todoFile %>\n mode: qa\n # A distinct memory scope from `plan`: implementation starts fresh, with\n # the plan already settled in `.gtd/TODO.md` (which this prompt reads\n # below) rather than carried in the planner's working memory.\n memory: build\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create or edit anything under `.gtd/` except deleting\n `<%= it.vars.todoFile %>`, which this prompt tells you to delete once\n its work is complete.\n\n The plan to implement is: <%~ it.read(it.vars.todoFile) %>\n\n Read that plan and implement it in full. Use TDD discipline: one test,\n then the implementation that passes it, then the next — never write\n all the tests first.\n\n Delete `<%= it.vars.todoFile %>` once the work it describes is complete\n and verified. Leave everything else uncommitted and finish your turn.\n on:\n \"* **\": checking\n\n checking:\n actor: check\n script: |\n #!/usr/bin/env bash\n # gtd check turn — the loop driver executes this verbatim, then\n # steps the check actor. Mechanics only: run the test suite and record a\n # red run's output as .gtd/FEEDBACK.md. What that output MEANS (another\n # fix round vs escalation) is decided by the workflow's own `on`/`retry`\n # rules at capture time — never here.\n #\n # `it.vars.testCommand` defaults to \"npm test\" (this workflow's own\n # `vars:` above) — override it with a top-level `.gtdrc`\n # `vars: { testCommand: ... }` key, or a `GTD_VAR_testCommand`\n # environment variable (highest precedence).\n #\n # A green run also removes any `<%= it.vars.feedbackFile %>` left over\n # from an earlier red run — there is no squash to discard it for us\n # anymore, so the script cleans it up itself before moving on to\n # review.\n set +e\n mkdir -p .gtd\n <%~ it.vars.testCommand %> > .gtd/.check-output 2>&1\n code=$?\n if [ \"$code\" -ne 0 ]; then\n if [ -s .gtd/.check-output ]; then\n mv .gtd/.check-output <%~ it.vars.feedbackFile %>\n else\n rm -f .gtd/.check-output\n printf 'npm test failed with exit code %s and produced no output.' \"$code\" > <%~ it.vars.feedbackFile %>\n fi\n else\n rm -f .gtd/.check-output\n rm -f <%~ it.vars.feedbackFile %>\n fi\n on:\n \"A .gtd/FEEDBACK.md\": fixing\n \"M .gtd/FEEDBACK.md\": fixing\n \"D .gtd/FEEDBACK.md\": reviewing\n \"C\": reviewing\n\n fixing:\n actor: agent\n file: <%= it.vars.feedbackFile %>\n # Its own memory scope: the fix loop re-enters `fixing` around `checking`\n # (up to the retry cap), so labelling every attempt `fix` lets the agent\n # remember what it already tried across the loop; a green check moves on\n # to the differently-labelled `reviewing`, clearing it.\n memory: fix\n retry:\n max: 3\n otherwise: escalate\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.feedbackFile %>`, which this prompt tells you to address.\n\n Read `<%= it.vars.feedbackFile %>` (the failing test output) and fix\n the code so the suite passes. Keep the change focused — do not\n refactor unrelated code. If the feedback is wrong, empty or delete\n `<%= it.vars.feedbackFile %>` instead of \"fixing\" a non-issue; the\n machine picks the dispute up either way.\n\n Leave everything uncommitted and finish your turn — do not commit.\n on:\n \"* **\": checking\n\n escalate:\n actor: human\n file: <%= it.vars.feedbackFile %>\n message: |\n The agent could not get the check to pass after repeated attempts.\n `<%= it.vars.feedbackFile %>` holds the last failing output.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"* **\":\n to: checking\n describe: >-\n Fix the failure yourself — edit the code and/or `.gtd/FEEDBACK.md` —\n to re-run the check (**checking**).\n\n reviewing:\n actor: agent\n file: <%= it.vars.reviewFile %>\n mode: review\n model: smart\n # A distinct memory scope again: the reviewer looks at the cycle's full\n # diff (inlined into its prompt) with a fresh perspective, not the\n # builder's or fixer's working memory.\n memory: review\n # `gtd review <commitish>` (a plain non-gtd branch, e.g. a colleague's PR)\n # enters here directly, recording the resolved base as a `Gtd-Review-Base:`\n # trailer on its empty entry commit — everything below then reuses the\n # existing reviewing/await-review/feedback-lap machinery unmodified over\n # <commitish>..HEAD (see STATES.md §11).\n reviewEntry: true\n prompt: |\n You are an autonomous coding agent. `.gtd/` holds this workflow's own\n state — never create, edit, or delete anything under `.gtd/` except\n `<%= it.vars.reviewFile %>`, which this prompt tells you to write.\n\n Write `<%= it.vars.reviewFile %>` to help a human review the cycle's\n full diff (inlined below) in this EXACT format:\n\n - First non-blank line: `# Review: <%= it.currentCommit.slice(0, 7) %>`\n - Somewhere in the document: `<!-- base: <%= it.startCommit %> -->`\n - At least one `## <Chunk Title>` heading, grouping hunks semantically\n (same feature/refactor/fix, even across files) — a short explanation\n of what changed and why, then one\n `- [ ] ./path/to/file.ts#42 — note` file pointer per hunk (checkboxes\n are for the human to tick, not you; every path is `./`-relative, with\n an optional `#line` and an optional ` — note`).\n\n Leave `<%= it.vars.reviewFile %>` uncommitted and finish your turn.\n <% if (it.processDiff.trim()) { %>\n\n ## Full cycle diff\n\n ```diff\n <%~ it.processDiff %>\n ```\n <% } %>\n on:\n \"* **\": await-review\n\n await-review:\n actor: human\n file: <%= it.vars.reviewFile %>\n mode: review\n # While the cycle rests here for human review, gtd opens a \"review checkout\n # window\": HEAD and the index are temporarily rewound to this cycle's start\n # (the process boundary — no `reviewBase` state is declared, so the base\n # defaults to it) with the working tree untouched, so the whole cycle diff\n # surfaces as ordinary uncommitted changes in the editor's git integration.\n # The window closes automatically on the next invocation, once the cycle\n # rests anywhere else (see src/ReviewWindow.ts, STATES.md §11).\n reviewWindow: true\n message: |\n `<%= it.vars.reviewFile %>` holds the review record for the completed\n cycle — one `- [ ]` checkbox per reviewable item, grouped into chunks.\n\n What each change does next (then run `gtd step human`):\n <% it.edges.forEach(function (e) { if (e.describe) { %>\n <%~ \"- \" + e.describe + \"\\n\" %>\n <% } }) %>\n on:\n \"D .gtd/REVIEW.md\":\n to: idle\n describe: >-\n Delete `.gtd/REVIEW.md` outright to approve the whole cycle at once\n and rest back at the start (**idle**).\n \"M .gtd/REVIEW.md\":\n to: review-deciding\n describe: >-\n Edit `.gtd/REVIEW.md` — tick a `- [ ]` box to `- [x]` to approve that\n item, or add notes — to hand it to a deterministic check\n (**review-deciding**) that closes the cycle out when every box is\n ticked, or extracts the still-unticked items into a fresh\n `.gtd/TODO.md` and loops back through **grilling** with that feedback.\n \"* **\":\n to: grilling\n describe: >-\n Change only code, leaving `.gtd/REVIEW.md` untouched, to send that as\n feedback straight back to **grilling**.\n\n review-deciding:\n actor: check\n file: <%= it.vars.reviewFile %>\n mode: review\n script: |\n #!/usr/bin/env bash\n # gtd check turn — the loop driver executes this verbatim, then\n # steps the check actor. Mechanics only: if .gtd/REVIEW.md still has any\n # unticked \"- [ ]\" pointer, extract those pointers (with their notes)\n # into a fresh .gtd/TODO.md; either way, remove .gtd/REVIEW.md. What\n # the resulting diff MEANS (feedback lap vs. full approval) is decided\n # by this state's own `on` rules at capture time — never here. The\n # A/M .gtd/TODO.md row is declared BEFORE the D .gtd/REVIEW.md row (see\n # this workflow's `on` map below) so a feedback lap — which always also\n # deletes .gtd/REVIEW.md — is captured as feedback, not mistaken for an\n # approval.\n set +e\n mkdir -p .gtd\n if grep -qE '^[[:space:]]*-[[:space:]]*\\[[[:space:]]*\\]' <%~ it.vars.reviewFile %> 2>/dev/null; then\n {\n echo \"Feedback from review — address these before continuing:\"\n echo\n grep -E '^[[:space:]]*-[[:space:]]*\\[[[:space:]]*\\]' <%~ it.vars.reviewFile %>\n } > <%~ it.vars.todoFile %>\n fi\n rm -f <%~ it.vars.reviewFile %>\n on:\n \"A .gtd/TODO.md\": grilling\n \"M .gtd/TODO.md\": grilling\n \"D .gtd/REVIEW.md\": idle\n \"C\": await-review\n";
|
|
191383
|
+
//#endregion
|
|
191384
|
+
//#region src/workflows/templates.ts
|
|
191385
|
+
/**
|
|
191386
|
+
* The `$schema` URL editors resolve for `.gtdrc.json` completion/validation —
|
|
191387
|
+
* written as the first key of every `gtd init` config (see `renderInitConfig`).
|
|
191388
|
+
*/
|
|
191389
|
+
const SCHEMA_URL = "https://raw.githubusercontent.com/pmelab/gtd/main/schema.json";
|
|
191390
|
+
/**
|
|
191391
|
+
* The bundled workflow templates `gtd init <name>` can scaffold, keyed by the
|
|
191392
|
+
* name the user passes. Each value is the raw YAML text of the template file,
|
|
191393
|
+
* imported as a string (tsdown's `.yaml` text loader / the vitest `rawMd`
|
|
191394
|
+
* transform — see tsdown.config.ts / tests/vitest.rawMd.ts), so this module
|
|
191395
|
+
* never touches the filesystem: it works identically in the dev checkout,
|
|
191396
|
+
* under `vitest`, and inside the single-file `dist/gtd.bundle.mjs` build.
|
|
191397
|
+
*/
|
|
191398
|
+
const WORKFLOW_TEMPLATES = {
|
|
191399
|
+
simple: simple_default,
|
|
191400
|
+
advanced: advanced_default
|
|
191401
|
+
};
|
|
191402
|
+
/** The template names as a plain array, for usage messages and validation. */
|
|
191403
|
+
const WORKFLOW_TEMPLATE_NAMES = Object.keys(WORKFLOW_TEMPLATES);
|
|
191404
|
+
const isWorkflowTemplateName = (name) => Object.prototype.hasOwnProperty.call(WORKFLOW_TEMPLATES, name);
|
|
191405
|
+
/**
|
|
191406
|
+
* Render the `.gtdrc.json` `gtd init <name>` writes: a `$schema` link (so
|
|
191407
|
+
* editors pick up completion/validation) plus the template's whole workflow
|
|
191408
|
+
* object nested under a `workflow:` key — exactly the shape a hand-authored
|
|
191409
|
+
* `.gtdrc` `workflow:` value takes (`{ vars, states }`). The template's
|
|
191410
|
+
* multi-line prompts/scripts become `\n`-escaped JSON strings; the config
|
|
191411
|
+
* loader parses them back identically to the YAML source.
|
|
191412
|
+
*/
|
|
191413
|
+
const renderInitConfig = (name) => {
|
|
191414
|
+
const workflow = (0, import_dist$1.parse)(WORKFLOW_TEMPLATES[name]);
|
|
191415
|
+
return JSON.stringify({
|
|
191416
|
+
$schema: SCHEMA_URL,
|
|
191417
|
+
workflow
|
|
191418
|
+
}, null, 2) + "\n";
|
|
191419
|
+
};
|
|
191420
|
+
//#endregion
|
|
191404
191421
|
//#region node_modules/eta/dist/index.mjs
|
|
191405
191422
|
var EtaError = class extends Error {
|
|
191406
191423
|
constructor(message) {
|
|
@@ -191952,6 +191969,23 @@ const parseCostTrailers = (messages) => {
|
|
|
191952
191969
|
}
|
|
191953
191970
|
return entries;
|
|
191954
191971
|
};
|
|
191972
|
+
const REVIEW_BASE_TRAILER_PREFIX = "Gtd-Review-Base: ";
|
|
191973
|
+
const REVIEW_BASE_TRAILER_RE = /^Gtd-Review-Base:[ \t]*(\S+)[ \t]*$/m;
|
|
191974
|
+
/**
|
|
191975
|
+
* Append a `Gtd-Review-Base: <base>` trailer (after a blank line) to a commit
|
|
191976
|
+
* `subject` — the entry commit `gtd review <commitish>` writes to start a new
|
|
191977
|
+
* review process. Mirrors `withCostTrailer`'s placement: the subject (first
|
|
191978
|
+
* line) is untouched, so `parseStateSubject`/`resolveState` read it back
|
|
191979
|
+
* exactly like any other turn commit.
|
|
191980
|
+
*/
|
|
191981
|
+
const withReviewBaseTrailer = (subject, base) => `${subject}\n\n${REVIEW_BASE_TRAILER_PREFIX}${base}`;
|
|
191982
|
+
/**
|
|
191983
|
+
* The `Gtd-Review-Base: <hash>` trailer recorded on a `gtd review <commitish>`
|
|
191984
|
+
* entry commit (see `withReviewBaseTrailer`), or `undefined` when `message`
|
|
191985
|
+
* carries none. Read back by `computeProcessRun` — ONLY off the process's
|
|
191986
|
+
* first (oldest) commit — to override the run's diff base.
|
|
191987
|
+
*/
|
|
191988
|
+
const parseReviewBaseTrailer = (message) => REVIEW_BASE_TRAILER_RE.exec(message)?.[1];
|
|
191955
191989
|
/** The total token cost across the given entries (`0` when none). */
|
|
191956
191990
|
const totalCostOf = (entries) => entries.reduce((sum, entry) => sum + entry.cost, 0);
|
|
191957
191991
|
/** Per-model token totals, highest-cost first (ties broken by model name for a stable order). */
|
|
@@ -191968,7 +192002,7 @@ const normalizeStatus = (raw) => raw === "A" ? "A" : raw === "D" ? "D" : "M";
|
|
|
191968
192002
|
/** Resolve HEAD's subject against the active workflow definition (the bundled default, or a compiled `.gtdrc` `workflow:` key). */
|
|
191969
192003
|
const resolveRest = () => gen(function* () {
|
|
191970
192004
|
const git = yield* GitService;
|
|
191971
|
-
const def = (yield* ConfigService).workflow;
|
|
192005
|
+
const def = (yield* (yield* ConfigService).load).workflow;
|
|
191972
192006
|
const state = resolveState(def, (yield* git.hasCommits()) ? yield* git.lastCommitSubject() : "");
|
|
191973
192007
|
const stateDef = def.states[state];
|
|
191974
192008
|
if (stateDef.actor === void 0) return yield* fail$2(/* @__PURE__ */ new Error(`gtd: resolved at commit state "${state}" — a process never rests there`));
|
|
@@ -192007,6 +192041,7 @@ const computeProcessRun = (git, def) => gen(function* () {
|
|
|
192007
192041
|
if (!(yield* git.hasCommits())) return {
|
|
192008
192042
|
startHash: "",
|
|
192009
192043
|
startParentHash: EMPTY_TREE$1,
|
|
192044
|
+
diffBase: EMPTY_TREE$1,
|
|
192010
192045
|
trace: [],
|
|
192011
192046
|
costEntries: []
|
|
192012
192047
|
};
|
|
@@ -192026,6 +192061,7 @@ const computeProcessRun = (git, def) => gen(function* () {
|
|
|
192026
192061
|
return {
|
|
192027
192062
|
startHash: startIdx < history.length ? history[startIdx].hash : history[history.length - 1].hash,
|
|
192028
192063
|
startParentHash,
|
|
192064
|
+
diffBase: (processCommits.length > 0 ? parseReviewBaseTrailer(processCommits[0].message) : void 0) ?? startParentHash,
|
|
192029
192065
|
trace,
|
|
192030
192066
|
costEntries
|
|
192031
192067
|
};
|
|
@@ -192078,7 +192114,7 @@ const buildTemplateContext = (git, read, state, actor, run, vars, edges, current
|
|
|
192078
192114
|
const hasCommits = yield* git.hasCommits();
|
|
192079
192115
|
const currentCommit = hasCommits ? yield* git.resolveRef("HEAD") : "";
|
|
192080
192116
|
const previousCommit = hasCommits ? yield* git.resolveRef("HEAD~1").pipe(catchAll(() => succeed$3(run.startParentHash))) : "";
|
|
192081
|
-
const processDiff = [yield* git.diffRef(run.
|
|
192117
|
+
const processDiff = [yield* git.diffRef(run.diffBase).pipe(catchAll(() => succeed$3(""))), yield* git.diffHead().pipe(catchAll(() => succeed$3("")))].filter((d) => d.trim().length > 0).join("\n\n");
|
|
192082
192118
|
const lastDiff = run.trace.length > 0 ? yield* git.commitDiff(currentCommit).pipe(catchAll(() => succeed$3(""))) : "";
|
|
192083
192119
|
const stepEntry = currentCost > 0 || currentModel !== void 0 ? [{
|
|
192084
192120
|
cost: currentCost,
|
|
@@ -192086,7 +192122,7 @@ const buildTemplateContext = (git, read, state, actor, run, vars, edges, current
|
|
|
192086
192122
|
}] : [];
|
|
192087
192123
|
const allCostEntries = [...run.costEntries, ...stepEntry];
|
|
192088
192124
|
return {
|
|
192089
|
-
startCommit: run.
|
|
192125
|
+
startCommit: run.diffBase,
|
|
192090
192126
|
currentCommit,
|
|
192091
192127
|
previousCommit,
|
|
192092
192128
|
state,
|
|
@@ -192289,10 +192325,12 @@ const closeReviewWindow = gen(function* () {
|
|
|
192289
192325
|
* subcommand.
|
|
192290
192326
|
*
|
|
192291
192327
|
* The base is the most-recent in-process `reviewBase` commit
|
|
192292
|
-
* (`reviewBaseHash`) or, absent any such state, the process
|
|
192293
|
-
* (`
|
|
192294
|
-
*
|
|
192295
|
-
*
|
|
192328
|
+
* (`reviewBaseHash`) or, absent any such state, the process's diff base
|
|
192329
|
+
* (`run.diffBase` — the process start, `startParentHash`, unless a
|
|
192330
|
+
* `gtd review <commitish>` entry commit overrode it via a `Gtd-Review-Base:`
|
|
192331
|
+
* trailer, see `computeProcessRun`). When that resolves to the empty tree (a
|
|
192332
|
+
* process with no prior commit) or to HEAD itself (an empty process), there
|
|
192333
|
+
* is nothing to surface and the window stays closed.
|
|
192296
192334
|
*
|
|
192297
192335
|
* Ordering is crash-safe: base ref → head ref → mixed reset → `.gtd/` index
|
|
192298
192336
|
* pin → intent-to-add. A crash before the head-ref write leaves only a stale
|
|
@@ -192301,11 +192339,11 @@ const closeReviewWindow = gen(function* () {
|
|
|
192301
192339
|
*/
|
|
192302
192340
|
const openReviewWindow = gen(function* () {
|
|
192303
192341
|
const git = yield* GitService;
|
|
192304
|
-
const def = (yield* ConfigService).workflow;
|
|
192342
|
+
const def = (yield* (yield* ConfigService).load).workflow;
|
|
192305
192343
|
if (!(yield* git.hasCommits())) return { opened: false };
|
|
192306
192344
|
if (!isReviewWindowState(def, resolveState(def, yield* git.lastCommitSubject()))) return { opened: false };
|
|
192307
192345
|
const run = yield* computeProcessRun(git, def);
|
|
192308
|
-
const base = (yield* reviewBaseHash(git, def, run)) ?? run.
|
|
192346
|
+
const base = (yield* reviewBaseHash(git, def, run)) ?? run.diffBase;
|
|
192309
192347
|
const headHash = yield* git.resolveRef("HEAD");
|
|
192310
192348
|
if (base === EMPTY_TREE || base === headHash) return { opened: false };
|
|
192311
192349
|
yield* git.updateRef(REVIEW_BASE_REF, base);
|
|
@@ -203842,7 +203880,7 @@ const worktreeLayerForRoot = (root) => WorktreeReader.Live.pipe(provide(Cwd.laye
|
|
|
203842
203880
|
*/
|
|
203843
203881
|
const loadModeMap = async (root, onWarn) => {
|
|
203844
203882
|
try {
|
|
203845
|
-
const config = await runPromise(ConfigService.pipe(provide$1(configLayerForRoot(root))));
|
|
203883
|
+
const config = await runPromise(ConfigService.pipe(flatMap$4((c) => c.load), provide$1(configLayerForRoot(root))));
|
|
203846
203884
|
const vars = resolveVars(config.workflowVars, config.rcVars, process.env);
|
|
203847
203885
|
const { map, warnings } = buildFileModeMap(config.workflow, vars, root);
|
|
203848
203886
|
for (const warning of warnings) onWarn(warning);
|
|
@@ -203861,7 +203899,7 @@ const loadModeMap = async (root, onWarn) => {
|
|
|
203861
203899
|
*/
|
|
203862
203900
|
const resolveSteeringFile = (root) => gen(function* () {
|
|
203863
203901
|
const git = yield* GitService;
|
|
203864
|
-
const config = yield* ConfigService;
|
|
203902
|
+
const config = yield* (yield* ConfigService).load;
|
|
203865
203903
|
const worktree = yield* WorktreeReader;
|
|
203866
203904
|
const envVars = yield* EnvVars;
|
|
203867
203905
|
const rest = yield* resolveRest();
|
|
@@ -204135,12 +204173,21 @@ const GTD_VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
|
204135
204173
|
const HELP_TEXT = `Usage: gtd [command] [options]
|
|
204136
204174
|
|
|
204137
204175
|
Commands:
|
|
204176
|
+
init <workflow> Scaffold a .gtdrc.json for this repo with the chosen
|
|
204177
|
+
bundled workflow inline (one of: simple, advanced). Run
|
|
204178
|
+
once per repo; refuses if a gtd config already exists.
|
|
204179
|
+
Leaves the file uncommitted for you to review and commit
|
|
204138
204180
|
step <actor> Authenticate as <actor>, match the resolved rest's
|
|
204139
204181
|
declared patterns against the pending changes, and commit
|
|
204140
204182
|
(or squash) the one resulting transition. Pass
|
|
204141
204183
|
--cost=<n> (optionally --model=<name>) to record the
|
|
204142
204184
|
just-finished invocation's token cost and model on the
|
|
204143
204185
|
turn commit (summed into it.processCost/processCostByModel)
|
|
204186
|
+
review <commitish>
|
|
204187
|
+
Start a NEW review process at the workflow's declared
|
|
204188
|
+
review-entry state (reviewEntry: true), reviewing
|
|
204189
|
+
<commitish>..HEAD — e.g. a colleague's PR branch. Requires
|
|
204190
|
+
a clean tree resting at the workflow's initial state
|
|
204144
204191
|
next Print the resolved rest's rendered script/prompt/message
|
|
204145
204192
|
(no mutation)
|
|
204146
204193
|
status Print the resolved rest's state/actor and which declared
|
|
@@ -204232,16 +204279,47 @@ const rejectExtraArgs = (command, argv) => {
|
|
|
204232
204279
|
/**
|
|
204233
204280
|
* `gtd lsp`: start the LSP server for `.gtd/` steering files over stdio.
|
|
204234
204281
|
* Rejects `--json` (not a state command) and extra positional arguments
|
|
204235
|
-
* (takes none). Dispatched BEFORE the known-subcommand guard
|
|
204236
|
-
* guard
|
|
204237
|
-
*
|
|
204238
|
-
* workflow state; see `src/Lsp.ts`'s module doc).
|
|
204282
|
+
* (takes none). Dispatched BEFORE the known-subcommand guard and the repo-root
|
|
204283
|
+
* guard — since the server needs no git/config/workflow dependency at all
|
|
204284
|
+
* (it's keyed on file name, not workflow state; see `src/Lsp.ts`'s module doc).
|
|
204239
204285
|
*/
|
|
204240
204286
|
const runLspCommand = (argv, json) => gen(function* () {
|
|
204241
204287
|
if (json) return yield* fail$2(/* @__PURE__ */ new Error("gtd lsp does not accept --json"));
|
|
204242
204288
|
yield* rejectExtraArgs("lsp", argv);
|
|
204243
204289
|
yield* startLspServer();
|
|
204244
204290
|
});
|
|
204291
|
+
/** The `choose one of: ...` suffix shared by every `gtd init` usage error. */
|
|
204292
|
+
const initWorkflowChoices = () => `choose one of: ${WORKFLOW_TEMPLATE_NAMES.join(", ")}`;
|
|
204293
|
+
/**
|
|
204294
|
+
* `gtd init <workflow>`: scaffold a `.gtdrc.json` carrying one of the bundled
|
|
204295
|
+
* workflow templates inline (`simple`/`advanced`). Dispatched like `lsp` —
|
|
204296
|
+
* BEFORE the closeReviewWindow/dispatch/openReviewWindow block — because it is
|
|
204297
|
+
* the ONE command that must run with no workflow configured yet: it needs no
|
|
204298
|
+
* `ConfigService` (which would throw the "no workflow" error pre-init) and no
|
|
204299
|
+
* review window. It still runs the repo-root guard (it writes `.gtdrc.json` at
|
|
204300
|
+
* the root) and refuses to clobber an existing config. The file is left
|
|
204301
|
+
* UNCOMMITTED, so the message warns to commit it before the first `gtd step`
|
|
204302
|
+
* (an uncommitted config is a pending change the initial state's `* **` edge
|
|
204303
|
+
* would otherwise capture).
|
|
204304
|
+
*/
|
|
204305
|
+
const runInitCommand = (argv, json, write) => gen(function* () {
|
|
204306
|
+
const args = commandArgs(argv);
|
|
204307
|
+
if (args.length === 0) return yield* fail$2(/* @__PURE__ */ new Error(`gtd init: missing workflow — ${initWorkflowChoices()}`));
|
|
204308
|
+
if (args.length > 1) return yield* fail$2(/* @__PURE__ */ new Error(`gtd init: too many arguments — expected one workflow, got: ${args.join(", ")}`));
|
|
204309
|
+
const name = args[0];
|
|
204310
|
+
if (!isWorkflowTemplateName(name)) return yield* fail$2(/* @__PURE__ */ new Error(`gtd init: unknown workflow '${name}' — ${initWorkflowChoices()}`));
|
|
204311
|
+
const git = yield* GitService;
|
|
204312
|
+
const fs = yield* FileSystem;
|
|
204313
|
+
yield* assertRunningFromRepoRoot(git, fs);
|
|
204314
|
+
const { root } = yield* Cwd;
|
|
204315
|
+
if (yield* anyConfigPresent(root)) return yield* fail$2(/* @__PURE__ */ new Error("gtd init: a gtd config already exists — remove it before re-initializing"));
|
|
204316
|
+
yield* fs.writeFileString(join(root, ".gtdrc.json"), renderInitConfig(name)).pipe(mapError$1((e) => e instanceof Error ? e : new Error(String(e))));
|
|
204317
|
+
if (json) write(JSON.stringify({
|
|
204318
|
+
written: ".gtdrc.json",
|
|
204319
|
+
workflow: name
|
|
204320
|
+
}) + "\n");
|
|
204321
|
+
else write(`Wrote .gtdrc.json with the "${name}" workflow.\n\nReview and commit it before starting: an uncommitted .gtdrc.json counts as a\npending change, so the initial state would capture it on the first step. Once\ncommitted, run \`gtd step human\` to begin.\n`);
|
|
204322
|
+
});
|
|
204245
204323
|
/**
|
|
204246
204324
|
* Resolve HEAD's rest, the current process run, and the template context for
|
|
204247
204325
|
* rendering that rest's OWN state/actor — the common prefix shared by `gtd
|
|
@@ -204251,7 +204329,7 @@ const runLspCommand = (argv, json) => gen(function* () {
|
|
|
204251
204329
|
* instead of sharing this helper).
|
|
204252
204330
|
*/
|
|
204253
204331
|
const resolveRestContext = (git) => gen(function* () {
|
|
204254
|
-
const config = yield* ConfigService;
|
|
204332
|
+
const config = yield* (yield* ConfigService).load;
|
|
204255
204333
|
const worktree = yield* WorktreeReader;
|
|
204256
204334
|
const envVars = yield* EnvVars;
|
|
204257
204335
|
const rest = yield* resolveRest();
|
|
@@ -204274,7 +204352,7 @@ const formatStepRefusal = (invoker, refusal) => refusal.reason === "out-of-turn"
|
|
|
204274
204352
|
*/
|
|
204275
204353
|
const stepAsActor = (invoker, cost, model) => gen(function* () {
|
|
204276
204354
|
const git = yield* GitService;
|
|
204277
|
-
const config = yield* ConfigService;
|
|
204355
|
+
const config = yield* (yield* ConfigService).load;
|
|
204278
204356
|
const worktree = yield* WorktreeReader;
|
|
204279
204357
|
const envVars = yield* EnvVars;
|
|
204280
204358
|
const rest = yield* resolveRest();
|
|
@@ -204323,6 +204401,52 @@ const runStepCommand = (argv, json, write, cost, model) => gen(function* () {
|
|
|
204323
204401
|
reportStepResult(result, json, write);
|
|
204324
204402
|
});
|
|
204325
204403
|
/**
|
|
204404
|
+
* `gtd review <commitish>`: start a brand NEW review process at the active
|
|
204405
|
+
* workflow's declared `reviewEntry: true` state (see
|
|
204406
|
+
* `PatternMachine.reviewEntryStateOf`), reviewing `<commitish>..HEAD` — e.g. a
|
|
204407
|
+
* colleague's PR branch pushed on top of a shared base, with no gtd process of
|
|
204408
|
+
* its own. Writes an ordinary EMPTY turn commit
|
|
204409
|
+
* (`gtd(human): <review-entry-state>`) carrying the resolved `<commitish>`'s
|
|
204410
|
+
* full hash as a `Gtd-Review-Base:` trailer (`withReviewBaseTrailer`) —
|
|
204411
|
+
* `computeProcessRun` reads that trailer back off this same commit (its
|
|
204412
|
+
* process's own oldest) to override the new process's diff base, so the
|
|
204413
|
+
* ENTIRE existing review flow (reviewing → await-review → feedback laps, the
|
|
204414
|
+
* `await-review` review checkout window) operates over `<commitish>..HEAD`
|
|
204415
|
+
* with zero duplicated logic — see `src/Edge.ts`/`src/ReviewWindow.ts`.
|
|
204416
|
+
*
|
|
204417
|
+
* Any failure below is a plain refusal: nothing is written.
|
|
204418
|
+
*
|
|
204419
|
+
* a. The machine must currently rest at the workflow's INITIAL state — a
|
|
204420
|
+
* plain non-gtd branch (the normal case) resolves there via the
|
|
204421
|
+
* inert-subject rule (STATES.md §5); a process already underway refuses.
|
|
204422
|
+
* b. The working tree must be clean.
|
|
204423
|
+
* c. The active workflow must declare a `reviewEntry: true` state.
|
|
204424
|
+
* d. `<commitish>` must resolve to a commit, be an ancestor of HEAD, and
|
|
204425
|
+
* differ from HEAD (nothing to review otherwise).
|
|
204426
|
+
*/
|
|
204427
|
+
const runReviewCommand = (argv, json, write) => gen(function* () {
|
|
204428
|
+
const args = commandArgs(argv);
|
|
204429
|
+
if (args.length === 0) return yield* fail$2(/* @__PURE__ */ new Error("gtd review: missing <commitish> argument"));
|
|
204430
|
+
if (args.length > 1) return yield* fail$2(/* @__PURE__ */ new Error(`gtd review: too many arguments — expected one <commitish>, got: ${args.join(", ")}`));
|
|
204431
|
+
const commitish = args[0];
|
|
204432
|
+
const git = yield* GitService;
|
|
204433
|
+
const rest = yield* resolveRest();
|
|
204434
|
+
if (rest.state !== initialStateOf(rest.def)) return yield* fail$2(/* @__PURE__ */ new Error(`gtd review: a process is already underway (resting at "${rest.state}") — finish or abandon it before starting a review`));
|
|
204435
|
+
if ((yield* pendingChanges(git)).length > 0) return yield* fail$2(/* @__PURE__ */ new Error("gtd review: the working tree must be clean before starting a review"));
|
|
204436
|
+
const entryState = reviewEntryStateOf(rest.def);
|
|
204437
|
+
if (entryState === void 0) return yield* fail$2(/* @__PURE__ */ new Error("gtd review: the active workflow declares no review entry state"));
|
|
204438
|
+
const resolvedBase = yield* git.resolveRef(commitish).pipe(mapError$1(() => /* @__PURE__ */ new Error(`gtd review: "${commitish}" does not resolve to a commit`)));
|
|
204439
|
+
if (resolvedBase === (yield* git.resolveRef("HEAD"))) return yield* fail$2(/* @__PURE__ */ new Error(`gtd review: "${commitish}" is HEAD — nothing to review`));
|
|
204440
|
+
if (!(yield* git.isAncestor(resolvedBase, "HEAD"))) return yield* fail$2(/* @__PURE__ */ new Error(`gtd review: "${commitish}" is not an ancestor of HEAD`));
|
|
204441
|
+
const subject = stateSubject("human", entryState);
|
|
204442
|
+
yield* git.commitAsIs(withReviewBaseTrailer(subject, resolvedBase));
|
|
204443
|
+
if (json) write(JSON.stringify({
|
|
204444
|
+
state: entryState,
|
|
204445
|
+
subject
|
|
204446
|
+
}) + "\n");
|
|
204447
|
+
else write(`committed: ${subject}\n`);
|
|
204448
|
+
});
|
|
204449
|
+
/**
|
|
204326
204450
|
* The self-validation instruction gtd APPENDS to a `prompt` rest that declares
|
|
204327
204451
|
* both `file:` and `mode:` — i.e. a state whose actor hands over a steering
|
|
204328
204452
|
* file `gtd validate` formats and checks. Appended ONLY to plain `gtd next`
|
|
@@ -204529,10 +204653,11 @@ const runStatusCommand = (argv, json, write) => gen(function* () {
|
|
|
204529
204653
|
const runMermaidCommand = (argv, json, write) => gen(function* () {
|
|
204530
204654
|
if (json) return yield* fail$2(/* @__PURE__ */ new Error("gtd mermaid does not accept --json"));
|
|
204531
204655
|
yield* rejectExtraArgs("mermaid", argv);
|
|
204532
|
-
write(renderMermaid((yield* ConfigService).workflow));
|
|
204656
|
+
write(renderMermaid((yield* (yield* ConfigService).load).workflow));
|
|
204533
204657
|
});
|
|
204534
204658
|
const KNOWN_SUBCOMMANDS = [
|
|
204535
204659
|
"step",
|
|
204660
|
+
"review",
|
|
204536
204661
|
"next",
|
|
204537
204662
|
"status",
|
|
204538
204663
|
"validate",
|
|
@@ -204583,6 +204708,7 @@ const assertRunningFromRepoRoot = (git, fs) => gen(function* () {
|
|
|
204583
204708
|
const dispatchKnownSubcommand = (sub, argv, json, write, cost, model) => {
|
|
204584
204709
|
switch (sub) {
|
|
204585
204710
|
case "step": return runStepCommand(argv, json, write, cost, model);
|
|
204711
|
+
case "review": return runReviewCommand(argv, json, write);
|
|
204586
204712
|
case "next": return runNextCommand(json, write);
|
|
204587
204713
|
case "status": return runStatusCommand(argv, json, write);
|
|
204588
204714
|
case "validate": return runValidateCommand(argv, json, write);
|
|
@@ -204593,15 +204719,17 @@ const dispatchKnownSubcommand = (sub, argv, json, write, cost, model) => {
|
|
|
204593
204719
|
* Factory that returns the gtd driver Effect with the given I/O options.
|
|
204594
204720
|
*
|
|
204595
204721
|
* The returned Effect requires `GitService | FileSystem.FileSystem |
|
|
204596
|
-
* ConfigService |
|
|
204722
|
+
* ConfigService | Cwd | WorktreeReader | EnvVars`. Production code calls
|
|
204597
204723
|
* this with no arguments; the test world supplies an in-memory layer set and
|
|
204598
204724
|
* captures stdout via the `write` callback.
|
|
204599
204725
|
*
|
|
204600
|
-
* v3 command surface: `step <actor>` / `next` / `
|
|
204601
|
-
* `
|
|
204602
|
-
*
|
|
204603
|
-
*
|
|
204604
|
-
*
|
|
204726
|
+
* v3 command surface: `step <actor>` / `next` / `status` / `validate` /
|
|
204727
|
+
* `mermaid` (see `src/Edge.ts` and `docs/design/pattern-machine-plan.md` §3),
|
|
204728
|
+
* plus `lsp` and `init <workflow>` — both dispatched before the config-reading
|
|
204729
|
+
* path since they must work with no workflow configured yet. Bare `gtd` or an
|
|
204730
|
+
* unknown subcommand is a usage error. Shared setup (argv parsing, the
|
|
204731
|
+
* repo-root guard) lives here; each subcommand's own logic is a named
|
|
204732
|
+
* `run*Command` function above.
|
|
204605
204733
|
*/
|
|
204606
204734
|
function makeProgram(opts = {}) {
|
|
204607
204735
|
const argv = opts.argv ?? process.argv;
|
|
@@ -204614,12 +204742,12 @@ function makeProgram(opts = {}) {
|
|
|
204614
204742
|
if (unknownOption !== void 0) return yield* fail$2(/* @__PURE__ */ new Error(`gtd: unknown option '${unknownOption}' — see \`gtd --help\``));
|
|
204615
204743
|
const { cost, model } = yield* parseStepFlags(argv, positional);
|
|
204616
204744
|
if (positional === "lsp") return yield* runLspCommand(argv, json);
|
|
204745
|
+
if (positional === "init") return yield* runInitCommand(argv, json, write);
|
|
204617
204746
|
const sub = yield* requireKnownSubcommand(positional, json, write);
|
|
204618
204747
|
const git = yield* GitService;
|
|
204619
204748
|
const fs = yield* FileSystem;
|
|
204620
204749
|
yield* assertRunningFromRepoRoot(git, fs);
|
|
204621
204750
|
yield* closeReviewWindow;
|
|
204622
|
-
yield* (yield* ConfigInit).ensure;
|
|
204623
204751
|
const outcome = yield* either(dispatchKnownSubcommand(sub, argv, json, write, cost, model));
|
|
204624
204752
|
const rearm = yield* either(openReviewWindow);
|
|
204625
204753
|
if (isLeft(outcome)) return yield* fail$2(outcome.left);
|
|
@@ -204632,7 +204760,7 @@ function makeProgram(opts = {}) {
|
|
|
204632
204760
|
//#endregion
|
|
204633
204761
|
//#region src/main.ts
|
|
204634
204762
|
if (runVersionOrHelp(process.argv, (chunk) => process.stdout.write(chunk))) process.exit(0);
|
|
204635
|
-
makeProgram().pipe(provide$1(GitService.Live), provide$1(ConfigService.Live), provide$1(
|
|
204763
|
+
makeProgram().pipe(provide$1(GitService.Live), provide$1(ConfigService.Live), provide$1(WorktreeReader.Live), provide$1(EnvVars.Live), provide$1(Cwd.Live), provide$1(layer), catchAll((error) => sync$1(() => {
|
|
204636
204764
|
if (process.argv.includes("--json") && !isEnveloped(error)) process.stdout.write(JSON.stringify({
|
|
204637
204765
|
state: "error",
|
|
204638
204766
|
prompt: error.message
|