tmux-ide 2.6.1 → 2.8.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 +36 -14
- package/bin/cli.js +4174 -1235
- package/bin/cli.ts +426 -72
- package/package.json +10 -6
- package/packages/contracts/src/__tests__/control.test.ts +154 -0
- package/packages/contracts/src/control.ts +217 -0
- package/packages/contracts/src/index.ts +1 -0
- package/packages/daemon/dist/agent-explain.d.ts +8 -1
- package/packages/daemon/dist/agent-explain.js +19 -3
- package/packages/daemon/dist/control/client.d.ts +23 -0
- package/packages/daemon/dist/control/client.js +105 -0
- package/packages/daemon/dist/control/dispatch.d.ts +34 -0
- package/packages/daemon/dist/control/dispatch.js +83 -0
- package/packages/daemon/dist/control/fanout.d.ts +19 -0
- package/packages/daemon/dist/control/fanout.js +37 -0
- package/packages/daemon/dist/control/frames.d.ts +23 -0
- package/packages/daemon/dist/control/frames.js +37 -0
- package/packages/daemon/dist/control/lifecycle.d.ts +45 -0
- package/packages/daemon/dist/control/lifecycle.js +114 -0
- package/packages/daemon/dist/control/server.d.ts +16 -0
- package/packages/daemon/dist/control/server.js +214 -0
- package/packages/daemon/dist/control/verbs.d.ts +11 -0
- package/packages/daemon/dist/control/verbs.js +91 -0
- package/packages/daemon/dist/doctor.d.ts +18 -0
- package/packages/daemon/dist/doctor.js +105 -15
- package/packages/daemon/dist/lib/agent-discovery.d.ts +27 -2
- package/packages/daemon/dist/lib/agent-discovery.js +29 -14
- package/packages/daemon/dist/lib/app-config.d.ts +106 -0
- package/packages/daemon/dist/lib/app-config.js +104 -5
- package/packages/daemon/dist/lib/manifest-pack.d.ts +79 -0
- package/packages/daemon/dist/lib/manifest-pack.js +232 -0
- package/packages/daemon/dist/lib/state-home.d.ts +2 -0
- package/packages/daemon/dist/lib/state-home.js +12 -0
- 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/lib/update-check.js +5 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +34 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/PkgInfo +1 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/AppIcon.icns +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +139 -0
- package/packages/daemon/dist/restore.d.ts +35 -8
- package/packages/daemon/dist/restore.js +52 -15
- package/packages/daemon/dist/send.d.ts +33 -1
- package/packages/daemon/dist/send.js +32 -19
- 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/control/client.ts +128 -0
- package/packages/daemon/src/control/dispatch.ts +107 -0
- package/packages/daemon/src/control/fanout.ts +44 -0
- package/packages/daemon/src/control/frames.ts +40 -0
- package/packages/daemon/src/control/lifecycle.ts +151 -0
- package/packages/daemon/src/control/server.ts +237 -0
- package/packages/daemon/src/control/verbs.ts +118 -0
- package/packages/daemon/src/doctor.ts +113 -28
- package/packages/daemon/src/lib/agent-discovery.ts +53 -13
- package/packages/daemon/src/lib/app-config.ts +194 -5
- package/packages/daemon/src/lib/manifest-pack.ts +255 -0
- package/packages/daemon/src/lib/state-home.ts +13 -0
- package/packages/daemon/src/lib/tui-binary.ts +165 -0
- package/packages/daemon/src/lib/update-check.ts +5 -0
- package/packages/daemon/src/restore.ts +53 -15
- package/packages/daemon/src/send.ts +55 -21
- package/packages/daemon/src/tui/chrome/events.ts +4 -4
- package/packages/daemon/src/tui/chrome/front-door.ts +39 -0
- package/packages/daemon/src/tui/chrome/notify-prefs.ts +58 -0
- package/packages/daemon/src/tui/chrome/notify-state.ts +76 -0
- package/packages/daemon/src/tui/chrome/notify.ts +737 -52
- package/packages/daemon/src/tui/chrome/updater.ts +318 -30
- package/packages/daemon/src/tui/compiled.ts +11 -3
- package/packages/daemon/src/tui/detect/classify.ts +49 -0
- package/packages/daemon/src/tui/detect/manifest-loader.ts +56 -6
- package/packages/daemon/src/tui/detect/manifest.ts +39 -3
- package/packages/daemon/src/tui/detect/manifests.ts +395 -33
- package/packages/daemon/src/tui/detect/process-tree.ts +33 -3
- package/packages/daemon/src/tui/detect/session-id.ts +503 -0
- package/packages/daemon/src/tui/integrations/opencode.ts +121 -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 +126 -0
- package/packages/daemon/src/tui/mirror/agent-lifecycle.ts +437 -0
- package/packages/daemon/src/tui/mirror/agent-rows.ts +155 -0
- package/packages/daemon/src/tui/mirror/app-state.ts +342 -0
- package/packages/daemon/src/tui/mirror/app.tsx +7048 -0
- package/packages/daemon/src/tui/mirror/attention.ts +110 -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 +367 -0
- package/packages/daemon/src/tui/mirror/diff-model.ts +387 -0
- package/packages/daemon/src/tui/mirror/editor-buffer.ts +117 -0
- package/packages/daemon/src/tui/mirror/file-tree.ts +322 -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/host-terminal.ts +49 -0
- package/packages/daemon/src/tui/mirror/hosted.ts +205 -0
- package/packages/daemon/src/tui/mirror/input-coalescer.ts +105 -0
- package/packages/daemon/src/tui/mirror/layout-parse.ts +154 -0
- package/packages/daemon/src/tui/mirror/menu-model.ts +210 -0
- package/packages/daemon/src/tui/mirror/palette.ts +564 -0
- package/packages/daemon/src/tui/mirror/pane-mirror.ts +578 -20
- package/packages/daemon/src/tui/mirror/pane-surface.tsx +422 -0
- package/packages/daemon/src/tui/mirror/perf-tap.ts +186 -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 +376 -0
- package/packages/daemon/src/tui/mirror/session-mirror.ts +724 -0
- package/packages/daemon/src/tui/mirror/settings-model.ts +425 -0
- package/packages/daemon/src/tui/mirror/sidebar.tsx +218 -0
- package/packages/daemon/src/tui/mirror/size-truth.ts +130 -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/mirror/theme.ts +45 -0
- package/packages/daemon/src/tui/team/entry.ts +34 -7
- package/packages/daemon/src/tui/team/fuzzy.ts +20 -0
- package/packages/daemon/src/tui/team/report.ts +11 -1
- package/packages/daemon/src/tui/team/sessions.ts +184 -17
- package/packages/daemon/src/tui/team/wait.ts +144 -0
- package/scripts/build-macos-notifier.mjs +160 -0
- package/scripts/build-tui.mjs +11 -4
- package/scripts/perf-mirror.mjs +313 -0
- package/scripts/postinstall.js +8 -1
- package/scripts/prepublish-check.mjs +37 -1
- package/scripts/publish-tap.sh +55 -0
- package/skill/SKILL.md +110 -2
- 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
|
@@ -223,6 +223,11 @@ export async function runUpdateCheck({ now = Date.now() } = {}) {
|
|
|
223
223
|
const cache = readUpdateCache();
|
|
224
224
|
if (!shouldCheck(cache?.lastCheckedAt ?? null, now))
|
|
225
225
|
return;
|
|
226
|
+
// The agent-detection manifest-pack refresh RIDES this same daily throttle
|
|
227
|
+
// (M25.4) — no timer of its own, gated again inside on `updates.manifests`
|
|
228
|
+
// (default false) and never throwing. Dynamic import: manifest-pack imports
|
|
229
|
+
// this module for getCurrentVersion, so a static import would be a cycle.
|
|
230
|
+
void import("./manifest-pack.js").then((m) => m.maybeRefreshManifestPack()).catch(() => { });
|
|
226
231
|
const fetched = await fetchLatestVersion();
|
|
227
232
|
writeUpdateCache({
|
|
228
233
|
lastCheckedAt: now,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>CFBundleDevelopmentRegion</key>
|
|
6
|
+
<string>en</string>
|
|
7
|
+
<key>CFBundleDisplayName</key>
|
|
8
|
+
<string>tmux-ide</string>
|
|
9
|
+
<key>CFBundleExecutable</key>
|
|
10
|
+
<string>tmux-ide-notifier</string>
|
|
11
|
+
<key>CFBundleIconFile</key>
|
|
12
|
+
<string>AppIcon</string>
|
|
13
|
+
<key>CFBundleIconName</key>
|
|
14
|
+
<string>AppIcon</string>
|
|
15
|
+
<key>CFBundleIdentifier</key>
|
|
16
|
+
<string>com.wavyrai.tmuxide.notifier</string>
|
|
17
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
18
|
+
<string>6.0</string>
|
|
19
|
+
<key>CFBundleName</key>
|
|
20
|
+
<string>tmux-ide</string>
|
|
21
|
+
<key>CFBundlePackageType</key>
|
|
22
|
+
<string>APPL</string>
|
|
23
|
+
<key>CFBundleShortVersionString</key>
|
|
24
|
+
<string>2.8.0</string>
|
|
25
|
+
<key>CFBundleVersion</key>
|
|
26
|
+
<string>2.8.0</string>
|
|
27
|
+
<key>LSMinimumSystemVersion</key>
|
|
28
|
+
<string>11.0</string>
|
|
29
|
+
<key>LSUIElement</key>
|
|
30
|
+
<true/>
|
|
31
|
+
<key>NSPrincipalClass</key>
|
|
32
|
+
<string>NSApplication</string>
|
|
33
|
+
</dict>
|
|
34
|
+
</plist>
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
APPL????
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>files</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>Resources/AppIcon.icns</key>
|
|
8
|
+
<data>
|
|
9
|
+
X3UpT06Bz44e6JibpJhhiMxRfno=
|
|
10
|
+
</data>
|
|
11
|
+
<key>Resources/Assets.car</key>
|
|
12
|
+
<data>
|
|
13
|
+
I3z+NDxWadVSrUS0xGMi1yZxHfA=
|
|
14
|
+
</data>
|
|
15
|
+
</dict>
|
|
16
|
+
<key>files2</key>
|
|
17
|
+
<dict>
|
|
18
|
+
<key>Resources/AppIcon.icns</key>
|
|
19
|
+
<dict>
|
|
20
|
+
<key>hash2</key>
|
|
21
|
+
<data>
|
|
22
|
+
tkz+Ee3lkawLrys701JjQo2Z+YlNtbudhtH13NTsrQw=
|
|
23
|
+
</data>
|
|
24
|
+
</dict>
|
|
25
|
+
<key>Resources/Assets.car</key>
|
|
26
|
+
<dict>
|
|
27
|
+
<key>hash2</key>
|
|
28
|
+
<data>
|
|
29
|
+
vuVGO00nkAAaL21f21SA22M7NZ311c+KH7/lizezdck=
|
|
30
|
+
</data>
|
|
31
|
+
</dict>
|
|
32
|
+
</dict>
|
|
33
|
+
<key>rules</key>
|
|
34
|
+
<dict>
|
|
35
|
+
<key>^Resources/</key>
|
|
36
|
+
<true/>
|
|
37
|
+
<key>^Resources/.*\.lproj/</key>
|
|
38
|
+
<dict>
|
|
39
|
+
<key>optional</key>
|
|
40
|
+
<true/>
|
|
41
|
+
<key>weight</key>
|
|
42
|
+
<real>1000</real>
|
|
43
|
+
</dict>
|
|
44
|
+
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
|
45
|
+
<dict>
|
|
46
|
+
<key>omit</key>
|
|
47
|
+
<true/>
|
|
48
|
+
<key>weight</key>
|
|
49
|
+
<real>1100</real>
|
|
50
|
+
</dict>
|
|
51
|
+
<key>^Resources/Base\.lproj/</key>
|
|
52
|
+
<dict>
|
|
53
|
+
<key>weight</key>
|
|
54
|
+
<real>1010</real>
|
|
55
|
+
</dict>
|
|
56
|
+
<key>^version.plist$</key>
|
|
57
|
+
<true/>
|
|
58
|
+
</dict>
|
|
59
|
+
<key>rules2</key>
|
|
60
|
+
<dict>
|
|
61
|
+
<key>.*\.dSYM($|/)</key>
|
|
62
|
+
<dict>
|
|
63
|
+
<key>weight</key>
|
|
64
|
+
<real>11</real>
|
|
65
|
+
</dict>
|
|
66
|
+
<key>^(.*/)?\.DS_Store$</key>
|
|
67
|
+
<dict>
|
|
68
|
+
<key>omit</key>
|
|
69
|
+
<true/>
|
|
70
|
+
<key>weight</key>
|
|
71
|
+
<real>2000</real>
|
|
72
|
+
</dict>
|
|
73
|
+
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
|
74
|
+
<dict>
|
|
75
|
+
<key>nested</key>
|
|
76
|
+
<true/>
|
|
77
|
+
<key>weight</key>
|
|
78
|
+
<real>10</real>
|
|
79
|
+
</dict>
|
|
80
|
+
<key>^.*</key>
|
|
81
|
+
<true/>
|
|
82
|
+
<key>^Info\.plist$</key>
|
|
83
|
+
<dict>
|
|
84
|
+
<key>omit</key>
|
|
85
|
+
<true/>
|
|
86
|
+
<key>weight</key>
|
|
87
|
+
<real>20</real>
|
|
88
|
+
</dict>
|
|
89
|
+
<key>^PkgInfo$</key>
|
|
90
|
+
<dict>
|
|
91
|
+
<key>omit</key>
|
|
92
|
+
<true/>
|
|
93
|
+
<key>weight</key>
|
|
94
|
+
<real>20</real>
|
|
95
|
+
</dict>
|
|
96
|
+
<key>^Resources/</key>
|
|
97
|
+
<dict>
|
|
98
|
+
<key>weight</key>
|
|
99
|
+
<real>20</real>
|
|
100
|
+
</dict>
|
|
101
|
+
<key>^Resources/.*\.lproj/</key>
|
|
102
|
+
<dict>
|
|
103
|
+
<key>optional</key>
|
|
104
|
+
<true/>
|
|
105
|
+
<key>weight</key>
|
|
106
|
+
<real>1000</real>
|
|
107
|
+
</dict>
|
|
108
|
+
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
|
109
|
+
<dict>
|
|
110
|
+
<key>omit</key>
|
|
111
|
+
<true/>
|
|
112
|
+
<key>weight</key>
|
|
113
|
+
<real>1100</real>
|
|
114
|
+
</dict>
|
|
115
|
+
<key>^Resources/Base\.lproj/</key>
|
|
116
|
+
<dict>
|
|
117
|
+
<key>weight</key>
|
|
118
|
+
<real>1010</real>
|
|
119
|
+
</dict>
|
|
120
|
+
<key>^[^/]+$</key>
|
|
121
|
+
<dict>
|
|
122
|
+
<key>nested</key>
|
|
123
|
+
<true/>
|
|
124
|
+
<key>weight</key>
|
|
125
|
+
<real>10</real>
|
|
126
|
+
</dict>
|
|
127
|
+
<key>^embedded\.provisionprofile$</key>
|
|
128
|
+
<dict>
|
|
129
|
+
<key>weight</key>
|
|
130
|
+
<real>20</real>
|
|
131
|
+
</dict>
|
|
132
|
+
<key>^version\.plist$</key>
|
|
133
|
+
<dict>
|
|
134
|
+
<key>weight</key>
|
|
135
|
+
<real>20</real>
|
|
136
|
+
</dict>
|
|
137
|
+
</dict>
|
|
138
|
+
</dict>
|
|
139
|
+
</plist>
|
|
@@ -32,19 +32,46 @@ export interface RestorePlan {
|
|
|
32
32
|
* config; failing that, we raw-rebuild from the recorded windows/panes.
|
|
33
33
|
*/
|
|
34
34
|
export declare function buildRestorePlan(snapshot: FleetSnapshot, liveSessionNames: Iterable<string>, ideProjects?: Map<string, string>): RestorePlan;
|
|
35
|
+
/**
|
|
36
|
+
* The native session-resume invocation per agent kind (M24.1 — the table was
|
|
37
|
+
* claude-only until then). ONLY VERIFIED entries ship; each spelling's source:
|
|
38
|
+
* - `claude --resume <id>`: the shipped integration (its hooks record the id).
|
|
39
|
+
* - `codex resume <id>`: `codex resume --help` — "Usage: codex resume
|
|
40
|
+
* [OPTIONS] [SESSION_ID] … Session id (UUID) or session name".
|
|
41
|
+
* - `opencode --session <id>`: `opencode --help` — "-s, --session session id
|
|
42
|
+
* to continue".
|
|
43
|
+
* - `cursor-agent --resume <id>`: `cursor-agent --help` — "--resume [chatId]
|
|
44
|
+
* Select a session to resume".
|
|
45
|
+
* - `copilot --resume=<id>`: GitHub Copilot CLI command reference —
|
|
46
|
+
* "--resume[=VALUE] Resume a previous interactive session … Optionally
|
|
47
|
+
* specify a session ID" (the `=` form, since the value is optional).
|
|
48
|
+
* Unverified (no entry): gemini, aider, goose, amp — no confirmed native
|
|
49
|
+
* per-session resume invocation at the time of writing.
|
|
50
|
+
*
|
|
51
|
+
* How `@agent_session_id` gets recorded per kind (M25.3):
|
|
52
|
+
* - claude — the hooks integration (`integration install claude`).
|
|
53
|
+
* - opencode — the plugin integration (`integration install opencode`).
|
|
54
|
+
* - codex, cursor — automatic: the chrome updater probes the agent's own
|
|
55
|
+
* on-disk session state ({@link ./tui/detect/session-id.ts}).
|
|
56
|
+
* - copilot — no automatic capture (skipped with evidence — see the
|
|
57
|
+
* session-id module header); fires only for panes whose agent self-reported
|
|
58
|
+
* the id per the agent contract
|
|
59
|
+
* (`tmux set-option -p @agent_session_id <id>`), which any kind may do.
|
|
60
|
+
*/
|
|
61
|
+
export declare const AGENT_RESUME_COMMANDS: Record<string, (id: string) => string>;
|
|
35
62
|
/**
|
|
36
63
|
* PURE — the command to relaunch a pane so its agent conversation is revived,
|
|
37
64
|
* or `null` for "no resume — leave it a plain shell (or replay `command`)".
|
|
38
65
|
*
|
|
39
66
|
* Decision table (only fires when `resumeAgents` is on):
|
|
40
|
-
* -
|
|
41
|
-
*
|
|
42
|
-
* {@link SAFE_SESSION_ID}
|
|
43
|
-
* feeding shell metacharacters into send-keys.
|
|
44
|
-
* -
|
|
45
|
-
* magical here (it resumes the most-recent conversation
|
|
46
|
-
* belong to a different pane); a fresh shell is the safe
|
|
47
|
-
* - any other agent → `null`. No
|
|
67
|
+
* - a kind in {@link AGENT_RESUME_COMMANDS} + a recorded `@agent_session_id` →
|
|
68
|
+
* that kind's resume invocation. The id is uuid-ish, but we don't trust the
|
|
69
|
+
* snapshot: unless it matches {@link SAFE_SESSION_ID} we bail to `null`
|
|
70
|
+
* rather than risk feeding shell metacharacters into send-keys.
|
|
71
|
+
* - a known kind with NO session id → `null`. A "continue most recent" flag
|
|
72
|
+
* would be too magical here (it resumes the cwd's most-recent conversation,
|
|
73
|
+
* which may belong to a different pane); a fresh shell is the safe default.
|
|
74
|
+
* - any other agent → `null`. No VERIFIED resume story.
|
|
48
75
|
* - `resumeAgents` off → always `null`.
|
|
49
76
|
*/
|
|
50
77
|
export declare function paneResumeCommand(pane: PaneSnapshot, opts: {
|
|
@@ -13,9 +13,11 @@
|
|
|
13
13
|
*
|
|
14
14
|
* `--resume-agents` (or `{ "restore": { "resumeAgents": true } }` in
|
|
15
15
|
* `~/.tmux-ide/config.json`) layers native agent-resume on top: a rebuilt
|
|
16
|
-
*
|
|
17
|
-
* `claude --resume <id>`,
|
|
18
|
-
*
|
|
16
|
+
* agent pane that carries a recorded `@agent_session_id` relaunches with its
|
|
17
|
+
* kind's VERIFIED resume invocation (`claude --resume <id>`, `codex resume
|
|
18
|
+
* <id>`, … — {@link AGENT_RESUME_COMMANDS}), reviving the actual conversation
|
|
19
|
+
* rather than opening a fresh shell. See {@link paneResumeCommand} for the
|
|
20
|
+
* exact decision table.
|
|
19
21
|
*
|
|
20
22
|
* {@link buildRestorePlan} + {@link paneResumeCommand} + {@link restorePrefs}
|
|
21
23
|
* are PURE (unit-tested); {@link restore} is the thin io wrapper that reads the
|
|
@@ -58,32 +60,67 @@ export function buildRestorePlan(snapshot, liveSessionNames, ideProjects = new M
|
|
|
58
60
|
// ---------------------------------------------------------------------------
|
|
59
61
|
// Pure — agent resume decision
|
|
60
62
|
// ---------------------------------------------------------------------------
|
|
61
|
-
/** A session id is trusted only if it's
|
|
62
|
-
|
|
63
|
+
/** A session id is trusted only if it's uuid-ish (`[A-Za-z0-9_-]` — underscore
|
|
64
|
+
* covers opencode's `ses_…` ids) — nothing shell-active. */
|
|
65
|
+
const SAFE_SESSION_ID = /^[A-Za-z0-9_-]+$/;
|
|
66
|
+
/**
|
|
67
|
+
* The native session-resume invocation per agent kind (M24.1 — the table was
|
|
68
|
+
* claude-only until then). ONLY VERIFIED entries ship; each spelling's source:
|
|
69
|
+
* - `claude --resume <id>`: the shipped integration (its hooks record the id).
|
|
70
|
+
* - `codex resume <id>`: `codex resume --help` — "Usage: codex resume
|
|
71
|
+
* [OPTIONS] [SESSION_ID] … Session id (UUID) or session name".
|
|
72
|
+
* - `opencode --session <id>`: `opencode --help` — "-s, --session session id
|
|
73
|
+
* to continue".
|
|
74
|
+
* - `cursor-agent --resume <id>`: `cursor-agent --help` — "--resume [chatId]
|
|
75
|
+
* Select a session to resume".
|
|
76
|
+
* - `copilot --resume=<id>`: GitHub Copilot CLI command reference —
|
|
77
|
+
* "--resume[=VALUE] Resume a previous interactive session … Optionally
|
|
78
|
+
* specify a session ID" (the `=` form, since the value is optional).
|
|
79
|
+
* Unverified (no entry): gemini, aider, goose, amp — no confirmed native
|
|
80
|
+
* per-session resume invocation at the time of writing.
|
|
81
|
+
*
|
|
82
|
+
* How `@agent_session_id` gets recorded per kind (M25.3):
|
|
83
|
+
* - claude — the hooks integration (`integration install claude`).
|
|
84
|
+
* - opencode — the plugin integration (`integration install opencode`).
|
|
85
|
+
* - codex, cursor — automatic: the chrome updater probes the agent's own
|
|
86
|
+
* on-disk session state ({@link ./tui/detect/session-id.ts}).
|
|
87
|
+
* - copilot — no automatic capture (skipped with evidence — see the
|
|
88
|
+
* session-id module header); fires only for panes whose agent self-reported
|
|
89
|
+
* the id per the agent contract
|
|
90
|
+
* (`tmux set-option -p @agent_session_id <id>`), which any kind may do.
|
|
91
|
+
*/
|
|
92
|
+
export const AGENT_RESUME_COMMANDS = {
|
|
93
|
+
claude: (id) => `claude --resume ${id}`,
|
|
94
|
+
codex: (id) => `codex resume ${id}`,
|
|
95
|
+
opencode: (id) => `opencode --session ${id}`,
|
|
96
|
+
cursor: (id) => `cursor-agent --resume ${id}`,
|
|
97
|
+
copilot: (id) => `copilot --resume=${id}`,
|
|
98
|
+
};
|
|
63
99
|
/**
|
|
64
100
|
* PURE — the command to relaunch a pane so its agent conversation is revived,
|
|
65
101
|
* or `null` for "no resume — leave it a plain shell (or replay `command`)".
|
|
66
102
|
*
|
|
67
103
|
* Decision table (only fires when `resumeAgents` is on):
|
|
68
|
-
* -
|
|
69
|
-
*
|
|
70
|
-
* {@link SAFE_SESSION_ID}
|
|
71
|
-
* feeding shell metacharacters into send-keys.
|
|
72
|
-
* -
|
|
73
|
-
* magical here (it resumes the most-recent conversation
|
|
74
|
-
* belong to a different pane); a fresh shell is the safe
|
|
75
|
-
* - any other agent → `null`. No
|
|
104
|
+
* - a kind in {@link AGENT_RESUME_COMMANDS} + a recorded `@agent_session_id` →
|
|
105
|
+
* that kind's resume invocation. The id is uuid-ish, but we don't trust the
|
|
106
|
+
* snapshot: unless it matches {@link SAFE_SESSION_ID} we bail to `null`
|
|
107
|
+
* rather than risk feeding shell metacharacters into send-keys.
|
|
108
|
+
* - a known kind with NO session id → `null`. A "continue most recent" flag
|
|
109
|
+
* would be too magical here (it resumes the cwd's most-recent conversation,
|
|
110
|
+
* which may belong to a different pane); a fresh shell is the safe default.
|
|
111
|
+
* - any other agent → `null`. No VERIFIED resume story.
|
|
76
112
|
* - `resumeAgents` off → always `null`.
|
|
77
113
|
*/
|
|
78
114
|
export function paneResumeCommand(pane, opts) {
|
|
79
115
|
if (!opts.resumeAgents)
|
|
80
116
|
return null;
|
|
81
|
-
|
|
117
|
+
const resume = pane.agent ? AGENT_RESUME_COMMANDS[pane.agent] : undefined;
|
|
118
|
+
if (!resume)
|
|
82
119
|
return null;
|
|
83
120
|
const id = pane.agentSessionId;
|
|
84
121
|
if (!id || !SAFE_SESSION_ID.test(id))
|
|
85
122
|
return null;
|
|
86
|
-
return
|
|
123
|
+
return resume(id);
|
|
87
124
|
}
|
|
88
125
|
/** PURE — how many of a session's panes would resume under `resumeAgents`. */
|
|
89
126
|
export function countResumableAgents(session, resumeAgents) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type PaneInfo } from "./widgets/lib/pane-comms.ts";
|
|
1
|
+
import { type PaneInfo, type PaneBusyStatus } from "./widgets/lib/pane-comms.ts";
|
|
2
2
|
export declare const LONG_MESSAGE_THRESHOLD = 150;
|
|
3
3
|
/**
|
|
4
4
|
* Write a long message to a dispatch file and return the short trigger command.
|
|
@@ -23,5 +23,37 @@ interface SendOptions {
|
|
|
23
23
|
* 5. Case-insensitive partial title match
|
|
24
24
|
*/
|
|
25
25
|
export declare function resolvePane(panes: PaneInfo[], target: string): PaneInfo | null;
|
|
26
|
+
/** What a delivery reports back (the `send --json` payload, and the control
|
|
27
|
+
* socket's `send` verb data). */
|
|
28
|
+
export interface DeliverResult {
|
|
29
|
+
ok: true;
|
|
30
|
+
session: string;
|
|
31
|
+
target: {
|
|
32
|
+
paneId: string;
|
|
33
|
+
name: string | null;
|
|
34
|
+
title: string;
|
|
35
|
+
role: string | null;
|
|
36
|
+
};
|
|
37
|
+
message: string;
|
|
38
|
+
busyStatus: PaneBusyStatus;
|
|
39
|
+
sentViaFile: boolean;
|
|
40
|
+
warning?: "agent_busy";
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Deliver `message` to a pane of `session` — the SHARED core behind the
|
|
44
|
+
* `tmux-ide send` CLI case and the control socket's `send` verb. Resolves
|
|
45
|
+
* `target` (pane id / @ide_name / title / role / partial title), adapts to
|
|
46
|
+
* the pane's busy status, and routes long messages through a dispatch file
|
|
47
|
+
* under `dir` when one is given (without a `dir` the text is sent directly).
|
|
48
|
+
* Throws `IdeError` (SESSION_NOT_FOUND / PANE_NOT_FOUND) — no printing here.
|
|
49
|
+
*/
|
|
50
|
+
export declare function deliverMessage(opts: {
|
|
51
|
+
session: string;
|
|
52
|
+
target: string;
|
|
53
|
+
message: string;
|
|
54
|
+
noEnter?: boolean;
|
|
55
|
+
/** The project dir long messages are dispatched through as a file. */
|
|
56
|
+
dir?: string;
|
|
57
|
+
}): DeliverResult;
|
|
26
58
|
export declare function send(targetDir: string | undefined, opts: SendOptions): Promise<void>;
|
|
27
59
|
export {};
|
|
@@ -64,21 +64,16 @@ function prepareMessage(message, busyStatus) {
|
|
|
64
64
|
}
|
|
65
65
|
return message;
|
|
66
66
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
throw new IdeError("Missing message. Usage: tmux-ide send <target> <message>", {
|
|
78
|
-
code: "USAGE",
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
// Verify session is running
|
|
67
|
+
/**
|
|
68
|
+
* Deliver `message` to a pane of `session` — the SHARED core behind the
|
|
69
|
+
* `tmux-ide send` CLI case and the control socket's `send` verb. Resolves
|
|
70
|
+
* `target` (pane id / @ide_name / title / role / partial title), adapts to
|
|
71
|
+
* the pane's busy status, and routes long messages through a dispatch file
|
|
72
|
+
* under `dir` when one is given (without a `dir` the text is sent directly).
|
|
73
|
+
* Throws `IdeError` (SESSION_NOT_FOUND / PANE_NOT_FOUND) — no printing here.
|
|
74
|
+
*/
|
|
75
|
+
export function deliverMessage(opts) {
|
|
76
|
+
const { session, target, noEnter, dir } = opts;
|
|
82
77
|
const state = getSessionState(session);
|
|
83
78
|
if (!state.running) {
|
|
84
79
|
throw new IdeError(`Session "${session}" is not running`, {
|
|
@@ -99,13 +94,13 @@ export async function send(targetDir, opts) {
|
|
|
99
94
|
});
|
|
100
95
|
}
|
|
101
96
|
const busyStatus = getPaneBusyStatus(session, pane.id);
|
|
102
|
-
const message = prepareMessage(
|
|
97
|
+
const message = prepareMessage(opts.message, busyStatus);
|
|
103
98
|
let sentViaFile = false;
|
|
104
99
|
if (noEnter) {
|
|
105
100
|
sendText(session, pane.id, message);
|
|
106
101
|
}
|
|
107
102
|
else {
|
|
108
|
-
const dispatch = writeDispatchFile(dir, pane.id, message);
|
|
103
|
+
const dispatch = dir ? writeDispatchFile(dir, pane.id, message) : null;
|
|
109
104
|
if (dispatch) {
|
|
110
105
|
sendCommand(session, pane.id, dispatch.triggerCmd);
|
|
111
106
|
sentViaFile = true;
|
|
@@ -114,7 +109,7 @@ export async function send(targetDir, opts) {
|
|
|
114
109
|
sendCommand(session, pane.id, message);
|
|
115
110
|
}
|
|
116
111
|
}
|
|
117
|
-
|
|
112
|
+
return {
|
|
118
113
|
ok: true,
|
|
119
114
|
session,
|
|
120
115
|
target: {
|
|
@@ -128,13 +123,31 @@ export async function send(targetDir, opts) {
|
|
|
128
123
|
sentViaFile,
|
|
129
124
|
...(busyStatus === "agent" ? { warning: "agent_busy" } : {}),
|
|
130
125
|
};
|
|
126
|
+
}
|
|
127
|
+
export async function send(targetDir, opts) {
|
|
128
|
+
const dir = resolve(targetDir ?? ".");
|
|
129
|
+
const { name: session } = getSessionName(dir);
|
|
130
|
+
const { json, to: target, message: rawMessage, noEnter } = opts;
|
|
131
|
+
if (!target) {
|
|
132
|
+
throw new IdeError("Missing target. Usage: tmux-ide send <target> <message>", {
|
|
133
|
+
code: "USAGE",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (!rawMessage) {
|
|
137
|
+
throw new IdeError("Missing message. Usage: tmux-ide send <target> <message>", {
|
|
138
|
+
code: "USAGE",
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const result = deliverMessage({ session, target, message: rawMessage, noEnter, dir });
|
|
142
|
+
const { message, busyStatus } = result;
|
|
143
|
+
const pane = result.target;
|
|
131
144
|
if (json) {
|
|
132
145
|
console.log(JSON.stringify(result, null, 2));
|
|
133
146
|
return;
|
|
134
147
|
}
|
|
135
148
|
const label = pane.name ?? pane.title;
|
|
136
149
|
const preview = message.length > 60 ? message.slice(0, 60) + "..." : message;
|
|
137
|
-
console.log(`Sent to "${label}" (${pane.
|
|
150
|
+
console.log(`Sent to "${label}" (${pane.paneId}): ${preview}`);
|
|
138
151
|
if (busyStatus === "agent") {
|
|
139
152
|
console.log("Warning: agent appears busy. Message sent anyway.");
|
|
140
153
|
}
|
|
@@ -6,5 +6,5 @@ interface BreadcrumbsProps {
|
|
|
6
6
|
theme: WidgetTheme;
|
|
7
7
|
onNavigate: (absolutePath: string) => void;
|
|
8
8
|
}
|
|
9
|
-
export declare function Breadcrumbs(props: BreadcrumbsProps):
|
|
9
|
+
export declare function Breadcrumbs(props: BreadcrumbsProps): import("solid-js").JSX.Element;
|
|
10
10
|
export {};
|
|
@@ -9,5 +9,5 @@ interface FileTreeProps {
|
|
|
9
9
|
onActivate: (node: TreeNode) => void;
|
|
10
10
|
onInputModeChange: (mode: "keyboard" | "mouse") => void;
|
|
11
11
|
}
|
|
12
|
-
export declare function FileTree(props: FileTreeProps):
|
|
12
|
+
export declare function FileTree(props: FileTreeProps): import("solid-js").JSX.Element;
|
|
13
13
|
export {};
|
|
@@ -16,4 +16,4 @@ export interface HelpOverlayProps {
|
|
|
16
16
|
* the surface's own keys. Replaces the surface body while `?` is held open;
|
|
17
17
|
* `esc`/`q`/`?` close it.
|
|
18
18
|
*/
|
|
19
|
-
export declare function HelpOverlay(props: HelpOverlayProps):
|
|
19
|
+
export declare function HelpOverlay(props: HelpOverlayProps): import("solid-js").JSX.Element;
|
|
@@ -5,5 +5,5 @@ interface AgentNamingProps {
|
|
|
5
5
|
onContinue: (names: string[]) => void;
|
|
6
6
|
theme: WidgetTheme;
|
|
7
7
|
}
|
|
8
|
-
export declare function AgentNaming(props: AgentNamingProps):
|
|
8
|
+
export declare function AgentNaming(props: AgentNamingProps): import("solid-js").JSX.Element;
|
|
9
9
|
export {};
|
|
@@ -9,5 +9,5 @@ interface ConfigTreeProps {
|
|
|
9
9
|
onConfigChange: (config: IdeConfig) => void;
|
|
10
10
|
theme: WidgetTheme;
|
|
11
11
|
}
|
|
12
|
-
export declare function ConfigTree(props: ConfigTreeProps):
|
|
12
|
+
export declare function ConfigTree(props: ConfigTreeProps): import("solid-js").JSX.Element;
|
|
13
13
|
export {};
|
|
@@ -5,5 +5,5 @@ interface LayoutPickerProps {
|
|
|
5
5
|
onSelect: (preset: LayoutPreset) => void;
|
|
6
6
|
theme: WidgetTheme;
|
|
7
7
|
}
|
|
8
|
-
export declare function LayoutPicker(props: LayoutPickerProps):
|
|
8
|
+
export declare function LayoutPicker(props: LayoutPickerProps): import("solid-js").JSX.Element;
|
|
9
9
|
export {};
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* the target pane.
|
|
15
15
|
*/
|
|
16
16
|
import { execFileSync } from "node:child_process";
|
|
17
|
-
import { explain, type DetectedState } from "./tui/detect/manifest.ts";
|
|
17
|
+
import { explain, type DetectedState, type ManifestConfidence } from "./tui/detect/manifest.ts";
|
|
18
18
|
import {
|
|
19
19
|
classifyInstant,
|
|
20
20
|
parseAuthority,
|
|
@@ -22,7 +22,11 @@ import {
|
|
|
22
22
|
type InstantState,
|
|
23
23
|
} from "./tui/detect/classify.ts";
|
|
24
24
|
import { getManifests } from "./tui/detect/manifest-loader.ts";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
describeSubtree,
|
|
27
|
+
readProcessTable,
|
|
28
|
+
resolveAgentCommand,
|
|
29
|
+
} from "./tui/detect/process-tree.ts";
|
|
26
30
|
import { readPaneSnapshot } from "./tui/detect/snapshot.ts";
|
|
27
31
|
import { IdeError } from "./lib/errors.ts";
|
|
28
32
|
|
|
@@ -53,6 +57,13 @@ export interface ExplainReport {
|
|
|
53
57
|
manifestId: string | null;
|
|
54
58
|
matchedCommand: string;
|
|
55
59
|
source: "hint" | "fast" | "tree" | "none";
|
|
60
|
+
/** The matched manifest's evidence confidence (null when nothing resolved). */
|
|
61
|
+
confidence: ManifestConfidence | null;
|
|
62
|
+
/**
|
|
63
|
+
* Executable-ish tokens seen in the pane's process subtree. Populated for
|
|
64
|
+
* the "no manifest matched" case so the report can say what it DID find.
|
|
65
|
+
*/
|
|
66
|
+
subtree: string[];
|
|
56
67
|
};
|
|
57
68
|
states: Array<{ state: DetectedState; matched: boolean }>;
|
|
58
69
|
winner: DetectedState | null;
|
|
@@ -137,6 +148,9 @@ export function buildReport(target: string): ExplainReport {
|
|
|
137
148
|
hint: info.hintRaw,
|
|
138
149
|
});
|
|
139
150
|
const manifest = resolved.manifest;
|
|
151
|
+
// When nothing resolved, record what the process-tree DID see so `explain`
|
|
152
|
+
// can point the user at an `@agent_hint` target instead of a dead end.
|
|
153
|
+
const subtree = manifest ? [] : describeSubtree(table, info.pid);
|
|
140
154
|
|
|
141
155
|
// Snapshot + per-state explanation.
|
|
142
156
|
const snapshot = { ...readPaneSnapshot(info.id), title: info.title };
|
|
@@ -168,6 +182,8 @@ export function buildReport(target: string): ExplainReport {
|
|
|
168
182
|
manifestId: manifest?.id ?? null,
|
|
169
183
|
matchedCommand: resolved.matchedCommand,
|
|
170
184
|
source: resolved.source,
|
|
185
|
+
confidence: manifest ? (manifest.confidence ?? "conservative") : null,
|
|
186
|
+
subtree,
|
|
171
187
|
},
|
|
172
188
|
states: explained.checked,
|
|
173
189
|
winner: explained.state,
|
|
@@ -223,10 +239,22 @@ export function renderReport(r: ExplainReport, opts: { color?: boolean } = {}):
|
|
|
223
239
|
}
|
|
224
240
|
|
|
225
241
|
// Resolution
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
242
|
+
if (r.resolution.manifestId) {
|
|
243
|
+
const conf =
|
|
244
|
+
r.resolution.confidence === "tuned"
|
|
245
|
+
? c("\x1b[32m", "tuned")
|
|
246
|
+
: dim(r.resolution.confidence ?? "conservative");
|
|
247
|
+
out.push(
|
|
248
|
+
` ${label("manifest")} ${r.resolution.manifestId} ${dim(`via ${r.resolution.source}` + (r.resolution.matchedCommand ? ` "${r.resolution.matchedCommand}"` : ""))} [${conf}]`,
|
|
249
|
+
);
|
|
250
|
+
} else {
|
|
251
|
+
// No manifest matched — say so, and report what the process-tree DID see so
|
|
252
|
+
// the user can set an `@agent_hint` or add a manifest.
|
|
253
|
+
const saw =
|
|
254
|
+
r.resolution.subtree.length > 0 ? r.resolution.subtree.join(", ") : r.pane.cmd || "(nothing)";
|
|
255
|
+
out.push(` ${label("manifest")} ${dim("none matched")} — ${dim(`process-tree saw: ${saw}`)}`);
|
|
256
|
+
out.push(` ${dim("set `tmux set-option -p @agent_hint <agent>` to force one")}`);
|
|
257
|
+
}
|
|
230
258
|
|
|
231
259
|
// Per-state rule results
|
|
232
260
|
out.push("");
|