@gigaflow/gmux 0.15.0 → 0.16.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +110 -80
  2. package/README.md +81 -57
  3. package/dist/adapters/claude-code.js +5 -2
  4. package/dist/adapters/claude-code.js.map +1 -1
  5. package/dist/adapters/codex.js +5 -2
  6. package/dist/adapters/codex.js.map +1 -1
  7. package/dist/cli/border-client.d.ts +1 -1
  8. package/dist/cli/border-client.js +1 -1
  9. package/dist/cli/commands/cockpit.d.ts +16 -6
  10. package/dist/cli/commands/cockpit.js +288 -20
  11. package/dist/cli/commands/cockpit.js.map +1 -1
  12. package/dist/cli/commands/daemon.d.ts +2 -19
  13. package/dist/cli/commands/daemon.js +39 -88
  14. package/dist/cli/commands/daemon.js.map +1 -1
  15. package/dist/cli/commands/doctor.js +24 -0
  16. package/dist/cli/commands/doctor.js.map +1 -1
  17. package/dist/cli/commands/organize.d.ts +18 -0
  18. package/dist/cli/commands/organize.js +94 -0
  19. package/dist/cli/commands/organize.js.map +1 -0
  20. package/dist/cli/commands/setup.js +13 -1
  21. package/dist/cli/commands/setup.js.map +1 -1
  22. package/dist/cli/commands/tmux.d.ts +56 -0
  23. package/dist/cli/commands/tmux.js +158 -28
  24. package/dist/cli/commands/tmux.js.map +1 -1
  25. package/dist/cli/gmux-render.d.ts +12 -1
  26. package/dist/cli/gmux-render.js +18 -11
  27. package/dist/cli/gmux-render.js.map +1 -1
  28. package/dist/cli/main.js +2 -0
  29. package/dist/cli/main.js.map +1 -1
  30. package/dist/cli/organize-confirm.d.ts +45 -0
  31. package/dist/cli/organize-confirm.js +57 -0
  32. package/dist/cli/organize-confirm.js.map +1 -0
  33. package/dist/cli/overlay-ask.d.ts +3 -1
  34. package/dist/cli/overlay-ask.js +5 -3
  35. package/dist/cli/overlay-ask.js.map +1 -1
  36. package/dist/cli/tmux-label.d.ts +11 -4
  37. package/dist/cli/tmux-label.js +17 -6
  38. package/dist/cli/tmux-label.js.map +1 -1
  39. package/dist/cli/work-report.d.ts +18 -0
  40. package/dist/cli/work-report.js +50 -0
  41. package/dist/cli/work-report.js.map +1 -0
  42. package/dist/core/bytes.d.ts +6 -0
  43. package/dist/core/bytes.js +13 -0
  44. package/dist/core/bytes.js.map +1 -0
  45. package/dist/core/organize-types.d.ts +95 -0
  46. package/dist/core/organize-types.js +33 -0
  47. package/dist/core/organize-types.js.map +1 -0
  48. package/dist/core/paths.d.ts +13 -0
  49. package/dist/core/paths.js +20 -1
  50. package/dist/core/paths.js.map +1 -1
  51. package/dist/services/ask-router.d.ts +44 -0
  52. package/dist/services/ask-router.js +63 -0
  53. package/dist/services/ask-router.js.map +1 -0
  54. package/dist/services/daemon-lock.d.ts +31 -0
  55. package/dist/services/daemon-lock.js +77 -0
  56. package/dist/services/daemon-lock.js.map +1 -0
  57. package/dist/services/daemon.js +35 -6
  58. package/dist/services/daemon.js.map +1 -1
  59. package/dist/services/guardian.js +4 -4
  60. package/dist/services/guardian.js.map +1 -1
  61. package/dist/services/organize.d.ts +91 -0
  62. package/dist/services/organize.js +462 -0
  63. package/dist/services/organize.js.map +1 -0
  64. package/dist/services/tmux-gateway.d.ts +12 -0
  65. package/dist/services/tmux-gateway.js +7 -1
  66. package/dist/services/tmux-gateway.js.map +1 -1
  67. package/dist/services/tmux.d.ts +17 -1
  68. package/dist/services/tmux.js +34 -1
  69. package/dist/services/tmux.js.map +1 -1
  70. package/dist/services/work-view.d.ts +67 -0
  71. package/dist/services/work-view.js +155 -0
  72. package/dist/services/work-view.js.map +1 -0
  73. package/package.json +1 -1
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Pure plan representation for `gmux organize`.
3
+ *
4
+ * A plan never carries a destructive op: the tagged union below is 1:1 with
5
+ * the tmux gateway's relocation verbs (`newWindow`, `renameWindow`,
6
+ * `joinPane`, `breakPane`, `swapPane`, `selectLayout`) and nothing else. There
7
+ * is deliberately no `kill-pane`/`kill-window` step — see the design
8
+ * invariant in the organize spec: reorganization relocates panes, it never
9
+ * destroys their processes.
10
+ */
11
+ /** tmux preset layouts we allow the planner to choose. */
12
+ export const LAYOUTS = [
13
+ "tiled",
14
+ "even-horizontal",
15
+ "even-vertical",
16
+ "main-horizontal",
17
+ "main-vertical",
18
+ ];
19
+ export function isLayoutName(v) {
20
+ return LAYOUTS.includes(v);
21
+ }
22
+ /**
23
+ * Pure. The plan as numbered preview lines: the summary, then one line per step
24
+ * (`1. <description>`). No footer and no styling — callers append their own
25
+ * ("run with --apply" for the command, an apply/cancel hint for the overlay).
26
+ * Shared so the CLI command and the ctrl-g overlay render an identical plan.
27
+ */
28
+ export function organizePreviewLines(plan) {
29
+ const lines = [plan.summary];
30
+ plan.steps.forEach((step, i) => lines.push(`${i + 1}. ${step.description}`));
31
+ return lines;
32
+ }
33
+ //# sourceMappingURL=organize-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organize-types.js","sourceRoot":"","sources":["../../src/core/organize-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,0DAA0D;AAC1D,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,OAAO;IACP,iBAAiB;IACjB,eAAe;IACf,iBAAiB;IACjB,eAAe;CACP,CAAC;AAGX,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAQ,OAA6B,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAiDD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAkB;IACrD,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC7E,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -14,6 +14,10 @@
14
14
  * category is named here and in AGENTS.md #1 rather than left as a habit,
