@try-works/dsh-recursive-mode 0.1.4 → 0.1.6

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.
Files changed (90) hide show
  1. package/lib/bootstrap.d.ts +6 -3
  2. package/lib/client/board.d.ts +4 -1
  3. package/lib/client/open-state.d.ts +20 -0
  4. package/lib/client/slots.d.ts +7 -1
  5. package/lib/client.js +135 -47
  6. package/lib/fs-intent.d.ts +34 -0
  7. package/lib/index.d.ts +1 -0
  8. package/lib/index.js +3429 -911
  9. package/lib/policy.d.ts +1 -1
  10. package/lib/runtime.d.ts +12 -5
  11. package/lib/ts-lint.d.ts +205 -0
  12. package/package.json +1 -1
  13. package/src/bootstrap.ts +338 -331
  14. package/src/client/board.tsx +0 -0
  15. package/src/client/open-state.ts +46 -0
  16. package/src/client/slots.ts +41 -20
  17. package/src/commands.ts +11 -0
  18. package/src/fs-intent.ts +84 -0
  19. package/src/index.ts +238 -226
  20. package/src/policy.ts +1 -1
  21. package/src/runtime.ts +19 -23
  22. package/src/ts-lint.ts +2110 -0
  23. package/references/scripts/__pycache__/recursive_phase_rules.cpython-314.pyc +0 -0
  24. package/references/scripts/lint-recursive-run.ps1 +0 -25
  25. package/references/scripts/lint-recursive-run.py +0 -2870
  26. package/references/scripts/recursive-closeout.ps1 +0 -38
  27. package/references/scripts/recursive-closeout.py +0 -541
  28. package/references/scripts/recursive-init.ps1 +0 -515
  29. package/references/scripts/recursive-init.py +0 -356
  30. package/references/scripts/recursive-lock.ps1 +0 -26
  31. package/references/scripts/recursive-lock.py +0 -302
  32. package/references/scripts/recursive-review-bundle.ps1 +0 -58
  33. package/references/scripts/recursive-review-bundle.py +0 -503
  34. package/references/scripts/recursive-router-cli-configure.ps1 +0 -2
  35. package/references/scripts/recursive-router-cli-configure.py +0 -9
  36. package/references/scripts/recursive-router-cli-init.ps1 +0 -2
  37. package/references/scripts/recursive-router-cli-init.py +0 -9
  38. package/references/scripts/recursive-router-cli-invoke.ps1 +0 -2
  39. package/references/scripts/recursive-router-cli-invoke.py +0 -9
  40. package/references/scripts/recursive-router-cli-probe.ps1 +0 -2
  41. package/references/scripts/recursive-router-cli-probe.py +0 -9
  42. package/references/scripts/recursive-router-cli-resolve.ps1 +0 -2
  43. package/references/scripts/recursive-router-cli-resolve.py +0 -9
  44. package/references/scripts/recursive-router-cli-validate.ps1 +0 -2
  45. package/references/scripts/recursive-router-cli-validate.py +0 -9
  46. package/references/scripts/recursive-router-configure.ps1 +0 -27
  47. package/references/scripts/recursive-router-configure.py +0 -74
  48. package/references/scripts/recursive-router-init.ps1 +0 -17
  49. package/references/scripts/recursive-router-init.py +0 -29
  50. package/references/scripts/recursive-router-invoke.ps1 +0 -47
  51. package/references/scripts/recursive-router-invoke.py +0 -103
  52. package/references/scripts/recursive-router-probe.ps1 +0 -25
  53. package/references/scripts/recursive-router-probe.py +0 -44
  54. package/references/scripts/recursive-router-resolve.ps1 +0 -26
  55. package/references/scripts/recursive-router-resolve.py +0 -46
  56. package/references/scripts/recursive-router-validate.ps1 +0 -17
  57. package/references/scripts/recursive-router-validate.py +0 -27
  58. package/references/scripts/recursive-status.ps1 +0 -23
  59. package/references/scripts/recursive-status.py +0 -2124
  60. package/references/scripts/recursive-subagent-action.ps1 +0 -98
  61. package/references/scripts/recursive-subagent-action.py +0 -197
  62. package/references/scripts/recursive-training-extract.ps1 +0 -23
  63. package/references/scripts/recursive-training-extract.py +0 -99
  64. package/references/scripts/recursive-training-grpo.ps1 +0 -52
  65. package/references/scripts/recursive-training-grpo.py +0 -1341
  66. package/references/scripts/recursive-training-loader.ps1 +0 -78
  67. package/references/scripts/recursive-training-loader.py +0 -558
  68. package/references/scripts/recursive-training-mcp.ps1 +0 -29
  69. package/references/scripts/recursive-training-mcp.py +0 -261
  70. package/references/scripts/recursive-training-phase8-trigger.ps1 +0 -55
  71. package/references/scripts/recursive-training-phase8-trigger.py +0 -137
  72. package/references/scripts/recursive-training-sync.ps1 +0 -29
  73. package/references/scripts/recursive-training-sync.py +0 -183
  74. package/references/scripts/recursive_phase_rules.py +0 -367
  75. package/references/scripts/recursive_router_cli_lib.py +0 -2
  76. package/references/scripts/recursive_router_lib.py +0 -2282
  77. package/references/scripts/verify-locks.ps1 +0 -25
  78. package/references/scripts/verify-locks.py +0 -353
  79. package/scripts/__pycache__/lint-recursive-run.cpython-314.pyc +0 -0
  80. package/scripts/__pycache__/recursive_phase_rules.cpython-314.pyc +0 -0
  81. package/scripts/install-recursive-mode.ps1 +0 -956
  82. package/scripts/install-recursive-mode.py +0 -750
  83. package/scripts/lint-recursive-run.py +0 -2870
  84. package/scripts/recursive-closeout.py +0 -541
  85. package/scripts/recursive-init.py +0 -356
  86. package/scripts/recursive-lock.py +0 -302
  87. package/scripts/recursive-status.py +0 -2124
  88. package/scripts/recursive_phase_rules.py +0 -367
  89. package/scripts/recursive_router_lib.py +0 -2282
  90. package/scripts/verify-locks.py +0 -353
