@pmelab/gtd 1.9.2 → 1.10.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 CHANGED
@@ -759,6 +759,7 @@ Commands:
759
759
  (default) Run the gtd driver loop — detect state, emit next prompt
760
760
  format <file> Format a markdown file in place
761
761
  review <target> Ad-hoc human review against a git ref or branch
762
+ status Print current state, next state, and pending edge actions (no actions, no prompt)
762
763
 
763
764
  Options:
764
765
  --json Output structured JSON instead of plain text
@@ -773,7 +774,7 @@ repository-state work — they run outside a repo and in any repo state.
773
774
 
774
775
  ## Subcommands
775
776
 
776
- gtd ships two subcommands: `format` and `review`.
777
+ gtd ships three subcommands: `format`, `review`, and `status`.
777
778
 
778
779
  ## Review subcommand
779
780
 
@@ -812,6 +813,82 @@ All errors exit with **code 1** and write a message to **stderr**:
812
813
  (nothing to review):
813
814
  `gtd review: nothing to review (<target> diff is empty after filtering)`
814
815
 
816
+ ## Status subcommand
817
+
818
+ ```bash
819
+ gtd status
820
+ ```
821
+
822
+ Pure, read-only introspection. Prints the current machine state, the state the
823
+ next real `gtd` run would stop at, and a short summary of the edge actions the
824
+ next run would perform. Performs **nothing** (no commit, reset, or file write)
825
+ and prints **no prompt** — guaranteed side-effect free.
826
+
827
+ ### Fields
828
+
829
+ | Field | Description |
830
+ | ----------------- | ----------------------------------------------------------------- |
831
+ | `state` | Current machine state |
832
+ | `nextState` | State the next `gtd` run would stop at, or `null` for edge-only |
833
+ | `willAutoAdvance` | `true` when the current state is edge-only (auto-advances on run) |
834
+ | `edgeActions` | List of edge actions the next run would perform before prompting |
835
+
836
+ ### One-hop semantics
837
+
838
+ `status` runs the same read-only gather+resolve the driver's first iteration
839
+ does, then reports it without looping or performing.
840
+
841
+ - A **prompt-bearing / human / terminal** current state reports itself as
842
+ `nextState`. The next run performs any pending edge action, then prompts
843
+ there.
844
+ - An **edge-only** current state reports `nextState: null` and
845
+ `willAutoAdvance: true`, naming the immediate edge action. Because the landing
846
+ state after auto-advance depends on side effects (test pass/fail, commits)
847
+ that `status` refuses to run, it honestly reports the current state rather
848
+ than guessing a landing state. There is **no** multi-hop simulation.
849
+
850
+ ### Output
851
+
852
+ Default (human-readable) — `building` example:
853
+
854
+ ```
855
+ State: building
856
+ Next state: building (next run prompts here)
857
+ Edge actions:
858
+ - commit pending changes as "gtd: building"
859
+ ```
860
+
861
+ With `--json` — same example:
862
+
863
+ ```json
864
+ {
865
+ "state": "building",
866
+ "nextState": "building",
867
+ "willAutoAdvance": false,
868
+ "edgeActions": ["commit pending changes as \"gtd: building\""]
869
+ }
870
+ ```
871
+
872
+ Edge-only example (`testing` state):
873
+
874
+ ```json
875
+ {
876
+ "state": "testing",
877
+ "nextState": null,
878
+ "willAutoAdvance": true,
879
+ "edgeActions": ["run the test suite (attempt 1)"]
880
+ }
881
+ ```
882
+
883
+ The JSON envelope contains no `prompt` field — this distinguishes it from the
884
+ default `gtd` run and `gtd review` JSON output.
885
+
886
+ ### Requirements
887
+
888
+ - Must be run from the **repository root** (same cwd guard as other repo
889
+ commands).
890
+ - Takes **no arguments** — extra args are rejected with an error.
891
+
815
892
  ## Format subcommand
816
893
 
817
894
  `gtd format` formats a markdown file in place:
@@ -389281,6 +389281,37 @@ var EDGE_ONLY_STATES2 = /* @__PURE__ */ new Set([
389281
389281
  "health-check"
389282
389282
  ]);
389283
389283
  var isEdgeOnly = (state) => EDGE_ONLY_STATES2.has(state);