15
15
  * because a rule with an unwritten exception is a rule the next person
16
16
  * "fixes".
17
+ *
18
+ * One deliberate exception: `workReportPath()` lives under `tmpdir()`, not
19
+ * either root, because it's a disposable render target overwritten on every
20
+ * report, not gmux state.
17
21
  */
18
22
  /** Root of gmux's config. Honors XDG_CONFIG_HOME. */
19
23
  export declare function configDir(): string;
@@ -26,6 +30,15 @@ export declare function indexPath(): string;
26
30
  /** Directory of generated summaries, one JSON file per session. */
27
31
  export declare function summaryDir(): string;
28
32
  export declare function summaryPath(harness: string, sessionId: string): string;
33
+ /** Directory of generated work-view fragments, one JSON file per session. */
34
+ export declare function workViewDir(): string;
35
+ /** Cached work-view (metadata + HTML fragment) for one session. */
36
+ export declare function workViewPath(harness: string, sessionId: string): string;
37
+ /**
38
+ * The assembled work report. One stable file in the temp dir so re-pressing `v`
39
+ * overwrites it and the `file://` link the cockpit shows stays the same.
40
+ */
41
+ export declare function workReportPath(): string;
29
42
  /**
30
43
  * The pane→session links written by `gmux run`. Cache, not config: keyed by tmux
31
44
  * pane ids that die with the server, disposable, regenerated by living in the
@@ -14,8 +14,12 @@
14
14
  * category is named here and in AGENTS.md #1 rather than left as a habit,
15
15
  * because a rule with an unwritten exception is a rule the next person
16
16
  * "fixes".
17
+ *
18
+ * One deliberate exception: `workReportPath()` lives under `tmpdir()`, not
19
+ * either root, because it's a disposable render target overwritten on every
20
+ * report, not gmux state.
17
21
  */