@@ -35,8 +35,11 @@ export interface StageBInput {
35
35
  }
36
36
  /**
37
37
  * Stage B workflow init at agent/session-start. New: bootstrap if missing +
38
- * enumerate runs. Resume: never re-bootstrap; enumerate + note the active run
39
- * (the caller re-reads only that run's current phase doc). Always scoped to
40
- * the given control-plane root.
38
+ * enumerate runs. Resume: idempotent REPAIR (not a full re-bootstrap) the
39
+ * upsert-only bootstrapScaffold adds missing control-plane files/dirs and
40
+ * re-upserts marked blocks WITHOUT overwriting user content or touching run/
41
+ * artifacts, then enumerates + notes the active run. The repair is safe on
42
+ * fresh, partial, AND complete scaffolds (run 09 R3/R6). Always scoped to the
43
+ * given control-plane root.
41
44
  */
42
45
  export declare function stageBWorkflowInit(input: StageBInput): StageBResult;
@@ -1,11 +1,14 @@
1
1
  import type { RecursiveProjection, RecursiveRunCard } from '../types.ts';
2
2
  import type { LiveProjectionValue } from './contract.ts';
3
+ import type { BoardSelection } from './open-state.ts';
3
4
  /** Flatten the worktree-grouped projection into a stable run list. */
4
5
  export declare function listRuns(projection: RecursiveProjection | undefined): RecursiveRunCard[];
5
6
  export interface BoardProps {
6
7
  /** The live host-route snapshot (null = not loaded / no recursive root). */
7
8
  snapshot: LiveProjectionValue | null;
9
+ /** Opens the drill-down inspector for a run (R3). */
10
+ onOpenInspector?: (selection: BoardSelection) => void;
8
11
  }