389284
+ var edgeActionHandlers = {
389285
+ transportReset: () => "reset the working tree to the gtd: transport parent",
389286
+ seedNewFeature: () => "seed a new feature (write the initial TODO.md)",
389287
+ seedAcceptReview: () => "seed the accept-review step",
389288
+ captureGrillingEdits: () => "capture pending grilling edits into TODO.md",
389289
+ runTest: (a5) => `run the test suite (attempt ${a5.errorCount + 1}${a5.capReached ? ", cap reached" : ""})`,
389290
+ commitPending: (a5) => {
389291
+ let msg = `commit pending changes as "${a5.prefix}"`;
389292
+ if (a5.removeTodo) msg += " (removing TODO.md)";
389293
+ if (a5.removeFeedback) msg += " (removing FEEDBACK.md)";
389294
+ if (a5.removeHealth) msg += " (removing HEALTH.md)";
389295
+ return msg;
389296
+ },
389297
+ runHealthCheck: (a5) => `run the health check${a5.commitErrorsReset ? " (resetting the error budget)" : ""}`,
389298
+ closePackage: () => "close the active package (commit gtd: package done)",
389299
+ commitReview: () => "commit the review record (REVIEW.md)",
389300
+ done: () => "finalize the review cycle (commit gtd: done)",
389301
+ squashCommit: (a5) => `squash the cycle onto ${a5.squashBase}`,
389302
+ removeHealthSentinel: () => "remove the health-squash sentinel before prompting for a squash message",
389303
+ removeStraySquashMsg: () => "remove a stray SQUASH_MSG.md"
389304
+ };
389305
+ var describeEdgeAction = (a5) => edgeActionHandlers[a5.kind](a5);
389306
+ var describeStatus = (result) => {
389307
+ const willAutoAdvance = isEdgeOnly(result.state);
389308
+ return {
389309
+ state: result.state,
389310
+ nextState: willAutoAdvance ? null : result.state,
389311
+ willAutoAdvance,
389312
+ edgeActions: result.edgeAction ? [describeEdgeAction(result.edgeAction)] : []
389313
+ };
389314
+ };
389284
389315
  var detect = () => Effect_exports.gen(function* () {
389285
389316
  const events = yield* gatherEvents();
389286
389317
  return yield* Effect_exports.try({
@@ -389298,6 +389329,7 @@ Commands:
389298
389329
  (default) Run the gtd driver loop \u2014 detect state, emit next prompt
389299
389330
  format <file> Format a markdown file in place
389300
389331
  review <target> Ad-hoc human review against a git ref or branch
389332
+ status Print current state, next state, and pending edge actions (no actions, no prompt)
389301
389333
 
389302
389334
  Options:
389303
389335
  --json Output structured JSON instead of plain text
@@ -389320,6 +389352,24 @@ var IDLE_RESULT = {
389320
389352
  }
389321
389353
  };
389322
389354
  var MAX_EDGE_HOPS = 100;
389355
+ var renderStatus = (s5) => {
389356
+ const lines3 = [];
389357
+ lines3.push(`State: ${s5.state}`);
389358
+ if (s5.willAutoAdvance) {
389359
+ lines3.push(`Next state: (edge-only \u2014 next run performs the action(s) below and auto-advances)`);
389360
+ } else {
389361
+ lines3.push(`Next state: ${s5.nextState} (next run prompts here)`);
389362
+ }
389363
+ if (s5.edgeActions.length === 0) {
389364
+ lines3.push(`Edge actions: none`);
389365
+ } else {
389366
+ lines3.push(`Edge actions:`);
389367
+ for (const action of s5.edgeActions) {
389368
+ lines3.push(` - ${action}`);
389369
+ }
389370
+ }
389371
+ return lines3.join("\n") + "\n";
389372
+ };
389323
389373
  function makeProgram(opts = {}) {
389324
389374
  const argv = opts.argv ?? process.argv;
389325
389375
  const write4 = opts.write ?? ((chunk4) => process.stdout.write(chunk4));
@@ -389404,6 +389454,22 @@ function makeProgram(opts = {}) {
389404
389454
  }
389405
389455
  return;
389406
389456
  }
389457
+ if (sub === "status") {
389458
+ const args2 = argv.slice(3).filter((a5) => a5.length > 0 && !a5.startsWith("--"));
389459
+ if (args2.length > 0) {
389460
+ return yield* Effect_exports.fail(
389461
+ new Error(`gtd status: too many arguments \u2014 expected none, got: ${args2.join(", ")}`)
389462
+ );
389463
+ }
389464
+ const result = yield* detect();
389465
+ const summary5 = describeStatus(result);
389466
+ if (json2) {
389467
+ write4(JSON.stringify(summary5) + "\n");
389468
+ } else {
389469
+ write4(renderStatus(summary5));
389470
+ }
389471
+ return;
389472
+ }
389407
389473
  if (sub !== void 0) {
389408
389474
  return yield* Effect_exports.fail(new Error(`unknown command '${sub}'`));
389409
389475
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmelab/gtd",
3
- "version": "1.9.2",
3
+ "version": "1.10.0",
4
4
  "private": false,
5
5
  "description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
6
6
  "bin": {