18
- import { homedir } from "node:os";
22
+ import { homedir, tmpdir } from "node:os";
19
23
  import { join } from "node:path";
20
24
  /** Root of gmux's config. Honors XDG_CONFIG_HOME. */
21
25
  export function configDir() {
@@ -44,6 +48,21 @@ export function summaryDir() {
44
48
  export function summaryPath(harness, sessionId) {
45
49
  return join(summaryDir(), `${harness}-${sessionId}.json`);
46
50
  }
51
+ /** Directory of generated work-view fragments, one JSON file per session. */
52
+ export function workViewDir() {
53
+ return join(cacheDir(), "work-views");
54
+ }
55
+ /** Cached work-view (metadata + HTML fragment) for one session. */
56
+ export function workViewPath(harness, sessionId) {
57
+ return join(workViewDir(), `${harness}-${sessionId}.json`);
58
+ }
59
+ /**
60
+ * The assembled work report. One stable file in the temp dir so re-pressing `v`
61
+ * overwrites it and the `file://` link the cockpit shows stays the same.
62
+ */
63
+ export function workReportPath() {
64
+ return join(tmpdir(), "gmux-work-report.html");
65
+ }
47
66
  /**
48
67
  * The pane→session links written by `gmux run`. Cache, not config: keyed by tmux
49
68
  * pane ids that die with the server, disposable, regenerated by living in the
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/core/paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Ask-box intent router.
3
+ *
4
+ * The ctrl-g overlay's ask box takes free text. Most of it is a *question* for
5
+ * the running agents (the existing broadcast). Some of it is a request to
6
+ * *rearrange the tmux workspace* ("group these by project", "put the shells in
7
+ * their own window"). `classifyIntent` decides which, using the same provider
8
+ * plumbing as summaries — a one-shot CLI that reads a prompt and writes text.
9
+ *
10
+ * SAFETY: this defaults to "ask" on every uncertainty. Empty prompt, no
11
+ * provider configured, a spawn failure, a timeout, or any reply that is not an
12
+ * unambiguous "ORGANIZE" all resolve to "ask", so a normal question is never
13
+ * misread as a reorganization. The caller then previews-and-confirms before
14
+ * anything touches tmux; misclassifying the other direction (a genuine reorg
15
+ * read as a question) merely broadcasts it, which is harmless.
16
+ */
17
+ import { runProviderCommand } from "./provider-process.js";
18
+ export type AskIntent = "organize" | "ask";
19
+ export interface ClassifyDeps {
20
+ /**
21
+ * The provider argv. Left `undefined` it is resolved from config (same as the
22
+ * summarizer). Pass `null` for "no provider" and a concrete argv (e.g. a
23
+ * `node -e` fake) in tests. When passed, it is used as-is — no config read.
24
+ */
25
+ command?: string[] | null;
26
+ /** The spawn seam, injectable for tests. Defaults to the real provider run. */
27
+ run?: typeof runProviderCommand;
28
+ }
29
+ /** Pure. The one-shot classification prompt. */
30
+ export declare function buildClassifyPrompt(prompt: string): string;
31
+ /**
32
+ * Pure. Reads a raw provider reply as an intent, conservatively: only the
33
+ * unambiguous first word "organize" (case-insensitive, punctuation ignored)
34
+ * counts as ORGANIZE. Anything else — including empty, prose, or "ask" — is
35
+ * "ask". Mirrors the classifier's "reply with exactly one word" instruction
36
+ * while tolerating a trailing period or newline.
37
+ */
38
+ export declare function parseIntent(raw: string): AskIntent;
39
+ /**
40
+ * Decide whether an ask-box prompt is a workspace-reorganization request
41
+ * ("organize") or a question for the agents ("ask"). Never throws; every
42
+ * failure path returns "ask".
43
+ */
44
+ export declare function classifyIntent(prompt: string, deps?: ClassifyDeps): Promise<AskIntent>;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Ask-box intent router.
3
+ *
4
+ * The ctrl-g overlay's ask box takes free text. Most of it is a *question* for
5
+ * the running agents (the existing broadcast). Some of it is a request to
6
+ * *rearrange the tmux workspace* ("group these by project", "put the shells in
7
+ * their own window"). `classifyIntent` decides which, using the same provider
8
+ * plumbing as summaries — a one-shot CLI that reads a prompt and writes text.
9
+ *
10
+ * SAFETY: this defaults to "ask" on every uncertainty. Empty prompt, no
11
+ * provider configured, a spawn failure, a timeout, or any reply that is not an
12
+ * unambiguous "ORGANIZE" all resolve to "ask", so a normal question is never
13
+ * misread as a reorganization. The caller then previews-and-confirms before
14
+ * anything touches tmux; misclassifying the other direction (a genuine reorg
15
+ * read as a question) merely broadcasts it, which is harmless.
16
+ */
17
+ import { readConfig, resolveSummaryCommand } from "./config.js";
18
+ import { runProviderCommand } from "./provider-process.js";
19
+ /** Kept tight: this is a single-word classification, not a summary or an answer. */
20
+ const CLASSIFY_TIMEOUT_MS = 30_000;
21
+ /** Pure. The one-shot classification prompt. */
22
+ export function buildClassifyPrompt(prompt) {
23
+ return [
24
+ "Classify the user's message. Reply with exactly one word and nothing else:",
25
+ "ORGANIZE if it asks to rearrange/reorganize the tmux workspace layout (windows, panes, tiling, grouping, moving panes),",
26
+ "or ASK if it is a question or instruction for the running agents.",
27
+ "",
28
+ `Message: ${prompt}`,
29
+ ].join("\n");
30
+ }
31
+ /**
32
+ * Pure. Reads a raw provider reply as an intent, conservatively: only the
33
+ * unambiguous first word "organize" (case-insensitive, punctuation ignored)
34
+ * counts as ORGANIZE. Anything else — including empty, prose, or "ask" — is
35
+ * "ask". Mirrors the classifier's "reply with exactly one word" instruction
36
+ * while tolerating a trailing period or newline.
37
+ */
38
+ export function parseIntent(raw) {
39
+ const first = raw.toLowerCase().match(/[a-z]+/);
40
+ return first && first[0] === "organize" ? "organize" : "ask";
41
+ }
42
+ /**
43
+ * Decide whether an ask-box prompt is a workspace-reorganization request
44
+ * ("organize") or a question for the agents ("ask"). Never throws; every
45
+ * failure path returns "ask".
46
+ */
47
+ export async function classifyIntent(prompt, deps = {}) {
48
+ const trimmed = prompt.trim();
49
+ if (trimmed === "")
50
+ return "ask"; // Nothing to classify — and never spawn for it.
51
+ const argv = deps.command !== undefined ? deps.command : resolveSummaryCommand(await readConfig());
52
+ if (!argv || argv.length === 0)
53
+ return "ask"; // No provider is a "no", not an error.
54
+ try {
55
+ const run = deps.run ?? runProviderCommand;
56
+ const raw = await run(argv, buildClassifyPrompt(trimmed), { timeoutMs: CLASSIFY_TIMEOUT_MS });
57
+ return parseIntent(raw);
58
+ }
59
+ catch {
60
+ return "ask"; // Timeout, nonzero exit, spawn failure — default to the safe path.
61
+ }
62
+ }
63
+ //# sourceMappingURL=ask-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ask-router.js","sourceRoot":"","sources":["../../src/services/ask-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAI3D,oFAAoF;AACpF,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAanC,gDAAgD;AAChD,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,OAAO;QACL,4EAA4E;QAC5E,yHAAyH;QACzH,mEAAmE;QACnE,EAAE;QACF,YAAY,MAAM,EAAE;KACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChD,OAAO,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc,EAAE,OAAqB,EAAE;IAC1E,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC,CAAC,gDAAgD;IAElF,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,MAAM,UAAU,EAAE,CAAC,CAAC;IACnG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,uCAAuC;IAErF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,kBAAkB,CAAC;QAC3C,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAC;QAC9F,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC,CAAC,mEAAmE;IACnF,CAAC;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The `gmux daemon run` PID lockfile — extracted to its own leaf module (no
3
+ * dependents within `cli/`) so `cli/tmux-label.ts` can detect a live daemon
4
+ * without importing `cli/commands/daemon.ts` (which itself imports
5
+ * `cli/tmux-label.ts` for `enableBorder`, and a cycle between the two is
6
+ * fragile to depend on even where it happens to work).
7
+ *
8
+ * Same shape (`{ pid, startedAt }`) and staleness rule as
9
+ * `services/auto-summarize.ts`'s lock: owner dead, or too old. It lives under
10
+ * `gmuxDir()` rather than `cacheDir()` directly because it is gmux-specific
11
+ * ephemeral state, not shared with the rest of gmux.
12
+ */
13
+ /** A lock older than this belongs to a process that died without cleaning up. */
14
+ export declare const DAEMON_LOCK_STALE_MS: number;
15
+ export interface DaemonLock {
16
+ pid: number;
17
+ startedAt: string;
18
+ }
19
+ export declare function daemonLockPath(): string;
20
+ /** A lock is stale when its owner is gone, or when it is simply too old. */
21
+ export declare function isDaemonLockStale(lock: DaemonLock, now?: Date, staleMs?: number): boolean;
22
+ /** Corrupt or missing lock reads as "not running" — never throws. */
23
+ export declare function readDaemonLock(): Promise<DaemonLock | null>;
24
+ export declare function releaseDaemonLock(): Promise<void>;
25
+ /**
26
+ * Take the lock for this process, refusing if a live one already exists.
27
+ *
28
+ * Returns false — never throws — when another daemon is already running, so
29
+ * callers can print a clear message and exit rather than crash.
30
+ */
31
+ export declare function acquireDaemonLock(now?: Date): Promise<boolean>;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * The `gmux daemon run` PID lockfile — extracted to its own leaf module (no
3
+ * dependents within `cli/`) so `cli/tmux-label.ts` can detect a live daemon
4
+ * without importing `cli/commands/daemon.ts` (which itself imports
5
+ * `cli/tmux-label.ts` for `enableBorder`, and a cycle between the two is
6
+ * fragile to depend on even where it happens to work).
7
+ *
8
+ * Same shape (`{ pid, startedAt }`) and staleness rule as
9
+ * `services/auto-summarize.ts`'s lock: owner dead, or too old. It lives under
10
+ * `gmuxDir()` rather than `cacheDir()` directly because it is gmux-specific
11
+ * ephemeral state, not shared with the rest of gmux.
12
+ */
13
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
14
+ import { join } from "node:path";
15
+ import { gmuxDir } from "../core/paths.js";
16
+ /** A lock older than this belongs to a process that died without cleaning up. */
17
+ export const DAEMON_LOCK_STALE_MS = 10 * 60_000;
18
+ export function daemonLockPath() {
19
+ return join(gmuxDir(), "daemon.lock");
20
+ }
21
+ function processAlive(pid) {
22
+ if (pid <= 0)
23
+ return false;
24
+ try {
25
+ process.kill(pid, 0);
26
+ return true;
27
+ }
28
+ catch (error) {
29
+ // EPERM means the process exists but belongs to someone else — still alive.
30
+ return error.code === "EPERM";
31
+ }
32
+ }
33
+ /** A lock is stale when its owner is gone, or when it is simply too old. */
34
+ export function isDaemonLockStale(lock, now = new Date(), staleMs = DAEMON_LOCK_STALE_MS) {
35
+ const started = Date.parse(lock.startedAt);
36
+ if (Number.isNaN(started))
37
+ return true;
38
+ if (now.getTime() - started > staleMs)
39
+ return true;
40
+ return !processAlive(lock.pid);
41
+ }
42
+ /** Corrupt or missing lock reads as "not running" — never throws. */
43
+ export async function readDaemonLock() {
44
+ try {
45
+ const parsed = JSON.parse(await readFile(daemonLockPath(), "utf8"));
46
+ if (typeof parsed?.startedAt !== "string")
47
+ return null;
48
+ const pid = Number(parsed.pid);
49
+ if (!Number.isFinite(pid))
50
+ return null;
51
+ return { pid, startedAt: parsed.startedAt };
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ async function writeDaemonLock(lock) {
58
+ await mkdir(gmuxDir(), { recursive: true });
59
+ await writeFile(daemonLockPath(), JSON.stringify(lock), "utf8");
60
+ }
61
+ export async function releaseDaemonLock() {
62
+ await rm(daemonLockPath(), { force: true });
63
+ }
64
+ /**
65
+ * Take the lock for this process, refusing if a live one already exists.
66
+ *
67
+ * Returns false — never throws — when another daemon is already running, so
68
+ * callers can print a clear message and exit rather than crash.
69
+ */
70
+ export async function acquireDaemonLock(now = new Date()) {
71
+ const existing = await readDaemonLock();
72
+ if (existing && !isDaemonLockStale(existing, now))
73
+ return false;
74
+ await writeDaemonLock({ pid: process.pid, startedAt: now.toISOString() });
75
+ return true;
76
+ }
77
+ //# sourceMappingURL=daemon-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-lock.js","sourceRoot":"","sources":["../../src/services/daemon-lock.ts"],"names":[],"mappings":"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"}
@@ -38,6 +38,11 @@ export class Daemon {
38
38
  this.sensors.delete(id);
39
39
  this.deps.model.markGone(id);
40
40
  }
41
+ // Collected here, enqueued after the loop against a single snapshot below
42
+ // — looking each pane's just-applied entry up via `model.snapshot()`
43
+ // *inside* this loop would take an O(n)-sized snapshot on every one of
44
+ // its n iterations.
45
+ const toEnqueue = [];
41
46
  for (const id of present) {
42
47
  this.deps.model.upsertIdentity(id);
43
48
  let sensor = this.sensors.get(id.paneId);
@@ -48,16 +53,26 @@ export class Daemon {
48
53
  try {
49
54
  const obs = await sensor.observe(now);
50
55
  this.deps.model.applyState(id.paneId, classifyState(obs, now), obs.lastActivityTs, now);
51
- if (this.deps.semantic) {
52
- const entry = this.deps.model.snapshot().panes.find((p) => p.identity.paneId === id.paneId);
53
- if (entry)
54
- this.deps.semantic.enqueue(entry, obs, now); // fire-and-forget, never awaited
55
- }
56
+ if (this.deps.semantic)
57
+ toEnqueue.push({ paneId: id.paneId, obs });
56
58
  }
57
59
  catch {
58
60
  // A single sensor failure must never abort the whole tick.
59
61
  }
60
62
  }
63
+ // Enqueue any collected observations against a snapshot taken exactly
64
+ // once per tick, shared with the guardian's pane list below (when
65
+ // resources are sampled this tick) instead of each taking its own O(n)
66
+ // snapshot.
67
+ const enqueueSemantic = (byId) => {
68
+ if (!this.deps.semantic)
69
+ return;
70
+ for (const { paneId, obs } of toEnqueue) {
71
+ const entry = byId.get(paneId);
72
+ if (entry)
73
+ this.deps.semantic.enqueue(entry, obs, now); // fire-and-forget, never awaited
74
+ }
75
+ };
61
76
  if (this.deps.resources) {
62
77
  try {
63
78
  const panePids = new Map(present.map((p) => [p.paneId, p.pid]));
@@ -65,6 +80,13 @@ export class Daemon {
65
80
  for (const [paneId, res] of perPane)
66
81
  this.deps.model.applyResources(paneId, res);
67
82
  this.deps.model.setHostPressure(host);
83
+ // The one `model.snapshot()` call for this tick — taken after
84
+ // resources are applied so it (and the guardian below) sees this
85
+ // tick's resource data, and reused via `byId` for the semantic
86
+ // enqueue above instead of a second full pass.
87
+ const snap = this.deps.model.snapshot();
88
+ const byId = new Map(snap.panes.map((p) => [p.identity.paneId, p]));
89
+ enqueueSemantic(byId);
68
90
  if (this.deps.guardian) {
69
91
  const agentIds = this.deps.resolveAgents?.(present)
70
92
  ?? present.filter((p) => p.harness && p.sessionId).map((p) => p.paneId);
@@ -73,7 +95,7 @@ export class Daemon {
73
95
  // identity.sessionId. `resolveAgents` is a test-only override, so
74
96
  // when it's set, patch those fields (decision purposes only, never
75
97
  // persisted to the model) so the guardian agrees with the override.
76
- const panes = this.deps.model.snapshot().panes.map((p) => {
98
+ const panes = snap.panes.map((p) => {
77
99
  if (!this.deps.resolveAgents || !agentIdSet.has(p.identity.paneId) || p.identity.harness)
78
100
  return p;
79
101
  return { ...p, identity: { ...p.identity, harness: "test-agent", sessionId: p.identity.paneId } };
@@ -100,6 +122,13 @@ export class Daemon {
100
122
  // Resource sampling / guardian failures must never abort the tick.
101
123
  }
102
124
  }
125
+ else if (this.deps.semantic) {
126
+ // No resource sampling this tick, so take the single snapshot here
127
+ // instead — same timing as before this change, minus the redundant
128
+ // per-pane snapshots.
129
+ const snap = this.deps.model.snapshot();
130
+ enqueueSemantic(new Map(snap.panes.map((p) => [p.identity.paneId, p])));
131
+ }
103
132
  this.deps.model.evictGone();
104
133
  }
105
134
  }
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/services/daemon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/services/daemon.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { basename } from "node:path";
2
+ import { formatBytes } from "../core/bytes.js";
2
3
  /**
3
4
  * Pure policy state machine: pressure + policy + clock in, a decision out.
4
5
  * The ONE component that acts (types into agents via the caller), so it
@@ -63,9 +64,8 @@ function topConsumer(panes, host) {
63
64
  return { culprit: null, label: "a source outside tracked panes" };
64
65
  }
65
66
  const name = best.identity.cwd ? basename(best.identity.cwd) : best.identity.command;
66
- // Binary units (matches the cockpit's formatBytes) so the same pane shows
67
- // the same GB figure in both the cockpit memory column and this message.
68
- const gb = (bestRss / 1024 ** 3).toFixed(1);
69
- return { culprit: best.identity.paneId, label: `window \`${name}\` (${gb} GB)` };
67
+ // Shared with the cockpit's memory column so the same pane shows the same
68
+ // figure in both places.
69
+ return { culprit: best.identity.paneId, label: `window \`${name}\` (${formatBytes(bestRss)})` };
70
70
  }
71
71
  //# sourceMappingURL=guardian.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"guardian.js","sourceRoot":"","sources":["../../src/services/guardian.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guardian.js","sourceRoot":"","sources":["../../src/services/guardian.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * `gmux organize`: turns the live pane set into an `OrganizePlan` (grouping
3
+ * work by project into windows) and, on request, executes that plan against
4
+ * the tmux gateway.
5
+ *
6
+ * Two planners share the `OrganizePlanner` interface:
7
+ * - `HeuristicOrganizePlanner` — deterministic, no model call, no I/O. The
8
+ * graceful floor: `gmux organize` with no intent and no provider always
9
+ * has something sensible to do.
10
+ * - `LlmOrganizePlanner` — reuses the same provider-CLI plumbing as session
11
+ * summaries (`resolveSummaryCommand` + `runProviderCommand`) to turn a
12
+ * natural-language intent into a plan, and NEVER throws: any missing
13
+ * provider, timeout, or malformed reply falls back to the heuristic.
14
+ *
15
+ * `applyPlan` is the only place in this file that touches the gateway. It
16
+ * validates every step against the live pane/window set (plus handles bound
17
+ * by earlier steps in the same run) and skips — never throws for — a step
18
+ * that no longer makes sense, so one stale reference can't abort the rest of
19
+ * the plan.
20
+ *
21
+ * DESTRUCTION GUARANTEE: `applyPlan` only ever calls
22
+ * `newWindow`/`renameWindow`/`joinPane`/`breakPane`/`swapPane`/`selectLayout`
23
+ * on the gateway. None of those kills a process. A pane moved out of a window
24
+ * that tmux then closes (now empty) has already left *with* its process.
25
+ * There is no code path from a plan to `kill-pane`/`kill-window` — those
26
+ * verbs do not appear anywhere below, and `OrganizeStep` has no op that maps
27
+ * to one.
28
+ */
29
+ import { type OrganizePane, type OrganizePlan, type OrganizePlanner, type OrganizeStep } from "../core/organize-types.js";
30
+ import type { TmuxGateway } from "./tmux-gateway.js";
31
+ export type { OrganizePlanner } from "../core/organize-types.js";
32
+ /**
33
+ * Deterministic, pure, no-I/O planner. Groups agent panes by project
34
+ * (`basename(cwd)`) into one window each, and idle shells into a shared
35
+ * "shells" window. Ignores `intent` — it has no natural-language input to
36
+ * interpret, which is exactly why it is the safe default and the fallback
37
+ * for `LlmOrganizePlanner`.
38
+ */
39
+ export declare class HeuristicOrganizePlanner implements OrganizePlanner {
40
+ plan(panes: OrganizePane[], _intent?: string): Promise<OrganizePlan>;
41
+ }
42
+ /** Pure. The prompt an LLM planner is asked to reply to. */
43
+ export declare function buildOrganizePrompt(panes: OrganizePane[], intent: string): string;
44
+ /**
45
+ * Pure. Turns a raw model reply into a validated `OrganizePlan`, dropping —
46
+ * never throwing on — any step that is malformed, references an unknown
47
+ * pane, or references a window handle not bound by an earlier step in the
48
+ * same reply. Mirrors `parseSummaryFields`/`parseLabelFields`: take the
49
+ * outermost `{`…`}` rather than trusting the whole reply to parse, because
50
+ * models fence or preface their JSON more often than they should.
51
+ */
52
+ export declare function parseOrganizePlan(raw: string, panes: OrganizePane[]): OrganizePlan;
53
+ /**
54
+ * LLM-backed planner. Reuses the exact provider infra the semantic labeler
55
+ * uses (`resolveSummaryCommand` + `runProviderCommand`). Never throws to the
56
+ * caller: no provider configured, no intent given, a timeout, a malformed
57
+ * reply, or a reply that parses to zero usable steps all fall back to
58
+ * `fallback` (a fresh `HeuristicOrganizePlanner` by default).
59
+ *
60
+ * `argv`, when passed to the constructor, is used as-is (mainly for tests, to
61
+ * avoid touching real config/PATH); when omitted, it is resolved fresh from
62
+ * config on every `plan()` call, same as the labeler does.
63
+ */
64
+ export declare class LlmOrganizePlanner implements OrganizePlanner {
65
+ private readonly fallback;
66
+ private readonly argv?;
67
+ constructor(fallback?: OrganizePlanner, argv?: string[] | null | undefined);
68
+ plan(panes: OrganizePane[], intent?: string): Promise<OrganizePlan>;
69
+ }
70
+ export interface ApplyResult {
71
+ applied: OrganizeStep[];
72
+ skipped: Array<{
73
+ step: OrganizeStep;
74
+ reason: string;
75
+ }>;
76
+ }
77
+ /**
78
+ * Execute a plan against `gateway`, one step at a time, in order.
79
+ *
80
+ * Every step is validated against the live pane/window set (snapshotted once,
81
+ * up front) plus any window handle a prior step in THIS run has bound —
82
+ * never against a fresh `listPanes()` call, so the validation set is stable
83
+ * for the whole run. A step that fails validation, or whose gateway call
84
+ * throws (a tmux race, "can't break the only pane in a window", …), is
85
+ * pushed to `skipped` with a reason and execution continues — one bad step
86
+ * never aborts the rest of the plan.
87
+ *
88
+ * Only `newWindow`/`renameWindow`/`joinPane`/`breakPane`/`swapPane`/
89
+ * `selectLayout` are ever called here. None of those kills a process.
90
+ */
91
+ export declare function applyPlan(plan: OrganizePlan, gateway: TmuxGateway): Promise<ApplyResult>;