@martintrojer/mu 0.3.1 → 0.3.2
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/dist/cli.js +747 -119
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +505 -72
- package/dist/index.js +601 -97
- package/dist/index.js.map +1 -1
- package/docs/ARCHITECTURE.md +4 -4
- package/docs/ROADMAP.md +8 -6
- package/docs/USAGE_GUIDE.md +73 -9
- package/docs/VOCABULARY.md +6 -3
- package/package.json +1 -1
- package/skills/mu/SKILL.md +42 -15
package/dist/index.d.ts
CHANGED
|
@@ -342,6 +342,19 @@ declare function enableMuPaneBordersForPane(paneId: string): Promise<void>;
|
|
|
342
342
|
* in hud_visual_cue_impl.
|
|
343
343
|
*/
|
|
344
344
|
declare function enableMuPaneBorders(target: string): Promise<void>;
|
|
345
|
+
/**
|
|
346
|
+
* Look up the TTY device path for a pane (e.g. `/dev/ttys012` on macOS,
|
|
347
|
+
* `/dev/pts/3` on Linux). Used by `mu agent kick` to find the
|
|
348
|
+
* foreground process group on the pane's TTY so it can be signalled
|
|
349
|
+
* directly — `tmux send-keys C-c` does NOT propagate to wrapped
|
|
350
|
+
* subprocesses inside a CLI like pi/claude/codex (the CLI catches it
|
|
351
|
+
* itself and treats it as a UI input). The escape hatch is signalling
|
|
352
|
+
* the foreground pgid of the underlying TTY from outside the pane.
|
|
353
|
+
*
|
|
354
|
+
* Throws `PaneNotFoundError` when the pane id is invalid or the pane
|
|
355
|
+
* has vanished. Throws `TmuxError` on any other tmux failure.
|
|
356
|
+
*/
|
|
357
|
+
declare function paneTTY(paneId: string): Promise<string>;
|
|
345
358
|
declare function getPaneTitle(paneId: string): Promise<string | undefined>;
|
|
346
359
|
/**
|
|
347
360
|
* Read the title of the *current* pane (the one whose shell is running this
|
|
@@ -451,6 +464,7 @@ declare const tmux$1_newSession: typeof newSession;
|
|
|
451
464
|
declare const tmux$1_newSessionWithPane: typeof newSessionWithPane;
|
|
452
465
|
declare const tmux$1_newWindow: typeof newWindow;
|
|
453
466
|
declare const tmux$1_paneExists: typeof paneExists;
|
|
467
|
+
declare const tmux$1_paneTTY: typeof paneTTY;
|
|
454
468
|
declare const tmux$1_parseAgentNameFromTitle: typeof parseAgentNameFromTitle;
|
|
455
469
|
declare const tmux$1_resetSleep: typeof resetSleep;
|
|
456
470
|
declare const tmux$1_resetTmuxExecutor: typeof resetTmuxExecutor;
|
|
@@ -464,7 +478,7 @@ declare const tmux$1_sleep: typeof sleep;
|
|
|
464
478
|
declare const tmux$1_splitWindow: typeof splitWindow;
|
|
465
479
|
declare const tmux$1_tmux: typeof tmux;
|
|
466
480
|
declare namespace tmux$1 {
|
|
467
|
-
export { type tmux$1_CaptureOptions as CaptureOptions, type tmux$1_NewSessionOptions as NewSessionOptions, type tmux$1_NewSessionWithPaneOptions as NewSessionWithPaneOptions, type tmux$1_NewWindowOptions as NewWindowOptions, tmux$1_PANE_ID_RE as PANE_ID_RE, tmux$1_PaneNotFoundError as PaneNotFoundError, type tmux$1_SendOptions as SendOptions, type tmux$1_SplitWindowOptions as SplitWindowOptions, tmux$1_TmuxError as TmuxError, type tmux$1_TmuxExecResult as TmuxExecResult, type tmux$1_TmuxExecutor as TmuxExecutor, type tmux$1_TmuxPane as TmuxPane, type tmux$1_TmuxSession as TmuxSession, type tmux$1_TmuxWindow as TmuxWindow, tmux$1_assertValidPaneId as assertValidPaneId, tmux$1_capturePane as capturePane, tmux$1_currentAgentName as currentAgentName, tmux$1_currentPaneSize as currentPaneSize, tmux$1_currentPaneTitle as currentPaneTitle, tmux$1_defaultSendDelayMs as defaultSendDelayMs, tmux$1_enableMuPaneBorders as enableMuPaneBorders, tmux$1_enableMuPaneBordersForPane as enableMuPaneBordersForPane, tmux$1_enableMuPaneBordersForSession as enableMuPaneBordersForSession, tmux$1_getPaneTitle as getPaneTitle, tmux$1_getWindowIdForPane as getWindowIdForPane, tmux$1_isValidPaneId as isValidPaneId, tmux$1_killPane as killPane, tmux$1_killSession as killSession, tmux$1_listPanes as listPanes, tmux$1_listPanesInSession as listPanesInSession, tmux$1_listSessions as listSessions, tmux$1_listWindows as listWindows, tmux$1_newSession as newSession, tmux$1_newSessionWithPane as newSessionWithPane, tmux$1_newWindow as newWindow, tmux$1_paneExists as paneExists, tmux$1_parseAgentNameFromTitle as parseAgentNameFromTitle, tmux$1_resetSleep as resetSleep, tmux$1_resetTmuxExecutor as resetTmuxExecutor, tmux$1_selectLayout as selectLayout, tmux$1_sendToPane as sendToPane, tmux$1_sessionExists as sessionExists, tmux$1_setPaneTitle as setPaneTitle, tmux$1_setSleepForTests as setSleepForTests, tmux$1_setTmuxExecutor as setTmuxExecutor, tmux$1_sleep as sleep, tmux$1_splitWindow as splitWindow, tmux$1_tmux as tmux };
|
|
481
|
+
export { type tmux$1_CaptureOptions as CaptureOptions, type tmux$1_NewSessionOptions as NewSessionOptions, type tmux$1_NewSessionWithPaneOptions as NewSessionWithPaneOptions, type tmux$1_NewWindowOptions as NewWindowOptions, tmux$1_PANE_ID_RE as PANE_ID_RE, tmux$1_PaneNotFoundError as PaneNotFoundError, type tmux$1_SendOptions as SendOptions, type tmux$1_SplitWindowOptions as SplitWindowOptions, tmux$1_TmuxError as TmuxError, type tmux$1_TmuxExecResult as TmuxExecResult, type tmux$1_TmuxExecutor as TmuxExecutor, type tmux$1_TmuxPane as TmuxPane, type tmux$1_TmuxSession as TmuxSession, type tmux$1_TmuxWindow as TmuxWindow, tmux$1_assertValidPaneId as assertValidPaneId, tmux$1_capturePane as capturePane, tmux$1_currentAgentName as currentAgentName, tmux$1_currentPaneSize as currentPaneSize, tmux$1_currentPaneTitle as currentPaneTitle, tmux$1_defaultSendDelayMs as defaultSendDelayMs, tmux$1_enableMuPaneBorders as enableMuPaneBorders, tmux$1_enableMuPaneBordersForPane as enableMuPaneBordersForPane, tmux$1_enableMuPaneBordersForSession as enableMuPaneBordersForSession, tmux$1_getPaneTitle as getPaneTitle, tmux$1_getWindowIdForPane as getWindowIdForPane, tmux$1_isValidPaneId as isValidPaneId, tmux$1_killPane as killPane, tmux$1_killSession as killSession, tmux$1_listPanes as listPanes, tmux$1_listPanesInSession as listPanesInSession, tmux$1_listSessions as listSessions, tmux$1_listWindows as listWindows, tmux$1_newSession as newSession, tmux$1_newSessionWithPane as newSessionWithPane, tmux$1_newWindow as newWindow, tmux$1_paneExists as paneExists, tmux$1_paneTTY as paneTTY, tmux$1_parseAgentNameFromTitle as parseAgentNameFromTitle, tmux$1_resetSleep as resetSleep, tmux$1_resetTmuxExecutor as resetTmuxExecutor, tmux$1_selectLayout as selectLayout, tmux$1_sendToPane as sendToPane, tmux$1_sessionExists as sessionExists, tmux$1_setPaneTitle as setPaneTitle, tmux$1_setSleepForTests as setSleepForTests, tmux$1_setTmuxExecutor as setTmuxExecutor, tmux$1_sleep as sleep, tmux$1_splitWindow as splitWindow, tmux$1_tmux as tmux };
|
|
468
482
|
}
|
|
469
483
|
|
|
470
484
|
/**
|
|
@@ -542,6 +556,52 @@ interface ReconcileReport {
|
|
|
542
556
|
}
|
|
543
557
|
declare function reconcile(db: Db, opts: ReconcileOptions): Promise<ReconcileReport>;
|
|
544
558
|
|
|
559
|
+
/**
|
|
560
|
+
* Pre-flight failure: the command mu would have spawned in the new
|
|
561
|
+
* pane doesn't resolve to a binary on PATH (and isn't an absolute /
|
|
562
|
+
* relative path that exists + is executable). Thrown by `spawnAgent`
|
|
563
|
+
* BEFORE `prestageWorkspace` so a typo in `--cli` never leaves an
|
|
564
|
+
* orphan workspace dir behind.
|
|
565
|
+
*
|
|
566
|
+
* Source: feedback ws task `fb_agent_spawn_no_validation`. Live
|
|
567
|
+
* dogfood report: `mu agent spawn worker-1 --cli pi-meta` on a host
|
|
568
|
+
* where the `pi-meta` binary wasn't on PATH printed `Spawned worker-1
|
|
569
|
+
* (pi-meta)` and the pane immediately died with `command not found`;
|
|
570
|
+
* the existing 1.5s liveness check sometimes missed it (the shell
|
|
571
|
+
* stays alive after the failed exec). Pre-flighting the PATH lookup
|
|
572
|
+
* surfaces the typo before any side effects (workspace, pane, DB row).
|
|
573
|
+
*
|
|
574
|
+
* Distinct from `AgentSpawnStartupError` (pane alive but parked at an
|
|
575
|
+
* error prompt) and `AgentDiedOnSpawnError` (pane vanished within the
|
|
576
|
+
* liveness window). All three carry different remediation hints, so
|
|
577
|
+
* they're separate types.
|
|
578
|
+
*/
|
|
579
|
+
declare class AgentSpawnCliNotFoundError extends Error implements HasNextSteps {
|
|
580
|
+
readonly cli: string;
|
|
581
|
+
/** First whitespace-separated token of the resolved command — the
|
|
582
|
+
* thing actually missing on PATH. Surfaced verbatim in the
|
|
583
|
+
* message so the operator sees what mu searched for (which may
|
|
584
|
+
* differ from `cli` when `$MU_<UPPER_CLI>_COMMAND` rewrites it). */
|
|
585
|
+
readonly binary: string;
|
|
586
|
+
/** Name of the env var that mu consulted before falling back to
|
|
587
|
+
* the bare `cli` value (e.g. `MU_PI_META_COMMAND`). Always set
|
|
588
|
+
* to the conventional name so the nextSteps hint can recommend
|
|
589
|
+
* exporting it. */
|
|
590
|
+
readonly envVarChecked: string;
|
|
591
|
+
readonly name = "AgentSpawnCliNotFoundError";
|
|
592
|
+
constructor(cli: string,
|
|
593
|
+
/** First whitespace-separated token of the resolved command — the
|
|
594
|
+
* thing actually missing on PATH. Surfaced verbatim in the
|
|
595
|
+
* message so the operator sees what mu searched for (which may
|
|
596
|
+
* differ from `cli` when `$MU_<UPPER_CLI>_COMMAND` rewrites it). */
|
|
597
|
+
binary: string,
|
|
598
|
+
/** Name of the env var that mu consulted before falling back to
|
|
599
|
+
* the bare `cli` value (e.g. `MU_PI_META_COMMAND`). Always set
|
|
600
|
+
* to the conventional name so the nextSteps hint can recommend
|
|
601
|
+
* exporting it. */
|
|
602
|
+
envVarChecked: string);
|
|
603
|
+
errorNextSteps(): NextStep[];
|
|
604
|
+
}
|
|
545
605
|
declare class AgentExistsError extends Error implements HasNextSteps {
|
|
546
606
|
readonly agentName: string;
|
|
547
607
|
readonly name = "AgentExistsError";
|
|
@@ -600,6 +660,52 @@ declare class AgentDiedOnSpawnError extends Error implements HasNextSteps {
|
|
|
600
660
|
constructor(agentName: string, paneId: string, scrollback: string | undefined);
|
|
601
661
|
errorNextSteps(): NextStep[];
|
|
602
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* Thrown when an agent's pane is alive AND staying alive after the
|
|
665
|
+
* liveness window, but its first burst of output matches a known
|
|
666
|
+
* provider-startup-failure pattern (missing API key, auth rejected, …).
|
|
667
|
+
* Source: feedback ws task `agent_spawn_model_auth_failure_counts_as_live`.
|
|
668
|
+
* Live dogfood report: `pi-meta --no-solo --model sonnet:high` printed
|
|
669
|
+
* `Error: No API key found for amazon-bedrock` and parked at a prompt.
|
|
670
|
+
* The pane stayed alive (1.5s liveness check passed) but the worker
|
|
671
|
+
* could never do work — the orchestrator only discovered this when
|
|
672
|
+
* `mu task wait` stalled minutes later.
|
|
673
|
+
*
|
|
674
|
+
* Distinct from `AgentDiedOnSpawnError`:
|
|
675
|
+
* - `AgentDiedOnSpawnError` → pane vanished within the liveness window
|
|
676
|
+
* (CLI exited fast).
|
|
677
|
+
* - `AgentSpawnStartupError` → pane alive, but the captured scrollback
|
|
678
|
+
* tail contains a curated provider-auth-failure pattern.
|
|
679
|
+
* The two carry different remediation hints (CLI override vs. fix the
|
|
680
|
+
* env var), so they're separate types instead of one with a flag.
|
|
681
|
+
*
|
|
682
|
+
* The pattern list is curated and short to keep false-positive risk low
|
|
683
|
+
* — the scan only looks at the last ~30 lines of the 50-line capture
|
|
684
|
+
* taken right after the liveness sleep, so matches naturally come from
|
|
685
|
+
* the CLI's first ~1.5s of output (not arbitrary later prompts the
|
|
686
|
+
* agent might type into).
|
|
687
|
+
*/
|
|
688
|
+
declare class AgentSpawnStartupError extends Error implements HasNextSteps {
|
|
689
|
+
readonly agentName: string;
|
|
690
|
+
readonly paneId: string;
|
|
691
|
+
/** The single scrollback line that matched a known startup-error
|
|
692
|
+
* pattern. Surfaced verbatim in the message so the operator sees
|
|
693
|
+
* what mu saw. */
|
|
694
|
+
readonly matchedLine: string;
|
|
695
|
+
/** Full captured scrollback (tail-trimmed already by
|
|
696
|
+
* awaitSpawnLiveness). Attached to the message for context. */
|
|
697
|
+
readonly scrollback: string;
|
|
698
|
+
readonly name = "AgentSpawnStartupError";
|
|
699
|
+
constructor(agentName: string, paneId: string,
|
|
700
|
+
/** The single scrollback line that matched a known startup-error
|
|
701
|
+
* pattern. Surfaced verbatim in the message so the operator sees
|
|
702
|
+
* what mu saw. */
|
|
703
|
+
matchedLine: string,
|
|
704
|
+
/** Full captured scrollback (tail-trimmed already by
|
|
705
|
+
* awaitSpawnLiveness). Attached to the message for context. */
|
|
706
|
+
scrollback: string);
|
|
707
|
+
errorNextSteps(): NextStep[];
|
|
708
|
+
}
|
|
603
709
|
/**
|
|
604
710
|
* Thrown when `closeAgent` is called on an agent that has an associated
|
|
605
711
|
* workspace AND the caller didn't explicitly opt into discarding it.
|
|
@@ -740,6 +846,31 @@ interface VcsBackend {
|
|
|
740
846
|
* the on-disk dir without touching the agent or pane.
|
|
741
847
|
*/
|
|
742
848
|
rebaseTo(workspacePath: string, fromRef?: string): Promise<RebaseResult>;
|
|
849
|
+
/**
|
|
850
|
+
* Cheap "is the working copy clean?" probe used by close-auto-free
|
|
851
|
+
* (allow_mu_agent_close_without_discard). Definition: ZERO uncommitted
|
|
852
|
+
* changes (no working-tree modifications, no staged changes, no
|
|
853
|
+
* untracked-not-ignored files). Pure observation; no fetch, no commit.
|
|
854
|
+
*
|
|
855
|
+
* Backend-specific:
|
|
856
|
+
* - git: empty `git status --porcelain` output.
|
|
857
|
+
* - jj: jj is auto-snapshotted, so the @ commit IS the WC; clean
|
|
858
|
+
* here means @ has no diff from its parent (empty `jj diff
|
|
859
|
+
* -r @ --summary`). A description-only difference still
|
|
860
|
+
* counts as clean.
|
|
861
|
+
* - sl: empty `sl status` output.
|
|
862
|
+
* - none: meaningless (cp -a snapshot has no notion of
|
|
863
|
+
* "committed" vs "uncommitted"); always returns true so the
|
|
864
|
+
* close-auto-free path treats every none-workspace as
|
|
865
|
+
* eligible for silent free (no commits can be lost; the only
|
|
866
|
+
* loss is local file edits, which the operator implicitly
|
|
867
|
+
* accepts by closing the agent).
|
|
868
|
+
*
|
|
869
|
+
* Returns false on any backend command failure — be conservative
|
|
870
|
+
* (we'd rather refuse a close than auto-free a workspace whose
|
|
871
|
+
* cleanliness we couldn't verify).
|
|
872
|
+
*/
|
|
873
|
+
isClean(workspacePath: string): Promise<boolean>;
|
|
743
874
|
/**
|
|
744
875
|
* List commits the workspace has on top of `baseRef`, oldest-first.
|
|
745
876
|
* Used by `mu workspace commits` (fb_workspace_commits_verb) to
|
|
@@ -754,6 +885,27 @@ interface VcsBackend {
|
|
|
754
885
|
* on backend command failure (unknown ref, missing repo).
|
|
755
886
|
*/
|
|
756
887
|
commitsSinceBase(workspacePath: string, baseRef: string): Promise<CommitSummary[]>;
|
|
888
|
+
/**
|
|
889
|
+
* Return the list of dirty (uncommitted / unstaged / untracked-not-
|
|
890
|
+
* ignored) paths in the workspace. Empty array = clean.
|
|
891
|
+
*
|
|
892
|
+
* Used by `mu workspace recreate` to refuse a free+create cycle on
|
|
893
|
+
* a dirty workspace unless the operator passes `--force` (the lossy
|
|
894
|
+
* escape hatch). Mirrors the dirty-check `rebaseTo` does internally.
|
|
895
|
+
*
|
|
896
|
+
* Backend semantics:
|
|
897
|
+
* - git: `git status --porcelain` (working-tree + staged +
|
|
898
|
+
* untracked-not-ignored, mirroring the rebaseTo path).
|
|
899
|
+
* - sl: `sl status` parsed for non-empty output.
|
|
900
|
+
* - jj: always-snapshotted, so no concept of "dirty" — returns [].
|
|
901
|
+
* - none: cp -a snapshots have no VCS, so we can't decide "dirty";
|
|
902
|
+
* returns [] so the caller doesn't refuse for an unanswerable
|
|
903
|
+
* question.
|
|
904
|
+
*
|
|
905
|
+
* Throws on backend command failure (the operator should see a
|
|
906
|
+
* real error, not a silent "clean").
|
|
907
|
+
*/
|
|
908
|
+
listDirtyFiles(workspacePath: string): Promise<string[]>;
|
|
757
909
|
}
|
|
758
910
|
declare const noneBackend: VcsBackend;
|
|
759
911
|
declare const gitBackend: VcsBackend;
|
|
@@ -779,6 +931,48 @@ declare function backendByName(name: VcsBackendName): VcsBackend;
|
|
|
779
931
|
* are still recognised as agents.
|
|
780
932
|
*/
|
|
781
933
|
declare function resolveCliCommand(cli: string): string;
|
|
934
|
+
/**
|
|
935
|
+
* Compute the `MU_<UPPER_CLI>_COMMAND` env var name mu consults when
|
|
936
|
+
* resolving `--cli <key>`. Hyphens in the cli key become underscores
|
|
937
|
+
* (env var names can't contain `-`); this matches the operator-aliases
|
|
938
|
+
* convention documented in the mu skill (e.g. `--cli pi-meta` →
|
|
939
|
+
* `MU_PI_META_COMMAND`).
|
|
940
|
+
*/
|
|
941
|
+
declare function envVarNameForCli(cli: string): string;
|
|
942
|
+
/**
|
|
943
|
+
* Resolve `--cli <key>` to its actual command string AND tell the
|
|
944
|
+
* caller whether the resolution came from a `MU_<UPPER_CLI>_COMMAND`
|
|
945
|
+
* env var or fell through to the bare cli name. The CLI uses this to
|
|
946
|
+
* surface env-var attribution in the spawn-success line so config
|
|
947
|
+
* issues are visible without `mu agent show`
|
|
948
|
+
* (fb_agent_spawn_no_validation, part C).
|
|
949
|
+
*/
|
|
950
|
+
declare function resolveCliCommandWithSource(cli: string): {
|
|
951
|
+
command: string;
|
|
952
|
+
envVar: string;
|
|
953
|
+
resolvedFromEnv: boolean;
|
|
954
|
+
};
|
|
955
|
+
interface CommandResolutionResult {
|
|
956
|
+
ok: boolean;
|
|
957
|
+
/** First whitespace-separated token of the command — the binary
|
|
958
|
+
* whose presence on PATH we checked. */
|
|
959
|
+
binary: string;
|
|
960
|
+
/** Absolute path of the resolved binary on PATH, when ok=true. */
|
|
961
|
+
resolvedPath?: string;
|
|
962
|
+
}
|
|
963
|
+
type CommandResolver = (command: string) => Promise<CommandResolutionResult>;
|
|
964
|
+
/** Override the PATH resolver. Tests use this to simulate "binary
|
|
965
|
+
* absent" / "binary present" without depending on what's actually
|
|
966
|
+
* installed. Production callers should never touch this. */
|
|
967
|
+
declare function setCommandResolverForTests(resolver: CommandResolver): void;
|
|
968
|
+
/** Restore the default PATH resolver. */
|
|
969
|
+
declare function resetCommandResolverForTests(): void;
|
|
970
|
+
/**
|
|
971
|
+
* Verify the first token of `command` resolves to a binary on PATH.
|
|
972
|
+
* Public so tests can call it directly; spawnAgent calls it before
|
|
973
|
+
* prestageWorkspace so a bad --cli never creates an orphan workspace.
|
|
974
|
+
*/
|
|
975
|
+
declare function checkCommandResolvable(command: string): Promise<CommandResolutionResult>;
|
|
782
976
|
interface SpawnAgentOptions {
|
|
783
977
|
name: string;
|
|
784
978
|
workstream: string;
|
|
@@ -899,6 +1093,119 @@ interface AdoptAgentResult {
|
|
|
899
1093
|
*/
|
|
900
1094
|
declare function adoptAgent(db: Db, opts: AdoptAgentOptions): Promise<AdoptAgentResult>;
|
|
901
1095
|
|
|
1096
|
+
/** The signal set kick supports. SIGINT is graceful (matches Ctrl-C
|
|
1097
|
+
* semantics — what the operator probably wanted in the first place);
|
|
1098
|
+
* SIGTERM is the polite escalation; SIGKILL is the unblockable
|
|
1099
|
+
* hammer. We deliberately don't expose arbitrary signals — the
|
|
1100
|
+
* three above are the actionable ones for "interrupt a wedged
|
|
1101
|
+
* foreground tool subprocess." */
|
|
1102
|
+
type KickSignal = "SIGINT" | "SIGTERM" | "SIGKILL";
|
|
1103
|
+
declare function isKickSignal(s: string): s is KickSignal;
|
|
1104
|
+
/**
|
|
1105
|
+
* Thrown when the foreground pgid lookup on a pane's TTY yields
|
|
1106
|
+
* either no rows at all (the pane is sitting at an idle shell with
|
|
1107
|
+
* no foreground job) or only the wrapping shell itself (the LLM CLI
|
|
1108
|
+
* — pi/claude/codex — is the foreground; signalling it would close
|
|
1109
|
+
* the agent, which is what `mu agent close` is for).
|
|
1110
|
+
*
|
|
1111
|
+
* Maps to the generic exit code 1 in handle.ts (this is a
|
|
1112
|
+
* runtime-state condition, not a typed not-found / conflict).
|
|
1113
|
+
*/
|
|
1114
|
+
declare class NoForegroundProcessError extends Error implements HasNextSteps {
|
|
1115
|
+
readonly agentName: string;
|
|
1116
|
+
readonly tty: string;
|
|
1117
|
+
readonly reason: "no-foreground" | "shell-only";
|
|
1118
|
+
readonly name = "NoForegroundProcessError";
|
|
1119
|
+
constructor(agentName: string, tty: string, reason: "no-foreground" | "shell-only");
|
|
1120
|
+
errorNextSteps(): NextStep[];
|
|
1121
|
+
}
|
|
1122
|
+
interface KickProcessExecResult {
|
|
1123
|
+
stdout: string;
|
|
1124
|
+
stderr: string;
|
|
1125
|
+
exitCode: number | null;
|
|
1126
|
+
}
|
|
1127
|
+
type KickProcessExecutor = (cmd: string, args: readonly string[]) => Promise<KickProcessExecResult>;
|
|
1128
|
+
/** Install a custom executor (for tests). Returns the previous one so
|
|
1129
|
+
* tests can restore cleanly. */
|
|
1130
|
+
declare function setKickProcessExecutor(executor: KickProcessExecutor): KickProcessExecutor;
|
|
1131
|
+
/** Restore the real executor. */
|
|
1132
|
+
declare function resetKickProcessExecutor(): void;
|
|
1133
|
+
interface PsRow {
|
|
1134
|
+
pid: number;
|
|
1135
|
+
pgid: number;
|
|
1136
|
+
/** ps's `stat` (or `state`) field. The presence of `+` means
|
|
1137
|
+
* "foreground process group on its controlling tty". */
|
|
1138
|
+
stat: string;
|
|
1139
|
+
/** Process command (just the comm; truncated, used for diagnostics). */
|
|
1140
|
+
comm: string;
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Parse `ps -t <tty> -o pid=,pgid=,stat=,comm=` output. Each non-blank
|
|
1144
|
+
* line is one process: four whitespace-separated fields. Defensive
|
|
1145
|
+
* about leading whitespace and command names with embedded spaces
|
|
1146
|
+
* (the comm is the LAST field — join the tail).
|
|
1147
|
+
*/
|
|
1148
|
+
declare function parsePsTtyOutput(output: string): PsRow[];
|
|
1149
|
+
/**
|
|
1150
|
+
* Resolve the foreground process group id for a TTY device path. The
|
|
1151
|
+
* canonical signal `ps`'s `stat` field uses is `+` (BSD/Darwin AND
|
|
1152
|
+
* Linux procps). We pick the first row whose stat contains `+`; its
|
|
1153
|
+
* `pgid` is the foreground pgid of that controlling terminal.
|
|
1154
|
+
*
|
|
1155
|
+
* Returns:
|
|
1156
|
+
* - `{ kind: "ok", pgid, fgRow }` on success
|
|
1157
|
+
* - `{ kind: "no-foreground" }` when no row carries `+` AND there
|
|
1158
|
+
* are no candidate rows at all
|
|
1159
|
+
* - `{ kind: "shell-only", pgid, fgRow }` when the foreground pgid
|
|
1160
|
+
* resolves to a shell whose comm is the agent's wrapping CLI
|
|
1161
|
+
* (caller decides whether to refuse — kick refuses)
|
|
1162
|
+
*
|
|
1163
|
+
* The wrapping-CLI guard is intentionally narrow: we only refuse
|
|
1164
|
+
* when the foreground process command matches one of the known
|
|
1165
|
+
* pi/claude/codex/zsh/bash shapes. Anything else (a `find`, a
|
|
1166
|
+
* `cargo build`, a `python script.py`) is exactly what we want to
|
|
1167
|
+
* signal — that's the unbounded-tool case the verb was built for.
|
|
1168
|
+
*/
|
|
1169
|
+
interface ForegroundLookup {
|
|
1170
|
+
kind: "ok" | "shell-only" | "no-foreground";
|
|
1171
|
+
pgid?: number;
|
|
1172
|
+
fgRow?: PsRow;
|
|
1173
|
+
/** All rows ps returned for the tty, for diagnostics / tests. */
|
|
1174
|
+
rows: PsRow[];
|
|
1175
|
+
}
|
|
1176
|
+
declare function foregroundPgid(tty: string): Promise<ForegroundLookup>;
|
|
1177
|
+
interface KickAgentOptions {
|
|
1178
|
+
workstream: string;
|
|
1179
|
+
/** Defaults to SIGINT (matches Ctrl-C semantics). */
|
|
1180
|
+
signal?: KickSignal;
|
|
1181
|
+
}
|
|
1182
|
+
interface KickAgentResult {
|
|
1183
|
+
agentName: string;
|
|
1184
|
+
paneId: string;
|
|
1185
|
+
/** TTY device path the foreground pgid was resolved against. */
|
|
1186
|
+
tty: string;
|
|
1187
|
+
/** The pgid we signalled. */
|
|
1188
|
+
signaledPgid: number;
|
|
1189
|
+
signal: KickSignal;
|
|
1190
|
+
/** The comm of the foreground process at the time of signal — useful
|
|
1191
|
+
* diagnostic in the event log ("we kicked a `find`, not a `cargo`"). */
|
|
1192
|
+
foregroundComm: string;
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Send `signal` to the foreground process group of an agent's pane
|
|
1196
|
+
* TTY. Default signal is SIGINT.
|
|
1197
|
+
*
|
|
1198
|
+
* Errors:
|
|
1199
|
+
* - `AgentNotFoundError` — the agent doesn't exist in this workstream.
|
|
1200
|
+
* - `PaneNotFoundError` (from paneTTY) — the agent's pane has vanished.
|
|
1201
|
+
* - `NoForegroundProcessError` — pane has no foreground job, OR the
|
|
1202
|
+
* foreground is the wrapping CLI itself (refuse; use `mu agent close`).
|
|
1203
|
+
*
|
|
1204
|
+
* Emits an `agent kick <name> (signal=..., pgid=..., comm=...)` event
|
|
1205
|
+
* on success.
|
|
1206
|
+
*/
|
|
1207
|
+
declare function kickAgent(db: Db, name: string, opts: KickAgentOptions): Promise<KickAgentResult>;
|
|
1208
|
+
|
|
902
1209
|
interface AgentRow {
|
|
903
1210
|
name: string;
|
|
904
1211
|
/** Foreign-name reference to the owning workstream. */
|
|
@@ -1025,14 +1332,17 @@ interface FreeAgentResult {
|
|
|
1025
1332
|
declare function freeAgent(db: Db, name: string, workstream: string): FreeAgentResult;
|
|
1026
1333
|
interface CloseAgentOptions {
|
|
1027
1334
|
/**
|
|
1028
|
-
*
|
|
1029
|
-
*
|
|
1030
|
-
*
|
|
1031
|
-
*
|
|
1032
|
-
* separately first.
|
|
1335
|
+
* Lossy override: when true, free the agent's workspace BEFORE
|
|
1336
|
+
* deleting the agent regardless of whether it's clean. (We control
|
|
1337
|
+
* the order rather than relying on FK cascade, which leaves the
|
|
1338
|
+
* on-disk dir orphaned.) Any pending changes / commits since fork
|
|
1339
|
+
* are gone unless the caller frees with `--commit` separately first.
|
|
1033
1340
|
*
|
|
1034
|
-
* When false (default)
|
|
1035
|
-
*
|
|
1341
|
+
* When false (default), behaviour depends on workspace state:
|
|
1342
|
+
* - clean (no uncommitted changes AND no commits since fork):
|
|
1343
|
+
* silently auto-free. allow_mu_agent_close_without_discard.
|
|
1344
|
+
* - dirty (uncommitted changes OR commits since fork): throw
|
|
1345
|
+
* WorkspacePreservedError so the caller decides explicitly.
|
|
1036
1346
|
* Surfaced as a real bug in the multi-agent dogfood teardown.
|
|
1037
1347
|
*/
|
|
1038
1348
|
discardWorkspace?: boolean;
|
|
@@ -1040,11 +1350,20 @@ interface CloseAgentOptions {
|
|
|
1040
1350
|
interface CloseAgentResult {
|
|
1041
1351
|
killedPane: boolean;
|
|
1042
1352
|
deletedRow: boolean;
|
|
1043
|
-
/** True iff the agent had an associated workspace AND
|
|
1044
|
-
*
|
|
1045
|
-
*
|
|
1046
|
-
*
|
|
1353
|
+
/** True iff the agent had an associated workspace AND we proactively
|
|
1354
|
+
* freed it — either because the caller passed `discardWorkspace:
|
|
1355
|
+
* true` (lossy) or because the workspace was clean and we
|
|
1356
|
+
* auto-freed (allow_mu_agent_close_without_discard). False on the
|
|
1357
|
+
* no-workspace path (nothing to free) and on the refused path (we
|
|
1358
|
+
* threw before doing anything). */
|
|
1047
1359
|
workspaceFreed: boolean;
|
|
1360
|
+
/** True iff `workspaceFreed` was triggered by the clean-workspace
|
|
1361
|
+
* auto-free path (no uncommitted changes AND no commits since
|
|
1362
|
+
* fork) rather than the explicit `discardWorkspace: true` override.
|
|
1363
|
+
* Lets the CLI render an accurate message ("auto-freed (clean)"
|
|
1364
|
+
* vs "workspace discarded") and gives JSON consumers a stable
|
|
1365
|
+
* signal. False on every other path. */
|
|
1366
|
+
workspaceAutoFreedClean: boolean;
|
|
1048
1367
|
}
|
|
1049
1368
|
/**
|
|
1050
1369
|
* Close an agent: kill its tmux pane and remove its DB row. Idempotent:
|
|
@@ -1052,15 +1371,22 @@ interface CloseAgentResult {
|
|
|
1052
1371
|
* - if the tmux pane is already gone, killPane swallows the error
|
|
1053
1372
|
*
|
|
1054
1373
|
* Workspace handling: closing an agent and freeing its workspace are
|
|
1055
|
-
* separate concerns (agent lifecycle vs disk artifacts)
|
|
1056
|
-
*
|
|
1057
|
-
*
|
|
1058
|
-
*
|
|
1059
|
-
*
|
|
1060
|
-
*
|
|
1061
|
-
*
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1374
|
+
* separate concerns (agent lifecycle vs disk artifacts). Three cases:
|
|
1375
|
+
*
|
|
1376
|
+
* - No workspace: close proceeds normally.
|
|
1377
|
+
* - Workspace exists AND is CLEAN (no uncommitted changes, no
|
|
1378
|
+
* commits since fork): silently auto-free (so a workspace that
|
|
1379
|
+
* contains nothing worth preserving doesn't make the operator
|
|
1380
|
+
* type --discard-workspace just to clean it up). Surfaced by
|
|
1381
|
+
* allow_mu_agent_close_without_discard — a misconfigured-spawn
|
|
1382
|
+
* teardown was needlessly forced through the lossy flag.
|
|
1383
|
+
* - Workspace exists AND has either uncommitted changes OR commits
|
|
1384
|
+
* since fork: REFUSE with WorkspacePreservedError so the operator
|
|
1385
|
+
* decides explicitly. Two resolutions:
|
|
1386
|
+
* 1. `freeWorkspace(db, name)` first, then `closeAgent(db, name)`.
|
|
1387
|
+
* Preserves the option to `--commit` pending changes.
|
|
1388
|
+
* 2. `closeAgent(db, name, { discardWorkspace: true })`.
|
|
1389
|
+
* One-shot; lossy.
|
|
1064
1390
|
*
|
|
1065
1391
|
* The CLI surfaces these as the two actionable nextSteps on the
|
|
1066
1392
|
* `WorkspacePreservedError` thrown by the refuse path.
|
|
@@ -1435,7 +1761,7 @@ declare class TaskHasOpenDependentsError extends Error implements HasNextSteps {
|
|
|
1435
1761
|
* The FK on `tasks.owner` references `agents.name`; without this guard
|
|
1436
1762
|
* the claim attempt would fail with the unhelpful 'FOREIGN KEY constraint
|
|
1437
1763
|
* failed' from SQLite. This typed error gives the user actionable next
|
|
1438
|
-
* steps (run `mu adopt <pane-id>` to register, or use --for to pick a
|
|
1764
|
+
* steps (run `mu agent adopt <pane-id>` to register, or use --for to pick a
|
|
1439
1765
|
* different agent).
|
|
1440
1766
|
*
|
|
1441
1767
|
* Maps to exit code 4 (conflict) via the cli.ts handler.
|
|
@@ -1449,7 +1775,7 @@ declare class ClaimerNotRegisteredError extends Error implements HasNextSteps {
|
|
|
1449
1775
|
* Three actionable resolutions in expected-frequency order:
|
|
1450
1776
|
* 1. --self : orchestrator pattern (working directly)
|
|
1451
1777
|
* 2. --for : dispatcher pattern (assigning to a worker)
|
|
1452
|
-
* 3. mu adopt: registration pattern (promote pane to worker)
|
|
1778
|
+
* 3. mu agent adopt: registration pattern (promote pane to worker)
|
|
1453
1779
|
*/
|
|
1454
1780
|
errorNextSteps(): NextStep[];
|
|
1455
1781
|
}
|
|
@@ -1571,17 +1897,14 @@ interface TaskWaitTaskState {
|
|
|
1571
1897
|
stuck: boolean;
|
|
1572
1898
|
}
|
|
1573
1899
|
interface TaskWaitResult {
|
|
1574
|
-
/** Per-task state at exit time. Same length and order as the input
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
anyReached: boolean;
|
|
1580
|
-
/** Wall-clock time spent waiting, in ms (always >= 0). */
|
|
1581
|
-
elapsedMs: number;
|
|
1900
|
+
/** Per-task state at exit time. Same length and order as the input
|
|
1901
|
+
* list. The caller derives all-reached / any-reached / elapsed
|
|
1902
|
+
* from this list (count `r.reachedTarget`) and from its own
|
|
1903
|
+
* startedAt clock — keeping the SDK return minimal. */
|
|
1904
|
+
refs: TaskWaitTaskState[];
|
|
1582
1905
|
/** True when we exited because of the timeout, not because the wait
|
|
1583
|
-
* condition was met.
|
|
1584
|
-
*
|
|
1906
|
+
* condition was met. Refs that did reach the target are still
|
|
1907
|
+
* reflected in `refs[i].reachedTarget` on partial-progress timeout. */
|
|
1585
1908
|
timedOut: boolean;
|
|
1586
1909
|
}
|
|
1587
1910
|
/**
|
|
@@ -1663,6 +1986,14 @@ interface CloseTaskOptions extends EvidenceOption {
|
|
|
1663
1986
|
* When false / omitted, behaves as bare `closeTask` (closes
|
|
1664
1987
|
* regardless of blocker status). */
|
|
1665
1988
|
ifReady?: boolean;
|
|
1989
|
+
/** Optional actor identity attributed to the synthetic `CLOSE: …`
|
|
1990
|
+
* note auto-inserted when `evidence` is non-empty (see closeTask
|
|
1991
|
+
* body). The CLI resolves this via `resolveActorIdentity()` so the
|
|
1992
|
+
* note carries the closing worker's name; SDK callers (tests,
|
|
1993
|
+
* internal use) may omit it (the note then carries no author, same
|
|
1994
|
+
* as a bare `addNote` without `--author`). Surfaced in mufeedback
|
|
1995
|
+
* task_close_evidence_does_not_append_the. */
|
|
1996
|
+
author?: string;
|
|
1666
1997
|
}
|
|
1667
1998
|
/** Convenience: setTaskStatus(db, id, "CLOSED"). Accepts evidence.
|
|
1668
1999
|
* Pre-snapshots the DB (snap_design §CAPTURE STRATEGY > WHEN). Skipped
|
|
@@ -1888,11 +2219,6 @@ declare function resolveActorIdentity(): Promise<string>;
|
|
|
1888
2219
|
interface TaskRow {
|
|
1889
2220
|
/** Per-workstream-unique TEXT name. The operator-facing identifier. */
|
|
1890
2221
|
name: string;
|
|
1891
|
-
/** Alias for `name` — the per-workstream-unique TEXT id. Emitted alongside
|
|
1892
|
-
* `name` so JSON consumers can dot-access the canonical field name without
|
|
1893
|
-
* having to know that, for tasks specifically, `name` plays the localId
|
|
1894
|
-
* role. Always equal to `name`. */
|
|
1895
|
-
localId: string;
|
|
1896
2222
|
/** Foreign-name reference to the owning workstream. */
|
|
1897
2223
|
workstreamName: string;
|
|
1898
2224
|
title: string;
|
|
@@ -1930,6 +2256,14 @@ declare function slugifyTitle(title: string): string;
|
|
|
1930
2256
|
* exceeds the SLUG_SOFT_CAP the verbose form had to
|
|
1931
2257
|
* cut at a word boundary (or hard-truncate); the
|
|
1932
2258
|
* cut clauses are gone with no in-band signal.
|
|
2259
|
+
* originalSlug — what the slug WOULD have been without the
|
|
2260
|
+
* SLUG_SOFT_CAP cut: full stripped slug with the
|
|
2261
|
+
* same `t_` digit-prefix correction and the same
|
|
2262
|
+
* SLUG_HARD_CAP ceiling, but no word-boundary
|
|
2263
|
+
* truncation. Equal to `slug` when nothing was
|
|
2264
|
+
* cut. The CLI surfaces this in `mu task add
|
|
2265
|
+
* --json` so scripted callers can detect the
|
|
2266
|
+
* truncation without grepping stderr.
|
|
1933
2267
|
* truncated — true iff `slug.length < strippedLength` AFTER the
|
|
1934
2268
|
* `t_` digit-prefix correction, i.e. real bytes were
|
|
1935
2269
|
* dropped. False for any title that fits under the
|
|
@@ -1937,12 +2271,14 @@ declare function slugifyTitle(title: string): string;
|
|
|
1937
2271
|
* is the `t_` prefix.
|
|
1938
2272
|
*
|
|
1939
2273
|
* The CLI's `mu task add` uses `truncated` to print a one-line stderr
|
|
1940
|
-
* hint pointing at the `<id>` positional override
|
|
1941
|
-
*
|
|
2274
|
+
* hint pointing at the `<id>` positional override and (under --json)
|
|
2275
|
+
* to surface `originalSlug` alongside `truncated:true`
|
|
2276
|
+
* (slugifytitle_silently_drops_clauses; task_add_slugify_silently_truncates_ids).
|
|
1942
2277
|
*/
|
|
1943
2278
|
interface SlugifyResult {
|
|
1944
2279
|
slug: string;
|
|
1945
2280
|
strippedLength: number;
|
|
2281
|
+
originalSlug: string;
|
|
1946
2282
|
truncated: boolean;
|
|
1947
2283
|
}
|
|
1948
2284
|
/**
|
|
@@ -1965,15 +2301,29 @@ declare function idFromTitle(db: Db, workstream: string, title: string): string;
|
|
|
1965
2301
|
* Result of `idFromTitleVerbose`: the unique-in-workstream id plus the
|
|
1966
2302
|
* truncated flag from the underlying slugify pass. Used by `mu task
|
|
1967
2303
|
* add` to decide whether to surface the stderr hint about lost clauses
|
|
1968
|
-
* (slugifytitle_silently_drops_clauses)
|
|
2304
|
+
* (slugifytitle_silently_drops_clauses) and to surface the un-truncated
|
|
2305
|
+
* slug in `--json` (task_add_slugify_silently_truncates_ids).
|
|
2306
|
+
*
|
|
2307
|
+
* id — the unique-in-workstream task id.
|
|
2308
|
+
* truncated — true iff the underlying slugify pass cut real
|
|
2309
|
+
* characters (collision-suffixing does NOT flip
|
|
2310
|
+
* this).
|
|
2311
|
+
* originalSlug — what the slug would have been without the
|
|
2312
|
+
* SLUG_SOFT_CAP cut. Equal to `id` when nothing was
|
|
2313
|
+
* cut AND no collision suffix was appended; for
|
|
2314
|
+
* the truncation-detection use case the only thing
|
|
2315
|
+
* the CLI cares about is the lossy-vs-not
|
|
2316
|
+
* comparison surfaced via `truncated`.
|
|
1969
2317
|
*/
|
|
1970
2318
|
interface IdFromTitleResult {
|
|
1971
2319
|
id: string;
|
|
1972
2320
|
truncated: boolean;
|
|
2321
|
+
originalSlug: string;
|
|
1973
2322
|
}
|
|
1974
2323
|
/**
|
|
1975
|
-
* Verbose sibling of `idFromTitle`: returns
|
|
1976
|
-
* `truncated` flag from the slugify pass
|
|
2324
|
+
* Verbose sibling of `idFromTitle`: returns the unique id, the
|
|
2325
|
+
* `truncated` flag from the slugify pass, and the un-truncated
|
|
2326
|
+
* `originalSlug` for `--json` consumers. Collision-suffixing (`_2`,
|
|
1977
2327
|
* `_3`, …) does not flip `truncated` — the underlying slug's lossiness
|
|
1978
2328
|
* is what the CLI hint cares about.
|
|
1979
2329
|
*/
|
|
@@ -2012,9 +2362,42 @@ declare function listInProgress(db: Db, workstream: string): TaskRow[];
|
|
|
2012
2362
|
* raw-row type that was duplicating RawTaskRow
|
|
2013
2363
|
* (review_code_raw_task_state_duplicate). */
|
|
2014
2364
|
declare function listRecentClosed(db: Db, workstream: string, limit?: number): TaskRow[];
|
|
2365
|
+
/** Optional filter knobs for `listNotes`. Default-everything-undefined
|
|
2366
|
+
* preserves the historical "return every note, oldest-first" shape so
|
|
2367
|
+
* every existing caller (cmdTaskShow's notes block, exporting.ts's
|
|
2368
|
+
* bucket renderer, agents.test.ts) keeps working unchanged.
|
|
2369
|
+
*
|
|
2370
|
+
* Filters compose multiplicatively when both apply (`since` AND
|
|
2371
|
+
* `tail`): the timestamp filter is applied first, then `tail` slices
|
|
2372
|
+
* the last N of what survived. The CLI surface (`mu task notes
|
|
2373
|
+
* --tail / --since / --since-claim`) lives in src/cli/tasks/edit.ts;
|
|
2374
|
+
* the mutex between `--since` and `--since-claim` is a CLI concern,
|
|
2375
|
+
* not enforced here — if both arrive at the SDK, `since` wins (it's
|
|
2376
|
+
* the explicit one) and `sinceClaim` is ignored. The auto-resolve
|
|
2377
|
+
* for `sinceClaim` (look up the most recent `task claim` event in
|
|
2378
|
+
* agent_logs) happens here so the SDK is self-contained for scripted
|
|
2379
|
+
* callers. */
|
|
2380
|
+
interface ListNotesOptions {
|
|
2381
|
+
/** Print only the last N notes (after any timestamp filter). Must
|
|
2382
|
+
* be a positive integer; a value of 0 returns no rows but is not
|
|
2383
|
+
* an error here — CLI-side validation rejects `--tail 0`. */
|
|
2384
|
+
tail?: number;
|
|
2385
|
+
/** ISO-8601 cutoff: only notes with `created_at > since` survive.
|
|
2386
|
+
* Comparison is lexicographic on the ISO string (matches the way
|
|
2387
|
+
* the rest of the codebase compares ISO timestamps). */
|
|
2388
|
+
since?: string;
|
|
2389
|
+
/** When true and `since` is unset, look up the `created_at` of the
|
|
2390
|
+
* most recent `task claim` event for this task and use it as the
|
|
2391
|
+
* cutoff. Falls back to no filter when no claim event exists
|
|
2392
|
+
* (equivalent to `--since-beginning`). */
|
|
2393
|
+
sinceClaim?: boolean;
|
|
2394
|
+
}
|
|
2015
2395
|
/** List notes for a task. Operator-facing local_id; resolves to the
|
|
2016
|
-
* surrogate task id via taskIdFor (with optional workstream scope).
|
|
2017
|
-
|
|
2396
|
+
* surrogate task id via taskIdFor (with optional workstream scope).
|
|
2397
|
+
*
|
|
2398
|
+
* Optional filters: see {@link ListNotesOptions}. Default behaviour
|
|
2399
|
+
* (no opts) is unchanged — every note, oldest-first. */
|
|
2400
|
+
declare function listNotes(db: Db, taskLocalId: string, workstream: string, opts?: ListNotesOptions): TaskNoteRow[];
|
|
2018
2401
|
/**
|
|
2019
2402
|
* All tasks currently owned by `agent` in a given workstream
|
|
2020
2403
|
* (v5: agents.name is per-workstream unique). Sorted by local_id.
|
|
@@ -2295,8 +2678,8 @@ interface ExportSourceManifest {
|
|
|
2295
2678
|
tasks: ExportTaskEntry[];
|
|
2296
2679
|
}
|
|
2297
2680
|
/** Top-level bucket manifest. `bucketVersion: 2` — the v0.3 shape.
|
|
2298
|
-
*
|
|
2299
|
-
*
|
|
2681
|
+
* Manifests without `bucketVersion: 2` fall through to the
|
|
2682
|
+
* `corrupt` lane in `readManifest`. */
|
|
2300
2683
|
interface ExportManifest {
|
|
2301
2684
|
/** Schema discriminator. Always 2 in this codebase. */
|
|
2302
2685
|
bucketVersion: 2;
|
|
@@ -2345,17 +2728,6 @@ interface RenderBucketResult {
|
|
|
2345
2728
|
manifestPath: string;
|
|
2346
2729
|
manifest: ExportManifest;
|
|
2347
2730
|
}
|
|
2348
|
-
/** Thrown when the operator points an export at a directory whose
|
|
2349
|
-
* existing manifest predates bucket layout (v1, single-source). The
|
|
2350
|
-
* fix is destructive (remove and re-export) so we refuse to touch
|
|
2351
|
-
* it in-place — the legacy directory may be checked into git and
|
|
2352
|
-
* the operator should choose between rebuilding it and picking a
|
|
2353
|
-
* new --out. */
|
|
2354
|
-
declare class LegacyExportLayoutError extends Error {
|
|
2355
|
-
readonly outDir: string;
|
|
2356
|
-
readonly name = "LegacyExportLayoutError";
|
|
2357
|
-
constructor(outDir: string);
|
|
2358
|
-
}
|
|
2359
2731
|
/**
|
|
2360
2732
|
* Render `input.sources` to disk under `input.outDir` in the v0.3
|
|
2361
2733
|
* bucket layout. Idempotent + additive:
|
|
@@ -2364,8 +2736,6 @@ declare class LegacyExportLayoutError extends Error {
|
|
|
2364
2736
|
* `input.sources` either appends (new) or refreshes (existing)
|
|
2365
2737
|
* its subdirectory; sources NOT in `input.sources` are left
|
|
2366
2738
|
* untouched.
|
|
2367
|
-
* - If it exists but with a legacy (v1) manifest, throw
|
|
2368
|
-
* `LegacyExportLayoutError`.
|
|
2369
2739
|
*
|
|
2370
2740
|
* Per-task idempotency is sha256-keyed: a re-export of the same
|
|
2371
2741
|
* source against an unchanged DB rewrites zero task files. Tasks
|
|
@@ -2547,9 +2917,6 @@ interface ExportResult {
|
|
|
2547
2917
|
* exporting a different workstream into the same bucket appends a
|
|
2548
2918
|
* sibling subdir.
|
|
2549
2919
|
*
|
|
2550
|
-
* Throws:
|
|
2551
|
-
* - `LegacyExportLayoutError` if `outDir` already contains a
|
|
2552
|
-
* pre-0.3 (single-source) manifest.json.
|
|
2553
2920
|
*/
|
|
2554
2921
|
declare function exportWorkstream(db: Db, opts: ExportWorkstreamOptions): ExportResult;
|
|
2555
2922
|
|
|
@@ -2560,12 +2927,6 @@ declare class ImportBucketInvalidError extends Error implements HasNextSteps {
|
|
|
2560
2927
|
constructor(bucketDir: string, reason: string);
|
|
2561
2928
|
errorNextSteps(): NextStep[];
|
|
2562
2929
|
}
|
|
2563
|
-
declare class ImportLegacyLayoutError extends Error implements HasNextSteps {
|
|
2564
|
-
readonly bucketDir: string;
|
|
2565
|
-
readonly name = "ImportLegacyLayoutError";
|
|
2566
|
-
constructor(bucketDir: string);
|
|
2567
|
-
errorNextSteps(): NextStep[];
|
|
2568
|
-
}
|
|
2569
2930
|
declare class WorkstreamAlreadyExistsError extends Error implements HasNextSteps {
|
|
2570
2931
|
readonly workstream: string;
|
|
2571
2932
|
readonly name = "WorkstreamAlreadyExistsError";
|
|
@@ -2627,8 +2988,8 @@ interface ImportBucketResult {
|
|
|
2627
2988
|
* Per source-ws transactional: a failure in source A rolls back A
|
|
2628
2989
|
* but leaves source B's import committed.
|
|
2629
2990
|
*
|
|
2630
|
-
* Throws on unrecoverable bucket-level errors (no manifest,
|
|
2631
|
-
*
|
|
2991
|
+
* Throws on unrecoverable bucket-level errors (no manifest,
|
|
2992
|
+
* --workstream override against multi-source). Per-source
|
|
2632
2993
|
* errors (frontmatter parse, edge ref, target name collision) leave
|
|
2633
2994
|
* the failing source's `errors` array populated and that source's
|
|
2634
2995
|
* counts at zero; siblings still attempt their own import.
|
|
@@ -2790,6 +3151,11 @@ interface CreateWorkspaceOptions {
|
|
|
2790
3151
|
backend?: VcsBackendName | VcsBackend;
|
|
2791
3152
|
/** Optional ref to base the workspace on. Backend-specific. */
|
|
2792
3153
|
parentRef?: string;
|
|
3154
|
+
/** INTERNAL. When false, suppress the `workspace create` system
|
|
3155
|
+
* event. Used by `recreateWorkspace` so the audit trail records
|
|
3156
|
+
* ONE atomic `workspace recreate` line instead of separate
|
|
3157
|
+
* free + create entries. Defaults to true. */
|
|
3158
|
+
_suppressEvent?: boolean;
|
|
2793
3159
|
}
|
|
2794
3160
|
/**
|
|
2795
3161
|
* Create a fresh workspace for an agent. Allocates the on-disk
|
|
@@ -2806,6 +3172,12 @@ interface FreeWorkspaceOptions {
|
|
|
2806
3172
|
/** If true, attempt to commit pending changes before tearing down.
|
|
2807
3173
|
* Backend-specific; see VcsBackend.freeWorkspace. */
|
|
2808
3174
|
commit?: boolean;
|
|
3175
|
+
/** INTERNAL. When false, suppress the `workspace free` system
|
|
3176
|
+
* event AND skip the pre-mutation snapshot capture. Used by
|
|
3177
|
+
* `recreateWorkspace` so the audit trail records ONE atomic
|
|
3178
|
+
* `workspace recreate` line and one snapshot for the whole
|
|
3179
|
+
* free+create cycle. Defaults to true. */
|
|
3180
|
+
_suppressEvent?: boolean;
|
|
2809
3181
|
}
|
|
2810
3182
|
interface FreeWorkspaceResult {
|
|
2811
3183
|
/** The committed ref, when `commit` was true and there was something
|
|
@@ -2824,6 +3196,67 @@ interface FreeWorkspaceResult {
|
|
|
2824
3196
|
declare function freeWorkspace(db: Db, agent: string, opts: FreeWorkspaceOptions & {
|
|
2825
3197
|
workstream: string;
|
|
2826
3198
|
}): Promise<FreeWorkspaceResult>;
|
|
3199
|
+
interface RecreateWorkspaceOptions {
|
|
3200
|
+
/** Same as createWorkspace; defaults to cwd. */
|
|
3201
|
+
projectRoot?: string;
|
|
3202
|
+
/** Same as createWorkspace; if undefined the previous backend is
|
|
3203
|
+
* reused (auto-detection re-runs only when --backend was passed). */
|
|
3204
|
+
backend?: VcsBackendName | VcsBackend;
|
|
3205
|
+
/** Same as createWorkspace; if undefined the new workspace bases on
|
|
3206
|
+
* the backend's current head (for git/jj/sl: the project's main),
|
|
3207
|
+
* which is the whole point of the verb. */
|
|
3208
|
+
parentRef?: string;
|
|
3209
|
+
/** When true, skip the dirty-check refusal and discard any
|
|
3210
|
+
* uncommitted changes in the existing workspace. The lossy escape
|
|
3211
|
+
* hatch — mirrors the implicit semantics of `mu workspace free`
|
|
3212
|
+
* without --commit. */
|
|
3213
|
+
force?: boolean;
|
|
3214
|
+
}
|
|
3215
|
+
interface RecreateWorkspaceResult {
|
|
3216
|
+
/** The freshly-created workspace row (the previous row is already
|
|
3217
|
+
* gone by the time we return). */
|
|
3218
|
+
workspace: WorkspaceRow;
|
|
3219
|
+
/** parent_ref of the WORKSPACE BEFORE recreate, so callers (and the
|
|
3220
|
+
* CLI's success message) can show "bumped from <old> -> <new>". */
|
|
3221
|
+
previousParentRef: string | null;
|
|
3222
|
+
}
|
|
3223
|
+
/**
|
|
3224
|
+
* Free + create in one atomic-ish verb. The whole point: between
|
|
3225
|
+
* waves the operator wants the SAME agent name with a fresh workspace
|
|
3226
|
+
* pinned to current main; doing `free` then `create` manually was the
|
|
3227
|
+
* dogfood-painful pattern (mufeedback note add_mu_workspace_recreate_free_create).
|
|
3228
|
+
*
|
|
3229
|
+
* Behaviour:
|
|
3230
|
+
* - Refuses with WorkspaceDirtyError if the existing workspace has
|
|
3231
|
+
* uncommitted changes (git/sl), UNLESS `force: true` is passed
|
|
3232
|
+
* (lossy escape hatch). jj is always-snapshotted so dirty is never
|
|
3233
|
+
* an issue; `none` has no VCS to consult so it never refuses.
|
|
3234
|
+
* - Reuses the SAME backend the previous workspace had unless
|
|
3235
|
+
* `backend` is explicitly overridden — a between-wave refresh
|
|
3236
|
+
* should not silently swap from git to none because the operator
|
|
3237
|
+
* happened to cd into a non-VCS dir. The override path matches
|
|
3238
|
+
* `mu workspace create --backend ...` semantics.
|
|
3239
|
+
* - Emits ONE `workspace recreate <agent>` event (with both old and
|
|
3240
|
+
* new parent_ref in the payload) instead of separate free + create
|
|
3241
|
+
* events. One pre-mutation snapshot is captured under the same
|
|
3242
|
+
* label so the undo trail shows one step.
|
|
3243
|
+
*
|
|
3244
|
+
* Throws:
|
|
3245
|
+
* - WorkspaceNotFoundError — no row for this (agent, workstream).
|
|
3246
|
+
* - AgentNotFoundError — propagated from createWorkspace's
|
|
3247
|
+
* typed agent check (the agent row
|
|
3248
|
+
* was deleted between the lookup and
|
|
3249
|
+
* the re-INSERT; vanishingly rare).
|
|
3250
|
+
* - WorkspaceDirtyError — dirty + !force.
|
|
3251
|
+
* - any backend-level Error — free or create failed.
|
|
3252
|
+
*
|
|
3253
|
+
* On a free-side failure the row + on-disk dir are best-effort gone;
|
|
3254
|
+
* on a create-side failure we surface the create error and the row is
|
|
3255
|
+
* already deleted (the operator can re-run `mu workspace create`).
|
|
3256
|
+
*/
|
|
3257
|
+
declare function recreateWorkspace(db: Db, agent: string, opts: RecreateWorkspaceOptions & {
|
|
3258
|
+
workstream: string;
|
|
3259
|
+
}): Promise<RecreateWorkspaceResult>;
|
|
2827
3260
|
|
|
2828
3261
|
type LogKind = "message" | "event" | "broadcast" | string;
|
|
2829
3262
|
interface LogRow {
|
|
@@ -3127,4 +3560,4 @@ declare function deleteSnapshot(db: Db, snapshotId: number): DeleteSnapshotResul
|
|
|
3127
3560
|
* the file is missing. Useful for `mu snapshot list --json` output. */
|
|
3128
3561
|
declare function snapshotFileSize(snapshot: SnapshotRow): number | null;
|
|
3129
3562
|
|
|
3130
|
-
export { type AddNoteOptions, type AddTaskOptions, type AddToArchiveResult, type AdoptAgentOptions, type AdoptAgentResult, AgentDiedOnSpawnError, AgentExistsError, AgentNotFoundError, AgentNotInWorkstreamError, type AgentRow, type AgentStatus, type AppendLogOptions, type Archive, ArchiveAlreadyExistsError, ArchiveLabelInvalidError, ArchiveNotFoundError, type ArchiveSearchHit, type ArchiveSourceSummary, type ArchiveSummary, type ArchivedTaskRow, type BlockEdgeResult, CURRENT_SCHEMA_VERSION, type CaptureOptions, type CaptureSnapshotResult, type ClaimResult, type ClaimTaskOptions, ClaimerNotRegisteredError, type CloseAgentOptions, type CloseAgentResult, CrossWorkstreamEdgeError, CycleError, type Db, type DeleteSnapshotResult, type DeleteTaskResult, type DestroyResult, type DetectedStatus, EXPECTED_TABLES, type EvidenceOption, type ExportArchiveOptions, type ExportArchiveResult, type ExportManifest, type ExportResult, type ExportSource, type ExportSourceManifest, type ExportTaskEntry, type ExportWorkstreamOptions, type FreeAgentResult, HomeDirAsProjectRootError, type IdFromTitleResult, ImportBucketInvalidError, type ImportBucketOptions, type ImportBucketResult, ImportEdgeRefMissingError, ImportFrontmatterParseError,
|
|
3563
|
+
export { type AddNoteOptions, type AddTaskOptions, type AddToArchiveResult, type AdoptAgentOptions, type AdoptAgentResult, AgentDiedOnSpawnError, AgentExistsError, AgentNotFoundError, AgentNotInWorkstreamError, type AgentRow, AgentSpawnCliNotFoundError, AgentSpawnStartupError, type AgentStatus, type AppendLogOptions, type Archive, ArchiveAlreadyExistsError, ArchiveLabelInvalidError, ArchiveNotFoundError, type ArchiveSearchHit, type ArchiveSourceSummary, type ArchiveSummary, type ArchivedTaskRow, type BlockEdgeResult, CURRENT_SCHEMA_VERSION, type CaptureOptions, type CaptureSnapshotResult, type ClaimResult, type ClaimTaskOptions, ClaimerNotRegisteredError, type CloseAgentOptions, type CloseAgentResult, type CommandResolutionResult, type CommandResolver, CrossWorkstreamEdgeError, CycleError, type Db, type DeleteSnapshotResult, type DeleteTaskResult, type DestroyResult, type DetectedStatus, EXPECTED_TABLES, type EvidenceOption, type ExportArchiveOptions, type ExportArchiveResult, type ExportManifest, type ExportResult, type ExportSource, type ExportSourceManifest, type ExportTaskEntry, type ExportWorkstreamOptions, type FreeAgentResult, HomeDirAsProjectRootError, type IdFromTitleResult, ImportBucketInvalidError, type ImportBucketOptions, type ImportBucketResult, ImportEdgeRefMissingError, ImportFrontmatterParseError, type ImportSourceResult, type InsertAgentInput, type KickAgentOptions, type KickAgentResult, type KickProcessExecutor, type KickSignal, type ListArchivedTasksOptions, type ListLiveAgentsOptions, type ListLogsOptions, type ListNotesOptions, type ListReadyOptions, type ListSnapshotsOptions, type ListTasksOptions, type LiveAgentsView, type LogKind, type LogRow, type NewSessionOptions, type NewWindowOptions, NoForegroundProcessError, type OpenDbOptions, PANE_ID_RE, PaneNotFoundError, type PruneMode, type PruneOptions, PruneOptionsInvalidError, type PruneResult, type ReconcileMode, type ReconcileOptions, type ReconcileReport, type RejectDeferOptions, type RejectDeferResult, type ReleaseResult, type ReleaseTaskOptions, type RemoveBlockEdgeResult, type RemoveFromArchiveResult, type RenderBucketInput, type RenderBucketResult, type ReparentTaskResult, type RestoreSnapshotResult, STATUSES_TERMINAL_OR_PARKED, STATUS_EMOJI, SchemaTooOldError, type SearchArchivesOptions, type SearchTasksOptions, type SendOptions, type SetStatusResult, type SlugifyResult, SnapshotFileMissingError, SnapshotNotFoundError, type SnapshotRow, SnapshotVersionMismatchError, type SpawnAgentOptions, type SplitWindowOptions, type StrandedWorkspaceOrphan, TASK_STATUSES, TASK_STATUS_LIST, TaskAlreadyOwnedError, type TaskEdgeWithStatus, type TaskEdges, type TaskEdgesWithStatus, TaskExistsError, TaskHasOpenDependentsError, TaskNotFoundError, TaskNotInWorkstreamError, type TaskNoteRow, type TaskRow, type TaskStatus, type TaskWaitOptions, type TaskWaitRef, type TaskWaitResult, type TaskWaitTaskState, TmuxError, type TmuxExecResult, type TmuxExecutor, type TmuxPane, type TmuxSession, type TmuxWindow, type Track, type UpdateTaskOptions, type UpdateTaskResult, type VcsBackend, type VcsBackendName, type CreateWorkspaceOptions$1 as VcsCreateWorkspaceOptions, type CreateWorkspaceResult as VcsCreateWorkspaceResult, type FreeWorkspaceOptions$1 as VcsFreeWorkspaceOptions, type FreeWorkspaceResult$1 as VcsFreeWorkspaceResult, type CreateWorkspaceOptions as WorkspaceCreateOptions, WorkspaceExistsError, type WorkspaceFailure, type FreeWorkspaceOptions as WorkspaceFreeOptions, type FreeWorkspaceResult as WorkspaceFreeResult, WorkspaceNotFoundError, type WorkspaceOrphan, WorkspacePathNotEmptyError, WorkspacePreservedError, type RecreateWorkspaceOptions as WorkspaceRecreateOptions, type RecreateWorkspaceResult as WorkspaceRecreateResult, type WorkspaceRow, WorkstreamAlreadyExistsError, WorkstreamNameInvalidError, type WorkstreamOptions, type WorkstreamSummary, addBlockEdge, addNote, addTask, addToArchive, adoptAgent, appendLog, assertValidPaneId, backendByName, capturePane, captureSnapshot, checkCommandResolvable, claimTask, closeAgent, closeTask, composeAgentTitle, createArchive, createWorkspace, currentAgentName, currentPaneTitle, decorateWithStaleness, defaultDbPath, defaultSendDelayMs, defaultSpawnLivenessMs, defaultStateDir, deferTask, deleteAgent, deleteArchive, deleteSnapshot, deleteTask, destroyWorkstream, detectBackend, detectPiStatus, emitEvent, ensureWorkstream, ensureWorkstreamStateDir, envVarNameForCli, exportArchive, exportSourceForWorkstream, exportSourcesForArchive, exportWorkstream, extractTail, foregroundPgid, freeAgent, freeWorkspace, gcMaxAgeDays, gcMaxCount, gcSnapshots, getAgent, getAgentByPane, getArchive, getParallelTracks, getPrerequisites, getTask, getTaskEdges, getTaskEdgesWithStatus, getWaitPollCount, getWorkspaceForAgent, gitBackend, idFromTitle, idFromTitleVerbose, importBucket, insertAgent, isKickSignal, isStaleVersion, isTaskStatus, isValidAgentName, isValidArchiveLabel, isValidPaneId, isValidTaskId, isValidWorkstreamName, jjBackend, kickAgent, killPane, killSession, latestSeq, listAgents, listAllOrphanWorkspaces, listArchivedTasks, listArchives, listBlocked, listGoals, listInProgress, listLiveAgents, listLogs, listNotes, listPanes, listPanesInSession, listReady, listRecentClosed, listSessions, listSnapshots, listTasks, listTasksByOwner, listWindows, listWorkspaceOrphans, listWorkspaces, listWorkstreams, newSession, newSessionWithPane, newWindow, noneBackend, openDb, openTask, paneExists, paneTTY, parseAgentNameFromTitle, parsePsTtyOutput, pruneSnapshots, readAgent, reconcile, recreateWorkspace, refreshAgentTitle, rejectTask, releaseTask, removeBlockEdge, removeFromArchive, renderToBucket, reparentTask, resetCommandResolverForTests, resetKickProcessExecutor, resetSleep, resetTmuxExecutor, resetWaitPollCount, resolveActorIdentity, resolveCliCommand, resolveCliCommandWithSource, restoreSnapshot, searchArchives, searchTasks, selectLayout, sendToAgent, sendToPane, sessionExists, setCommandResolverForTests, setKickProcessExecutor, setPaneTitle, setSleepForTests, setTaskStatus, setTmuxExecutor, setWaitSleepForTests, setWaitStuckWarnForTests, slBackend, sleep, slugifyTitle, slugifyTitleVerbose, snapshotFileSize, snapshotsDir, spawnAgent, splitWindow, summarizeWorkstream, tmux$1 as tmux, updateAgentStatus, updateTask, waitForTasks, workspacePath, workspacesRoot, workstreamStateDir };
|