tmux-ide 2.6.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -9
- package/bin/cli.js +1013 -516
- package/bin/cli.ts +62 -5
- package/package.json +9 -6
- package/packages/daemon/dist/agent-explain.d.ts +8 -1
- package/packages/daemon/dist/agent-explain.js +19 -3
- package/packages/daemon/dist/lib/tui-binary.d.ts +57 -0
- package/packages/daemon/dist/lib/tui-binary.js +130 -0
- package/packages/daemon/dist/widgets/explorer/breadcrumbs.d.ts +1 -1
- package/packages/daemon/dist/widgets/explorer/footer.d.ts +1 -1
- package/packages/daemon/dist/widgets/explorer/tree.d.ts +1 -1
- package/packages/daemon/dist/widgets/lib/help-overlay.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/agent-naming.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/config-tree.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/detect-panel.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/field-editor.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/footer.d.ts +1 -1
- package/packages/daemon/dist/widgets/setup/layout-picker.d.ts +1 -1
- package/packages/daemon/src/agent-explain.ts +34 -6
- package/packages/daemon/src/lib/app-config.ts +93 -2
- package/packages/daemon/src/lib/tui-binary.ts +165 -0
- package/packages/daemon/src/tui/chrome/notify.ts +221 -34
- package/packages/daemon/src/tui/chrome/updater.ts +89 -7
- package/packages/daemon/src/tui/compiled.ts +11 -3
- package/packages/daemon/src/tui/detect/classify.ts +15 -0
- package/packages/daemon/src/tui/detect/manifest-loader.ts +2 -1
- package/packages/daemon/src/tui/detect/manifest.ts +15 -0
- package/packages/daemon/src/tui/detect/manifests.ts +161 -33
- package/packages/daemon/src/tui/detect/process-tree.ts +20 -0
- package/packages/daemon/src/tui/main.ts +13 -1
- package/packages/daemon/src/tui/mirror/ack-writer.ts +77 -0
- package/packages/daemon/src/tui/mirror/agent-chip.ts +97 -0
- package/packages/daemon/src/tui/mirror/agent-rows.ts +133 -0
- package/packages/daemon/src/tui/mirror/app-state.ts +179 -0
- package/packages/daemon/src/tui/mirror/app.tsx +5265 -0
- package/packages/daemon/src/tui/mirror/blit.ts +186 -0
- package/packages/daemon/src/tui/mirror/control-client.ts +80 -9
- package/packages/daemon/src/tui/mirror/dialog-model.ts +298 -0
- package/packages/daemon/src/tui/mirror/dialog-stack.ts +354 -0
- package/packages/daemon/src/tui/mirror/diff-model.ts +112 -0
- package/packages/daemon/src/tui/mirror/editor-buffer.ts +117 -0
- package/packages/daemon/src/tui/mirror/file-tree.ts +97 -0
- package/packages/daemon/src/tui/mirror/focus-border.ts +57 -0
- package/packages/daemon/src/tui/mirror/folder-picker.ts +124 -0
- package/packages/daemon/src/tui/mirror/home-model.ts +174 -0
- package/packages/daemon/src/tui/mirror/input-coalescer.ts +105 -0
- package/packages/daemon/src/tui/mirror/menu-model.ts +187 -0
- package/packages/daemon/src/tui/mirror/palette.ts +274 -0
- package/packages/daemon/src/tui/mirror/pane-mirror.ts +499 -19
- package/packages/daemon/src/tui/mirror/pane-surface.tsx +415 -0
- package/packages/daemon/src/tui/mirror/perf-tap.ts +160 -0
- package/packages/daemon/src/tui/mirror/resize-model.ts +85 -0
- package/packages/daemon/src/tui/mirror/scrollbar-model.ts +88 -0
- package/packages/daemon/src/tui/mirror/search-model.ts +70 -0
- package/packages/daemon/src/tui/mirror/selection.ts +262 -0
- package/packages/daemon/src/tui/mirror/session-mirror.ts +443 -0
- package/packages/daemon/src/tui/mirror/settings-model.ts +345 -0
- package/packages/daemon/src/tui/mirror/size-truth.ts +77 -0
- package/packages/daemon/src/tui/mirror/spans.ts +46 -0
- package/packages/daemon/src/tui/mirror/status-grammar.ts +32 -0
- package/packages/daemon/src/tui/team/entry.ts +34 -7
- package/packages/daemon/src/tui/team/report.ts +11 -1
- package/packages/daemon/src/tui/team/sessions.ts +102 -13
- package/scripts/build-tui.mjs +11 -4
- package/scripts/perf-mirror.mjs +313 -0
- package/skill/SKILL.md +22 -0
- package/templates/AGENTS.md +14 -7
- package/templates/agent-team-monorepo.yml +8 -0
- package/templates/agent-team-nextjs.yml +8 -0
- package/templates/agent-team.yml +10 -0
- package/templates/convex.yml +2 -0
- package/templates/default.yml +11 -5
- package/templates/go.yml +4 -0
- package/templates/missions.yml +6 -0
- package/templates/nextjs.yml +4 -0
- package/templates/python.yml +4 -0
- package/templates/skills/backend.md +5 -12
- package/templates/skills/frontend.md +5 -12
- package/templates/skills/general-worker.md +5 -12
- package/templates/skills/researcher.md +7 -12
- package/templates/skills/reviewer.md +7 -16
- package/templates/vite.yml +4 -0
- package/packages/daemon/src/tui/mirror/viewer.tsx +0 -166
package/bin/cli.js
CHANGED
|
@@ -1628,7 +1628,7 @@ var init_manifest = __esm({
|
|
|
1628
1628
|
});
|
|
1629
1629
|
|
|
1630
1630
|
// packages/daemon/src/tui/detect/manifests.ts
|
|
1631
|
-
var BRAILLE_SPINNER, CLAUDE, CODEX, OPENCODE, GEMINI, AIDER, COPILOT, SHELL, BUNDLED_MANIFESTS;
|
|
1631
|
+
var BRAILLE_SPINNER, CLAUDE, CODEX, OPENCODE, GEMINI, AIDER, COPILOT, CURSOR, GOOSE, AMP, SHELL, BUNDLED_MANIFESTS;
|
|
1632
1632
|
var init_manifests = __esm({
|
|
1633
1633
|
"packages/daemon/src/tui/detect/manifests.ts"() {
|
|
1634
1634
|
"use strict";
|
|
@@ -1636,6 +1636,7 @@ var init_manifests = __esm({
|
|
|
1636
1636
|
CLAUDE = {
|
|
1637
1637
|
id: "claude",
|
|
1638
1638
|
commands: ["claude"],
|
|
1639
|
+
confidence: "tuned",
|
|
1639
1640
|
states: {
|
|
1640
1641
|
// Approval / confirmation prompts — Claude is waiting on the user.
|
|
1641
1642
|
// Claude's approval UI is a bordered box asking a "Do you want …?" question
|
|
@@ -1682,40 +1683,56 @@ var init_manifests = __esm({
|
|
|
1682
1683
|
};
|
|
1683
1684
|
CODEX = {
|
|
1684
1685
|
id: "codex",
|
|
1685
|
-
commands: ["codex"],
|
|
1686
|
+
commands: ["codex", "codex.exe"],
|
|
1687
|
+
confidence: "tuned",
|
|
1686
1688
|
states: {
|
|
1689
|
+
// TUNED against real captures (codex-cli v0.142.5, driven through a turn).
|
|
1690
|
+
// The command-approval dialog and the directory-trust prompt are the two
|
|
1691
|
+
// "blocked" screens. Codex's approval menu uses a "› 1." numbered arrow —
|
|
1692
|
+
// note the arrow is "›" (U+203A), NOT claude's "❯".
|
|
1687
1693
|
blocked: {
|
|
1688
1694
|
any: [
|
|
1689
|
-
//
|
|
1690
|
-
//
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
{ region: "bottom", contains: "
|
|
1694
|
-
|
|
1695
|
-
{ region: "bottom", contains: "
|
|
1695
|
+
// seen (command approval): "Would you like to run the following
|
|
1696
|
+
// command?" above a "$ <cmd>" preview and the numbered menu.
|
|
1697
|
+
{ region: "bottom", contains: "Would you like to run", caseInsensitive: true },
|
|
1698
|
+
// seen: the highlighted approval option "› 1. Yes, proceed".
|
|
1699
|
+
{ region: "bottom", contains: "Yes, proceed" },
|
|
1700
|
+
// seen: "3. No, and tell Codex what to do differently (esc)".
|
|
1701
|
+
{ region: "bottom", contains: "No, and tell Codex" },
|
|
1702
|
+
// seen: the confirm footer under the approval menu.
|
|
1703
|
+
{ region: "bottom", contains: "Press enter to confirm", caseInsensitive: true },
|
|
1704
|
+
// seen (directory-trust prompt on first launch in an untrusted dir):
|
|
1705
|
+
// "Do you trust the contents of this directory?" + "1. Yes, continue".
|
|
1706
|
+
{ region: "bottom", contains: "Do you trust the contents", caseInsensitive: true }
|
|
1696
1707
|
]
|
|
1697
1708
|
},
|
|
1698
1709
|
working: {
|
|
1699
1710
|
any: [
|
|
1700
|
-
//
|
|
1701
|
-
//
|
|
1702
|
-
// the
|
|
1711
|
+
// seen (verbatim): the working status line is
|
|
1712
|
+
// "• Working (6s • esc to interrupt)".
|
|
1713
|
+
// Both the "Working (" prefix and the shared "esc to interrupt" hint
|
|
1714
|
+
// are present for the whole turn.
|
|
1715
|
+
{ region: "bottom", regex: "Working \\(\\d" },
|
|
1703
1716
|
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
1704
1717
|
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
1705
1718
|
{ region: "title", regex: BRAILLE_SPINNER }
|
|
1706
1719
|
]
|
|
1707
1720
|
}
|
|
1708
|
-
// done: omitted. NOTE (seen, NOT used):
|
|
1709
|
-
//
|
|
1710
|
-
// "
|
|
1711
|
-
//
|
|
1721
|
+
// done: omitted. NOTE (seen, NOT used): a finished turn leaves the agent's
|
|
1722
|
+
// answer above the idle "›" input box (placeholder "Find and fix a bug in
|
|
1723
|
+
// @filename") and a "gpt-5.5 xhigh · <cwd>" status line; older builds also
|
|
1724
|
+
// showed "Goal achieved (5m)". None are working/blocked evidence, so codex
|
|
1725
|
+
// correctly falls through to idle and the classifier infers done.
|
|
1712
1726
|
}
|
|
1713
1727
|
};
|
|
1714
1728
|
OPENCODE = {
|
|
1715
1729
|
id: "opencode",
|
|
1716
|
-
commands: ["opencode"],
|
|
1730
|
+
commands: ["opencode", "opencode.exe"],
|
|
1731
|
+
confidence: "conservative",
|
|
1717
1732
|
states: {
|
|
1718
|
-
//
|
|
1733
|
+
// conservative — a live capture was attempted (opencode v1.17.10) but its
|
|
1734
|
+
// local auth DB errored ("no such column: name") and the TUI rendered
|
|
1735
|
+
// blank, so these stay best-effort. High-precision only.
|
|
1719
1736
|
blocked: {
|
|
1720
1737
|
any: [
|
|
1721
1738
|
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
@@ -1735,8 +1752,10 @@ var init_manifests = __esm({
|
|
|
1735
1752
|
GEMINI = {
|
|
1736
1753
|
id: "gemini",
|
|
1737
1754
|
commands: ["gemini"],
|
|
1755
|
+
confidence: "conservative",
|
|
1738
1756
|
states: {
|
|
1739
|
-
//
|
|
1757
|
+
// conservative — gemini-cli needs a Google account/API key to reach a
|
|
1758
|
+
// working state, so no live capture was taken. High-precision only.
|
|
1740
1759
|
blocked: {
|
|
1741
1760
|
any: [
|
|
1742
1761
|
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
@@ -1758,28 +1777,43 @@ var init_manifests = __esm({
|
|
|
1758
1777
|
AIDER = {
|
|
1759
1778
|
id: "aider",
|
|
1760
1779
|
commands: ["aider"],
|
|
1780
|
+
confidence: "tuned",
|
|
1761
1781
|
states: {
|
|
1762
|
-
//
|
|
1763
|
-
//
|
|
1782
|
+
// TUNED from aider's installed source (v0.86.2). Every confirmation renders
|
|
1783
|
+
// through `io.confirm_ask` (io.py), which appends the literal option string
|
|
1784
|
+
// " (Y)es/(N)o" (plus "/(A)ll/(S)kip all" or "/(D)on't ask again") and a
|
|
1785
|
+
// "[Yes]:"/"[No]:" default — so "(Y)es/(N)o" is aider's exact, universal
|
|
1786
|
+
// blocked marker. The specific questions below are verbatim from
|
|
1787
|
+
// base_coder.py / commands.py.
|
|
1764
1788
|
blocked: {
|
|
1765
1789
|
any: [
|
|
1766
1790
|
{ region: "bottom", contains: "(Y)es/(N)o", caseInsensitive: true },
|
|
1767
|
-
{ region: "bottom", contains: "
|
|
1768
|
-
{ region: "bottom", contains: "
|
|
1791
|
+
{ region: "bottom", contains: "Add file to the chat", caseInsensitive: true },
|
|
1792
|
+
{ region: "bottom", contains: "Allow edits to file", caseInsensitive: true },
|
|
1793
|
+
{ region: "bottom", contains: "Add command output to the chat", caseInsensitive: true },
|
|
1794
|
+
{ region: "bottom", contains: "Run pip install", caseInsensitive: true }
|
|
1769
1795
|
]
|
|
1770
1796
|
},
|
|
1771
|
-
//
|
|
1772
|
-
//
|
|
1797
|
+
// TUNED: while a turn runs aider shows a `WaitingSpinner` (waiting.py)
|
|
1798
|
+
// rendered as "[░█ ] Waiting for <model>" — the text is literally
|
|
1799
|
+
// "Waiting for LLM" or "Waiting for " + the model name (base_coder.py:1440).
|
|
1800
|
+
// aider's spinner uses a "░█" scanner, NOT braille, so "Waiting for " is the
|
|
1801
|
+
// real invariant; the braille probe is kept only as a harmless fallback.
|
|
1773
1802
|
working: {
|
|
1774
|
-
any: [
|
|
1803
|
+
any: [
|
|
1804
|
+
{ region: "bottom", contains: "Waiting for ", caseInsensitive: false },
|
|
1805
|
+
{ region: "bottom", regex: BRAILLE_SPINNER }
|
|
1806
|
+
]
|
|
1775
1807
|
}
|
|
1776
1808
|
}
|
|
1777
1809
|
};
|
|
1778
1810
|
COPILOT = {
|
|
1779
1811
|
id: "copilot",
|
|
1780
1812
|
commands: ["copilot", "github-copilot", "github-copilot-cli"],
|
|
1813
|
+
confidence: "conservative",
|
|
1781
1814
|
states: {
|
|
1782
|
-
//
|
|
1815
|
+
// conservative — github-copilot-cli needs a GitHub account, so no live
|
|
1816
|
+
// capture was taken. High-precision only.
|
|
1783
1817
|
blocked: {
|
|
1784
1818
|
any: [
|
|
1785
1819
|
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
@@ -1796,9 +1830,89 @@ var init_manifests = __esm({
|
|
|
1796
1830
|
}
|
|
1797
1831
|
}
|
|
1798
1832
|
};
|
|
1833
|
+
CURSOR = {
|
|
1834
|
+
id: "cursor",
|
|
1835
|
+
commands: ["cursor-agent", "cursor"],
|
|
1836
|
+
confidence: "conservative",
|
|
1837
|
+
states: {
|
|
1838
|
+
// conservative — cursor-agent (Cursor CLI) was launched live but sits on a
|
|
1839
|
+
// "Press any key to log in…" pre-auth screen without an account, so no
|
|
1840
|
+
// working/blocked turn could be captured. The pre-auth splash ("Cursor
|
|
1841
|
+
// Agent" / "Press any key to log in") is idle chrome and deliberately NOT
|
|
1842
|
+
// matched here. Markers below are high-precision guesses from public
|
|
1843
|
+
// knowledge of its approval/streaming UI. NOTE: cursor-agent runs under
|
|
1844
|
+
// `node`, so it resolves via the process-tree (argv0 basename), not the
|
|
1845
|
+
// pane's `current_command`.
|
|
1846
|
+
blocked: {
|
|
1847
|
+
any: [
|
|
1848
|
+
{ region: "bottom", contains: "Do you want", caseInsensitive: false },
|
|
1849
|
+
{ region: "bottom", contains: "Run this command", caseInsensitive: true },
|
|
1850
|
+
{ region: "bottom", contains: "Apply this edit", caseInsensitive: true },
|
|
1851
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true }
|
|
1852
|
+
]
|
|
1853
|
+
},
|
|
1854
|
+
working: {
|
|
1855
|
+
any: [
|
|
1856
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
1857
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
1858
|
+
{ region: "title", regex: BRAILLE_SPINNER }
|
|
1859
|
+
]
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
};
|
|
1863
|
+
GOOSE = {
|
|
1864
|
+
id: "goose",
|
|
1865
|
+
commands: ["goose"],
|
|
1866
|
+
confidence: "conservative",
|
|
1867
|
+
states: {
|
|
1868
|
+
// conservative — Block's goose CLI needs a configured provider, so no live
|
|
1869
|
+
// capture was taken. High-precision only; markers are best-effort from
|
|
1870
|
+
// public knowledge of its confirmation/streaming UI.
|
|
1871
|
+
blocked: {
|
|
1872
|
+
any: [
|
|
1873
|
+
{ region: "bottom", contains: "Do you want", caseInsensitive: false },
|
|
1874
|
+
{ region: "bottom", contains: "Allow this tool", caseInsensitive: true },
|
|
1875
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
1876
|
+
{ region: "bottom", contains: "[y/n]", caseInsensitive: true }
|
|
1877
|
+
]
|
|
1878
|
+
},
|
|
1879
|
+
working: {
|
|
1880
|
+
any: [
|
|
1881
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
1882
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
1883
|
+
{ region: "title", regex: BRAILLE_SPINNER }
|
|
1884
|
+
]
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
};
|
|
1888
|
+
AMP = {
|
|
1889
|
+
id: "amp",
|
|
1890
|
+
commands: ["amp"],
|
|
1891
|
+
confidence: "conservative",
|
|
1892
|
+
states: {
|
|
1893
|
+
// conservative — Sourcegraph's amp CLI needs an account, so no live capture
|
|
1894
|
+
// was taken. High-precision only; markers are best-effort from public
|
|
1895
|
+
// knowledge of its approval/streaming UI.
|
|
1896
|
+
blocked: {
|
|
1897
|
+
any: [
|
|
1898
|
+
{ region: "bottom", contains: "Do you want", caseInsensitive: false },
|
|
1899
|
+
{ region: "bottom", contains: "Allow", caseInsensitive: false },
|
|
1900
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true }
|
|
1901
|
+
]
|
|
1902
|
+
},
|
|
1903
|
+
working: {
|
|
1904
|
+
any: [
|
|
1905
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
1906
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
1907
|
+
{ region: "title", regex: BRAILLE_SPINNER }
|
|
1908
|
+
]
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1799
1912
|
SHELL = {
|
|
1800
1913
|
id: "shell",
|
|
1801
1914
|
commands: ["bash", "zsh", "sh", "fish", "nu"],
|
|
1915
|
+
confidence: "conservative",
|
|
1802
1916
|
states: {
|
|
1803
1917
|
// Catch-all: a raw shell is almost always idle. We only flag an explicit
|
|
1804
1918
|
// interactive confirmation as blocked; "working" is unreliable to read
|
|
@@ -1818,6 +1932,9 @@ var init_manifests = __esm({
|
|
|
1818
1932
|
GEMINI,
|
|
1819
1933
|
AIDER,
|
|
1820
1934
|
COPILOT,
|
|
1935
|
+
CURSOR,
|
|
1936
|
+
GOOSE,
|
|
1937
|
+
AMP,
|
|
1821
1938
|
SHELL
|
|
1822
1939
|
];
|
|
1823
1940
|
}
|
|
@@ -1829,7 +1946,8 @@ __export(classify_exports, {
|
|
|
1829
1946
|
classifyInstant: () => classifyInstant,
|
|
1830
1947
|
classifyPaneCommand: () => classifyPaneCommand,
|
|
1831
1948
|
createStatusTracker: () => createStatusTracker,
|
|
1832
|
-
parseAuthority: () => parseAuthority
|
|
1949
|
+
parseAuthority: () => parseAuthority,
|
|
1950
|
+
parseAuthorityEpoch: () => parseAuthorityEpoch
|
|
1833
1951
|
});
|
|
1834
1952
|
function parseAuthority(raw, nowSec) {
|
|
1835
1953
|
if (!raw) return null;
|
|
@@ -1843,6 +1961,13 @@ function parseAuthority(raw, nowSec) {
|
|
|
1843
1961
|
}
|
|
1844
1962
|
return state;
|
|
1845
1963
|
}
|
|
1964
|
+
function parseAuthorityEpoch(raw) {
|
|
1965
|
+
if (!raw) return null;
|
|
1966
|
+
const sep2 = raw.lastIndexOf(":");
|
|
1967
|
+
if (sep2 === -1) return null;
|
|
1968
|
+
const epoch = Number(raw.slice(sep2 + 1));
|
|
1969
|
+
return Number.isFinite(epoch) ? epoch : null;
|
|
1970
|
+
}
|
|
1846
1971
|
function classifyInstant(snapshot, manifest) {
|
|
1847
1972
|
if (!manifest) return "unknown";
|
|
1848
1973
|
const { state } = evaluateManifest(snapshot, manifest);
|
|
@@ -2004,7 +2129,8 @@ function normalizeStates(m) {
|
|
|
2004
2129
|
if (m.states.blocked) states.blocked = m.states.blocked;
|
|
2005
2130
|
if (m.states.working) states.working = m.states.working;
|
|
2006
2131
|
if (m.states.done) states.done = m.states.done;
|
|
2007
|
-
|
|
2132
|
+
const confidence = m.confidence === "tuned" ? "tuned" : "conservative";
|
|
2133
|
+
return { id: m.id, commands: m.commands, states, confidence };
|
|
2008
2134
|
}
|
|
2009
2135
|
function warnOnce(path2, reason) {
|
|
2010
2136
|
if (warned.has(path2)) return;
|
|
@@ -2070,6 +2196,16 @@ function subtreeCommands(entries, rootPid, maxDepth = 6) {
|
|
|
2070
2196
|
walk(rootPid, 0);
|
|
2071
2197
|
return commands;
|
|
2072
2198
|
}
|
|
2199
|
+
function describeSubtree(entries, rootPid, limit = 8) {
|
|
2200
|
+
const seen = [];
|
|
2201
|
+
for (const command2 of subtreeCommands(entries, rootPid)) {
|
|
2202
|
+
for (const token of commandTokens(command2)) {
|
|
2203
|
+
if (!seen.includes(token)) seen.push(token);
|
|
2204
|
+
if (seen.length >= limit) return seen;
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
return seen;
|
|
2208
|
+
}
|
|
2073
2209
|
function readProcessTable() {
|
|
2074
2210
|
try {
|
|
2075
2211
|
const raw = execFileSync2("ps", ["-axo", "pid=,ppid=,command="], {
|
|
@@ -2159,6 +2295,7 @@ var init_snapshot = __esm({
|
|
|
2159
2295
|
var sessions_exports = {};
|
|
2160
2296
|
__export(sessions_exports, {
|
|
2161
2297
|
SIDEBAR_PANE_OPTION: () => SIDEBAR_PANE_OPTION,
|
|
2298
|
+
buildAgentEntry: () => buildAgentEntry,
|
|
2162
2299
|
excludeSidebarPanes: () => excludeSidebarPanes,
|
|
2163
2300
|
isListableSession: () => isListableSession,
|
|
2164
2301
|
listTeamSessions: () => listTeamSessions,
|
|
@@ -2166,6 +2303,22 @@ __export(sessions_exports, {
|
|
|
2166
2303
|
rollupWindows: () => rollupWindows
|
|
2167
2304
|
});
|
|
2168
2305
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
2306
|
+
function buildAgentEntry(input) {
|
|
2307
|
+
const { manifest, pane } = input;
|
|
2308
|
+
if (!manifest || manifest.id === "shell") return null;
|
|
2309
|
+
return {
|
|
2310
|
+
paneId: pane.id,
|
|
2311
|
+
windowIndex: pane.windowIndex,
|
|
2312
|
+
session: input.sessionName,
|
|
2313
|
+
kind: manifest.id,
|
|
2314
|
+
state: input.state,
|
|
2315
|
+
confidence: manifest.confidence ?? "conservative",
|
|
2316
|
+
since: input.since,
|
|
2317
|
+
title: pane.title,
|
|
2318
|
+
command: pane.cmd,
|
|
2319
|
+
dir: pane.dir
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2169
2322
|
function excludeSidebarPanes(panes) {
|
|
2170
2323
|
return panes.filter((pane) => !pane.sidebar);
|
|
2171
2324
|
}
|
|
@@ -2196,23 +2349,23 @@ function listTeamSessions(tracker, opts = {}) {
|
|
|
2196
2349
|
const panes = excludeSidebarPanes(panesBySession.get(name) ?? []);
|
|
2197
2350
|
const seen = opts.viewed === name;
|
|
2198
2351
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
2199
|
-
const
|
|
2352
|
+
const agents = [];
|
|
2200
2353
|
const statuses = panes.map((pane) => {
|
|
2201
2354
|
const authority = parseAuthority(pane.authority, nowSec);
|
|
2202
2355
|
let status2;
|
|
2203
2356
|
let manifest;
|
|
2357
|
+
let since = null;
|
|
2204
2358
|
if (authority !== null) {
|
|
2359
|
+
since = parseAuthorityEpoch(pane.authority);
|
|
2205
2360
|
if (authority === "done" && seen) {
|
|
2206
2361
|
ackDone(pane.id, nowSec);
|
|
2207
2362
|
status2 = "idle";
|
|
2208
2363
|
} else {
|
|
2209
2364
|
status2 = authority;
|
|
2210
2365
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
}).manifest;
|
|
2215
|
-
}
|
|
2366
|
+
manifest = resolveAgentCommand(pane.cmd, pane.pid, processTable, {
|
|
2367
|
+
hint: pane.hint
|
|
2368
|
+
}).manifest;
|
|
2216
2369
|
} else {
|
|
2217
2370
|
manifest = resolveAgentCommand(pane.cmd, pane.pid, processTable, {
|
|
2218
2371
|
hint: pane.hint
|
|
@@ -2226,6 +2379,8 @@ function listTeamSessions(tracker, opts = {}) {
|
|
|
2226
2379
|
agent: manifest && manifest.id !== "shell" ? manifest.id : null,
|
|
2227
2380
|
status: status2
|
|
2228
2381
|
});
|
|
2382
|
+
const entry = buildAgentEntry({ sessionName: name, pane, manifest, state: status2, since });
|
|
2383
|
+
if (entry) agents.push(entry);
|
|
2229
2384
|
return status2;
|
|
2230
2385
|
});
|
|
2231
2386
|
return {
|
|
@@ -2236,7 +2391,8 @@ function listTeamSessions(tracker, opts = {}) {
|
|
|
2236
2391
|
status: rollupStatus(statuses),
|
|
2237
2392
|
// `panes` and `statuses` are parallel (statuses = panes.map(...)), so
|
|
2238
2393
|
// the pure rollup can group each pane's window with its resolved status.
|
|
2239
|
-
windowList: rollupWindows(panes, statuses)
|
|
2394
|
+
windowList: rollupWindows(panes, statuses),
|
|
2395
|
+
agents
|
|
2240
2396
|
};
|
|
2241
2397
|
});
|
|
2242
2398
|
}
|
|
@@ -2245,9 +2401,11 @@ function collectPanes() {
|
|
|
2245
2401
|
"list-panes",
|
|
2246
2402
|
"-a",
|
|
2247
2403
|
"-F",
|
|
2248
|
-
// Window fields sit before pane_title so the (tab-safe)
|
|
2249
|
-
// trailing catch-all — window names don't contain tabs
|
|
2250
|
-
|
|
2404
|
+
// Window fields + pane_current_path sit before pane_title so the (tab-safe)
|
|
2405
|
+
// title stays the trailing catch-all — window names/paths don't contain tabs
|
|
2406
|
+
// in practice. pane_current_path rides this SAME list-panes call (no extra
|
|
2407
|
+
// tmux round-trip) so per-pane agent entries can carry a working dir.
|
|
2408
|
+
`#{session_name} #{pane_id} #{pane_pid} #{pane_current_command} #{@agent_state} #{@agent_hint} #{${SIDEBAR_PANE_OPTION}} #{window_index} #{window_name} #{window_active} #{pane_current_path} #{pane_title}`
|
|
2251
2409
|
]);
|
|
2252
2410
|
const bySession = /* @__PURE__ */ new Map();
|
|
2253
2411
|
for (const line of raw.split("\n").filter(Boolean)) {
|
|
@@ -2262,6 +2420,7 @@ function collectPanes() {
|
|
|
2262
2420
|
windowIndex = "0",
|
|
2263
2421
|
windowName = "",
|
|
2264
2422
|
windowActive = "0",
|
|
2423
|
+
dir = "",
|
|
2265
2424
|
...titleParts
|
|
2266
2425
|
] = line.split(" ");
|
|
2267
2426
|
if (!session) continue;
|
|
@@ -2276,6 +2435,7 @@ function collectPanes() {
|
|
|
2276
2435
|
windowIndex: Number(windowIndex) || 0,
|
|
2277
2436
|
windowName,
|
|
2278
2437
|
windowActive: windowActive === "1",
|
|
2438
|
+
dir,
|
|
2279
2439
|
title: titleParts.join(" ")
|
|
2280
2440
|
});
|
|
2281
2441
|
bySession.set(session, list);
|
|
@@ -2325,10 +2485,277 @@ var init_sessions2 = __esm({
|
|
|
2325
2485
|
}
|
|
2326
2486
|
});
|
|
2327
2487
|
|
|
2328
|
-
// packages/daemon/src/
|
|
2329
|
-
|
|
2330
|
-
|
|
2488
|
+
// packages/daemon/src/lib/update-check.ts
|
|
2489
|
+
var update_check_exports = {};
|
|
2490
|
+
__export(update_check_exports, {
|
|
2491
|
+
CHECK_INTERVAL_MS: () => CHECK_INTERVAL_MS,
|
|
2492
|
+
REGISTRY_URL: () => REGISTRY_URL,
|
|
2493
|
+
compareSemver: () => compareSemver,
|
|
2494
|
+
deriveStatus: () => deriveStatus,
|
|
2495
|
+
fetchLatestVersion: () => fetchLatestVersion,
|
|
2496
|
+
getCurrentVersion: () => getCurrentVersion,
|
|
2497
|
+
getUpdateStatus: () => getUpdateStatus,
|
|
2498
|
+
isNewer: () => isNewer,
|
|
2499
|
+
markUpdateNotified: () => markUpdateNotified,
|
|
2500
|
+
maybeCheckForUpdate: () => maybeCheckForUpdate,
|
|
2501
|
+
parseRegistryResponse: () => parseRegistryResponse,
|
|
2502
|
+
readUpdateCache: () => readUpdateCache,
|
|
2503
|
+
runUpdateCheck: () => runUpdateCheck,
|
|
2504
|
+
shouldCheck: () => shouldCheck,
|
|
2505
|
+
updateCachePath: () => updateCachePath,
|
|
2506
|
+
writeUpdateCache: () => writeUpdateCache
|
|
2507
|
+
});
|
|
2508
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2509
|
+
import { homedir as homedir2 } from "node:os";
|
|
2510
|
+
import { dirname, join as join2 } from "node:path";
|
|
2331
2511
|
import { fileURLToPath } from "node:url";
|
|
2512
|
+
function parseSemver(version) {
|
|
2513
|
+
const core = version.trim().replace(/^v/i, "").split("+")[0] ?? "";
|
|
2514
|
+
const dash = core.indexOf("-");
|
|
2515
|
+
const main = dash === -1 ? core : core.slice(0, dash);
|
|
2516
|
+
const pre = dash === -1 ? "" : core.slice(dash + 1);
|
|
2517
|
+
const parts = main.split(".");
|
|
2518
|
+
const num = (i) => {
|
|
2519
|
+
const n = Number.parseInt(parts[i] ?? "", 10);
|
|
2520
|
+
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
2521
|
+
};
|
|
2522
|
+
return { nums: [num(0), num(1), num(2)], pre };
|
|
2523
|
+
}
|
|
2524
|
+
function compareSemver(a, b) {
|
|
2525
|
+
const pa = parseSemver(a);
|
|
2526
|
+
const pb = parseSemver(b);
|
|
2527
|
+
for (let i = 0; i < 3; i++) {
|
|
2528
|
+
if (pa.nums[i] !== pb.nums[i]) return pa.nums[i] < pb.nums[i] ? -1 : 1;
|
|
2529
|
+
}
|
|
2530
|
+
if (pa.pre === pb.pre) return 0;
|
|
2531
|
+
if (pa.pre === "") return 1;
|
|
2532
|
+
if (pb.pre === "") return -1;
|
|
2533
|
+
return pa.pre < pb.pre ? -1 : 1;
|
|
2534
|
+
}
|
|
2535
|
+
function isNewer(latest, current) {
|
|
2536
|
+
return compareSemver(latest, current) === 1;
|
|
2537
|
+
}
|
|
2538
|
+
function shouldCheck(lastCheckedAt, nowMs) {
|
|
2539
|
+
if (lastCheckedAt === null) return true;
|
|
2540
|
+
return nowMs - lastCheckedAt >= CHECK_INTERVAL_MS;
|
|
2541
|
+
}
|
|
2542
|
+
function parseRegistryResponse(json2) {
|
|
2543
|
+
try {
|
|
2544
|
+
const parsed = JSON.parse(json2);
|
|
2545
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
2546
|
+
const version = parsed.version;
|
|
2547
|
+
return typeof version === "string" && version.length > 0 ? version : null;
|
|
2548
|
+
} catch {
|
|
2549
|
+
return null;
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
function deriveStatus(latest, currentVersion) {
|
|
2553
|
+
return {
|
|
2554
|
+
latest,
|
|
2555
|
+
updateAvailable: latest !== null && isNewer(latest, currentVersion)
|
|
2556
|
+
};
|
|
2557
|
+
}
|
|
2558
|
+
function updateCachePath() {
|
|
2559
|
+
const home = process.env.TMUX_IDE_HOME ?? join2(homedir2(), ".tmux-ide");
|
|
2560
|
+
return join2(home, "update-check.json");
|
|
2561
|
+
}
|
|
2562
|
+
function readUpdateCache() {
|
|
2563
|
+
const path2 = updateCachePath();
|
|
2564
|
+
if (!existsSync(path2)) return null;
|
|
2565
|
+
try {
|
|
2566
|
+
const parsed = JSON.parse(readFileSync3(path2, "utf-8"));
|
|
2567
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
2568
|
+
const obj = parsed;
|
|
2569
|
+
const lastCheckedAt = typeof obj.lastCheckedAt === "number" ? obj.lastCheckedAt : null;
|
|
2570
|
+
const latest = typeof obj.latest === "string" && obj.latest.length > 0 ? obj.latest : null;
|
|
2571
|
+
const notified = Array.isArray(obj.notified) ? obj.notified.filter((v) => typeof v === "string") : void 0;
|
|
2572
|
+
return { lastCheckedAt, latest, ...notified ? { notified } : {} };
|
|
2573
|
+
} catch {
|
|
2574
|
+
return null;
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
function writeUpdateCache(cache3) {
|
|
2578
|
+
const path2 = updateCachePath();
|
|
2579
|
+
try {
|
|
2580
|
+
mkdirSync(dirname(path2), { recursive: true });
|
|
2581
|
+
writeFileSync2(path2, JSON.stringify(cache3));
|
|
2582
|
+
} catch {
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
async function fetchLatestVersion(timeoutMs = 3e3) {
|
|
2586
|
+
const controller = new AbortController();
|
|
2587
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2588
|
+
try {
|
|
2589
|
+
const res = await fetch(REGISTRY_URL, { signal: controller.signal });
|
|
2590
|
+
if (!res.ok) return null;
|
|
2591
|
+
return parseRegistryResponse(await res.text());
|
|
2592
|
+
} catch {
|
|
2593
|
+
return null;
|
|
2594
|
+
} finally {
|
|
2595
|
+
clearTimeout(timer);
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
function getCurrentVersion() {
|
|
2599
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
2600
|
+
const candidates = [
|
|
2601
|
+
join2(here, "../package.json"),
|
|
2602
|
+
// bundled bin/cli.js → repo root
|
|
2603
|
+
join2(here, "../../../../package.json")
|
|
2604
|
+
// dev src/lib → repo root
|
|
2605
|
+
];
|
|
2606
|
+
for (const candidate of candidates) {
|
|
2607
|
+
try {
|
|
2608
|
+
const parsed = JSON.parse(readFileSync3(candidate, "utf-8"));
|
|
2609
|
+
if (typeof parsed.version === "string" && parsed.version.length > 0) return parsed.version;
|
|
2610
|
+
} catch {
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
return "0.0.0";
|
|
2614
|
+
}
|
|
2615
|
+
function getUpdateStatus({
|
|
2616
|
+
currentVersion = getCurrentVersion()
|
|
2617
|
+
} = {}) {
|
|
2618
|
+
const cache3 = readUpdateCache();
|
|
2619
|
+
return deriveStatus(cache3?.latest ?? null, currentVersion);
|
|
2620
|
+
}
|
|
2621
|
+
async function runUpdateCheck({ now = Date.now() } = {}) {
|
|
2622
|
+
const cache3 = readUpdateCache();
|
|
2623
|
+
if (!shouldCheck(cache3?.lastCheckedAt ?? null, now)) return;
|
|
2624
|
+
const fetched = await fetchLatestVersion();
|
|
2625
|
+
writeUpdateCache({
|
|
2626
|
+
lastCheckedAt: now,
|
|
2627
|
+
latest: fetched ?? cache3?.latest ?? null,
|
|
2628
|
+
...cache3?.notified ? { notified: cache3.notified } : {}
|
|
2629
|
+
});
|
|
2630
|
+
}
|
|
2631
|
+
function maybeCheckForUpdate({
|
|
2632
|
+
enabled,
|
|
2633
|
+
now = Date.now(),
|
|
2634
|
+
currentVersion = getCurrentVersion()
|
|
2635
|
+
}) {
|
|
2636
|
+
if (!enabled) return { latest: null, updateAvailable: false };
|
|
2637
|
+
const status2 = getUpdateStatus({ now, currentVersion });
|
|
2638
|
+
void runUpdateCheck({ now }).catch(() => {
|
|
2639
|
+
});
|
|
2640
|
+
return status2;
|
|
2641
|
+
}
|
|
2642
|
+
function markUpdateNotified(version) {
|
|
2643
|
+
const cache3 = readUpdateCache() ?? { lastCheckedAt: null, latest: null };
|
|
2644
|
+
const notified = cache3.notified ?? [];
|
|
2645
|
+
if (notified.includes(version)) return false;
|
|
2646
|
+
writeUpdateCache({ ...cache3, notified: [...notified, version] });
|
|
2647
|
+
return true;
|
|
2648
|
+
}
|
|
2649
|
+
var REGISTRY_URL, CHECK_INTERVAL_MS;
|
|
2650
|
+
var init_update_check = __esm({
|
|
2651
|
+
"packages/daemon/src/lib/update-check.ts"() {
|
|
2652
|
+
"use strict";
|
|
2653
|
+
REGISTRY_URL = "https://registry.npmjs.org/tmux-ide/latest";
|
|
2654
|
+
CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2655
|
+
}
|
|
2656
|
+
});
|
|
2657
|
+
|
|
2658
|
+
// packages/daemon/src/lib/tui-binary.ts
|
|
2659
|
+
var tui_binary_exports = {};
|
|
2660
|
+
__export(tui_binary_exports, {
|
|
2661
|
+
MIN_TUI_BINARY_BYTES: () => MIN_TUI_BINARY_BYTES,
|
|
2662
|
+
RELEASE_REPO: () => RELEASE_REPO,
|
|
2663
|
+
bunTargetForTag: () => bunTargetForTag,
|
|
2664
|
+
downloadTuiBinary: () => downloadTuiBinary,
|
|
2665
|
+
downloadedTuiPath: () => downloadedTuiPath,
|
|
2666
|
+
findDownloadedTui: () => findDownloadedTui,
|
|
2667
|
+
normalizeVersion: () => normalizeVersion,
|
|
2668
|
+
releaseAssetName: () => releaseAssetName,
|
|
2669
|
+
releaseAssetUrl: () => releaseAssetUrl,
|
|
2670
|
+
tuiPlatformTag: () => tuiPlatformTag,
|
|
2671
|
+
tuiStateHome: () => tuiStateHome
|
|
2672
|
+
});
|
|
2673
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2674
|
+
import { homedir as homedir3 } from "node:os";
|
|
2675
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
2676
|
+
import { gunzipSync } from "node:zlib";
|
|
2677
|
+
function tuiPlatformTag(platform = process.platform, arch = process.arch) {
|
|
2678
|
+
return SUPPORTED[`${platform}-${arch}`] ?? null;
|
|
2679
|
+
}
|
|
2680
|
+
function bunTargetForTag(tag) {
|
|
2681
|
+
return `bun-${tag}`;
|
|
2682
|
+
}
|
|
2683
|
+
function releaseAssetName(tag) {
|
|
2684
|
+
return `tmux-ide-tui-${tag}.gz`;
|
|
2685
|
+
}
|
|
2686
|
+
function normalizeVersion(version) {
|
|
2687
|
+
return version.startsWith("v") ? version.slice(1) : version;
|
|
2688
|
+
}
|
|
2689
|
+
function releaseAssetUrl(version, tag) {
|
|
2690
|
+
return `https://github.com/${RELEASE_REPO}/releases/download/v${normalizeVersion(version)}/${releaseAssetName(tag)}`;
|
|
2691
|
+
}
|
|
2692
|
+
function downloadedTuiPath(home, tag, version) {
|
|
2693
|
+
return join3(home, "bin", `tmux-ide-tui-${tag}-${normalizeVersion(version)}`);
|
|
2694
|
+
}
|
|
2695
|
+
function tuiStateHome() {
|
|
2696
|
+
return process.env.TMUX_IDE_HOME ?? join3(homedir3(), ".tmux-ide");
|
|
2697
|
+
}
|
|
2698
|
+
function findDownloadedTui(version = getCurrentVersion()) {
|
|
2699
|
+
const tag = tuiPlatformTag();
|
|
2700
|
+
if (!tag) return null;
|
|
2701
|
+
const path2 = downloadedTuiPath(tuiStateHome(), tag, version);
|
|
2702
|
+
return existsSync2(path2) ? path2 : null;
|
|
2703
|
+
}
|
|
2704
|
+
async function downloadTuiBinary(opts = {}) {
|
|
2705
|
+
const log = opts.log ?? (() => {
|
|
2706
|
+
});
|
|
2707
|
+
const version = normalizeVersion(opts.version ?? getCurrentVersion());
|
|
2708
|
+
const tag = tuiPlatformTag();
|
|
2709
|
+
if (!tag) {
|
|
2710
|
+
throw new Error(
|
|
2711
|
+
`no prebuilt TUI binary is published for ${process.platform}-${process.arch} \u2014 install bun (https://bun.sh) to run the TUI surfaces from source instead`
|
|
2712
|
+
);
|
|
2713
|
+
}
|
|
2714
|
+
const url = releaseAssetUrl(version, tag);
|
|
2715
|
+
const dest = downloadedTuiPath(tuiStateHome(), tag, version);
|
|
2716
|
+
mkdirSync2(dirname2(dest), { recursive: true });
|
|
2717
|
+
log(`downloading ${url}`);
|
|
2718
|
+
const res = await fetch(url);
|
|
2719
|
+
if (!res.ok) {
|
|
2720
|
+
throw new Error(
|
|
2721
|
+
`could not download the TUI binary (${url} \u2192 HTTP ${res.status} ${res.statusText}). Check that release v${version} exists and published its assets.`
|
|
2722
|
+
);
|
|
2723
|
+
}
|
|
2724
|
+
const gz = Buffer.from(await res.arrayBuffer());
|
|
2725
|
+
const bin = gunzipSync(gz);
|
|
2726
|
+
if (bin.byteLength < MIN_TUI_BINARY_BYTES) {
|
|
2727
|
+
throw new Error(
|
|
2728
|
+
`the downloaded TUI binary is only ${bin.byteLength} bytes (expected >10MB) \u2014 treating it as corrupt and leaving the previous binary (if any) in place`
|
|
2729
|
+
);
|
|
2730
|
+
}
|
|
2731
|
+
const tmp = `${dest}.${process.pid}.tmp`;
|
|
2732
|
+
writeFileSync3(tmp, bin, { mode: 493 });
|
|
2733
|
+
chmodSync(tmp, 493);
|
|
2734
|
+
renameSync(tmp, dest);
|
|
2735
|
+
const mb = (bin.byteLength / 1024 / 1024).toFixed(1);
|
|
2736
|
+
log(`installed ${dest} (${mb} MB)`);
|
|
2737
|
+
return { path: dest, bytes: bin.byteLength };
|
|
2738
|
+
}
|
|
2739
|
+
var RELEASE_REPO, MIN_TUI_BINARY_BYTES, SUPPORTED;
|
|
2740
|
+
var init_tui_binary = __esm({
|
|
2741
|
+
"packages/daemon/src/lib/tui-binary.ts"() {
|
|
2742
|
+
"use strict";
|
|
2743
|
+
init_update_check();
|
|
2744
|
+
RELEASE_REPO = "wavyrai/tmux-ide";
|
|
2745
|
+
MIN_TUI_BINARY_BYTES = 10 * 1024 * 1024;
|
|
2746
|
+
SUPPORTED = {
|
|
2747
|
+
"darwin-arm64": "darwin-arm64",
|
|
2748
|
+
"darwin-x64": "darwin-x64",
|
|
2749
|
+
"linux-x64": "linux-x64",
|
|
2750
|
+
"linux-arm64": "linux-arm64"
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
});
|
|
2754
|
+
|
|
2755
|
+
// packages/daemon/src/tui/compiled.ts
|
|
2756
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
2757
|
+
import { dirname as dirname3, resolve as resolve4 } from "node:path";
|
|
2758
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2332
2759
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2333
2760
|
function resolveTuiLaunch(input) {
|
|
2334
2761
|
if (input.checkoutExists && input.bunAvailable) {
|
|
@@ -2347,23 +2774,23 @@ function resolveTuiLaunch(input) {
|
|
|
2347
2774
|
reasons.push("the `bun` runtime is not installed (https://bun.sh)");
|
|
2348
2775
|
}
|
|
2349
2776
|
reasons.push(
|
|
2350
|
-
"no compiled `tmux-ide-tui` binary was found (build one with `pnpm build:tui`, or reinstall a release that ships it)"
|
|
2777
|
+
"no compiled `tmux-ide-tui` binary was found (build one with `pnpm build:tui`, download it with `tmux-ide update --tui-binary`, or reinstall a release that ships it)"
|
|
2351
2778
|
);
|
|
2352
2779
|
return { mode: "unavailable", reasons };
|
|
2353
2780
|
}
|
|
2354
2781
|
function findCompiledTui() {
|
|
2355
2782
|
const override = process.env.TMUX_IDE_TUI_BIN;
|
|
2356
|
-
if (override) return
|
|
2783
|
+
if (override) return existsSync3(override) ? override : null;
|
|
2357
2784
|
const anchors = [];
|
|
2358
|
-
if (process.argv[1]) anchors.push(
|
|
2785
|
+
if (process.argv[1]) anchors.push(dirname3(process.argv[1]));
|
|
2359
2786
|
anchors.push(__dirname);
|
|
2360
2787
|
for (const anchor of anchors) {
|
|
2361
2788
|
for (const rel of BINARY_RELS) {
|
|
2362
2789
|
const candidate = resolve4(anchor, rel);
|
|
2363
|
-
if (
|
|
2790
|
+
if (existsSync3(candidate)) return candidate;
|
|
2364
2791
|
}
|
|
2365
2792
|
}
|
|
2366
|
-
return
|
|
2793
|
+
return findDownloadedTui();
|
|
2367
2794
|
}
|
|
2368
2795
|
function isBunAvailable() {
|
|
2369
2796
|
try {
|
|
@@ -2377,7 +2804,8 @@ var __dirname, BINARY_RELS;
|
|
|
2377
2804
|
var init_compiled = __esm({
|
|
2378
2805
|
"packages/daemon/src/tui/compiled.ts"() {
|
|
2379
2806
|
"use strict";
|
|
2380
|
-
|
|
2807
|
+
init_tui_binary();
|
|
2808
|
+
__dirname = dirname3(fileURLToPath2(import.meta.url));
|
|
2381
2809
|
BINARY_RELS = [
|
|
2382
2810
|
"../packages/daemon/dist/tui/tmux-ide-tui",
|
|
2383
2811
|
"../../dist/tui/tmux-ide-tui",
|
|
@@ -2405,15 +2833,15 @@ __export(sidebar_exports, {
|
|
|
2405
2833
|
sidebarWidgetCommand: () => sidebarWidgetCommand,
|
|
2406
2834
|
sidebarWidgetScript: () => sidebarWidgetScript
|
|
2407
2835
|
});
|
|
2408
|
-
import { existsSync as
|
|
2409
|
-
import { dirname as
|
|
2410
|
-
import { fileURLToPath as
|
|
2836
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
2837
|
+
import { dirname as dirname4, resolve as resolve5 } from "node:path";
|
|
2838
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
2411
2839
|
function sidebarWidgetScript() {
|
|
2412
2840
|
const candidates = [
|
|
2413
2841
|
resolve5(__dirname2, "../../widgets/sidebar/index.tsx"),
|
|
2414
2842
|
resolve5(__dirname2, "../packages/daemon/src/widgets/sidebar/index.tsx")
|
|
2415
2843
|
];
|
|
2416
|
-
return candidates.find((p) =>
|
|
2844
|
+
return candidates.find((p) => existsSync4(p)) ?? candidates[0];
|
|
2417
2845
|
}
|
|
2418
2846
|
function sidebarWidgetCommand(scriptPath, session, dir, theme) {
|
|
2419
2847
|
const args = [`--session=${session}`, `--dir=${dir}`];
|
|
@@ -2422,7 +2850,7 @@ function sidebarWidgetCommand(scriptPath, session, dir, theme) {
|
|
|
2422
2850
|
surface: "sidebar",
|
|
2423
2851
|
scriptPath,
|
|
2424
2852
|
args,
|
|
2425
|
-
checkoutExists:
|
|
2853
|
+
checkoutExists: existsSync4(scriptPath),
|
|
2426
2854
|
bunAvailable: isBunAvailable(),
|
|
2427
2855
|
compiledBinary: findCompiledTui()
|
|
2428
2856
|
});
|
|
@@ -2501,7 +2929,7 @@ var init_sidebar = __esm({
|
|
|
2501
2929
|
init_shell();
|
|
2502
2930
|
init_sessions2();
|
|
2503
2931
|
init_compiled();
|
|
2504
|
-
__dirname2 =
|
|
2932
|
+
__dirname2 = dirname4(fileURLToPath3(import.meta.url));
|
|
2505
2933
|
SIDEBAR_KEY = "M-b";
|
|
2506
2934
|
DEFAULT_SIDEBAR_WIDTH = 30;
|
|
2507
2935
|
}
|
|
@@ -2514,12 +2942,12 @@ __export(resolve_exports, {
|
|
|
2514
2942
|
resolveWidgetCommand: () => resolveWidgetCommand,
|
|
2515
2943
|
resolveWidgetSpawn: () => resolveWidgetSpawn
|
|
2516
2944
|
});
|
|
2517
|
-
import { resolve as resolve6, dirname as
|
|
2518
|
-
import { existsSync as
|
|
2519
|
-
import { fileURLToPath as
|
|
2945
|
+
import { resolve as resolve6, dirname as dirname5 } from "node:path";
|
|
2946
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
2947
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
2520
2948
|
function widgetEntryPath(entry) {
|
|
2521
2949
|
const sibling = resolve6(__dirname3, entry);
|
|
2522
|
-
if (
|
|
2950
|
+
if (existsSync5(sibling)) return sibling;
|
|
2523
2951
|
return resolve6(__dirname3, "../packages/daemon/src/widgets", entry);
|
|
2524
2952
|
}
|
|
2525
2953
|
function widgetArgs(opts) {
|
|
@@ -2536,7 +2964,7 @@ function resolveWidgetCommand(type, opts) {
|
|
|
2536
2964
|
surface: type,
|
|
2537
2965
|
scriptPath,
|
|
2538
2966
|
args: widgetArgs(opts),
|
|
2539
|
-
checkoutExists:
|
|
2967
|
+
checkoutExists: existsSync5(scriptPath),
|
|
2540
2968
|
bunAvailable: isBunAvailable(),
|
|
2541
2969
|
compiledBinary: findCompiledTui()
|
|
2542
2970
|
});
|
|
@@ -2557,7 +2985,7 @@ function resolveWidgetSpawn(type, opts) {
|
|
|
2557
2985
|
surface: type,
|
|
2558
2986
|
scriptPath,
|
|
2559
2987
|
args: widgetArgs(opts),
|
|
2560
|
-
checkoutExists:
|
|
2988
|
+
checkoutExists: existsSync5(scriptPath),
|
|
2561
2989
|
bunAvailable: isBunAvailable(),
|
|
2562
2990
|
compiledBinary: findCompiledTui()
|
|
2563
2991
|
});
|
|
@@ -2573,7 +3001,7 @@ var init_resolve = __esm({
|
|
|
2573
3001
|
"use strict";
|
|
2574
3002
|
init_shell();
|
|
2575
3003
|
init_compiled();
|
|
2576
|
-
__dirname3 =
|
|
3004
|
+
__dirname3 = dirname5(fileURLToPath4(import.meta.url));
|
|
2577
3005
|
WIDGET_ENTRY_POINTS = {
|
|
2578
3006
|
explorer: "explorer/index.tsx",
|
|
2579
3007
|
changes: "changes/index.tsx",
|
|
@@ -2582,7 +3010,7 @@ var init_resolve = __esm({
|
|
|
2582
3010
|
config: "config/index.tsx",
|
|
2583
3011
|
sidebar: "sidebar/index.tsx"
|
|
2584
3012
|
};
|
|
2585
|
-
REPO_ROOT =
|
|
3013
|
+
REPO_ROOT = existsSync5(resolve6(__dirname3, "explorer/index.tsx")) ? resolve6(__dirname3, "../../../..") : resolve6(__dirname3, "..");
|
|
2586
3014
|
WIDGET_TYPES = Object.keys(WIDGET_ENTRY_POINTS);
|
|
2587
3015
|
}
|
|
2588
3016
|
});
|
|
@@ -2597,11 +3025,14 @@ __export(app_config_exports, {
|
|
|
2597
3025
|
appConfigPath: () => appConfigPath,
|
|
2598
3026
|
getAppConfig: () => getAppConfig,
|
|
2599
3027
|
loadAppConfig: () => loadAppConfig,
|
|
2600
|
-
|
|
3028
|
+
loadRawAppConfig: () => loadRawAppConfig,
|
|
3029
|
+
mergeConfigPatch: () => mergeConfigPatch,
|
|
3030
|
+
parseAppConfig: () => parseAppConfig,
|
|
3031
|
+
updateAppConfig: () => updateAppConfig
|
|
2601
3032
|
});
|
|
2602
|
-
import { existsSync as
|
|
2603
|
-
import { homedir as
|
|
2604
|
-
import { join as
|
|
3033
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3034
|
+
import { homedir as homedir4 } from "node:os";
|
|
3035
|
+
import { dirname as dirname6, join as join4 } from "node:path";
|
|
2605
3036
|
function asObject(value) {
|
|
2606
3037
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2607
3038
|
}
|
|
@@ -2629,6 +3060,7 @@ function parseAppConfig(input) {
|
|
|
2629
3060
|
const welcome = asObject(root.welcome);
|
|
2630
3061
|
const integrations = asObject(root.integrations);
|
|
2631
3062
|
const worktrees = asObject(root.worktrees);
|
|
3063
|
+
const app = asObject(root.app);
|
|
2632
3064
|
return {
|
|
2633
3065
|
keys: {
|
|
2634
3066
|
popup: pickString(keys.popup, D.keys.popup),
|
|
@@ -2670,17 +3102,18 @@ function parseAppConfig(input) {
|
|
|
2670
3102
|
updates: { check: pickBool(updates.check, D.updates.check) },
|
|
2671
3103
|
welcome: { show: pickBool(welcome.show, D.welcome.show) },
|
|
2672
3104
|
integrations: { offer: pickBool(integrations.offer, D.integrations.offer) },
|
|
2673
|
-
worktrees: { dir: pickString(worktrees.dir, D.worktrees.dir) }
|
|
3105
|
+
worktrees: { dir: pickString(worktrees.dir, D.worktrees.dir) },
|
|
3106
|
+
app: { frontDoor: pickBool(app.frontDoor, D.app.frontDoor) }
|
|
2674
3107
|
};
|
|
2675
3108
|
}
|
|
2676
3109
|
function appConfigPath() {
|
|
2677
|
-
return process.env.TMUX_IDE_CONFIG ??
|
|
3110
|
+
return process.env.TMUX_IDE_CONFIG ?? join4(homedir4(), ".tmux-ide", "config.json");
|
|
2678
3111
|
}
|
|
2679
3112
|
function loadAppConfig() {
|
|
2680
3113
|
const path2 = appConfigPath();
|
|
2681
|
-
if (!
|
|
3114
|
+
if (!existsSync6(path2)) return parseAppConfig(void 0);
|
|
2682
3115
|
try {
|
|
2683
|
-
return parseAppConfig(JSON.parse(
|
|
3116
|
+
return parseAppConfig(JSON.parse(readFileSync4(path2, "utf-8")));
|
|
2684
3117
|
} catch {
|
|
2685
3118
|
return parseAppConfig(void 0);
|
|
2686
3119
|
}
|
|
@@ -2692,6 +3125,45 @@ function getAppConfig() {
|
|
|
2692
3125
|
function _resetForTests() {
|
|
2693
3126
|
cached = null;
|
|
2694
3127
|
}
|
|
3128
|
+
function loadRawAppConfig() {
|
|
3129
|
+
const path2 = appConfigPath();
|
|
3130
|
+
if (!existsSync6(path2)) return {};
|
|
3131
|
+
try {
|
|
3132
|
+
const parsed = JSON.parse(readFileSync4(path2, "utf-8"));
|
|
3133
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
3134
|
+
} catch {
|
|
3135
|
+
return {};
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
function isPlainObject(value) {
|
|
3139
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
3140
|
+
}
|
|
3141
|
+
function mergeConfigPatch(raw, patch) {
|
|
3142
|
+
const out = { ...raw };
|
|
3143
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
3144
|
+
if (value === void 0) {
|
|
3145
|
+
delete out[key];
|
|
3146
|
+
} else if (isPlainObject(value) && isPlainObject(out[key])) {
|
|
3147
|
+
out[key] = mergeConfigPatch(out[key], value);
|
|
3148
|
+
} else if (isPlainObject(value)) {
|
|
3149
|
+
out[key] = mergeConfigPatch({}, value);
|
|
3150
|
+
} else {
|
|
3151
|
+
out[key] = value;
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
return out;
|
|
3155
|
+
}
|
|
3156
|
+
function updateAppConfig(patch) {
|
|
3157
|
+
const path2 = appConfigPath();
|
|
3158
|
+
const merged = mergeConfigPatch(loadRawAppConfig(), patch);
|
|
3159
|
+
mkdirSync3(dirname6(path2), { recursive: true });
|
|
3160
|
+
const tmp = `${path2}.${process.pid}.${Date.now()}.tmp`;
|
|
3161
|
+
writeFileSync4(tmp, `${JSON.stringify(merged, null, 2)}
|
|
3162
|
+
`, "utf-8");
|
|
3163
|
+
renameSync2(tmp, path2);
|
|
3164
|
+
cached = null;
|
|
3165
|
+
return parseAppConfig(merged);
|
|
3166
|
+
}
|
|
2695
3167
|
var DEFAULT_APP_CONFIG, DEFAULT_THEME, DEFAULT_KEYS, cached;
|
|
2696
3168
|
var init_app_config = __esm({
|
|
2697
3169
|
"packages/daemon/src/lib/app-config.ts"() {
|
|
@@ -2724,7 +3196,8 @@ var init_app_config = __esm({
|
|
|
2724
3196
|
updates: { check: true },
|
|
2725
3197
|
welcome: { show: true },
|
|
2726
3198
|
integrations: { offer: true },
|
|
2727
|
-
worktrees: { dir: "" }
|
|
3199
|
+
worktrees: { dir: "" },
|
|
3200
|
+
app: { frontDoor: false }
|
|
2728
3201
|
};
|
|
2729
3202
|
DEFAULT_THEME = DEFAULT_APP_CONFIG.theme;
|
|
2730
3203
|
DEFAULT_KEYS = DEFAULT_APP_CONFIG.keys;
|
|
@@ -2733,9 +3206,9 @@ var init_app_config = __esm({
|
|
|
2733
3206
|
});
|
|
2734
3207
|
|
|
2735
3208
|
// packages/daemon/src/tui/team/keymap.ts
|
|
2736
|
-
import { existsSync as
|
|
2737
|
-
import { homedir as
|
|
2738
|
-
import { join as
|
|
3209
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
|
|
3210
|
+
import { homedir as homedir5 } from "node:os";
|
|
3211
|
+
import { join as join5 } from "node:path";
|
|
2739
3212
|
var ACTION_ORDER, DEFAULT_KEYMAP;
|
|
2740
3213
|
var init_keymap = __esm({
|
|
2741
3214
|
"packages/daemon/src/tui/team/keymap.ts"() {
|
|
@@ -3164,24 +3637,24 @@ __export(welcome_exports, {
|
|
|
3164
3637
|
welcomeMarkerPath: () => welcomeMarkerPath
|
|
3165
3638
|
});
|
|
3166
3639
|
import { spawn as spawn2 } from "node:child_process";
|
|
3167
|
-
import { existsSync as
|
|
3168
|
-
import { homedir as
|
|
3169
|
-
import { dirname as
|
|
3640
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3641
|
+
import { homedir as homedir6 } from "node:os";
|
|
3642
|
+
import { dirname as dirname7, join as join6 } from "node:path";
|
|
3170
3643
|
function renderKey2(tmuxKey) {
|
|
3171
3644
|
return tmuxKey.replace(/M-/g, "\u2325").replace(/C-/g, "^").replace(/S-/g, "\u21E7");
|
|
3172
3645
|
}
|
|
3173
3646
|
function welcomeMarkerPath() {
|
|
3174
|
-
const home = process.env.TMUX_IDE_HOME ??
|
|
3175
|
-
return
|
|
3647
|
+
const home = process.env.TMUX_IDE_HOME ?? join6(homedir6(), ".tmux-ide");
|
|
3648
|
+
return join6(home, "welcomed");
|
|
3176
3649
|
}
|
|
3177
3650
|
function shouldShowWelcome() {
|
|
3178
|
-
return !
|
|
3651
|
+
return !existsSync8(welcomeMarkerPath()) && getAppConfig().welcome.show;
|
|
3179
3652
|
}
|
|
3180
3653
|
function markWelcomed() {
|
|
3181
3654
|
const path2 = welcomeMarkerPath();
|
|
3182
3655
|
try {
|
|
3183
|
-
|
|
3184
|
-
|
|
3656
|
+
mkdirSync4(dirname7(path2), { recursive: true });
|
|
3657
|
+
writeFileSync5(path2, (/* @__PURE__ */ new Date()).toISOString());
|
|
3185
3658
|
} catch {
|
|
3186
3659
|
}
|
|
3187
3660
|
}
|
|
@@ -3241,20 +3714,20 @@ __export(claude_exports, {
|
|
|
3241
3714
|
uninstallClaudeIntegration: () => uninstallClaudeIntegration
|
|
3242
3715
|
});
|
|
3243
3716
|
import {
|
|
3244
|
-
chmodSync,
|
|
3717
|
+
chmodSync as chmodSync2,
|
|
3245
3718
|
copyFileSync,
|
|
3246
|
-
existsSync as
|
|
3247
|
-
mkdirSync as
|
|
3248
|
-
readFileSync as
|
|
3249
|
-
writeFileSync as
|
|
3719
|
+
existsSync as existsSync9,
|
|
3720
|
+
mkdirSync as mkdirSync5,
|
|
3721
|
+
readFileSync as readFileSync6,
|
|
3722
|
+
writeFileSync as writeFileSync6
|
|
3250
3723
|
} from "node:fs";
|
|
3251
|
-
import { homedir as
|
|
3252
|
-
import { dirname as
|
|
3724
|
+
import { homedir as homedir7 } from "node:os";
|
|
3725
|
+
import { dirname as dirname8, join as join7 } from "node:path";
|
|
3253
3726
|
function hookScriptPath() {
|
|
3254
|
-
return
|
|
3727
|
+
return join7(homedir7(), HOOK_SCRIPT_RELPATH);
|
|
3255
3728
|
}
|
|
3256
3729
|
function claudeSettingsPath() {
|
|
3257
|
-
return process.env.TMUX_IDE_CLAUDE_SETTINGS ??
|
|
3730
|
+
return process.env.TMUX_IDE_CLAUDE_SETTINGS ?? join7(homedir7(), ".claude", "settings.json");
|
|
3258
3731
|
}
|
|
3259
3732
|
function isOurs(group) {
|
|
3260
3733
|
return group.hooks?.some((h) => h.command?.includes(HOOK_SCRIPT_RELPATH)) ?? false;
|
|
@@ -3287,24 +3760,24 @@ function isInstalled(settings) {
|
|
|
3287
3760
|
return Object.values(settings.hooks ?? {}).some((groups) => groups.some(isOurs));
|
|
3288
3761
|
}
|
|
3289
3762
|
function readSettings(path2) {
|
|
3290
|
-
if (!
|
|
3763
|
+
if (!existsSync9(path2)) return {};
|
|
3291
3764
|
try {
|
|
3292
|
-
return JSON.parse(
|
|
3765
|
+
return JSON.parse(readFileSync6(path2, "utf8"));
|
|
3293
3766
|
} catch {
|
|
3294
3767
|
throw new Error(`${path2} is not valid JSON \u2014 fix or move it, then retry`);
|
|
3295
3768
|
}
|
|
3296
3769
|
}
|
|
3297
3770
|
function installClaudeIntegration() {
|
|
3298
3771
|
const script = hookScriptPath();
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3772
|
+
mkdirSync5(dirname8(script), { recursive: true });
|
|
3773
|
+
writeFileSync6(script, HOOK_SCRIPT, "utf8");
|
|
3774
|
+
chmodSync2(script, 493);
|
|
3302
3775
|
const settingsPath = claudeSettingsPath();
|
|
3303
|
-
|
|
3776
|
+
mkdirSync5(dirname8(settingsPath), { recursive: true });
|
|
3304
3777
|
const settings = readSettings(settingsPath);
|
|
3305
3778
|
const backup = `${settingsPath}.tmux-ide.bak`;
|
|
3306
|
-
if (
|
|
3307
|
-
|
|
3779
|
+
if (existsSync9(settingsPath) && !existsSync9(backup)) copyFileSync(settingsPath, backup);
|
|
3780
|
+
writeFileSync6(settingsPath, `${JSON.stringify(mergeHooks(settings, script), null, 2)}
|
|
3308
3781
|
`, "utf8");
|
|
3309
3782
|
return { scriptPath: script, settingsPath };
|
|
3310
3783
|
}
|
|
@@ -3313,7 +3786,7 @@ function uninstallClaudeIntegration() {
|
|
|
3313
3786
|
const settings = readSettings(settingsPath);
|
|
3314
3787
|
const wasInstalled = isInstalled(settings);
|
|
3315
3788
|
if (wasInstalled) {
|
|
3316
|
-
|
|
3789
|
+
writeFileSync6(settingsPath, `${JSON.stringify(removeHooks(settings), null, 2)}
|
|
3317
3790
|
`, "utf8");
|
|
3318
3791
|
}
|
|
3319
3792
|
return { settingsPath, wasInstalled };
|
|
@@ -3321,7 +3794,7 @@ function uninstallClaudeIntegration() {
|
|
|
3321
3794
|
function claudeIntegrationStatus() {
|
|
3322
3795
|
return {
|
|
3323
3796
|
installed: isInstalled(readSettings(claudeSettingsPath())),
|
|
3324
|
-
scriptExists:
|
|
3797
|
+
scriptExists: existsSync9(hookScriptPath())
|
|
3325
3798
|
};
|
|
3326
3799
|
}
|
|
3327
3800
|
var HOOK_SCRIPT_RELPATH, HOOK_SCRIPT, EVENT_STATES;
|
|
@@ -3360,12 +3833,12 @@ __export(offer_exports, {
|
|
|
3360
3833
|
shouldOfferIntegration: () => shouldOfferIntegration
|
|
3361
3834
|
});
|
|
3362
3835
|
import { execFileSync as execFileSync5, spawn as spawn3 } from "node:child_process";
|
|
3363
|
-
import { existsSync as
|
|
3364
|
-
import { homedir as
|
|
3365
|
-
import { dirname as
|
|
3836
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
3837
|
+
import { homedir as homedir8 } from "node:os";
|
|
3838
|
+
import { dirname as dirname9, join as join8 } from "node:path";
|
|
3366
3839
|
function integrationOfferMarkerPath() {
|
|
3367
|
-
const home = process.env.TMUX_IDE_HOME ??
|
|
3368
|
-
return
|
|
3840
|
+
const home = process.env.TMUX_IDE_HOME ?? join8(homedir8(), ".tmux-ide");
|
|
3841
|
+
return join8(home, "integration-offered");
|
|
3369
3842
|
}
|
|
3370
3843
|
function shouldOfferIntegration(input) {
|
|
3371
3844
|
return input.claudeOnPath && !input.integrationInstalled && !input.markerPresent && input.offerEnabled;
|
|
@@ -3373,8 +3846,8 @@ function shouldOfferIntegration(input) {
|
|
|
3373
3846
|
function markIntegrationOffered() {
|
|
3374
3847
|
const path2 = integrationOfferMarkerPath();
|
|
3375
3848
|
try {
|
|
3376
|
-
|
|
3377
|
-
|
|
3849
|
+
mkdirSync6(dirname9(path2), { recursive: true });
|
|
3850
|
+
writeFileSync7(path2, (/* @__PURE__ */ new Date()).toISOString());
|
|
3378
3851
|
} catch {
|
|
3379
3852
|
}
|
|
3380
3853
|
}
|
|
@@ -3399,7 +3872,7 @@ function maybeOfferIntegrationPopup() {
|
|
|
3399
3872
|
offer = shouldOfferIntegration({
|
|
3400
3873
|
claudeOnPath: claudeOnPath(),
|
|
3401
3874
|
integrationInstalled: status2.installed,
|
|
3402
|
-
markerPresent:
|
|
3875
|
+
markerPresent: existsSync10(integrationOfferMarkerPath()),
|
|
3403
3876
|
offerEnabled: getAppConfig().integrations.offer
|
|
3404
3877
|
});
|
|
3405
3878
|
} catch {
|
|
@@ -3455,176 +3928,6 @@ var init_kitty_keys = __esm({
|
|
|
3455
3928
|
}
|
|
3456
3929
|
});
|
|
3457
3930
|
|
|
3458
|
-
// packages/daemon/src/lib/update-check.ts
|
|
3459
|
-
var update_check_exports = {};
|
|
3460
|
-
__export(update_check_exports, {
|
|
3461
|
-
CHECK_INTERVAL_MS: () => CHECK_INTERVAL_MS,
|
|
3462
|
-
REGISTRY_URL: () => REGISTRY_URL,
|
|
3463
|
-
compareSemver: () => compareSemver,
|
|
3464
|
-
deriveStatus: () => deriveStatus,
|
|
3465
|
-
fetchLatestVersion: () => fetchLatestVersion,
|
|
3466
|
-
getCurrentVersion: () => getCurrentVersion,
|
|
3467
|
-
getUpdateStatus: () => getUpdateStatus,
|
|
3468
|
-
isNewer: () => isNewer,
|
|
3469
|
-
markUpdateNotified: () => markUpdateNotified,
|
|
3470
|
-
maybeCheckForUpdate: () => maybeCheckForUpdate,
|
|
3471
|
-
parseRegistryResponse: () => parseRegistryResponse,
|
|
3472
|
-
readUpdateCache: () => readUpdateCache,
|
|
3473
|
-
runUpdateCheck: () => runUpdateCheck,
|
|
3474
|
-
shouldCheck: () => shouldCheck,
|
|
3475
|
-
updateCachePath: () => updateCachePath,
|
|
3476
|
-
writeUpdateCache: () => writeUpdateCache
|
|
3477
|
-
});
|
|
3478
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3479
|
-
import { homedir as homedir7 } from "node:os";
|
|
3480
|
-
import { dirname as dirname7, join as join7 } from "node:path";
|
|
3481
|
-
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
3482
|
-
function parseSemver(version) {
|
|
3483
|
-
const core = version.trim().replace(/^v/i, "").split("+")[0] ?? "";
|
|
3484
|
-
const dash = core.indexOf("-");
|
|
3485
|
-
const main = dash === -1 ? core : core.slice(0, dash);
|
|
3486
|
-
const pre = dash === -1 ? "" : core.slice(dash + 1);
|
|
3487
|
-
const parts = main.split(".");
|
|
3488
|
-
const num = (i) => {
|
|
3489
|
-
const n = Number.parseInt(parts[i] ?? "", 10);
|
|
3490
|
-
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
3491
|
-
};
|
|
3492
|
-
return { nums: [num(0), num(1), num(2)], pre };
|
|
3493
|
-
}
|
|
3494
|
-
function compareSemver(a, b) {
|
|
3495
|
-
const pa = parseSemver(a);
|
|
3496
|
-
const pb = parseSemver(b);
|
|
3497
|
-
for (let i = 0; i < 3; i++) {
|
|
3498
|
-
if (pa.nums[i] !== pb.nums[i]) return pa.nums[i] < pb.nums[i] ? -1 : 1;
|
|
3499
|
-
}
|
|
3500
|
-
if (pa.pre === pb.pre) return 0;
|
|
3501
|
-
if (pa.pre === "") return 1;
|
|
3502
|
-
if (pb.pre === "") return -1;
|
|
3503
|
-
return pa.pre < pb.pre ? -1 : 1;
|
|
3504
|
-
}
|
|
3505
|
-
function isNewer(latest, current) {
|
|
3506
|
-
return compareSemver(latest, current) === 1;
|
|
3507
|
-
}
|
|
3508
|
-
function shouldCheck(lastCheckedAt, nowMs) {
|
|
3509
|
-
if (lastCheckedAt === null) return true;
|
|
3510
|
-
return nowMs - lastCheckedAt >= CHECK_INTERVAL_MS;
|
|
3511
|
-
}
|
|
3512
|
-
function parseRegistryResponse(json2) {
|
|
3513
|
-
try {
|
|
3514
|
-
const parsed = JSON.parse(json2);
|
|
3515
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
3516
|
-
const version = parsed.version;
|
|
3517
|
-
return typeof version === "string" && version.length > 0 ? version : null;
|
|
3518
|
-
} catch {
|
|
3519
|
-
return null;
|
|
3520
|
-
}
|
|
3521
|
-
}
|
|
3522
|
-
function deriveStatus(latest, currentVersion) {
|
|
3523
|
-
return {
|
|
3524
|
-
latest,
|
|
3525
|
-
updateAvailable: latest !== null && isNewer(latest, currentVersion)
|
|
3526
|
-
};
|
|
3527
|
-
}
|
|
3528
|
-
function updateCachePath() {
|
|
3529
|
-
const home = process.env.TMUX_IDE_HOME ?? join7(homedir7(), ".tmux-ide");
|
|
3530
|
-
return join7(home, "update-check.json");
|
|
3531
|
-
}
|
|
3532
|
-
function readUpdateCache() {
|
|
3533
|
-
const path2 = updateCachePath();
|
|
3534
|
-
if (!existsSync9(path2)) return null;
|
|
3535
|
-
try {
|
|
3536
|
-
const parsed = JSON.parse(readFileSync6(path2, "utf-8"));
|
|
3537
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
3538
|
-
const obj = parsed;
|
|
3539
|
-
const lastCheckedAt = typeof obj.lastCheckedAt === "number" ? obj.lastCheckedAt : null;
|
|
3540
|
-
const latest = typeof obj.latest === "string" && obj.latest.length > 0 ? obj.latest : null;
|
|
3541
|
-
const notified = Array.isArray(obj.notified) ? obj.notified.filter((v) => typeof v === "string") : void 0;
|
|
3542
|
-
return { lastCheckedAt, latest, ...notified ? { notified } : {} };
|
|
3543
|
-
} catch {
|
|
3544
|
-
return null;
|
|
3545
|
-
}
|
|
3546
|
-
}
|
|
3547
|
-
function writeUpdateCache(cache3) {
|
|
3548
|
-
const path2 = updateCachePath();
|
|
3549
|
-
try {
|
|
3550
|
-
mkdirSync4(dirname7(path2), { recursive: true });
|
|
3551
|
-
writeFileSync5(path2, JSON.stringify(cache3));
|
|
3552
|
-
} catch {
|
|
3553
|
-
}
|
|
3554
|
-
}
|
|
3555
|
-
async function fetchLatestVersion(timeoutMs = 3e3) {
|
|
3556
|
-
const controller = new AbortController();
|
|
3557
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
3558
|
-
try {
|
|
3559
|
-
const res = await fetch(REGISTRY_URL, { signal: controller.signal });
|
|
3560
|
-
if (!res.ok) return null;
|
|
3561
|
-
return parseRegistryResponse(await res.text());
|
|
3562
|
-
} catch {
|
|
3563
|
-
return null;
|
|
3564
|
-
} finally {
|
|
3565
|
-
clearTimeout(timer);
|
|
3566
|
-
}
|
|
3567
|
-
}
|
|
3568
|
-
function getCurrentVersion() {
|
|
3569
|
-
const here = dirname7(fileURLToPath4(import.meta.url));
|
|
3570
|
-
const candidates = [
|
|
3571
|
-
join7(here, "../package.json"),
|
|
3572
|
-
// bundled bin/cli.js → repo root
|
|
3573
|
-
join7(here, "../../../../package.json")
|
|
3574
|
-
// dev src/lib → repo root
|
|
3575
|
-
];
|
|
3576
|
-
for (const candidate of candidates) {
|
|
3577
|
-
try {
|
|
3578
|
-
const parsed = JSON.parse(readFileSync6(candidate, "utf-8"));
|
|
3579
|
-
if (typeof parsed.version === "string" && parsed.version.length > 0) return parsed.version;
|
|
3580
|
-
} catch {
|
|
3581
|
-
}
|
|
3582
|
-
}
|
|
3583
|
-
return "0.0.0";
|
|
3584
|
-
}
|
|
3585
|
-
function getUpdateStatus({
|
|
3586
|
-
currentVersion = getCurrentVersion()
|
|
3587
|
-
} = {}) {
|
|
3588
|
-
const cache3 = readUpdateCache();
|
|
3589
|
-
return deriveStatus(cache3?.latest ?? null, currentVersion);
|
|
3590
|
-
}
|
|
3591
|
-
async function runUpdateCheck({ now = Date.now() } = {}) {
|
|
3592
|
-
const cache3 = readUpdateCache();
|
|
3593
|
-
if (!shouldCheck(cache3?.lastCheckedAt ?? null, now)) return;
|
|
3594
|
-
const fetched = await fetchLatestVersion();
|
|
3595
|
-
writeUpdateCache({
|
|
3596
|
-
lastCheckedAt: now,
|
|
3597
|
-
latest: fetched ?? cache3?.latest ?? null,
|
|
3598
|
-
...cache3?.notified ? { notified: cache3.notified } : {}
|
|
3599
|
-
});
|
|
3600
|
-
}
|
|
3601
|
-
function maybeCheckForUpdate({
|
|
3602
|
-
enabled,
|
|
3603
|
-
now = Date.now(),
|
|
3604
|
-
currentVersion = getCurrentVersion()
|
|
3605
|
-
}) {
|
|
3606
|
-
if (!enabled) return { latest: null, updateAvailable: false };
|
|
3607
|
-
const status2 = getUpdateStatus({ now, currentVersion });
|
|
3608
|
-
void runUpdateCheck({ now }).catch(() => {
|
|
3609
|
-
});
|
|
3610
|
-
return status2;
|
|
3611
|
-
}
|
|
3612
|
-
function markUpdateNotified(version) {
|
|
3613
|
-
const cache3 = readUpdateCache() ?? { lastCheckedAt: null, latest: null };
|
|
3614
|
-
const notified = cache3.notified ?? [];
|
|
3615
|
-
if (notified.includes(version)) return false;
|
|
3616
|
-
writeUpdateCache({ ...cache3, notified: [...notified, version] });
|
|
3617
|
-
return true;
|
|
3618
|
-
}
|
|
3619
|
-
var REGISTRY_URL, CHECK_INTERVAL_MS;
|
|
3620
|
-
var init_update_check = __esm({
|
|
3621
|
-
"packages/daemon/src/lib/update-check.ts"() {
|
|
3622
|
-
"use strict";
|
|
3623
|
-
REGISTRY_URL = "https://registry.npmjs.org/tmux-ide/latest";
|
|
3624
|
-
CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3625
|
-
}
|
|
3626
|
-
});
|
|
3627
|
-
|
|
3628
3931
|
// packages/daemon/src/schemas/registry.ts
|
|
3629
3932
|
import { z as z9 } from "zod";
|
|
3630
3933
|
var RegisteredProjectSchemaZ, RegisterProjectRequestSchemaZ, InitProjectRequestSchemaZ, ProjectTemplateSchemaZ;
|
|
@@ -3663,7 +3966,7 @@ var init_registry = __esm({
|
|
|
3663
3966
|
|
|
3664
3967
|
// packages/daemon/src/lib/project-probe.ts
|
|
3665
3968
|
import { execFile } from "node:child_process";
|
|
3666
|
-
import { existsSync as
|
|
3969
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
3667
3970
|
import { basename as basename3, isAbsolute, resolve as resolve7 } from "node:path";
|
|
3668
3971
|
function sanitizeName(raw) {
|
|
3669
3972
|
return raw.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "").replace(/^-+|-+$/g, "");
|
|
@@ -3694,7 +3997,7 @@ var init_project_probe = __esm({
|
|
|
3694
3997
|
"use strict";
|
|
3695
3998
|
GIT_TIMEOUT_MS = 2e3;
|
|
3696
3999
|
realIo = {
|
|
3697
|
-
exists:
|
|
4000
|
+
exists: existsSync11,
|
|
3698
4001
|
runGit: (args, cwd) => new Promise((resolveResult) => {
|
|
3699
4002
|
execFile(
|
|
3700
4003
|
"git",
|
|
@@ -3715,9 +4018,9 @@ var init_project_probe = __esm({
|
|
|
3715
4018
|
|
|
3716
4019
|
// packages/daemon/src/lib/project-registry.ts
|
|
3717
4020
|
import { EventEmitter } from "node:events";
|
|
3718
|
-
import { existsSync as
|
|
3719
|
-
import { homedir as
|
|
3720
|
-
import { dirname as
|
|
4021
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync3, writeFileSync as writeFileSync8 } from "node:fs";
|
|
4022
|
+
import { homedir as homedir9 } from "node:os";
|
|
4023
|
+
import { dirname as dirname10, isAbsolute as isAbsolute2, join as join9, resolve as resolve8 } from "node:path";
|
|
3721
4024
|
import { z as z10 } from "zod";
|
|
3722
4025
|
function applyAction(state, action) {
|
|
3723
4026
|
switch (action.type) {
|
|
@@ -3749,14 +4052,14 @@ function buildRegisteredProject(probe, name, registeredAt) {
|
|
|
3749
4052
|
function registryDir() {
|
|
3750
4053
|
const override = process.env[REGISTRY_DIR_ENV];
|
|
3751
4054
|
if (override && override.length > 0) return override;
|
|
3752
|
-
return
|
|
4055
|
+
return join9(homedir9(), ".tmux-ide");
|
|
3753
4056
|
}
|
|
3754
4057
|
function registryPath() {
|
|
3755
|
-
return
|
|
4058
|
+
return join9(registryDir(), "projects.json");
|
|
3756
4059
|
}
|
|
3757
4060
|
function readDisk() {
|
|
3758
4061
|
const path2 = registryPath();
|
|
3759
|
-
if (!
|
|
4062
|
+
if (!existsSync12(path2)) return [];
|
|
3760
4063
|
const raw = readFileSync7(path2, "utf-8");
|
|
3761
4064
|
if (raw.trim().length === 0) return [];
|
|
3762
4065
|
let parsed;
|
|
@@ -3779,12 +4082,12 @@ function readDisk() {
|
|
|
3779
4082
|
}
|
|
3780
4083
|
function writeDisk(projects) {
|
|
3781
4084
|
const path2 = registryPath();
|
|
3782
|
-
const dir =
|
|
3783
|
-
|
|
4085
|
+
const dir = dirname10(path2);
|
|
4086
|
+
mkdirSync7(dir, { recursive: true });
|
|
3784
4087
|
const file = { version: 1, projects };
|
|
3785
4088
|
const tmpPath = `${path2}.tmp`;
|
|
3786
|
-
|
|
3787
|
-
|
|
4089
|
+
writeFileSync8(tmpPath, JSON.stringify(file, null, 2) + "\n");
|
|
4090
|
+
renameSync3(tmpPath, path2);
|
|
3788
4091
|
}
|
|
3789
4092
|
function ensureCache() {
|
|
3790
4093
|
if (cache2 !== null) return cache2;
|
|
@@ -3803,7 +4106,7 @@ function getProject(name) {
|
|
|
3803
4106
|
return ensureCache().find((p) => p.name === name) ?? null;
|
|
3804
4107
|
}
|
|
3805
4108
|
async function registerProject(input) {
|
|
3806
|
-
const exists = input.exists ??
|
|
4109
|
+
const exists = input.exists ?? existsSync12;
|
|
3807
4110
|
const absoluteDir = isAbsolute2(input.dir) ? input.dir : resolve8(input.dir);
|
|
3808
4111
|
if (!exists(absoluteDir)) {
|
|
3809
4112
|
throw new ProjectDirNotFoundError(absoluteDir);
|
|
@@ -4013,9 +4316,9 @@ __export(events_exports, {
|
|
|
4013
4316
|
formatEventLine: () => formatEventLine,
|
|
4014
4317
|
shouldRotate: () => shouldRotate
|
|
4015
4318
|
});
|
|
4016
|
-
import { appendFileSync, existsSync as
|
|
4017
|
-
import { homedir as
|
|
4018
|
-
import { join as
|
|
4319
|
+
import { appendFileSync, existsSync as existsSync13, mkdirSync as mkdirSync8, renameSync as renameSync4, statSync } from "node:fs";
|
|
4320
|
+
import { homedir as homedir10 } from "node:os";
|
|
4321
|
+
import { join as join10 } from "node:path";
|
|
4019
4322
|
function diffFleet(prev, next) {
|
|
4020
4323
|
const state = /* @__PURE__ */ new Map();
|
|
4021
4324
|
const events = [];
|
|
@@ -4042,15 +4345,15 @@ function formatEventLine(ev, paint = (_s, t) => t) {
|
|
|
4042
4345
|
return `${isoTime(ev.ts)} ${ev.session} ${from} \u2192 ${paint(ev.to, ev.to)}`;
|
|
4043
4346
|
}
|
|
4044
4347
|
function eventsPath() {
|
|
4045
|
-
return
|
|
4348
|
+
return join10(homedir10(), ".tmux-ide", "events.jsonl");
|
|
4046
4349
|
}
|
|
4047
4350
|
function appendEvents(events, now = () => (/* @__PURE__ */ new Date()).toISOString()) {
|
|
4048
4351
|
if (events.length === 0) return;
|
|
4049
4352
|
const path2 = eventsPath();
|
|
4050
4353
|
try {
|
|
4051
|
-
|
|
4052
|
-
if (
|
|
4053
|
-
|
|
4354
|
+
mkdirSync8(join10(homedir10(), ".tmux-ide"), { recursive: true });
|
|
4355
|
+
if (existsSync13(path2) && shouldRotate(statSync(path2).size)) {
|
|
4356
|
+
renameSync4(path2, `${path2}.1`);
|
|
4054
4357
|
}
|
|
4055
4358
|
const ts = now();
|
|
4056
4359
|
const lines = events.map((e) => `${JSON.stringify({ ts, ...e })}
|
|
@@ -4069,25 +4372,38 @@ var init_events = __esm({
|
|
|
4069
4372
|
|
|
4070
4373
|
// packages/daemon/src/tui/chrome/notify.ts
|
|
4071
4374
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
4072
|
-
|
|
4073
|
-
|
|
4375
|
+
import { existsSync as existsSync14, readFileSync as readFileSync8 } from "node:fs";
|
|
4376
|
+
function statusPhrase(to) {
|
|
4377
|
+
return to === "blocked" ? "needs input" : "finished";
|
|
4378
|
+
}
|
|
4379
|
+
function notifyMessage(ev) {
|
|
4380
|
+
const agent = ev.agent && ev.agent.length > 0 ? ev.agent : "agent";
|
|
4381
|
+
const where = ev.location && ev.location.length > 0 ? ev.location : ev.session;
|
|
4382
|
+
const text = `${agent} ${ev.to} \xB7 ${where} \u2014 ${statusPhrase(ev.to)}`;
|
|
4383
|
+
return text.length > NOTIFY_MAX_LEN ? `${text.slice(0, NOTIFY_MAX_LEN - 1)}\u2026` : text;
|
|
4384
|
+
}
|
|
4385
|
+
function enabledStates(prefs) {
|
|
4386
|
+
const states = /* @__PURE__ */ new Set();
|
|
4387
|
+
if (prefs.onBlocked) states.add("blocked");
|
|
4388
|
+
if (prefs.onDone) states.add("done");
|
|
4389
|
+
return states;
|
|
4074
4390
|
}
|
|
4075
|
-
function decideNotifications(events, clients, lastNotified, nowMs) {
|
|
4391
|
+
function decideNotifications(events, clients, lastNotified, nowMs, states = NOTIFY_STATES) {
|
|
4076
4392
|
const nextLastNotified = new Map(lastNotified);
|
|
4077
4393
|
const toasts = [];
|
|
4078
4394
|
const system = [];
|
|
4079
4395
|
for (const ev of events) {
|
|
4080
|
-
if (!
|
|
4396
|
+
if (!states.has(ev.to)) continue;
|
|
4081
4397
|
const key = `${ev.session}:${ev.to}`;
|
|
4082
4398
|
const last = nextLastNotified.get(key);
|
|
4083
4399
|
if (last !== void 0 && nowMs - last < NOTIFY_DEBOUNCE_MS) continue;
|
|
4084
4400
|
nextLastNotified.set(key, nowMs);
|
|
4085
|
-
const message = notifyMessage(ev
|
|
4401
|
+
const message = notifyMessage(ev);
|
|
4086
4402
|
for (const c of clients) {
|
|
4087
4403
|
if (c.session === ev.session) continue;
|
|
4088
4404
|
toasts.push({ client: c.client, message });
|
|
4089
4405
|
}
|
|
4090
|
-
system.push({ message });
|
|
4406
|
+
system.push({ message, session: ev.session });
|
|
4091
4407
|
}
|
|
4092
4408
|
return { toasts, system, nextLastNotified };
|
|
4093
4409
|
}
|
|
@@ -4115,23 +4431,100 @@ function sendToasts(toasts) {
|
|
|
4115
4431
|
}
|
|
4116
4432
|
}
|
|
4117
4433
|
}
|
|
4118
|
-
function
|
|
4434
|
+
function hasTerminalNotifier() {
|
|
4435
|
+
try {
|
|
4436
|
+
execFileSync6("which", ["terminal-notifier"], { stdio: "ignore" });
|
|
4437
|
+
return true;
|
|
4438
|
+
} catch {
|
|
4439
|
+
return false;
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
function shellSingleQuote(value) {
|
|
4443
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
4444
|
+
}
|
|
4445
|
+
function terminalNotifierArgs(n) {
|
|
4446
|
+
return [
|
|
4447
|
+
"-title",
|
|
4448
|
+
"tmux-ide",
|
|
4449
|
+
"-message",
|
|
4450
|
+
n.message,
|
|
4451
|
+
"-execute",
|
|
4452
|
+
`tmux switch-client -t ${shellSingleQuote(n.session)}`
|
|
4453
|
+
];
|
|
4454
|
+
}
|
|
4455
|
+
function sendSystemNotification(n) {
|
|
4119
4456
|
if (process.platform !== "darwin") return;
|
|
4120
|
-
const escaped = message.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
4121
4457
|
try {
|
|
4458
|
+
if (hasTerminalNotifier()) {
|
|
4459
|
+
execFileSync6("terminal-notifier", terminalNotifierArgs(n), { stdio: "ignore" });
|
|
4460
|
+
return;
|
|
4461
|
+
}
|
|
4462
|
+
const escaped = n.message.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
4122
4463
|
execFileSync6("osascript", ["-e", `display notification "${escaped}" with title "tmux-ide"`], {
|
|
4123
4464
|
stdio: "ignore"
|
|
4124
4465
|
});
|
|
4125
4466
|
} catch {
|
|
4126
4467
|
}
|
|
4127
4468
|
}
|
|
4469
|
+
function asObject2(value) {
|
|
4470
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
4471
|
+
}
|
|
4472
|
+
function pickBool2(value, fallback) {
|
|
4473
|
+
return typeof value === "boolean" ? value : fallback;
|
|
4474
|
+
}
|
|
4475
|
+
function parseHHMM(value) {
|
|
4476
|
+
if (typeof value !== "string") return null;
|
|
4477
|
+
const m = /^(\d{2}):(\d{2})$/.exec(value.trim());
|
|
4478
|
+
if (!m) return null;
|
|
4479
|
+
const hours = Number(m[1]);
|
|
4480
|
+
const minutes = Number(m[2]);
|
|
4481
|
+
if (hours > 23 || minutes > 59) return null;
|
|
4482
|
+
return hours * 60 + minutes;
|
|
4483
|
+
}
|
|
4484
|
+
function inQuietHours(now, quiet) {
|
|
4485
|
+
if (!quiet) return false;
|
|
4486
|
+
const start2 = parseHHMM(quiet.start);
|
|
4487
|
+
const end = parseHHMM(quiet.end);
|
|
4488
|
+
if (start2 === null || end === null || start2 === end) return false;
|
|
4489
|
+
const nowMin = now.getHours() * 60 + now.getMinutes();
|
|
4490
|
+
return start2 < end ? nowMin >= start2 && nowMin < end : nowMin >= start2 || nowMin < end;
|
|
4491
|
+
}
|
|
4492
|
+
function parseQuietHours(value) {
|
|
4493
|
+
const o = asObject2(value);
|
|
4494
|
+
const start2 = typeof o.start === "string" ? o.start : null;
|
|
4495
|
+
const end = typeof o.end === "string" ? o.end : null;
|
|
4496
|
+
if (start2 === null || end === null) return null;
|
|
4497
|
+
if (parseHHMM(start2) === null || parseHHMM(end) === null) return null;
|
|
4498
|
+
return { start: start2, end };
|
|
4499
|
+
}
|
|
4500
|
+
function parseNotificationPrefs(rawConfig) {
|
|
4501
|
+
const base = parseAppConfig(rawConfig).notifications;
|
|
4502
|
+
const n = asObject2(asObject2(rawConfig).notifications);
|
|
4503
|
+
return {
|
|
4504
|
+
enabled: pickBool2(n.enabled, DEFAULT_NOTIFICATION_PREFS.enabled),
|
|
4505
|
+
toast: base.toast,
|
|
4506
|
+
macos: base.macos,
|
|
4507
|
+
onBlocked: pickBool2(n.onBlocked, DEFAULT_NOTIFICATION_PREFS.onBlocked),
|
|
4508
|
+
onDone: pickBool2(n.onDone, DEFAULT_NOTIFICATION_PREFS.onDone),
|
|
4509
|
+
quietHours: parseQuietHours(n.quietHours)
|
|
4510
|
+
};
|
|
4511
|
+
}
|
|
4128
4512
|
function applyKillSwitch(prefs, envValue) {
|
|
4129
|
-
return envValue === "0" ? { toast: false, macos: false } : prefs;
|
|
4513
|
+
return envValue === "0" ? { ...prefs, enabled: false, toast: false, macos: false } : prefs;
|
|
4514
|
+
}
|
|
4515
|
+
function readRawConfig() {
|
|
4516
|
+
const path2 = appConfigPath();
|
|
4517
|
+
if (!existsSync14(path2)) return void 0;
|
|
4518
|
+
try {
|
|
4519
|
+
return JSON.parse(readFileSync8(path2, "utf-8"));
|
|
4520
|
+
} catch {
|
|
4521
|
+
return void 0;
|
|
4522
|
+
}
|
|
4130
4523
|
}
|
|
4131
4524
|
function readNotificationPrefs() {
|
|
4132
|
-
return applyKillSwitch(
|
|
4525
|
+
return applyKillSwitch(parseNotificationPrefs(readRawConfig()), process.env.TMUX_IDE_NOTIFY);
|
|
4133
4526
|
}
|
|
4134
|
-
var NOTIFY_STATES, NOTIFY_DEBOUNCE_MS;
|
|
4527
|
+
var NOTIFY_STATES, NOTIFY_DEBOUNCE_MS, NOTIFY_MAX_LEN, DEFAULT_NOTIFICATION_PREFS;
|
|
4135
4528
|
var init_notify = __esm({
|
|
4136
4529
|
"packages/daemon/src/tui/chrome/notify.ts"() {
|
|
4137
4530
|
"use strict";
|
|
@@ -4139,13 +4532,22 @@ var init_notify = __esm({
|
|
|
4139
4532
|
init_app_config();
|
|
4140
4533
|
NOTIFY_STATES = /* @__PURE__ */ new Set(["blocked", "done"]);
|
|
4141
4534
|
NOTIFY_DEBOUNCE_MS = 3e4;
|
|
4535
|
+
NOTIFY_MAX_LEN = 120;
|
|
4536
|
+
DEFAULT_NOTIFICATION_PREFS = {
|
|
4537
|
+
enabled: true,
|
|
4538
|
+
toast: true,
|
|
4539
|
+
macos: false,
|
|
4540
|
+
onBlocked: true,
|
|
4541
|
+
onDone: true,
|
|
4542
|
+
quietHours: null
|
|
4543
|
+
};
|
|
4142
4544
|
}
|
|
4143
4545
|
});
|
|
4144
4546
|
|
|
4145
4547
|
// packages/daemon/src/tui/chrome/snapshot.ts
|
|
4146
|
-
import { existsSync as
|
|
4147
|
-
import { homedir as
|
|
4148
|
-
import { dirname as
|
|
4548
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync9, renameSync as renameSync5, writeFileSync as writeFileSync9 } from "node:fs";
|
|
4549
|
+
import { homedir as homedir11 } from "node:os";
|
|
4550
|
+
import { dirname as dirname11, join as join11 } from "node:path";
|
|
4149
4551
|
import { z as z11 } from "zod";
|
|
4150
4552
|
function isBareShell(cmd) {
|
|
4151
4553
|
return /^-?(zsh|bash|sh|fish|dash|ksh|tcsh|csh|nu)$/.test(cmd.trim());
|
|
@@ -4260,29 +4662,29 @@ function collectFleetSnapshot(io = defaultIo) {
|
|
|
4260
4662
|
return buildSnapshot(rawPanes, rawSessions, io.processTable());
|
|
4261
4663
|
}
|
|
4262
4664
|
function snapshotPath() {
|
|
4263
|
-
return
|
|
4665
|
+
return join11(homedir11(), ".tmux-ide", "snapshot.json");
|
|
4264
4666
|
}
|
|
4265
4667
|
function writeSnapshot(snapshot) {
|
|
4266
4668
|
const path2 = snapshotPath();
|
|
4267
4669
|
try {
|
|
4268
|
-
|
|
4670
|
+
mkdirSync9(dirname11(path2), { recursive: true });
|
|
4269
4671
|
const tmp = `${path2}.tmp`;
|
|
4270
|
-
|
|
4271
|
-
if (
|
|
4672
|
+
writeFileSync9(tmp, JSON.stringify(snapshot, null, 2) + "\n");
|
|
4673
|
+
if (existsSync15(path2)) {
|
|
4272
4674
|
try {
|
|
4273
|
-
|
|
4675
|
+
renameSync5(path2, `${path2}.1`);
|
|
4274
4676
|
} catch {
|
|
4275
4677
|
}
|
|
4276
4678
|
}
|
|
4277
|
-
|
|
4679
|
+
renameSync5(tmp, path2);
|
|
4278
4680
|
} catch {
|
|
4279
4681
|
}
|
|
4280
4682
|
}
|
|
4281
4683
|
function readSnapshot() {
|
|
4282
4684
|
const path2 = snapshotPath();
|
|
4283
4685
|
try {
|
|
4284
|
-
if (!
|
|
4285
|
-
const raw =
|
|
4686
|
+
if (!existsSync15(path2)) return null;
|
|
4687
|
+
const raw = readFileSync9(path2, "utf-8");
|
|
4286
4688
|
if (raw.trim().length === 0) return null;
|
|
4287
4689
|
const result = FleetSnapshotSchemaZ.safeParse(JSON.parse(raw));
|
|
4288
4690
|
return result.success ? result.data : null;
|
|
@@ -4379,7 +4781,10 @@ __export(updater_exports, {
|
|
|
4379
4781
|
UPDATER_PID_OPTION: () => UPDATER_PID_OPTION,
|
|
4380
4782
|
UPDATER_SESSION: () => UPDATER_SESSION,
|
|
4381
4783
|
adoptedSessionsFrom: () => adoptedSessionsFrom,
|
|
4784
|
+
enrichEvents: () => enrichEvents,
|
|
4382
4785
|
listAdoptedSessions: () => listAdoptedSessions,
|
|
4786
|
+
paneLocation: () => paneLocation,
|
|
4787
|
+
pickRepresentativePane: () => pickRepresentativePane,
|
|
4383
4788
|
runUpdaterLoop: () => runUpdaterLoop,
|
|
4384
4789
|
runUpdaterTick: () => runUpdaterTick,
|
|
4385
4790
|
seedSessionStatus: () => seedSessionStatus,
|
|
@@ -4436,7 +4841,7 @@ function runUpdaterTick(deps2) {
|
|
|
4436
4841
|
for (const [name, status2] of state) deps2.prevState.set(name, status2);
|
|
4437
4842
|
if (events.length > 0) {
|
|
4438
4843
|
deps2.appendEvents(events);
|
|
4439
|
-
dispatchNotifications(deps2, events);
|
|
4844
|
+
dispatchNotifications(deps2, enrichEvents(events, panes, deps2.locatePane));
|
|
4440
4845
|
}
|
|
4441
4846
|
}
|
|
4442
4847
|
}
|
|
@@ -4452,16 +4857,54 @@ function writeChips(deps2, adopted, panes, theme) {
|
|
|
4452
4857
|
writeChip(pane.paneId, chip);
|
|
4453
4858
|
}
|
|
4454
4859
|
}
|
|
4860
|
+
function pickRepresentativePane(session, to, panes) {
|
|
4861
|
+
const matching = panes.filter((p) => p.sessionName === session && p.status === to);
|
|
4862
|
+
if (matching.length === 0) return null;
|
|
4863
|
+
return matching.find((p) => p.agent !== null) ?? matching[0];
|
|
4864
|
+
}
|
|
4865
|
+
function enrichEvents(events, panes, locate) {
|
|
4866
|
+
return events.map((ev) => {
|
|
4867
|
+
const notifiable = ev.to === "blocked" || ev.to === "done";
|
|
4868
|
+
const rep = notifiable ? pickRepresentativePane(ev.session, ev.to, panes) : null;
|
|
4869
|
+
return {
|
|
4870
|
+
...ev,
|
|
4871
|
+
agent: rep?.agent ?? null,
|
|
4872
|
+
location: rep && locate ? locate(rep.paneId) : ev.session
|
|
4873
|
+
};
|
|
4874
|
+
});
|
|
4875
|
+
}
|
|
4455
4876
|
function dispatchNotifications(deps2, events) {
|
|
4456
4877
|
const { listClients, lastNotified, now, prefs, sendToasts: toast, sendSystem } = deps2;
|
|
4457
4878
|
if (!listClients || !lastNotified || !now || !prefs) return;
|
|
4879
|
+
if (!prefs.enabled) return;
|
|
4458
4880
|
if (!prefs.toast && !prefs.macos) return;
|
|
4459
|
-
const
|
|
4881
|
+
const nowMs = now();
|
|
4882
|
+
const decision = decideNotifications(
|
|
4883
|
+
events,
|
|
4884
|
+
listClients(),
|
|
4885
|
+
lastNotified,
|
|
4886
|
+
nowMs,
|
|
4887
|
+
enabledStates(prefs)
|
|
4888
|
+
);
|
|
4460
4889
|
lastNotified.clear();
|
|
4461
4890
|
for (const [key, ts] of decision.nextLastNotified) lastNotified.set(key, ts);
|
|
4462
4891
|
if (prefs.toast && toast) toast(decision.toasts);
|
|
4463
|
-
if (prefs.macos && sendSystem) {
|
|
4464
|
-
for (const
|
|
4892
|
+
if (prefs.macos && sendSystem && !inQuietHours(new Date(nowMs), prefs.quietHours)) {
|
|
4893
|
+
for (const n of decision.system) sendSystem(n);
|
|
4894
|
+
}
|
|
4895
|
+
}
|
|
4896
|
+
function paneLocation(paneId) {
|
|
4897
|
+
try {
|
|
4898
|
+
const raw = runTmux([
|
|
4899
|
+
"display-message",
|
|
4900
|
+
"-p",
|
|
4901
|
+
"-t",
|
|
4902
|
+
paneId,
|
|
4903
|
+
"#{session_name}:#{window_index}.#{pane_index}"
|
|
4904
|
+
]).toString().trim();
|
|
4905
|
+
return raw || paneId;
|
|
4906
|
+
} catch {
|
|
4907
|
+
return paneId;
|
|
4465
4908
|
}
|
|
4466
4909
|
}
|
|
4467
4910
|
function dispatchUpdateToast(deps2, version) {
|
|
@@ -4553,6 +4996,7 @@ function runUpdaterLoop() {
|
|
|
4553
4996
|
prefs: readNotificationPrefs(),
|
|
4554
4997
|
sendToasts,
|
|
4555
4998
|
sendSystem: sendSystemNotification,
|
|
4999
|
+
locatePane: paneLocation,
|
|
4556
5000
|
maybeCheckForUpdate: () => maybeCheckForUpdate({ enabled: config2.updates.check }),
|
|
4557
5001
|
markUpdateNotified
|
|
4558
5002
|
});
|
|
@@ -4872,12 +5316,12 @@ __export(canonical_daemon_exports, {
|
|
|
4872
5316
|
warnOnDaemonVersionSkew: () => warnOnDaemonVersionSkew,
|
|
4873
5317
|
writeCanonicalDaemonInfo: () => writeCanonicalDaemonInfo
|
|
4874
5318
|
});
|
|
4875
|
-
import { existsSync as
|
|
4876
|
-
import { homedir as
|
|
4877
|
-
import { dirname as
|
|
5319
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync10, renameSync as renameSync6, rmSync, writeFileSync as writeFileSync10 } from "node:fs";
|
|
5320
|
+
import { homedir as homedir12 } from "node:os";
|
|
5321
|
+
import { dirname as dirname12, join as join12 } from "node:path";
|
|
4878
5322
|
function getCanonicalDaemonInfoPath() {
|
|
4879
|
-
const dir = process.env[DAEMON_INFO_DIR_ENV] ?? process.env[REGISTRY_DIR_ENV2] ??
|
|
4880
|
-
return
|
|
5323
|
+
const dir = process.env[DAEMON_INFO_DIR_ENV] ?? process.env[REGISTRY_DIR_ENV2] ?? join12(homedir12(), ".tmux-ide");
|
|
5324
|
+
return join12(dir, DAEMON_INFO_FILE);
|
|
4881
5325
|
}
|
|
4882
5326
|
function parseCanonicalDaemonInfo(raw) {
|
|
4883
5327
|
if (!raw || typeof raw !== "object") return null;
|
|
@@ -4900,7 +5344,7 @@ function parseCanonicalDaemonInfo(raw) {
|
|
|
4900
5344
|
}
|
|
4901
5345
|
function writeCanonicalDaemonInfo(info) {
|
|
4902
5346
|
const path2 = getCanonicalDaemonInfoPath();
|
|
4903
|
-
|
|
5347
|
+
mkdirSync10(dirname12(path2), { recursive: true });
|
|
4904
5348
|
const tmpPath = `${path2}.${process.pid}.${Date.now()}.tmp`;
|
|
4905
5349
|
const persisted = {
|
|
4906
5350
|
pid: info.pid,
|
|
@@ -4910,14 +5354,14 @@ function writeCanonicalDaemonInfo(info) {
|
|
|
4910
5354
|
bindHostname: info.bindHostname,
|
|
4911
5355
|
authToken: info.authToken
|
|
4912
5356
|
};
|
|
4913
|
-
|
|
4914
|
-
|
|
5357
|
+
writeFileSync10(tmpPath, JSON.stringify(persisted, null, 2) + "\n", "utf-8");
|
|
5358
|
+
renameSync6(tmpPath, path2);
|
|
4915
5359
|
}
|
|
4916
5360
|
function readCanonicalDaemonInfo() {
|
|
4917
5361
|
const path2 = getCanonicalDaemonInfoPath();
|
|
4918
|
-
if (!
|
|
5362
|
+
if (!existsSync16(path2)) return null;
|
|
4919
5363
|
try {
|
|
4920
|
-
return parseCanonicalDaemonInfo(JSON.parse(
|
|
5364
|
+
return parseCanonicalDaemonInfo(JSON.parse(readFileSync10(path2, "utf-8")));
|
|
4921
5365
|
} catch {
|
|
4922
5366
|
return null;
|
|
4923
5367
|
}
|
|
@@ -5207,13 +5651,13 @@ var init_launch = __esm({
|
|
|
5207
5651
|
|
|
5208
5652
|
// packages/daemon/src/detect.ts
|
|
5209
5653
|
import { resolve as resolve10, basename as basename4 } from "node:path";
|
|
5210
|
-
import { readFileSync as
|
|
5654
|
+
import { readFileSync as readFileSync11, existsSync as existsSync17 } from "node:fs";
|
|
5211
5655
|
function fileExists(dir, name) {
|
|
5212
|
-
return
|
|
5656
|
+
return existsSync17(resolve10(dir, name));
|
|
5213
5657
|
}
|
|
5214
5658
|
function readJson(dir, name) {
|
|
5215
5659
|
try {
|
|
5216
|
-
return JSON.parse(
|
|
5660
|
+
return JSON.parse(readFileSync11(resolve10(dir, name), "utf-8"));
|
|
5217
5661
|
} catch {
|
|
5218
5662
|
return null;
|
|
5219
5663
|
}
|
|
@@ -5275,7 +5719,7 @@ function detectStack(dir) {
|
|
|
5275
5719
|
detected.language = detected.language ?? "python";
|
|
5276
5720
|
detected.reasons.push('Detected Python from "pyproject.toml" or "requirements.txt".');
|
|
5277
5721
|
try {
|
|
5278
|
-
const pyproject =
|
|
5722
|
+
const pyproject = readFileSync11(resolve10(dir, "pyproject.toml"), "utf-8");
|
|
5279
5723
|
if (pyproject.includes("fastapi"))
|
|
5280
5724
|
pushFramework(detected, "fastapi", 'Found "fastapi" in pyproject.toml.');
|
|
5281
5725
|
else if (pyproject.includes("django"))
|
|
@@ -5422,28 +5866,28 @@ __export(skill_sync_exports, {
|
|
|
5422
5866
|
syncSkill: () => syncSkill,
|
|
5423
5867
|
versionMarker: () => versionMarker
|
|
5424
5868
|
});
|
|
5425
|
-
import { existsSync as
|
|
5426
|
-
import { homedir as
|
|
5427
|
-
import { dirname as
|
|
5869
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync12, readFileSync as readFileSync13, writeFileSync as writeFileSync12 } from "node:fs";
|
|
5870
|
+
import { homedir as homedir13 } from "node:os";
|
|
5871
|
+
import { dirname as dirname14, join as join14 } from "node:path";
|
|
5428
5872
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
5429
5873
|
function claudeDir() {
|
|
5430
|
-
return process.env.TMUX_IDE_CLAUDE_DIR ??
|
|
5874
|
+
return process.env.TMUX_IDE_CLAUDE_DIR ?? join14(homedir13(), ".claude");
|
|
5431
5875
|
}
|
|
5432
5876
|
function skillTargetDir() {
|
|
5433
|
-
return
|
|
5877
|
+
return join14(claudeDir(), "skills", "tmux-ide");
|
|
5434
5878
|
}
|
|
5435
5879
|
function skillTargetFile() {
|
|
5436
|
-
return
|
|
5880
|
+
return join14(skillTargetDir(), "SKILL.md");
|
|
5437
5881
|
}
|
|
5438
5882
|
function defaultSkillSource() {
|
|
5439
|
-
const here =
|
|
5883
|
+
const here = dirname14(fileURLToPath6(import.meta.url));
|
|
5440
5884
|
const candidates = [
|
|
5441
|
-
|
|
5885
|
+
join14(here, "../skill/SKILL.md"),
|
|
5442
5886
|
// bundled bin/cli.js → repo root
|
|
5443
|
-
|
|
5887
|
+
join14(here, "../../../../skill/SKILL.md")
|
|
5444
5888
|
// dev src/lib → repo root
|
|
5445
5889
|
];
|
|
5446
|
-
return candidates.find((c) =>
|
|
5890
|
+
return candidates.find((c) => existsSync19(c)) ?? candidates[0];
|
|
5447
5891
|
}
|
|
5448
5892
|
function versionMarker(version) {
|
|
5449
5893
|
return `<!-- tmux-ide-skill-version: ${version} -->`;
|
|
@@ -5457,10 +5901,10 @@ function rewriteVersionMarker(content, version) {
|
|
|
5457
5901
|
return content.replace(VERSION_MARKER_RE, versionMarker(version));
|
|
5458
5902
|
}
|
|
5459
5903
|
function installedSkillVersion(dir = skillTargetDir()) {
|
|
5460
|
-
const file =
|
|
5461
|
-
if (!
|
|
5904
|
+
const file = join14(dir, "SKILL.md");
|
|
5905
|
+
if (!existsSync19(file)) return null;
|
|
5462
5906
|
try {
|
|
5463
|
-
return parseSkillVersion(
|
|
5907
|
+
return parseSkillVersion(readFileSync13(file, "utf-8"));
|
|
5464
5908
|
} catch {
|
|
5465
5909
|
return null;
|
|
5466
5910
|
}
|
|
@@ -5469,15 +5913,15 @@ function syncSkill({
|
|
|
5469
5913
|
source = defaultSkillSource(),
|
|
5470
5914
|
version = getCurrentVersion()
|
|
5471
5915
|
} = {}) {
|
|
5472
|
-
const rendered = rewriteVersionMarker(
|
|
5916
|
+
const rendered = rewriteVersionMarker(readFileSync13(source, "utf-8"), version);
|
|
5473
5917
|
const dir = skillTargetDir();
|
|
5474
|
-
const target =
|
|
5475
|
-
const existing =
|
|
5918
|
+
const target = join14(dir, "SKILL.md");
|
|
5919
|
+
const existing = existsSync19(target) ? readFileSync13(target, "utf-8") : null;
|
|
5476
5920
|
if (existing === rendered) {
|
|
5477
5921
|
return { action: "unchanged", path: target, to: version };
|
|
5478
5922
|
}
|
|
5479
|
-
|
|
5480
|
-
|
|
5923
|
+
mkdirSync12(dir, { recursive: true });
|
|
5924
|
+
writeFileSync12(target, rendered, "utf-8");
|
|
5481
5925
|
if (existing === null) return { action: "installed", path: target, to: version };
|
|
5482
5926
|
return { action: "updated", path: target, from: parseSkillVersion(existing), to: version };
|
|
5483
5927
|
}
|
|
@@ -5678,8 +6122,8 @@ var init_PtyAdapter = __esm({
|
|
|
5678
6122
|
});
|
|
5679
6123
|
|
|
5680
6124
|
// packages/daemon/src/terminal/NodePtyAdapter.ts
|
|
5681
|
-
import { chmodSync as
|
|
5682
|
-
import { dirname as
|
|
6125
|
+
import { chmodSync as chmodSync3, existsSync as existsSync22, statSync as statSync2 } from "node:fs";
|
|
6126
|
+
import { dirname as dirname16, join as join15 } from "node:path";
|
|
5683
6127
|
import { createRequire } from "node:module";
|
|
5684
6128
|
import * as pty from "node-pty";
|
|
5685
6129
|
function candidateSpawnHelperPaths() {
|
|
@@ -5690,11 +6134,11 @@ function candidateSpawnHelperPaths() {
|
|
|
5690
6134
|
} catch {
|
|
5691
6135
|
return [];
|
|
5692
6136
|
}
|
|
5693
|
-
const pkgDir =
|
|
6137
|
+
const pkgDir = dirname16(pkgJsonPath);
|
|
5694
6138
|
return [
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
6139
|
+
join15(pkgDir, "build", "Release", "spawn-helper"),
|
|
6140
|
+
join15(pkgDir, "build", "Debug", "spawn-helper"),
|
|
6141
|
+
join15(pkgDir, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper")
|
|
5698
6142
|
];
|
|
5699
6143
|
}
|
|
5700
6144
|
function ensureNodePtySpawnHelperExecutable(options = {}) {
|
|
@@ -5702,9 +6146,9 @@ function ensureNodePtySpawnHelperExecutable(options = {}) {
|
|
|
5702
6146
|
if (!options.force && !options.explicitPath && helperEnsured) return;
|
|
5703
6147
|
const candidates = options.explicitPath ? [options.explicitPath] : candidateSpawnHelperPaths();
|
|
5704
6148
|
for (const candidate of candidates) {
|
|
5705
|
-
if (!
|
|
6149
|
+
if (!existsSync22(candidate)) continue;
|
|
5706
6150
|
try {
|
|
5707
|
-
|
|
6151
|
+
chmodSync3(candidate, 493);
|
|
5708
6152
|
} catch {
|
|
5709
6153
|
}
|
|
5710
6154
|
}
|
|
@@ -6691,9 +7135,9 @@ var init_pane_comms = __esm({
|
|
|
6691
7135
|
|
|
6692
7136
|
// packages/daemon/src/lib/workspace-registry.ts
|
|
6693
7137
|
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
6694
|
-
import { existsSync as
|
|
6695
|
-
import { homedir as
|
|
6696
|
-
import { dirname as
|
|
7138
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync14, renameSync as renameSync8, writeFileSync as writeFileSync13 } from "node:fs";
|
|
7139
|
+
import { homedir as homedir14 } from "node:os";
|
|
7140
|
+
import { dirname as dirname17, join as join16 } from "node:path";
|
|
6697
7141
|
import { z as z12 } from "zod";
|
|
6698
7142
|
function getDefaultWorkspaceRegistry() {
|
|
6699
7143
|
if (!_default) _default = new WorkspaceRegistry();
|
|
@@ -6742,7 +7186,7 @@ var init_workspace_registry = __esm({
|
|
|
6742
7186
|
workspaces = [];
|
|
6743
7187
|
loaded = false;
|
|
6744
7188
|
constructor(options = {}) {
|
|
6745
|
-
this.dir = options.dir ?? process.env[REGISTRY_DIR_ENV3] ??
|
|
7189
|
+
this.dir = options.dir ?? process.env[REGISTRY_DIR_ENV3] ?? join16(homedir14(), ".tmux-ide");
|
|
6746
7190
|
this.listSessions = options.listSessions ?? defaultListSessions;
|
|
6747
7191
|
this.emitter.setMaxListeners(0);
|
|
6748
7192
|
}
|
|
@@ -6809,14 +7253,14 @@ var init_workspace_registry = __esm({
|
|
|
6809
7253
|
}
|
|
6810
7254
|
// ----------------- io -----------------
|
|
6811
7255
|
filePath() {
|
|
6812
|
-
return
|
|
7256
|
+
return join16(this.dir, "workspaces.json");
|
|
6813
7257
|
}
|
|
6814
7258
|
readDisk() {
|
|
6815
7259
|
const path2 = this.filePath();
|
|
6816
|
-
if (!
|
|
7260
|
+
if (!existsSync23(path2)) return [];
|
|
6817
7261
|
let parsed;
|
|
6818
7262
|
try {
|
|
6819
|
-
parsed = JSON.parse(
|
|
7263
|
+
parsed = JSON.parse(readFileSync14(path2, "utf-8"));
|
|
6820
7264
|
} catch {
|
|
6821
7265
|
return [];
|
|
6822
7266
|
}
|
|
@@ -6826,11 +7270,11 @@ var init_workspace_registry = __esm({
|
|
|
6826
7270
|
}
|
|
6827
7271
|
writeDisk() {
|
|
6828
7272
|
const path2 = this.filePath();
|
|
6829
|
-
|
|
7273
|
+
mkdirSync13(dirname17(path2), { recursive: true });
|
|
6830
7274
|
const file = { version: 1, workspaces: this.workspaces };
|
|
6831
7275
|
const tmp = `${path2}.tmp`;
|
|
6832
|
-
|
|
6833
|
-
|
|
7276
|
+
writeFileSync13(tmp, JSON.stringify(file, null, 2) + "\n");
|
|
7277
|
+
renameSync8(tmp, path2);
|
|
6834
7278
|
}
|
|
6835
7279
|
/** @internal Test-only: assert the registry is loaded. */
|
|
6836
7280
|
_isLoaded() {
|
|
@@ -7111,14 +7555,14 @@ var init_auth_token = __esm({
|
|
|
7111
7555
|
});
|
|
7112
7556
|
|
|
7113
7557
|
// packages/daemon/src/lib/app-settings.ts
|
|
7114
|
-
import { existsSync as
|
|
7115
|
-
import { dirname as
|
|
7116
|
-
import { homedir as
|
|
7558
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync15, renameSync as renameSync9, writeFileSync as writeFileSync14 } from "node:fs";
|
|
7559
|
+
import { dirname as dirname18, join as join17 } from "node:path";
|
|
7560
|
+
import { homedir as homedir15 } from "node:os";
|
|
7117
7561
|
function settingsDir() {
|
|
7118
|
-
return process.env.TMUX_IDE_SETTINGS_DIR ??
|
|
7562
|
+
return process.env.TMUX_IDE_SETTINGS_DIR ?? join17(homedir15(), ".tmux-ide");
|
|
7119
7563
|
}
|
|
7120
7564
|
function appSettingsPath() {
|
|
7121
|
-
return
|
|
7565
|
+
return join17(settingsDir(), "app-settings.json");
|
|
7122
7566
|
}
|
|
7123
7567
|
function normalizeSettings(value) {
|
|
7124
7568
|
if (!value || typeof value !== "object") return structuredClone(DEFAULT_SETTINGS);
|
|
@@ -7131,20 +7575,20 @@ function normalizeSettings(value) {
|
|
|
7131
7575
|
}
|
|
7132
7576
|
function readAppSettings() {
|
|
7133
7577
|
const path2 = appSettingsPath();
|
|
7134
|
-
if (!
|
|
7578
|
+
if (!existsSync24(path2)) return structuredClone(DEFAULT_SETTINGS);
|
|
7135
7579
|
try {
|
|
7136
|
-
return normalizeSettings(JSON.parse(
|
|
7580
|
+
return normalizeSettings(JSON.parse(readFileSync15(path2, "utf-8")));
|
|
7137
7581
|
} catch {
|
|
7138
7582
|
return structuredClone(DEFAULT_SETTINGS);
|
|
7139
7583
|
}
|
|
7140
7584
|
}
|
|
7141
7585
|
function writeAppSettings(next) {
|
|
7142
7586
|
const path2 = appSettingsPath();
|
|
7143
|
-
|
|
7587
|
+
mkdirSync14(dirname18(path2), { recursive: true });
|
|
7144
7588
|
const tmp = `${path2}.${process.pid}.${Date.now()}.tmp`;
|
|
7145
|
-
|
|
7589
|
+
writeFileSync14(tmp, `${JSON.stringify(normalizeSettings(next), null, 2)}
|
|
7146
7590
|
`, "utf-8");
|
|
7147
|
-
|
|
7591
|
+
renameSync9(tmp, path2);
|
|
7148
7592
|
}
|
|
7149
7593
|
var DEFAULT_SETTINGS;
|
|
7150
7594
|
var init_app_settings = __esm({
|
|
@@ -7332,16 +7776,16 @@ var init_active_projects = __esm({
|
|
|
7332
7776
|
|
|
7333
7777
|
// packages/daemon/src/send.ts
|
|
7334
7778
|
import { randomUUID } from "node:crypto";
|
|
7335
|
-
import { resolve as resolve17, join as
|
|
7336
|
-
import { existsSync as
|
|
7779
|
+
import { resolve as resolve17, join as join18 } from "node:path";
|
|
7780
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "node:fs";
|
|
7337
7781
|
function writeDispatchFile(dir, paneId, message) {
|
|
7338
7782
|
if (message.length <= LONG_MESSAGE_THRESHOLD) return null;
|
|
7339
|
-
const dispatchDir =
|
|
7340
|
-
if (!
|
|
7783
|
+
const dispatchDir = join18(dir, ".tasks", "dispatch");
|
|
7784
|
+
if (!existsSync25(dispatchDir)) mkdirSync15(dispatchDir, { recursive: true });
|
|
7341
7785
|
const paneSlug = paneId.replace("%", "");
|
|
7342
7786
|
const filename = `send-${paneSlug}-${Date.now()}-${randomUUID().slice(0, 8)}.md`;
|
|
7343
|
-
const filePath =
|
|
7344
|
-
|
|
7787
|
+
const filePath = join18(dispatchDir, filename);
|
|
7788
|
+
writeFileSync15(filePath, message);
|
|
7345
7789
|
return { filePath, triggerCmd: `Read and execute: .tasks/dispatch/${filename}` };
|
|
7346
7790
|
}
|
|
7347
7791
|
function resolvePane(panes, target) {
|
|
@@ -7585,19 +8029,19 @@ var init_schemas = __esm({
|
|
|
7585
8029
|
});
|
|
7586
8030
|
|
|
7587
8031
|
// packages/daemon/src/lib/terminals-store.ts
|
|
7588
|
-
import { existsSync as
|
|
7589
|
-
import { dirname as
|
|
8032
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync16, renameSync as renameSync10, writeFileSync as writeFileSync16 } from "node:fs";
|
|
8033
|
+
import { dirname as dirname19, join as join19 } from "node:path";
|
|
7590
8034
|
function path(dir) {
|
|
7591
|
-
return
|
|
8035
|
+
return join19(dir, TERMINALS_FILE);
|
|
7592
8036
|
}
|
|
7593
8037
|
function ensureDir(dir) {
|
|
7594
|
-
|
|
8038
|
+
mkdirSync16(dirname19(path(dir)), { recursive: true });
|
|
7595
8039
|
}
|
|
7596
8040
|
function loadTerminals(dir) {
|
|
7597
8041
|
const file = path(dir);
|
|
7598
|
-
if (!
|
|
8042
|
+
if (!existsSync26(file)) return [];
|
|
7599
8043
|
try {
|
|
7600
|
-
const body =
|
|
8044
|
+
const body = readFileSync16(file, "utf-8");
|
|
7601
8045
|
const parsed = JSON.parse(body);
|
|
7602
8046
|
if (!parsed.terminals || !Array.isArray(parsed.terminals)) return [];
|
|
7603
8047
|
return parsed.terminals.filter((t) => isTerminal(t)).map((t) => ({ ...t }));
|
|
@@ -7614,8 +8058,8 @@ function writeAtomic(dir, terminals) {
|
|
|
7614
8058
|
ensureDir(dir);
|
|
7615
8059
|
const file = path(dir);
|
|
7616
8060
|
const tmp = `${file}.tmp`;
|
|
7617
|
-
|
|
7618
|
-
|
|
8061
|
+
writeFileSync16(tmp, JSON.stringify({ terminals }, null, 2) + "\n");
|
|
8062
|
+
renameSync10(tmp, file);
|
|
7619
8063
|
}
|
|
7620
8064
|
function upsertTerminal(dir, input) {
|
|
7621
8065
|
if (!SAFE_ID.test(input.id)) {
|
|
@@ -7676,9 +8120,9 @@ __export(auth_service_exports, {
|
|
|
7676
8120
|
AuthService: () => AuthService
|
|
7677
8121
|
});
|
|
7678
8122
|
import * as crypto2 from "node:crypto";
|
|
7679
|
-
import { readFileSync as
|
|
7680
|
-
import { join as
|
|
7681
|
-
import { homedir as
|
|
8123
|
+
import { readFileSync as readFileSync17, existsSync as existsSync27 } from "node:fs";
|
|
8124
|
+
import { join as join20 } from "node:path";
|
|
8125
|
+
import { homedir as homedir16 } from "node:os";
|
|
7682
8126
|
function base64url(buf) {
|
|
7683
8127
|
const b = typeof buf === "string" ? Buffer.from(buf) : buf;
|
|
7684
8128
|
return b.toString("base64url");
|
|
@@ -7820,10 +8264,10 @@ var init_auth_service = __esm({
|
|
|
7820
8264
|
}
|
|
7821
8265
|
checkSSHKeyAuthorization(userId, publicKey) {
|
|
7822
8266
|
try {
|
|
7823
|
-
const home = userId === process.env.USER ?
|
|
7824
|
-
const authKeysPath =
|
|
7825
|
-
if (!
|
|
7826
|
-
const authorizedKeys =
|
|
8267
|
+
const home = userId === process.env.USER ? homedir16() : `/home/${userId}`;
|
|
8268
|
+
const authKeysPath = join20(home, ".ssh", "authorized_keys");
|
|
8269
|
+
if (!existsSync27(authKeysPath)) return false;
|
|
8270
|
+
const authorizedKeys = readFileSync17(authKeysPath, "utf-8");
|
|
7827
8271
|
const parts = publicKey.trim().split(" ");
|
|
7828
8272
|
const keyData = parts.length > 1 ? parts[1] : parts[0];
|
|
7829
8273
|
return authorizedKeys.includes(keyData);
|
|
@@ -8221,11 +8665,11 @@ var init_project_context = __esm({
|
|
|
8221
8665
|
});
|
|
8222
8666
|
|
|
8223
8667
|
// packages/daemon/src/command-center/actions/handlers/config-actions.ts
|
|
8224
|
-
import { existsSync as
|
|
8225
|
-
import { join as
|
|
8668
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
8669
|
+
import { join as join21 } from "node:path";
|
|
8226
8670
|
function mutateConfigAction(input, deps2, fn) {
|
|
8227
8671
|
const context = resolveProjectContext(input, deps2);
|
|
8228
|
-
if (!
|
|
8672
|
+
if (!existsSync28(join21(context.dir, "ide.yml"))) {
|
|
8229
8673
|
throw new ActionError({
|
|
8230
8674
|
code: "ide_yml_missing",
|
|
8231
8675
|
message: "ide.yml was not found",
|
|
@@ -8648,8 +9092,8 @@ var init_inspect = __esm({
|
|
|
8648
9092
|
|
|
8649
9093
|
// packages/daemon/src/lib/filesystem-browser.ts
|
|
8650
9094
|
import { realpathSync, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
8651
|
-
import { homedir as
|
|
8652
|
-
import { isAbsolute as isAbsolute3, join as
|
|
9095
|
+
import { homedir as homedir17 } from "node:os";
|
|
9096
|
+
import { isAbsolute as isAbsolute3, join as join22, resolve as resolve19, sep } from "node:path";
|
|
8653
9097
|
function isUnderRoot(canonical, root) {
|
|
8654
9098
|
if (canonical === root) return true;
|
|
8655
9099
|
const prefix = root.endsWith(sep) ? root : root + sep;
|
|
@@ -8678,7 +9122,7 @@ var init_filesystem_browser = __esm({
|
|
|
8678
9122
|
});
|
|
8679
9123
|
|
|
8680
9124
|
// packages/daemon/src/lib/project-inspect.ts
|
|
8681
|
-
import { existsSync as
|
|
9125
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
8682
9126
|
import { isAbsolute as isAbsolute4, resolve as resolve20 } from "node:path";
|
|
8683
9127
|
function narrowPackageManager(raw) {
|
|
8684
9128
|
if (!raw) return null;
|
|
@@ -8689,7 +9133,7 @@ function inferTestCommand(packageManager) {
|
|
|
8689
9133
|
return packageManager === "npm" ? "npm test" : `${packageManager} test`;
|
|
8690
9134
|
}
|
|
8691
9135
|
async function inspectProject(dir, io = {}) {
|
|
8692
|
-
const exists = io.exists ??
|
|
9136
|
+
const exists = io.exists ?? existsSync29;
|
|
8693
9137
|
const absoluteDir = isAbsolute4(dir) ? dir : resolve20(dir);
|
|
8694
9138
|
if (!exists(absoluteDir)) {
|
|
8695
9139
|
throw new InspectDirNotFoundError(absoluteDir);
|
|
@@ -8730,8 +9174,8 @@ var init_project_inspect = __esm({
|
|
|
8730
9174
|
|
|
8731
9175
|
// packages/daemon/src/lib/project-onboard.ts
|
|
8732
9176
|
import yaml2 from "js-yaml";
|
|
8733
|
-
import { existsSync as
|
|
8734
|
-
import { join as
|
|
9177
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
9178
|
+
import { join as join23 } from "node:path";
|
|
8735
9179
|
function composeIdeYmlConfig(input) {
|
|
8736
9180
|
if (!Number.isInteger(input.agents) || input.agents < 1 || input.agents > 3) {
|
|
8737
9181
|
throw new OnboardInvalidInputError(
|
|
@@ -8789,8 +9233,8 @@ function composeIdeYmlConfig(input) {
|
|
|
8789
9233
|
}
|
|
8790
9234
|
return config2;
|
|
8791
9235
|
}
|
|
8792
|
-
function assertNoExistingIdeYml(dir, exists =
|
|
8793
|
-
const path2 =
|
|
9236
|
+
function assertNoExistingIdeYml(dir, exists = existsSync30) {
|
|
9237
|
+
const path2 = join23(dir, "ide.yml");
|
|
8794
9238
|
if (exists(path2)) {
|
|
8795
9239
|
throw new OnboardConflictError(path2);
|
|
8796
9240
|
}
|
|
@@ -8825,23 +9269,23 @@ __export(server_exports, {
|
|
|
8825
9269
|
});
|
|
8826
9270
|
import { execFile as execFile2 } from "node:child_process";
|
|
8827
9271
|
import { promisify } from "node:util";
|
|
8828
|
-
import { existsSync as
|
|
8829
|
-
import { join as
|
|
9272
|
+
import { existsSync as existsSync31, readFileSync as readFileSync18, readdirSync as readdirSync4 } from "node:fs";
|
|
9273
|
+
import { join as join24, dirname as dirname20, basename as basename8 } from "node:path";
|
|
8830
9274
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
8831
9275
|
import { Hono } from "hono";
|
|
8832
9276
|
import { streamSSE } from "hono/streaming";
|
|
8833
9277
|
import { cors } from "hono/cors";
|
|
8834
9278
|
import { zValidator } from "@hono/zod-validator";
|
|
8835
9279
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
8836
|
-
import { homedir as
|
|
9280
|
+
import { homedir as homedir18 } from "node:os";
|
|
8837
9281
|
import { isAbsolute as isAbsolute5, resolve as pathResolve } from "node:path";
|
|
8838
9282
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
8839
9283
|
import { WebSocketServer } from "ws";
|
|
8840
9284
|
function resolvePackageVersion() {
|
|
8841
|
-
const candidates = [
|
|
9285
|
+
const candidates = [join24(__dirname5, "../../package.json"), join24(__dirname5, "../package.json")];
|
|
8842
9286
|
for (const candidate of candidates) {
|
|
8843
9287
|
try {
|
|
8844
|
-
const parsed = JSON.parse(
|
|
9288
|
+
const parsed = JSON.parse(readFileSync18(candidate, "utf-8"));
|
|
8845
9289
|
if (typeof parsed.version === "string") return parsed.version;
|
|
8846
9290
|
} catch {
|
|
8847
9291
|
}
|
|
@@ -8905,7 +9349,7 @@ function sandboxResolveDir(rawDir) {
|
|
|
8905
9349
|
if (trimmed.includes("\0")) {
|
|
8906
9350
|
return { error: "invalid-path", message: "Path contains a null byte", status: 400 };
|
|
8907
9351
|
}
|
|
8908
|
-
const home = process.env.TMUX_IDE_HOME_OVERRIDE && process.env.TMUX_IDE_HOME_OVERRIDE.trim().length > 0 ? process.env.TMUX_IDE_HOME_OVERRIDE :
|
|
9352
|
+
const home = process.env.TMUX_IDE_HOME_OVERRIDE && process.env.TMUX_IDE_HOME_OVERRIDE.trim().length > 0 ? process.env.TMUX_IDE_HOME_OVERRIDE : homedir18();
|
|
8909
9353
|
let candidate = trimmed;
|
|
8910
9354
|
if (candidate === "~") {
|
|
8911
9355
|
candidate = home;
|
|
@@ -9575,7 +10019,7 @@ function createApp(options = {}) {
|
|
|
9575
10019
|
if (!parsed.success) {
|
|
9576
10020
|
return c.json({ error: "Invalid request", details: parsed.error.issues }, 400);
|
|
9577
10021
|
}
|
|
9578
|
-
if (!
|
|
10022
|
+
if (!existsSync31(parsed.data.dir)) {
|
|
9579
10023
|
return c.json({ error: `Directory "${parsed.data.dir}" does not exist` }, 400);
|
|
9580
10024
|
}
|
|
9581
10025
|
const jobId = randomUUID3();
|
|
@@ -9681,9 +10125,9 @@ function createApp(options = {}) {
|
|
|
9681
10125
|
}
|
|
9682
10126
|
function listAvailableTemplates() {
|
|
9683
10127
|
const __filename = fileURLToPath8(import.meta.url);
|
|
9684
|
-
const __dir =
|
|
9685
|
-
const templatesDir =
|
|
9686
|
-
if (!
|
|
10128
|
+
const __dir = dirname20(__filename);
|
|
10129
|
+
const templatesDir = join24(__dir, "..", "..", "..", "..", "templates");
|
|
10130
|
+
if (!existsSync31(templatesDir)) return [];
|
|
9687
10131
|
const labels = {
|
|
9688
10132
|
default: { label: "Default", description: "Single Claude pane + dev/shell row" },
|
|
9689
10133
|
nextjs: {
|
|
@@ -9772,7 +10216,7 @@ var init_server = __esm({
|
|
|
9772
10216
|
init_filesystem_browser();
|
|
9773
10217
|
init_project_inspect();
|
|
9774
10218
|
init_project_onboard();
|
|
9775
|
-
__dirname5 =
|
|
10219
|
+
__dirname5 = dirname20(fileURLToPath8(import.meta.url));
|
|
9776
10220
|
pkgVersion = resolvePackageVersion();
|
|
9777
10221
|
projectStreamConnections = 0;
|
|
9778
10222
|
sseMetrics = {
|
|
@@ -10975,7 +11419,7 @@ var require_package = __commonJS({
|
|
|
10975
11419
|
"package.json"(exports, module) {
|
|
10976
11420
|
module.exports = {
|
|
10977
11421
|
name: "tmux-ide",
|
|
10978
|
-
version: "2.
|
|
11422
|
+
version: "2.7.0",
|
|
10979
11423
|
description: "Turn any project into a tmux-powered terminal IDE with a simple ide.yml",
|
|
10980
11424
|
type: "module",
|
|
10981
11425
|
bin: {
|
|
@@ -11040,15 +11484,15 @@ var require_package = __commonJS({
|
|
|
11040
11484
|
dependencies: {
|
|
11041
11485
|
"@hono/node-server": "^1.19.11",
|
|
11042
11486
|
"@hono/zod-validator": "^0.7.6",
|
|
11043
|
-
"@opentui/core": "^0.
|
|
11044
|
-
"@opentui/solid": "^0.
|
|
11487
|
+
"@opentui/core": "^0.4.3",
|
|
11488
|
+
"@opentui/solid": "^0.4.3",
|
|
11045
11489
|
"@parcel/watcher": "^2.5.6",
|
|
11046
11490
|
"@types/ws": "^8.18.1",
|
|
11047
11491
|
hono: "^4.12.8",
|
|
11048
11492
|
ignore: "^7.0.5",
|
|
11049
11493
|
"js-yaml": "^4.1.1",
|
|
11050
11494
|
"node-pty": "1.2.0-beta.12",
|
|
11051
|
-
"solid-js": "
|
|
11495
|
+
"solid-js": "1.9.12",
|
|
11052
11496
|
ws: "^8.20.0",
|
|
11053
11497
|
zod: "^4.3.6"
|
|
11054
11498
|
},
|
|
@@ -11057,7 +11501,10 @@ var require_package = __commonJS({
|
|
|
11057
11501
|
"@parcel/watcher",
|
|
11058
11502
|
"esbuild",
|
|
11059
11503
|
"node-pty"
|
|
11060
|
-
]
|
|
11504
|
+
],
|
|
11505
|
+
overrides: {
|
|
11506
|
+
zod: "^4.3.6"
|
|
11507
|
+
}
|
|
11061
11508
|
},
|
|
11062
11509
|
devDependencies: {
|
|
11063
11510
|
"@eslint/js": "^10.0.1",
|
|
@@ -11075,7 +11522,7 @@ var require_package = __commonJS({
|
|
|
11075
11522
|
vitest: "^4.1.0"
|
|
11076
11523
|
},
|
|
11077
11524
|
optionalDependencies: {
|
|
11078
|
-
"@opentui/core-darwin-arm64": "^0.
|
|
11525
|
+
"@opentui/core-darwin-arm64": "^0.4.3"
|
|
11079
11526
|
}
|
|
11080
11527
|
};
|
|
11081
11528
|
}
|
|
@@ -11106,7 +11553,10 @@ function toFleetJson(projects) {
|
|
|
11106
11553
|
active: w.active,
|
|
11107
11554
|
panes: w.panes,
|
|
11108
11555
|
status: w.status
|
|
11109
|
-
}))
|
|
11556
|
+
})),
|
|
11557
|
+
// A pre-agents TeamSession (older constructor/test) yields `[]` — the
|
|
11558
|
+
// contract always exposes the array.
|
|
11559
|
+
agents: s.agents ?? []
|
|
11110
11560
|
}))
|
|
11111
11561
|
}))
|
|
11112
11562
|
};
|
|
@@ -11188,6 +11638,7 @@ function buildReport(target) {
|
|
|
11188
11638
|
hint: info.hintRaw
|
|
11189
11639
|
});
|
|
11190
11640
|
const manifest = resolved2.manifest;
|
|
11641
|
+
const subtree = manifest ? [] : describeSubtree(table, info.pid);
|
|
11191
11642
|
const snapshot = { ...readPaneSnapshot(info.id), title: info.title };
|
|
11192
11643
|
const explained = manifest ? explain(snapshot, manifest) : {
|
|
11193
11644
|
state: null,
|
|
@@ -11209,7 +11660,9 @@ function buildReport(target) {
|
|
|
11209
11660
|
resolution: {
|
|
11210
11661
|
manifestId: manifest?.id ?? null,
|
|
11211
11662
|
matchedCommand: resolved2.matchedCommand,
|
|
11212
|
-
source: resolved2.source
|
|
11663
|
+
source: resolved2.source,
|
|
11664
|
+
confidence: manifest ? manifest.confidence ?? "conservative" : null,
|
|
11665
|
+
subtree
|
|
11213
11666
|
},
|
|
11214
11667
|
states: explained.checked,
|
|
11215
11668
|
winner: explained.state,
|
|
@@ -11245,10 +11698,16 @@ function renderReport(r, opts = {}) {
|
|
|
11245
11698
|
} else {
|
|
11246
11699
|
out.push(` ${label("hint")} ${dim4("(unset)")}`);
|
|
11247
11700
|
}
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
)
|
|
11701
|
+
if (r.resolution.manifestId) {
|
|
11702
|
+
const conf = r.resolution.confidence === "tuned" ? c("\x1B[32m", "tuned") : dim4(r.resolution.confidence ?? "conservative");
|
|
11703
|
+
out.push(
|
|
11704
|
+
` ${label("manifest")} ${r.resolution.manifestId} ${dim4(`via ${r.resolution.source}` + (r.resolution.matchedCommand ? ` "${r.resolution.matchedCommand}"` : ""))} [${conf}]`
|
|
11705
|
+
);
|
|
11706
|
+
} else {
|
|
11707
|
+
const saw = r.resolution.subtree.length > 0 ? r.resolution.subtree.join(", ") : r.pane.cmd || "(nothing)";
|
|
11708
|
+
out.push(` ${label("manifest")} ${dim4("none matched")} \u2014 ${dim4(`process-tree saw: ${saw}`)}`);
|
|
11709
|
+
out.push(` ${dim4("set `tmux set-option -p @agent_hint <agent>` to force one")}`);
|
|
11710
|
+
}
|
|
11252
11711
|
out.push("");
|
|
11253
11712
|
out.push(bold4(" state rules"));
|
|
11254
11713
|
if (r.states.length === 0) {
|
|
@@ -11322,7 +11781,7 @@ __export(worktree_exports, {
|
|
|
11322
11781
|
worktreeSessionName: () => worktreeSessionName
|
|
11323
11782
|
});
|
|
11324
11783
|
import { execFileSync as execFileSync13 } from "node:child_process";
|
|
11325
|
-
import { basename as basename9, dirname as
|
|
11784
|
+
import { basename as basename9, dirname as dirname21, isAbsolute as isAbsolute6, join as join25, resolve as resolve22 } from "node:path";
|
|
11326
11785
|
function sanitizeForTmux(part) {
|
|
11327
11786
|
return part.replace(/[.:/\s]+/g, "-");
|
|
11328
11787
|
}
|
|
@@ -11331,11 +11790,11 @@ function worktreeSessionName(project, branch) {
|
|
|
11331
11790
|
}
|
|
11332
11791
|
function defaultWorktreeBaseDir(repoDir) {
|
|
11333
11792
|
const abs = resolve22(repoDir);
|
|
11334
|
-
return
|
|
11793
|
+
return join25(dirname21(abs), `${basename9(abs)}-worktrees`);
|
|
11335
11794
|
}
|
|
11336
11795
|
function worktreePath(repoDir, branch, configuredDir) {
|
|
11337
11796
|
const base = configuredDir && configuredDir.length > 0 ? isAbsolute6(configuredDir) ? configuredDir : resolve22(repoDir, configuredDir) : defaultWorktreeBaseDir(repoDir);
|
|
11338
|
-
return
|
|
11797
|
+
return join25(base, branch);
|
|
11339
11798
|
}
|
|
11340
11799
|
function parseWorktreeList(porcelain) {
|
|
11341
11800
|
const entries = [];
|
|
@@ -11479,8 +11938,8 @@ __export(update_exports, {
|
|
|
11479
11938
|
runUpdate: () => runUpdate
|
|
11480
11939
|
});
|
|
11481
11940
|
import { execSync as execSync4 } from "node:child_process";
|
|
11482
|
-
import { existsSync as
|
|
11483
|
-
import { dirname as
|
|
11941
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
11942
|
+
import { dirname as dirname22, join as join26 } from "node:path";
|
|
11484
11943
|
function detectPackageManager(cliPath) {
|
|
11485
11944
|
const p = cliPath.toLowerCase();
|
|
11486
11945
|
if (/(^|\/)\.?bun(\/|$)/.test(p)) return "bun";
|
|
@@ -11526,8 +11985,8 @@ function renderPlan(plan, { current, latest, dryRun }) {
|
|
|
11526
11985
|
function findGitCheckoutRoot(startDir) {
|
|
11527
11986
|
let dir = startDir;
|
|
11528
11987
|
for (; ; ) {
|
|
11529
|
-
if (
|
|
11530
|
-
const parent =
|
|
11988
|
+
if (existsSync32(join26(dir, ".git"))) return dir;
|
|
11989
|
+
const parent = dirname22(dir);
|
|
11531
11990
|
if (parent === dir) return null;
|
|
11532
11991
|
dir = parent;
|
|
11533
11992
|
}
|
|
@@ -11659,65 +12118,68 @@ var init_server2 = __esm({
|
|
|
11659
12118
|
// bin/cli.ts
|
|
11660
12119
|
init_launch();
|
|
11661
12120
|
import { parseArgs } from "node:util";
|
|
11662
|
-
import { resolve as resolve23, dirname as
|
|
12121
|
+
import { resolve as resolve23, dirname as dirname23, join as join27 } from "node:path";
|
|
11663
12122
|
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
11664
|
-
import { existsSync as
|
|
12123
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
11665
12124
|
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
11666
12125
|
|
|
11667
12126
|
// packages/daemon/src/tui/team/entry.ts
|
|
11668
|
-
function
|
|
11669
|
-
|
|
12127
|
+
function resolveEntry(opts) {
|
|
12128
|
+
if (opts.teamFlag) return "cockpit";
|
|
12129
|
+
if (opts.hasIdeYml) return "project";
|
|
12130
|
+
return opts.frontDoor ? "app" : "cockpit";
|
|
11670
12131
|
}
|
|
11671
12132
|
|
|
11672
12133
|
// bin/cli.ts
|
|
12134
|
+
init_app_config();
|
|
11673
12135
|
init_compiled();
|
|
11674
12136
|
|
|
11675
12137
|
// packages/daemon/src/init.ts
|
|
11676
12138
|
init_detect();
|
|
11677
12139
|
init_output();
|
|
11678
12140
|
import {
|
|
11679
|
-
existsSync as
|
|
11680
|
-
readFileSync as
|
|
11681
|
-
writeFileSync as
|
|
11682
|
-
renameSync as
|
|
11683
|
-
mkdirSync as
|
|
12141
|
+
existsSync as existsSync18,
|
|
12142
|
+
readFileSync as readFileSync12,
|
|
12143
|
+
writeFileSync as writeFileSync11,
|
|
12144
|
+
renameSync as renameSync7,
|
|
12145
|
+
mkdirSync as mkdirSync11,
|
|
11684
12146
|
readdirSync as readdirSync2,
|
|
11685
12147
|
copyFileSync as copyFileSync2
|
|
11686
12148
|
} from "node:fs";
|
|
11687
|
-
import { resolve as resolve11, join as
|
|
12149
|
+
import { resolve as resolve11, join as join13, basename as basename5, dirname as dirname13 } from "node:path";
|
|
11688
12150
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
11689
|
-
var __dirname4 =
|
|
12151
|
+
var __dirname4 = dirname13(fileURLToPath5(import.meta.url));
|
|
11690
12152
|
function copyTemplateSkills(targetDir) {
|
|
11691
12153
|
const created = [];
|
|
11692
12154
|
const templateSkillsDir = resolve11(__dirname4, "..", "..", "..", "templates", "skills");
|
|
11693
|
-
if (!
|
|
11694
|
-
|
|
12155
|
+
if (!existsSync18(templateSkillsDir)) return created;
|
|
12156
|
+
mkdirSync11(targetDir, { recursive: true });
|
|
11695
12157
|
for (const file of readdirSync2(templateSkillsDir)) {
|
|
11696
12158
|
if (!file.endsWith(".md")) continue;
|
|
11697
|
-
const destination =
|
|
11698
|
-
copyFileSync2(
|
|
12159
|
+
const destination = join13(targetDir, file);
|
|
12160
|
+
copyFileSync2(join13(templateSkillsDir, file), destination);
|
|
11699
12161
|
created.push(destination);
|
|
11700
12162
|
}
|
|
11701
12163
|
return created;
|
|
11702
12164
|
}
|
|
11703
12165
|
function scaffoldLibraryStubs(dir) {
|
|
11704
12166
|
const created = [];
|
|
11705
|
-
const libraryDir =
|
|
11706
|
-
if (!
|
|
11707
|
-
|
|
12167
|
+
const libraryDir = join13(dir, ".tmux-ide", "library");
|
|
12168
|
+
if (!existsSync18(libraryDir)) {
|
|
12169
|
+
mkdirSync11(libraryDir, { recursive: true });
|
|
11708
12170
|
created.push(libraryDir);
|
|
11709
12171
|
}
|
|
11710
|
-
const archPath =
|
|
11711
|
-
if (!
|
|
11712
|
-
|
|
12172
|
+
const archPath = join13(libraryDir, "architecture.md");
|
|
12173
|
+
if (!existsSync18(archPath)) {
|
|
12174
|
+
writeFileSync11(
|
|
11713
12175
|
archPath,
|
|
11714
12176
|
"# Architecture\n\n<!-- Describe your project's architecture here. This context is injected into agent dispatch prompts. -->\n"
|
|
11715
12177
|
);
|
|
11716
12178
|
created.push(archPath);
|
|
11717
12179
|
}
|
|
11718
|
-
const learningsPath =
|
|
11719
|
-
if (!
|
|
11720
|
-
|
|
12180
|
+
const learningsPath = join13(libraryDir, "learnings.md");
|
|
12181
|
+
if (!existsSync18(learningsPath)) {
|
|
12182
|
+
writeFileSync11(
|
|
11721
12183
|
learningsPath,
|
|
11722
12184
|
"# Learnings\n\n<!-- Task summaries are automatically appended here by the orchestrator. -->\n"
|
|
11723
12185
|
);
|
|
@@ -11727,13 +12189,13 @@ function scaffoldLibraryStubs(dir) {
|
|
|
11727
12189
|
}
|
|
11728
12190
|
function scaffoldValidationContract(dir) {
|
|
11729
12191
|
const created = [];
|
|
11730
|
-
const tasksDir =
|
|
11731
|
-
if (!
|
|
11732
|
-
|
|
12192
|
+
const tasksDir = join13(dir, ".tasks");
|
|
12193
|
+
if (!existsSync18(tasksDir)) {
|
|
12194
|
+
mkdirSync11(tasksDir, { recursive: true });
|
|
11733
12195
|
}
|
|
11734
|
-
const contractPath =
|
|
11735
|
-
if (!
|
|
11736
|
-
|
|
12196
|
+
const contractPath = join13(tasksDir, "validation-contract.md");
|
|
12197
|
+
if (!existsSync18(contractPath)) {
|
|
12198
|
+
writeFileSync11(
|
|
11737
12199
|
contractPath,
|
|
11738
12200
|
"# Validation Contract\n\n<!-- Define assertions that the validator agent will verify. Example: -->\n<!-- - VAL-001: All tests pass -->\n<!-- - VAL-002: No TypeScript errors -->\n<!-- - VAL-003: Lint passes with zero warnings -->\n"
|
|
11739
12201
|
);
|
|
@@ -11744,11 +12206,11 @@ function scaffoldValidationContract(dir) {
|
|
|
11744
12206
|
function scaffoldAgentsMd(dir, name) {
|
|
11745
12207
|
const created = [];
|
|
11746
12208
|
const agentsTemplatePath = resolve11(__dirname4, "..", "..", "..", "templates", "AGENTS.md");
|
|
11747
|
-
if (
|
|
11748
|
-
const agentsPath =
|
|
11749
|
-
if (!
|
|
11750
|
-
const content =
|
|
11751
|
-
|
|
12209
|
+
if (existsSync18(agentsTemplatePath)) {
|
|
12210
|
+
const agentsPath = join13(dir, "AGENTS.md");
|
|
12211
|
+
if (!existsSync18(agentsPath)) {
|
|
12212
|
+
const content = readFileSync12(agentsTemplatePath, "utf-8").replace(/{{name}}/g, name);
|
|
12213
|
+
writeFileSync11(agentsPath, content);
|
|
11752
12214
|
created.push(agentsPath);
|
|
11753
12215
|
}
|
|
11754
12216
|
}
|
|
@@ -11766,7 +12228,7 @@ function scaffoldTeamWorkspace(dir, name) {
|
|
|
11766
12228
|
}
|
|
11767
12229
|
function scaffoldMissionsWorkspace(dir, name) {
|
|
11768
12230
|
const created = [];
|
|
11769
|
-
const skillsDir =
|
|
12231
|
+
const skillsDir = join13(dir, ".tmux-ide", "skills");
|
|
11770
12232
|
created.push(...copyTemplateSkills(skillsDir));
|
|
11771
12233
|
created.push(...scaffoldTeamWorkspace(dir, name));
|
|
11772
12234
|
return created;
|
|
@@ -11777,30 +12239,30 @@ async function init({
|
|
|
11777
12239
|
} = {}) {
|
|
11778
12240
|
const dir = process.cwd();
|
|
11779
12241
|
const configPath = resolve11(dir, "ide.yml");
|
|
11780
|
-
if (
|
|
12242
|
+
if (existsSync18(configPath)) {
|
|
11781
12243
|
outputError("ide.yml already exists in this directory", "EXISTS");
|
|
11782
12244
|
}
|
|
11783
12245
|
if (template) {
|
|
11784
12246
|
const templatePath = resolve11(__dirname4, "..", "..", "..", "templates", `${template}.yml`);
|
|
11785
|
-
if (!
|
|
12247
|
+
if (!existsSync18(templatePath)) {
|
|
11786
12248
|
outputError(`Template "${template}" not found`, "NOT_FOUND");
|
|
11787
12249
|
}
|
|
11788
|
-
let content =
|
|
12250
|
+
let content = readFileSync12(templatePath, "utf-8");
|
|
11789
12251
|
const name2 = basename5(dir);
|
|
11790
12252
|
content = content.replace(/^name: .+/m, `name: ${name2}`);
|
|
11791
12253
|
const tmpPath = configPath + ".tmp";
|
|
11792
|
-
|
|
11793
|
-
|
|
12254
|
+
writeFileSync11(tmpPath, content);
|
|
12255
|
+
renameSync7(tmpPath, configPath);
|
|
11794
12256
|
let created;
|
|
11795
12257
|
if (template === "missions") {
|
|
11796
12258
|
created = scaffoldMissionsWorkspace(dir, name2);
|
|
11797
12259
|
} else if (isTeamTemplate(template)) {
|
|
11798
12260
|
created = [
|
|
11799
|
-
...copyTemplateSkills(
|
|
12261
|
+
...copyTemplateSkills(join13(dir, ".tmux-ide", "skills")),
|
|
11800
12262
|
...scaffoldTeamWorkspace(dir, name2)
|
|
11801
12263
|
];
|
|
11802
12264
|
} else {
|
|
11803
|
-
created = copyTemplateSkills(
|
|
12265
|
+
created = copyTemplateSkills(join13(dir, ".tmux-ide", "skills"));
|
|
11804
12266
|
}
|
|
11805
12267
|
if (json2) {
|
|
11806
12268
|
console.log(JSON.stringify({ created: true, template, name: name2, paths: created }));
|
|
@@ -11820,8 +12282,8 @@ async function init({
|
|
|
11820
12282
|
const config2 = suggestConfig(dir, detected);
|
|
11821
12283
|
const yaml3 = (await import("js-yaml")).default;
|
|
11822
12284
|
const tmpPath2 = configPath + ".tmp";
|
|
11823
|
-
|
|
11824
|
-
|
|
12285
|
+
writeFileSync11(tmpPath2, yaml3.dump(config2, { lineWidth: -1, noRefs: true, quotingType: '"' }));
|
|
12286
|
+
renameSync7(tmpPath2, configPath);
|
|
11825
12287
|
const desc = detected.frameworks.join(" + ");
|
|
11826
12288
|
if (json2) {
|
|
11827
12289
|
console.log(JSON.stringify({ created: true, detected: detected.frameworks, name }));
|
|
@@ -11832,11 +12294,11 @@ async function init({
|
|
|
11832
12294
|
}
|
|
11833
12295
|
} else {
|
|
11834
12296
|
const templatePath = resolve11(__dirname4, "..", "..", "..", "templates", "default.yml");
|
|
11835
|
-
let content =
|
|
12297
|
+
let content = readFileSync12(templatePath, "utf-8");
|
|
11836
12298
|
content = content.replace(/^name: .+/m, `name: ${name}`);
|
|
11837
12299
|
const tmpPath3 = configPath + ".tmp";
|
|
11838
|
-
|
|
11839
|
-
|
|
12300
|
+
writeFileSync11(tmpPath3, content);
|
|
12301
|
+
renameSync7(tmpPath3, configPath);
|
|
11840
12302
|
if (json2) {
|
|
11841
12303
|
console.log(JSON.stringify({ created: true, template: "default", name }));
|
|
11842
12304
|
} else {
|
|
@@ -11846,8 +12308,8 @@ async function init({
|
|
|
11846
12308
|
console.log("Edit it to configure your workspace, then run: tmux-ide");
|
|
11847
12309
|
}
|
|
11848
12310
|
}
|
|
11849
|
-
const skillsDir =
|
|
11850
|
-
if (!
|
|
12311
|
+
const skillsDir = join13(dir, ".tmux-ide", "skills");
|
|
12312
|
+
if (!existsSync18(skillsDir)) {
|
|
11851
12313
|
const created = copyTemplateSkills(skillsDir);
|
|
11852
12314
|
if (created.length > 0 && !json2) {
|
|
11853
12315
|
console.log("Copied built-in skill templates to .tmux-ide/skills/");
|
|
@@ -11935,8 +12397,8 @@ init_skill_sync();
|
|
|
11935
12397
|
init_agent_discovery();
|
|
11936
12398
|
init_compiled();
|
|
11937
12399
|
import { execSync as execSync3 } from "node:child_process";
|
|
11938
|
-
import { existsSync as
|
|
11939
|
-
import { resolve as resolve14, dirname as
|
|
12400
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
12401
|
+
import { resolve as resolve14, dirname as dirname15 } from "node:path";
|
|
11940
12402
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
11941
12403
|
function agentIntegrationRows(agents) {
|
|
11942
12404
|
return presentAgents(agents).map((agent) => {
|
|
@@ -12006,7 +12468,7 @@ async function doctor({
|
|
|
12006
12468
|
checks.push(
|
|
12007
12469
|
check("ide.yml exists", () => {
|
|
12008
12470
|
const path2 = resolve14(".", "ide.yml");
|
|
12009
|
-
if (!
|
|
12471
|
+
if (!existsSync20(path2)) throw new Error("not found in current directory");
|
|
12010
12472
|
return "found";
|
|
12011
12473
|
})
|
|
12012
12474
|
);
|
|
@@ -12014,11 +12476,11 @@ async function doctor({
|
|
|
12014
12476
|
check(
|
|
12015
12477
|
"TUI surfaces (cockpit / widgets)",
|
|
12016
12478
|
() => {
|
|
12017
|
-
const here =
|
|
12479
|
+
const here = dirname15(fileURLToPath7(import.meta.url));
|
|
12018
12480
|
const checkoutEntry = [
|
|
12019
12481
|
resolve14(here, "../packages/daemon/src/tui/team/index.tsx"),
|
|
12020
12482
|
resolve14(here, "tui/team/index.tsx")
|
|
12021
|
-
].find(
|
|
12483
|
+
].find(existsSync20);
|
|
12022
12484
|
const binary = findCompiledTui();
|
|
12023
12485
|
if (checkoutEntry && isBunAvailable()) return "dev checkout (bun)";
|
|
12024
12486
|
if (binary) return `compiled binary (${binary})`;
|
|
@@ -12121,11 +12583,11 @@ init_yaml_io();
|
|
|
12121
12583
|
init_src();
|
|
12122
12584
|
init_canonical_daemon();
|
|
12123
12585
|
import { resolve as resolve15 } from "node:path";
|
|
12124
|
-
import { existsSync as
|
|
12586
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
12125
12587
|
async function status(targetDir, { json: json2 } = {}) {
|
|
12126
12588
|
const dir = resolve15(targetDir ?? ".");
|
|
12127
12589
|
const { name: session } = getSessionName(dir);
|
|
12128
|
-
const configExists =
|
|
12590
|
+
const configExists = existsSync21(resolve15(dir, "ide.yml"));
|
|
12129
12591
|
const state = getSessionState(session);
|
|
12130
12592
|
const running = state.running;
|
|
12131
12593
|
let panes = [];
|
|
@@ -12577,7 +13039,9 @@ function reportPlan(plan, snapshot, { json: json2, dryRun, restored, launched, r
|
|
|
12577
13039
|
init_send();
|
|
12578
13040
|
init_errors2();
|
|
12579
13041
|
init_output();
|
|
12580
|
-
var __dirname6 =
|
|
13042
|
+
var __dirname6 = dirname23(fileURLToPath9(import.meta.url));
|
|
13043
|
+
var selfPath = fileURLToPath9(import.meta.url);
|
|
13044
|
+
var nodeCliPath = selfPath.endsWith(".js") ? selfPath : resolve23(__dirname6, "cli.js");
|
|
12581
13045
|
var { positionals, values } = parseArgs({
|
|
12582
13046
|
allowPositionals: true,
|
|
12583
13047
|
strict: false,
|
|
@@ -12655,6 +13119,7 @@ var knownCommands = /* @__PURE__ */ new Set([
|
|
|
12655
13119
|
"send",
|
|
12656
13120
|
"settings",
|
|
12657
13121
|
"team",
|
|
13122
|
+
"app",
|
|
12658
13123
|
"switcher",
|
|
12659
13124
|
"wait",
|
|
12660
13125
|
"events",
|
|
@@ -12715,6 +13180,7 @@ ${bold3("Usage:")}
|
|
|
12715
13180
|
${dim3("(--resume-agents revives claude conversations via claude --resume)")}
|
|
12716
13181
|
${cyan2("tmux-ide attach")} ${dim3("Reattach to a running session")}
|
|
12717
13182
|
${cyan2("tmux-ide team")} [--json] ${dim3("TUI over all tmux sessions (--json prints fleet state)")}
|
|
13183
|
+
${cyan2("tmux-ide app")} [session] ${dim3("Unified app: fleet home + live session mirror (bare = home)")}
|
|
12718
13184
|
${cyan2("tmux-ide switcher")} ${dim3("Compact session picker (opens in the M-p popup on adopted sessions)")}
|
|
12719
13185
|
${cyan2("tmux-ide wait agent-status")} <session> --status <s> [--timeout <ms>]
|
|
12720
13186
|
${dim3("Block until a session reaches a status (exit 0 match / 1 timeout)")}
|
|
@@ -12782,7 +13248,7 @@ function execBunWidget(surface, scriptPath, args, commandLabel, extraEnv = {}) {
|
|
|
12782
13248
|
surface,
|
|
12783
13249
|
scriptPath,
|
|
12784
13250
|
args,
|
|
12785
|
-
checkoutExists:
|
|
13251
|
+
checkoutExists: existsSync33(scriptPath),
|
|
12786
13252
|
bunAvailable: isBunAvailable(),
|
|
12787
13253
|
compiledBinary: findCompiledTui()
|
|
12788
13254
|
});
|
|
@@ -12793,7 +13259,12 @@ Install bun (https://bun.sh) \u2014 the TUI surfaces run on it. Sources ship wit
|
|
|
12793
13259
|
{ code: "USAGE", exitCode: 1 }
|
|
12794
13260
|
);
|
|
12795
13261
|
}
|
|
12796
|
-
const env = {
|
|
13262
|
+
const env = {
|
|
13263
|
+
...process.env,
|
|
13264
|
+
TMUX_IDE_CWD: process.cwd(),
|
|
13265
|
+
TMUX_IDE_CLI: nodeCliPath,
|
|
13266
|
+
...extraEnv
|
|
13267
|
+
};
|
|
12797
13268
|
if (launch2.mode === "bun") {
|
|
12798
13269
|
execFileSync14(launch2.bin, launch2.argv, {
|
|
12799
13270
|
stdio: "inherit",
|
|
@@ -12811,9 +13282,13 @@ async function printFleetJson() {
|
|
|
12811
13282
|
console.log(JSON.stringify(toFleetJson2(listTeamProjects2(createStatusTracker2())), null, 2));
|
|
12812
13283
|
}
|
|
12813
13284
|
var teamScriptPath = resolve23(__dirname6, "../packages/daemon/src/tui/team/index.tsx");
|
|
13285
|
+
var appScriptPath = resolve23(__dirname6, "../packages/daemon/src/tui/mirror/app.tsx");
|
|
12814
13286
|
function launchTeamCockpit() {
|
|
12815
13287
|
execBunWidget("team", teamScriptPath, [], "team");
|
|
12816
13288
|
}
|
|
13289
|
+
function launchApp() {
|
|
13290
|
+
execBunWidget("app", appScriptPath, [], "app");
|
|
13291
|
+
}
|
|
12817
13292
|
try {
|
|
12818
13293
|
switch (command) {
|
|
12819
13294
|
case "start": {
|
|
@@ -12831,13 +13306,19 @@ try {
|
|
|
12831
13306
|
}
|
|
12832
13307
|
}
|
|
12833
13308
|
const targetDir = resolve23(startTargetDir || ".");
|
|
12834
|
-
const hasIdeYml =
|
|
12835
|
-
|
|
13309
|
+
const hasIdeYml = existsSync33(join27(targetDir, "ide.yml"));
|
|
13310
|
+
const entry = resolveEntry({
|
|
13311
|
+
hasIdeYml,
|
|
13312
|
+
teamFlag: values.team === true,
|
|
13313
|
+
frontDoor: loadAppConfig().app.frontDoor
|
|
13314
|
+
});
|
|
13315
|
+
if (entry !== "project") {
|
|
12836
13316
|
if (json) {
|
|
12837
13317
|
await printFleetJson();
|
|
12838
13318
|
break;
|
|
12839
13319
|
}
|
|
12840
|
-
|
|
13320
|
+
if (entry === "app") launchApp();
|
|
13321
|
+
else launchTeamCockpit();
|
|
12841
13322
|
break;
|
|
12842
13323
|
}
|
|
12843
13324
|
await launch(startTargetDir, { json });
|
|
@@ -12937,8 +13418,8 @@ try {
|
|
|
12937
13418
|
const messageStart = values.to ? 1 : 2;
|
|
12938
13419
|
let message = positionals.slice(messageStart).join(" ");
|
|
12939
13420
|
if (!message && !process.stdin.isTTY) {
|
|
12940
|
-
const { readFileSync:
|
|
12941
|
-
message =
|
|
13421
|
+
const { readFileSync: readFileSync19 } = await import("node:fs");
|
|
13422
|
+
message = readFileSync19(0, "utf-8").trim();
|
|
12942
13423
|
}
|
|
12943
13424
|
await send(null, { json, to: target, message, noEnter: values["no-enter"] });
|
|
12944
13425
|
break;
|
|
@@ -12963,6 +13444,12 @@ try {
|
|
|
12963
13444
|
launchTeamCockpit();
|
|
12964
13445
|
break;
|
|
12965
13446
|
}
|
|
13447
|
+
case "app": {
|
|
13448
|
+
const session = positionals[1];
|
|
13449
|
+
const appArgs = session ? [`--target=${session}`] : [];
|
|
13450
|
+
execBunWidget("app", appScriptPath, appArgs, "app");
|
|
13451
|
+
break;
|
|
13452
|
+
}
|
|
12966
13453
|
case "switcher": {
|
|
12967
13454
|
const clientArg = typeof values.client === "string" ? values.client : "";
|
|
12968
13455
|
execBunWidget("team", teamScriptPath, [], "switcher", { TMUX_IDE_PICKER_CLIENT: clientArg });
|
|
@@ -13055,10 +13542,10 @@ try {
|
|
|
13055
13542
|
}
|
|
13056
13543
|
}
|
|
13057
13544
|
case "events": {
|
|
13058
|
-
const { readFileSync:
|
|
13545
|
+
const { readFileSync: readFileSync19, existsSync: existsSync34, statSync: statSync5, openSync, readSync, closeSync } = await import("node:fs");
|
|
13059
13546
|
const { eventsPath: eventsPath2, formatEventLine: formatEventLine2 } = await Promise.resolve().then(() => (init_events(), events_exports));
|
|
13060
13547
|
const path2 = eventsPath2();
|
|
13061
|
-
if (!
|
|
13548
|
+
if (!existsSync34(path2)) {
|
|
13062
13549
|
console.log("no events yet \u2014 is a session adopted? (the chrome updater writes events)");
|
|
13063
13550
|
break;
|
|
13064
13551
|
}
|
|
@@ -13078,7 +13565,7 @@ try {
|
|
|
13078
13565
|
} catch {
|
|
13079
13566
|
}
|
|
13080
13567
|
};
|
|
13081
|
-
const allLines =
|
|
13568
|
+
const allLines = readFileSync19(path2, "utf8").split("\n").filter((l) => l.trim().length > 0);
|
|
13082
13569
|
for (const line of allLines.slice(-50)) printLine(line);
|
|
13083
13570
|
if (!values.follow) break;
|
|
13084
13571
|
let offset = statSync5(path2).size;
|
|
@@ -13485,7 +13972,7 @@ Known panels: ${POPUP_WIDGETS2.join(", ")}.`,
|
|
|
13485
13972
|
const mainPath = worktrees[0]?.path ?? repoDir;
|
|
13486
13973
|
const projectName = getSessionName2(mainPath).name;
|
|
13487
13974
|
async function openWorktreeSession(wtPath, name) {
|
|
13488
|
-
if (
|
|
13975
|
+
if (existsSync33(join27(wtPath, "ide.yml"))) {
|
|
13489
13976
|
await launch(wtPath, { attach: false, sessionName: name });
|
|
13490
13977
|
} else {
|
|
13491
13978
|
if (!hasSession2(name)) createDetachedSession2(name, wtPath);
|
|
@@ -13622,6 +14109,16 @@ Known panels: ${POPUP_WIDGETS2.join(", ")}.`,
|
|
|
13622
14109
|
break;
|
|
13623
14110
|
}
|
|
13624
14111
|
case "update": {
|
|
14112
|
+
if (values["tui-binary"] === true) {
|
|
14113
|
+
const { downloadTuiBinary: downloadTuiBinary2 } = await Promise.resolve().then(() => (init_tui_binary(), tui_binary_exports));
|
|
14114
|
+
const { path: path2 } = await downloadTuiBinary2({ log: (m) => console.error(m) });
|
|
14115
|
+
if (json) {
|
|
14116
|
+
console.log(JSON.stringify({ ok: true, path: path2 }, null, 2));
|
|
14117
|
+
} else {
|
|
14118
|
+
console.log(`TUI binary ready: ${path2}`);
|
|
14119
|
+
}
|
|
14120
|
+
break;
|
|
14121
|
+
}
|
|
13625
14122
|
const { runUpdate: runUpdate2 } = await Promise.resolve().then(() => (init_update(), update_exports));
|
|
13626
14123
|
const dryRun = values["dry-run"] === true;
|
|
13627
14124
|
const plan = runUpdate2({ cliDir: __dirname6, dryRun });
|