9
- export declare function Board({ snapshot }: BoardProps): import("react").DetailedReactHTMLElement<{
12
+ export declare function Board({ snapshot, onOpenInspector }: BoardProps): import("react").DetailedReactHTMLElement<{
10
13
  className: string;
11
14
  }, HTMLElement> | null;
@@ -0,0 +1,20 @@
1
+ export interface BoardSelection {
2
+ worktreeRoot: string;
3
+ runId: string;
4
+ }
5
+ export interface BoardState {
6
+ open: boolean;
7
+ selection: BoardSelection | null;
8
+ }
9
+ export interface BoardOpenState {
10
+ get(): BoardState;
11
+ openBoard(): void;
12
+ close(): void;
13
+ openInspector(selection: BoardSelection): void;
14
+ backToBoard(): void;
15
+ subscribe(fn: () => void): () => void;
16
+ }
17
+ export declare function createBoardState(initial?: BoardState): BoardOpenState;
18
+ /** App-wide singleton shared by the launcher, overlay, board, and inspector. */
19
+ export declare const boardState: BoardOpenState;
20
+ export declare function useBoardState(store?: BoardOpenState): BoardState;
@@ -1,5 +1,11 @@
1
- import type { ClientContext } from './contract.ts';
1
+ import type { ClientContext, ISessionsLike } from './contract.ts';
2
2
  import { isRecursivePreset, currentSessionCwd } from './contract.ts';
3
+ export type OverlayContent = 'hidden' | 'board' | 'inspector';
4
+ /** Gate: hidden unless open AND recursive preset; inspector when a run is selected. */
5
+ export declare function overlayContent(state: {
6
+ open: boolean;
7
+ selection: unknown;
8
+ }, sessions: ISessionsLike): OverlayContent;
3
9
  export declare function registerSlots(ctx: ClientContext): () => void;
4
10
  /** Export the gate helpers for tests. */
5
11
  export { isRecursivePreset, currentSessionCwd };
package/lib/client.js CHANGED
@@ -224,13 +224,17 @@ window.__ModuleLoader__.load({
224
224
  //#endregion
225
225
  //#region src/client/board.tsx
226
226
  /**
227
- * Board overlay (SP2 R1): the kanban view over the live host-route projection.
228
- * Pure read-only renderer — no filesystem, no run-file scraping. Cards are
229
- * grouped by worktree root (the projection's outer key) and mapped to their
230
- * current-phase lane via derive.columnForRun. Renders NOTHING when the snapshot
231
- * is null (no recursive root / not loaded) or the current session is not on the
232
- * recursive preset (the gate lives in slots.ts — here an empty projection with
233
- * a null root is a no-op, not an "empty board").
227
+ * Board overlay (SP2 R1 + run 08 R3): the kanban view over the live host-route
228
+ * projection. Pure read-only renderer — no filesystem, no run-file scraping.
229
+ * Cards are grouped by worktree root (the projection's outer key) and mapped to
230
+ * their current-phase lane via derive.columnForRun. Renders NOTHING when the
231
+ * snapshot is null (no recursive root / not loaded) or the current session is
232
+ * not on the recursive preset (the gate lives in slots.ts — here an empty
233
+ * projection with a null root is a no-op, not an "empty board").
234
+ *
235
+ * Run 08 R3: a card click opens the drill-down inspector via onOpenInspector
236
+ * (the overlay swaps board <-> inspector in slots.ts). Read-only still: the
237
+ * callback only selects a run for display; no mutation.
234
238
  */
235
239
  /** Flatten the worktree-grouped projection into a stable run list. */
236
240
  function listRuns(projection) {
@@ -239,13 +243,17 @@ window.__ModuleLoader__.load({
239
243
  for (const worktreeRoot of Object.keys(projection)) for (const runId of Object.keys(projection[worktreeRoot])) runs.push(projection[worktreeRoot][runId]);
240
244
  return runs;
241
245
  }
242
- function Board({ snapshot }) {
246
+ function Board({ snapshot, onOpenInspector }) {
243
247
  if (snapshot === null || snapshot.root === null) return null;
244
248
  const runs = listRuns(snapshot.projection);
245
249
  if (runs.length === 0) return (0, react.createElement)("div", {
246
250
  className: "rec-board",
247
251
  "data-empty": true
248
252
  }, (0, react.createElement)("p", { className: "rec-board-empty" }, "No recursive runs in this workspace yet."));
253
+ const open = (run) => () => onOpenInspector?.({
254
+ worktreeRoot: run.worktreeRoot,
255
+ runId: run.runId
256
+ });
249
257
  return (0, react.createElement)("div", { className: "rec-board" }, KANBAN_LANES.map((lane) => {
250
258
  const laneRuns = runs.filter((r) => columnForRun(r) === lane.id);
251
259
  return (0, react.createElement)("section", {
@@ -255,7 +263,8 @@ window.__ModuleLoader__.load({
255
263
  const facts = cardFacts(run);
256
264
  return (0, react.createElement)("article", {
257
265
  key: run.worktreeRoot + "\0" + run.runId,
258
- className: "rec-card"
266
+ className: "rec-card",
267
+ onClick: open(run)
259
268
  }, (0, react.createElement)("div", { className: "rec-card-head" }, (0, react.createElement)("strong", {}, run.runId), facts.tampered && (0, react.createElement)("span", {
260
269
  className: "rec-badge rec-badge-tamper",
261
270
  title: "tampered"
@@ -267,6 +276,26 @@ window.__ModuleLoader__.load({
267
276
  }));
268
277
  }
269
278
  //#endregion
279
+ //#region src/client/inspector.tsx
280
+ /**
281
+ * Drill-down inspector (SP2 R1): the details-seat swap rendering one run's
282
+ * phase chain (with the always-shown 3.5 row + full 6/7/8 rows), lock badges,
283
+ * and tamper/gate facts — all from the live host-route snapshot, never disk.
284
+ */
285
+ function Inspector({ runId, worktreeRoot, snapshot, onBackToToolDetails }) {
286
+ const card = snapshot?.projection?.[worktreeRoot]?.[runId];
287
+ if (card === void 0) return (0, react.createElement)("div", { className: "rec-inspector" }, (0, react.createElement)("p", {}, "Run not found: " + runId));
288
+ const facts = cardFacts(card);
289
+ const rows = expandPhaseRows(card);
290
+ return (0, react.createElement)("div", { className: "rec-inspector" }, (0, react.createElement)("div", { className: "rec-inspector-head" }, (0, react.createElement)("button", {
291
+ onClick: onBackToToolDetails,
292
+ className: "rec-back"
293
+ }, "← back to tool details"), (0, react.createElement)("h2", {}, runId), (0, react.createElement)("span", { className: "rec-badge" }, facts.state), facts.tampered && (0, react.createElement)("span", { className: "rec-badge rec-badge-tamper" }, "tampered")), (0, react.createElement)("section", { className: "rec-inspector-section" }, (0, react.createElement)("h3", {}, "Phases"), rows.map((row) => (0, react.createElement)("div", {
294
+ key: row.phase,
295
+ className: "rec-phase-row"
296
+ }, (0, react.createElement)("span", { className: "rec-phase-id" }, row.phase), (0, react.createElement)("span", { className: "rec-phase-name" }, row.fileName ?? "—"), (0, react.createElement)("span", { className: "rec-badge" }, row.status), row.lockHash !== void 0 && (0, react.createElement)("code", { className: "rec-lockhash" }, "#" + row.lockHash.slice(0, 8))))), facts.gateBlocked && (0, react.createElement)("section", { className: "rec-inspector-section" }, (0, react.createElement)("h3", {}, "Gate"), (0, react.createElement)("p", {}, "Blocked (" + (facts.gateKind ?? "") + ")")));
297
+ }
298
+ //#endregion
270
299
  //#region src/client/strip.tsx
271
300
  /**
272
301
  * Status strip (SP2 R1): a thin glance at the active run's phase, gate state,
@@ -409,22 +438,90 @@ window.__ModuleLoader__.load({
409
438
  return state;
410
439
  }
411
440
  //#endregion
441
+ //#region src/client/open-state.ts
442
+ /**
443
+ * Shared board open-state (run 08 R1): module-level store behind the launcher,
444
+ * the shell.overlay entry, and the board<->inspector swap. Zero session events,
445
+ * zero host mutation — the client remains a read-only projection (R5/R9).
446
+ */
447
+ function createBoardState(initial = {
448
+ open: false,
449
+ selection: null
450
+ }) {
451
+ let state = initial;
452
+ const listeners = /* @__PURE__ */ new Set();
453
+ const emit = () => {
454
+ for (const fn of listeners) fn();
455
+ };
456
+ return {
457
+ get: () => state,
458
+ openBoard: () => {
459
+ state = {
460
+ ...state,
461
+ open: true
462
+ };
463
+ emit();
464
+ },
465
+ close: () => {
466
+ state = {
467
+ ...state,
468
+ open: false,
469
+ selection: null
470
+ };
471
+ emit();
472
+ },
473
+ openInspector: (selection) => {
474
+ state = {
475
+ ...state,
476
+ open: true,
477
+ selection
478
+ };
479
+ emit();
480
+ },
481
+ backToBoard: () => {
482
+ state = {
483
+ ...state,
484
+ selection: null
485
+ };
486
+ emit();
487
+ },
488
+ subscribe: (fn) => {
489
+ listeners.add(fn);
490
+ return () => {
491
+ listeners.delete(fn);
492
+ };
493
+ }
494
+ };
495
+ }
496
+ /** App-wide singleton shared by the launcher, overlay, board, and inspector. */
497
+ const boardState = createBoardState();
498
+ function useBoardState(store = boardState) {
499
+ return (0, react.useSyncExternalStore)(store.subscribe, store.get, store.get);
500
+ }
501
+ //#endregion
412
502
  //#region src/client/slots.ts
413
503
  /**
414
- * Slot registrations (SP2 R1): the sidebar launcher, the shell.overlay board,
415
- * the conversation.input.dock status strip, and the settings.section page.
416
- *
417
- * The board (root scope) and strip (session scope) render ONLY for the
418
- * recursive preset — the gate reads the CURRENT session's agentPreset from the
419
- * sessions list snapshot (acceptance #3: a non-recursive session shows NOTHING).
504
+ * Slot registrations (SP2 R1 + run 08 R1/R3/R4): the sidebar launcher, the
505
+ * shell.overlay board, the conversation.input.dock status strip, and the
506
+ * settings.section page.
420
507
  *
421
- * The board reads cwd from the ROOT-scope standard prop useSessions (the
422
- * scoped-slots root branch provides ONLY useSessions/useWorkspaces useProjection
423
- * is session-scope-only, which is why the board never reads it). The strip reads
424
- * its sessionId from the session-scope standard prop.
508
+ * Run 08: the launcher now OPENS the shared board store (onClick), and the
509
+ * shell.overlay entry renders the board gated on that store + the recursive
510
+ * preset, swapping to the drill-down inspector when a run is selected. The
511
+ * board reads cwd from the ROOT-scope standard prop useSessions (the scoped-
512
+ * slots root branch provides ONLY useSessions/useWorkspaces — useProjection is
513
+ * session-scope-only). The strip reads its sessionId from the session-scope
514
+ * standard prop.
425
515
  *
426
516
  * READ-ONLY (R9): the client only GETs the live host route; no mutation.
427
517
  */
518
+ /** Gate: hidden unless open AND recursive preset; inspector when a run is selected. */
519
+ function overlayContent(state, sessions) {
520
+ if (!state.open) return "hidden";
521
+ if (!isRecursivePreset(sessions)) return "hidden";
522
+ if (state.selection !== null) return "inspector";
523
+ return "board";
524
+ }
428
525
  function registerSlots(ctx) {
429
526
  const disposers = [];
430
527
  disposers.push(ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
@@ -434,7 +531,8 @@ window.__ModuleLoader__.load({
434
531
  label: "Recursive runs"
435
532
  }, () => (0, react.createElement)("button", {
436
533
  className: "rec-launcher",
437
- title: "Recursive runs"
534
+ title: "Recursive runs",
535
+ onClick: () => boardState.openBoard()
438
536
  }, "⧉"))));
439
537
  disposers.push(ctx.slots.inject("shell.overlay", () => ctx.slots.register({
440
538
  name: "shell.overlay",
@@ -443,7 +541,7 @@ window.__ModuleLoader__.load({
443
541
  }, (props) => {
444
542
  const useSessions = props?.useSessions;
445
543
  if (useSessions === void 0) return null;
446
- return (0, react.createElement)(RecursiveBoardGate, { useSessions });
544
+ return (0, react.createElement)(RecursiveBoardOverlay, { useSessions });
447
545
  })));
448
546
  disposers.push(ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
449
547
  name: "conversation.input.dock",
@@ -468,23 +566,33 @@ window.__ModuleLoader__.load({
468
566
  };
469
567
  }
470
568
  /**
471
- * Board gate: renders the board ONLY when the current session is on the
472
- * recursive preset. The gate itself reads the live sessions snapshot via the
473
- * standard useSessions hook and re-renders on change.
569
+ * Board overlay: subscribes to the shared board store, gates on open + recursive
570
+ * preset, and swaps board <-> inspector (run 08 R1/R3).
474
571
  */
475
- function RecursiveBoardGate({ useSessions }) {
572
+ function RecursiveBoardOverlay({ useSessions }) {
573
+ const board = useBoardState();
476
574
  const list = useSessions();
477
575
  const sessions = { list: {
478
576
  getSnapshot: () => list,
479
577
  subscribe: () => () => {}
480
578
  } };
481
- if (!isRecursivePreset(sessions)) return null;
579
+ const content = overlayContent(board, sessions);
580
+ if (content === "hidden") return null;
482
581
  const cwd = currentSessionCwd(sessions);
483
582
  const snapshot = useLiveProjection({
484
583
  sessionId: list.current,
485
584
  cwd
486
585
  });
487
- return (0, react.createElement)(Board, { snapshot });
586
+ if (content === "inspector" && board.selection !== null) return (0, react.createElement)(Inspector, {
587
+ runId: board.selection.runId,
588
+ worktreeRoot: board.selection.worktreeRoot,
589
+ snapshot,
590
+ onBackToToolDetails: () => boardState.backToBoard()
591
+ });
592
+ return (0, react.createElement)(Board, {
593
+ snapshot,
594
+ onOpenInspector: (sel) => boardState.openInspector(sel)
595
+ });
488
596
  }
489
597
  /**
490
598
  * Strip gate: same preset gate, session-scoped sessionId; the strip renders
@@ -516,26 +624,6 @@ window.__ModuleLoader__.load({
516
624
  globalThis.__dshRecursiveModeClientApplied = void 0;
517
625
  }
518
626
  //#endregion
519
- //#region src/client/inspector.tsx
520
- /**
521
- * Drill-down inspector (SP2 R1): the details-seat swap rendering one run's
522
- * phase chain (with the always-shown 3.5 row + full 6/7/8 rows), lock badges,
523
- * and tamper/gate facts — all from the live host-route snapshot, never disk.
524
- */
525
- function Inspector({ runId, worktreeRoot, snapshot, onBackToToolDetails }) {
526
- const card = snapshot?.projection?.[worktreeRoot]?.[runId];
527
- if (card === void 0) return (0, react.createElement)("div", { className: "rec-inspector" }, (0, react.createElement)("p", {}, "Run not found: " + runId));
528
- const facts = cardFacts(card);
529
- const rows = expandPhaseRows(card);
530
- return (0, react.createElement)("div", { className: "rec-inspector" }, (0, react.createElement)("div", { className: "rec-inspector-head" }, (0, react.createElement)("button", {
531
- onClick: onBackToToolDetails,
532
- className: "rec-back"
533
- }, "← back to tool details"), (0, react.createElement)("h2", {}, runId), (0, react.createElement)("span", { className: "rec-badge" }, facts.state), facts.tampered && (0, react.createElement)("span", { className: "rec-badge rec-badge-tamper" }, "tampered")), (0, react.createElement)("section", { className: "rec-inspector-section" }, (0, react.createElement)("h3", {}, "Phases"), rows.map((row) => (0, react.createElement)("div", {
534
- key: row.phase,
535
- className: "rec-phase-row"
536
- }, (0, react.createElement)("span", { className: "rec-phase-id" }, row.phase), (0, react.createElement)("span", { className: "rec-phase-name" }, row.fileName ?? "—"), (0, react.createElement)("span", { className: "rec-badge" }, row.status), row.lockHash !== void 0 && (0, react.createElement)("code", { className: "rec-lockhash" }, "#" + row.lockHash.slice(0, 8))))), facts.gateBlocked && (0, react.createElement)("section", { className: "rec-inspector-section" }, (0, react.createElement)("h3", {}, "Gate"), (0, react.createElement)("p", {}, "Blocked (" + (facts.gateKind ?? "") + ")")));
537
- }
538
- //#endregion
539
627
  //#region src/client/index.ts
540
628
  /** Required services (fiber inject waiting — the runtime must be up first). */
541
629
  const inject = [
@@ -0,0 +1,34 @@
1
+ import type { WorkspaceRegistryLike } from './workspace.ts';
2
+ export interface FsPolicyIntent {
3
+ worktreeRoot: string;
4
+ runId: string;
5
+ /** The current phase doc file name (e.g. 03-implementation-summary.md), when determinable. */
6
+ currentPhaseFile: string | null;
7
+ }
8
+ /**
9
+ * Resolve the recursive policy intent for one agent from the FILESYSTEM.
10
+ * Returns null when no control-plane root or run is present (callers defer,
11
+ * never fail hard). The registry path is async-only in the harness, so when a
12
+ * registry is present this resolves the session cwd directly (B4: the session
13
+ * cwd IS the workspace root when unregistered; a registered workspace resolves
14
+ * to the same cwd via resolveControlPlaneRoot's sync fallback).
15
+ */
16
+ export declare function fsPolicyIntent(agent: {
17
+ session?: {
18
+ header?: {
19
+ cwd?: string;
20
+ };
21
+ };
22
+ } | null | undefined, registry?: WorkspaceRegistryLike | null, cwdFallback?: string): FsPolicyIntent | null;
23
+ /**
24
+ * Async variant for callers that CAN await (pre-step listener, routes): uses
25
+ * resolveControlPlaneRoot for registry canonicalization when a registry is
26
+ * present, falling back to the session cwd (B4).
27
+ */
28
+ export declare function fsPolicyIntentAsync(agent: {
29
+ session?: {
30
+ header?: {
31
+ cwd?: string;
32
+ };
33
+ };
34
+ } | null | undefined, registry?: WorkspaceRegistryLike | null, cwdFallback?: string): Promise<FsPolicyIntent | null>;
package/lib/index.d.ts CHANGED
@@ -37,4 +37,5 @@ export declare const inject: string[];
37
37
  */
38
38
  export declare function apply(ctx: Context, config?: {
39
39
  shellOnly?: boolean;
40
+ repoRoot?: string;
40
41
  }): void;