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
|
@@ -8,14 +8,26 @@
|
|
|
8
8
|
* stay minimal. Treat every matcher below as a heuristic to tune, not a
|
|
9
9
|
* contract — edit freely as agent UIs change.
|
|
10
10
|
*
|
|
11
|
-
* EVIDENCE PROVENANCE
|
|
12
|
-
* REAL captured screens
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
11
|
+
* EVIDENCE PROVENANCE (per-manifest `confidence`):
|
|
12
|
+
* - `tuned` — built from REAL captured screens or the agent's own source
|
|
13
|
+
* strings. `claude`, `codex`, and `aider` are tuned: codex from live
|
|
14
|
+
* `tmux capture-pane` frames driven through a real turn (idle / working /
|
|
15
|
+
* command-approval / trust prompt), aider from the verbatim prompt strings
|
|
16
|
+
* in its installed source (`io.py` confirm_ask, `waiting.py` spinner).
|
|
17
|
+
* Each matcher cites the invariant it was built from ("seen: …").
|
|
18
|
+
* - `conservative` — best-effort from public docs/common knowledge
|
|
19
|
+
* (`opencode`, `gemini`, `copilot`, `cursor`, `goose`, `amp`, and the
|
|
20
|
+
* M25.4 breadth set: `devin`, `kimi`, `pi`, `grok`, `kiro`, `cline`,
|
|
21
|
+
* `droid`, `kilo`). Every matcher is HIGH-PRECISION (esc-to-interrupt /
|
|
22
|
+
* spinner / explicit y-n / "Do you want") so it can never false-positive
|
|
23
|
+
* on a plain prompt, but it may miss real states until a live capture
|
|
24
|
+
* upgrades it. `opencode` was attempted live but its local auth DB errored
|
|
25
|
+
* and the TUI rendered blank, so it stays conservative. Several M25.4
|
|
26
|
+
* manifests carry source/binary-VERBATIM markers (see each manifest's
|
|
27
|
+
* provenance comment) but stay conservative until a live capture confirms
|
|
28
|
+
* the surrounding chrome.
|
|
29
|
+
*
|
|
30
|
+
* Users can override any of these with a JSON file in
|
|
19
31
|
* `~/.tmux-ide/agent-detection/` (see `manifest-loader.ts`).
|
|
20
32
|
*/
|
|
21
33
|
import type { AgentManifest } from "./manifest.ts";
|
|
@@ -27,6 +39,7 @@ const BRAILLE_SPINNER = "[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]";
|
|
|
27
39
|
const CLAUDE: AgentManifest = {
|
|
28
40
|
id: "claude",
|
|
29
41
|
commands: ["claude"],
|
|
42
|
+
confidence: "tuned",
|
|
30
43
|
states: {
|
|
31
44
|
// Approval / confirmation prompts — Claude is waiting on the user.
|
|
32
45
|
// Claude's approval UI is a bordered box asking a "Do you want …?" question
|
|
@@ -74,41 +87,57 @@ const CLAUDE: AgentManifest = {
|
|
|
74
87
|
|
|
75
88
|
const CODEX: AgentManifest = {
|
|
76
89
|
id: "codex",
|
|
77
|
-
commands: ["codex"],
|
|
90
|
+
commands: ["codex", "codex.exe"],
|
|
91
|
+
confidence: "tuned",
|
|
78
92
|
states: {
|
|
93
|
+
// TUNED against real captures (codex-cli v0.142.5, driven through a turn).
|
|
94
|
+
// The command-approval dialog and the directory-trust prompt are the two
|
|
95
|
+
// "blocked" screens. Codex's approval menu uses a "› 1." numbered arrow —
|
|
96
|
+
// note the arrow is "›" (U+203A), NOT claude's "❯".
|
|
79
97
|
blocked: {
|
|
80
98
|
any: [
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
{ region: "bottom", contains: "
|
|
86
|
-
|
|
87
|
-
{ region: "bottom", contains: "
|
|
99
|
+
// seen (command approval): "Would you like to run the following
|
|
100
|
+
// command?" above a "$ <cmd>" preview and the numbered menu.
|
|
101
|
+
{ region: "bottom", contains: "Would you like to run", caseInsensitive: true },
|
|
102
|
+
// seen: the highlighted approval option "› 1. Yes, proceed".
|
|
103
|
+
{ region: "bottom", contains: "Yes, proceed" },
|
|
104
|
+
// seen: "3. No, and tell Codex what to do differently (esc)".
|
|
105
|
+
{ region: "bottom", contains: "No, and tell Codex" },
|
|
106
|
+
// seen: the confirm footer under the approval menu.
|
|
107
|
+
{ region: "bottom", contains: "Press enter to confirm", caseInsensitive: true },
|
|
108
|
+
// seen (directory-trust prompt on first launch in an untrusted dir):
|
|
109
|
+
// "Do you trust the contents of this directory?" + "1. Yes, continue".
|
|
110
|
+
{ region: "bottom", contains: "Do you trust the contents", caseInsensitive: true },
|
|
88
111
|
],
|
|
89
112
|
},
|
|
90
113
|
working: {
|
|
91
114
|
any: [
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
// the
|
|
115
|
+
// seen (verbatim): the working status line is
|
|
116
|
+
// "• Working (6s • esc to interrupt)".
|
|
117
|
+
// Both the "Working (" prefix and the shared "esc to interrupt" hint
|
|
118
|
+
// are present for the whole turn.
|
|
119
|
+
{ region: "bottom", regex: "Working \\(\\d" },
|
|
95
120
|
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
96
121
|
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
97
122
|
{ region: "title", regex: BRAILLE_SPINNER },
|
|
98
123
|
],
|
|
99
124
|
},
|
|
100
|
-
// done: omitted. NOTE (seen, NOT used):
|
|
101
|
-
//
|
|
102
|
-
// "
|
|
103
|
-
//
|
|
125
|
+
// done: omitted. NOTE (seen, NOT used): a finished turn leaves the agent's
|
|
126
|
+
// answer above the idle "›" input box (placeholder "Find and fix a bug in
|
|
127
|
+
// @filename") and a "gpt-5.5 xhigh · <cwd>" status line; older builds also
|
|
128
|
+
// showed "Goal achieved (5m)". None are working/blocked evidence, so codex
|
|
129
|
+
// correctly falls through to idle and the classifier infers done.
|
|
104
130
|
},
|
|
105
131
|
};
|
|
106
132
|
|
|
107
133
|
const OPENCODE: AgentManifest = {
|
|
108
134
|
id: "opencode",
|
|
109
|
-
commands: ["opencode"],
|
|
135
|
+
commands: ["opencode", "opencode.exe"],
|
|
136
|
+
confidence: "conservative",
|
|
110
137
|
states: {
|
|
111
|
-
//
|
|
138
|
+
// conservative — a live capture was attempted (opencode v1.17.10) but its
|
|
139
|
+
// local auth DB errored ("no such column: name") and the TUI rendered
|
|
140
|
+
// blank, so these stay best-effort. High-precision only.
|
|
112
141
|
blocked: {
|
|
113
142
|
any: [
|
|
114
143
|
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
@@ -129,8 +158,10 @@ const OPENCODE: AgentManifest = {
|
|
|
129
158
|
const GEMINI: AgentManifest = {
|
|
130
159
|
id: "gemini",
|
|
131
160
|
commands: ["gemini"],
|
|
161
|
+
confidence: "conservative",
|
|
132
162
|
states: {
|
|
133
|
-
//
|
|
163
|
+
// conservative — gemini-cli needs a Google account/API key to reach a
|
|
164
|
+
// working state, so no live capture was taken. High-precision only.
|
|
134
165
|
blocked: {
|
|
135
166
|
any: [
|
|
136
167
|
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
@@ -153,20 +184,33 @@ const GEMINI: AgentManifest = {
|
|
|
153
184
|
const AIDER: AgentManifest = {
|
|
154
185
|
id: "aider",
|
|
155
186
|
commands: ["aider"],
|
|
187
|
+
confidence: "tuned",
|
|
156
188
|
states: {
|
|
157
|
-
//
|
|
158
|
-
//
|
|
189
|
+
// TUNED from aider's installed source (v0.86.2). Every confirmation renders
|
|
190
|
+
// through `io.confirm_ask` (io.py), which appends the literal option string
|
|
191
|
+
// " (Y)es/(N)o" (plus "/(A)ll/(S)kip all" or "/(D)on't ask again") and a
|
|
192
|
+
// "[Yes]:"/"[No]:" default — so "(Y)es/(N)o" is aider's exact, universal
|
|
193
|
+
// blocked marker. The specific questions below are verbatim from
|
|
194
|
+
// base_coder.py / commands.py.
|
|
159
195
|
blocked: {
|
|
160
196
|
any: [
|
|
161
197
|
{ region: "bottom", contains: "(Y)es/(N)o", caseInsensitive: true },
|
|
162
|
-
{ region: "bottom", contains: "? [Yes]", caseInsensitive: true },
|
|
163
198
|
{ region: "bottom", contains: "Add file to the chat", caseInsensitive: true },
|
|
199
|
+
{ region: "bottom", contains: "Allow edits to file", caseInsensitive: true },
|
|
200
|
+
{ region: "bottom", contains: "Add command output to the chat", caseInsensitive: true },
|
|
201
|
+
{ region: "bottom", contains: "Run pip install", caseInsensitive: true },
|
|
164
202
|
],
|
|
165
203
|
},
|
|
166
|
-
//
|
|
167
|
-
//
|
|
204
|
+
// TUNED: while a turn runs aider shows a `WaitingSpinner` (waiting.py)
|
|
205
|
+
// rendered as "[░█ ] Waiting for <model>" — the text is literally
|
|
206
|
+
// "Waiting for LLM" or "Waiting for " + the model name (base_coder.py:1440).
|
|
207
|
+
// aider's spinner uses a "░█" scanner, NOT braille, so "Waiting for " is the
|
|
208
|
+
// real invariant; the braille probe is kept only as a harmless fallback.
|
|
168
209
|
working: {
|
|
169
|
-
any: [
|
|
210
|
+
any: [
|
|
211
|
+
{ region: "bottom", contains: "Waiting for ", caseInsensitive: false },
|
|
212
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
213
|
+
],
|
|
170
214
|
},
|
|
171
215
|
},
|
|
172
216
|
};
|
|
@@ -174,8 +218,10 @@ const AIDER: AgentManifest = {
|
|
|
174
218
|
const COPILOT: AgentManifest = {
|
|
175
219
|
id: "copilot",
|
|
176
220
|
commands: ["copilot", "github-copilot", "github-copilot-cli"],
|
|
221
|
+
confidence: "conservative",
|
|
177
222
|
states: {
|
|
178
|
-
//
|
|
223
|
+
// conservative — github-copilot-cli needs a GitHub account, so no live
|
|
224
|
+
// capture was taken. High-precision only.
|
|
179
225
|
blocked: {
|
|
180
226
|
any: [
|
|
181
227
|
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
@@ -193,9 +239,314 @@ const COPILOT: AgentManifest = {
|
|
|
193
239
|
},
|
|
194
240
|
};
|
|
195
241
|
|
|
242
|
+
const CURSOR: AgentManifest = {
|
|
243
|
+
id: "cursor",
|
|
244
|
+
commands: ["cursor-agent", "cursor"],
|
|
245
|
+
confidence: "conservative",
|
|
246
|
+
states: {
|
|
247
|
+
// conservative — cursor-agent (Cursor CLI) was launched live but sits on a
|
|
248
|
+
// "Press any key to log in…" pre-auth screen without an account, so no
|
|
249
|
+
// working/blocked turn could be captured. The pre-auth splash ("Cursor
|
|
250
|
+
// Agent" / "Press any key to log in") is idle chrome and deliberately NOT
|
|
251
|
+
// matched here. Markers below are high-precision guesses from public
|
|
252
|
+
// knowledge of its approval/streaming UI. NOTE: cursor-agent runs under
|
|
253
|
+
// `node`, so it resolves via the process-tree (argv0 basename), not the
|
|
254
|
+
// pane's `current_command`.
|
|
255
|
+
blocked: {
|
|
256
|
+
any: [
|
|
257
|
+
{ region: "bottom", contains: "Do you want", caseInsensitive: false },
|
|
258
|
+
{ region: "bottom", contains: "Run this command", caseInsensitive: true },
|
|
259
|
+
{ region: "bottom", contains: "Apply this edit", caseInsensitive: true },
|
|
260
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
working: {
|
|
264
|
+
any: [
|
|
265
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
266
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
267
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const GOOSE: AgentManifest = {
|
|
274
|
+
id: "goose",
|
|
275
|
+
commands: ["goose"],
|
|
276
|
+
confidence: "conservative",
|
|
277
|
+
states: {
|
|
278
|
+
// conservative — Block's goose CLI needs a configured provider, so no live
|
|
279
|
+
// capture was taken. High-precision only; markers are best-effort from
|
|
280
|
+
// public knowledge of its confirmation/streaming UI.
|
|
281
|
+
blocked: {
|
|
282
|
+
any: [
|
|
283
|
+
{ region: "bottom", contains: "Do you want", caseInsensitive: false },
|
|
284
|
+
{ region: "bottom", contains: "Allow this tool", caseInsensitive: true },
|
|
285
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
286
|
+
{ region: "bottom", contains: "[y/n]", caseInsensitive: true },
|
|
287
|
+
],
|
|
288
|
+
},
|
|
289
|
+
working: {
|
|
290
|
+
any: [
|
|
291
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
292
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
293
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const AMP: AgentManifest = {
|
|
300
|
+
id: "amp",
|
|
301
|
+
commands: ["amp"],
|
|
302
|
+
confidence: "conservative",
|
|
303
|
+
states: {
|
|
304
|
+
// conservative — Sourcegraph's amp CLI needs an account, so no live capture
|
|
305
|
+
// was taken. High-precision only; markers are best-effort from public
|
|
306
|
+
// knowledge of its approval/streaming UI.
|
|
307
|
+
blocked: {
|
|
308
|
+
any: [
|
|
309
|
+
{ region: "bottom", contains: "Do you want", caseInsensitive: false },
|
|
310
|
+
{ region: "bottom", contains: "Allow", caseInsensitive: false },
|
|
311
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
312
|
+
],
|
|
313
|
+
},
|
|
314
|
+
working: {
|
|
315
|
+
any: [
|
|
316
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
317
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
318
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
const DEVIN: AgentManifest = {
|
|
325
|
+
id: "devin",
|
|
326
|
+
commands: ["devin"],
|
|
327
|
+
confidence: "conservative",
|
|
328
|
+
states: {
|
|
329
|
+
// conservative — Devin CLI (Cognition; closed-source native Rust binary,
|
|
330
|
+
// needs an account). Blocked markers are VERBATIM from docs.devin.ai
|
|
331
|
+
// (cli/changelog + essential-commands via llms-full.txt): the plan-mode
|
|
332
|
+
// approval menu and the always-allow option. Devin has NO verified working
|
|
333
|
+
// text (its spinner is unlabeled; docs say interrupt is Ctrl+C, not esc),
|
|
334
|
+
// so working keeps only the shared spinner probe.
|
|
335
|
+
blocked: {
|
|
336
|
+
any: [
|
|
337
|
+
// seen (docs): "Yes, implement plan and accept edits" / "…and bypass
|
|
338
|
+
// permissions" — the shared prefix covers both.
|
|
339
|
+
{ region: "bottom", contains: "Yes, implement plan and" },
|
|
340
|
+
{ region: "bottom", contains: "No, plan needs changes" },
|
|
341
|
+
{ region: "bottom", contains: "Yes, always allow" },
|
|
342
|
+
],
|
|
343
|
+
},
|
|
344
|
+
working: {
|
|
345
|
+
any: [
|
|
346
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
347
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
348
|
+
],
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const KIMI: AgentManifest = {
|
|
354
|
+
id: "kimi",
|
|
355
|
+
commands: ["kimi"],
|
|
356
|
+
confidence: "conservative",
|
|
357
|
+
states: {
|
|
358
|
+
// conservative — kimi CLI (MoonshotAI/kimi-cli, Python/prompt_toolkit).
|
|
359
|
+
// Blocked markers are VERBATIM from its source (_approval_panel.py): the
|
|
360
|
+
// approval panel's "<tool> is requesting approval to …" header and its
|
|
361
|
+
// option labels. Its working spinner is a text-less moon animation (not
|
|
362
|
+
// braille), so working may under-detect until tuned — the braille probe is
|
|
363
|
+
// kept only as the shared fallback. ("Thinking"/"Thought for" deliberately
|
|
364
|
+
// NOT matched: they persist in the transcript after the turn.)
|
|
365
|
+
blocked: {
|
|
366
|
+
any: [
|
|
367
|
+
{ region: "bottom", contains: " is requesting approval to " },
|
|
368
|
+
{ region: "bottom", contains: "Approve for this session" },
|
|
369
|
+
{ region: "bottom", contains: "Reject, tell the model" },
|
|
370
|
+
],
|
|
371
|
+
},
|
|
372
|
+
working: {
|
|
373
|
+
any: [
|
|
374
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
375
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const PI: AgentManifest = {
|
|
382
|
+
id: "pi",
|
|
383
|
+
commands: ["pi"],
|
|
384
|
+
confidence: "conservative",
|
|
385
|
+
states: {
|
|
386
|
+
// conservative — pi (@mariozechner/pi-coding-agent, node). Working marker
|
|
387
|
+
// is VERBATIM from its source (interactive-mode.ts): the status line is
|
|
388
|
+
// `Working... (esc to interrupt)`, with `(esc to cancel)` retry/compacting
|
|
389
|
+
// variants. Its core has NO distinctive approval wording (generic Yes/No
|
|
390
|
+
// via extensions), so blocked is deliberately ABSENT — authority
|
|
391
|
+
// self-reporting or the classifier's fallback carry it.
|
|
392
|
+
working: {
|
|
393
|
+
any: [
|
|
394
|
+
{ region: "bottom", contains: "Working... (esc to interrupt)" },
|
|
395
|
+
{ region: "bottom", contains: "(esc to cancel)" },
|
|
396
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
397
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
398
|
+
],
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
const GROK: AgentManifest = {
|
|
404
|
+
id: "grok",
|
|
405
|
+
commands: ["grok"],
|
|
406
|
+
confidence: "conservative",
|
|
407
|
+
states: {
|
|
408
|
+
// conservative — the community grok CLI (@vibe-kit/grok-cli /
|
|
409
|
+
// superagent-ai/grok-cli, node/OpenTUI). Working markers are VERBATIM from
|
|
410
|
+
// its source (app.tsx / headless output.ts): the mid-turn placeholder
|
|
411
|
+
// carries "(esc to interrupt)" and headless prints "⏳ Processing...".
|
|
412
|
+
// Its ConfirmationDialog wording could not be verified, so blocked keeps
|
|
413
|
+
// only a high-precision generic. NOTE: xAI's separate official
|
|
414
|
+
// `grok-build` binary substring-matches this manifest but has no verified
|
|
415
|
+
// strings — it will simply read idle until evidence exists.
|
|
416
|
+
blocked: {
|
|
417
|
+
any: [{ region: "bottom", contains: "(y/n)", caseInsensitive: true }],
|
|
418
|
+
},
|
|
419
|
+
working: {
|
|
420
|
+
any: [
|
|
421
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
422
|
+
{ region: "bottom", contains: "⏳ Processing" },
|
|
423
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
424
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
425
|
+
],
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
const KIRO: AgentManifest = {
|
|
431
|
+
id: "kiro",
|
|
432
|
+
commands: ["kiro-cli", "kiro"],
|
|
433
|
+
confidence: "conservative",
|
|
434
|
+
states: {
|
|
435
|
+
// conservative — Kiro CLI (AWS; the rebranded Amazon Q Developer CLI, a
|
|
436
|
+
// native Rust binary). Markers are VERBATIM from the open-source
|
|
437
|
+
// predecessor (aws/amazon-q-developer-cli chat-cli/src/cli/chat/mod.rs):
|
|
438
|
+
// the tool-approval question and the Spinners::Dots "Thinking..." status.
|
|
439
|
+
// The closed Kiro build may drift — same lineage, pending live capture.
|
|
440
|
+
// NOTE: the predecessor's 1-char `q` binary is deliberately NOT a command
|
|
441
|
+
// token (a single letter substring-matches far too much).
|
|
442
|
+
blocked: {
|
|
443
|
+
any: [
|
|
444
|
+
{ region: "bottom", contains: "Allow this action?" },
|
|
445
|
+
{ region: "bottom", contains: "to trust (always allow) this tool" },
|
|
446
|
+
],
|
|
447
|
+
},
|
|
448
|
+
working: {
|
|
449
|
+
any: [
|
|
450
|
+
{ region: "bottom", contains: "Thinking..." },
|
|
451
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
452
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
453
|
+
],
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const CLINE: AgentManifest = {
|
|
459
|
+
id: "cline",
|
|
460
|
+
commands: ["cline"],
|
|
461
|
+
confidence: "conservative",
|
|
462
|
+
states: {
|
|
463
|
+
// conservative — cline CLI (cline.bot; CLI 2.0 ships prebuilt platform
|
|
464
|
+
// binaries, OpenTUI). Markers are VERBATIM from its source: the approval
|
|
465
|
+
// dialog's "Cline needs permission" title + "Approve tool call?" header
|
|
466
|
+
// (tui/components/dialogs/tool-approval.tsx) and the streaming
|
|
467
|
+
// "Thinking... (esc to cancel)" status (chat-message-list.tsx).
|
|
468
|
+
blocked: {
|
|
469
|
+
any: [
|
|
470
|
+
{ region: "bottom", contains: "Cline needs permission" },
|
|
471
|
+
{ region: "bottom", contains: "Approve tool call?" },
|
|
472
|
+
{ region: "bottom", contains: "[y] Approve" },
|
|
473
|
+
],
|
|
474
|
+
},
|
|
475
|
+
working: {
|
|
476
|
+
any: [
|
|
477
|
+
{ region: "bottom", contains: "esc to cancel", caseInsensitive: true },
|
|
478
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
479
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
480
|
+
],
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const DROID: AgentManifest = {
|
|
486
|
+
id: "droid",
|
|
487
|
+
commands: ["droid"],
|
|
488
|
+
confidence: "conservative",
|
|
489
|
+
states: {
|
|
490
|
+
// conservative — droid (Factory CLI) was launched live (v0.114.1) but sits
|
|
491
|
+
// on an auth-gated login menu without a Factory account, so only the login
|
|
492
|
+
// splash could be captured (it reads IDLE and is deliberately not matched:
|
|
493
|
+
// "Please login with your Factory account to continue." / "> Login").
|
|
494
|
+
// Verified live: droid runs as its OWN process (`pane_current_command` =
|
|
495
|
+
// "droid"), so command matching needs no process-tree help.
|
|
496
|
+
// Blocked markers are docs-derived (docs.factory.ai auto-run levels,
|
|
497
|
+
// corroborated third-party walkthrough) — the Spec-mode proceed menu names
|
|
498
|
+
// its autonomy levels verbatim. Pending live-capture confirmation.
|
|
499
|
+
blocked: {
|
|
500
|
+
any: [
|
|
501
|
+
{ region: "bottom", contains: "Proceed, manual approval" },
|
|
502
|
+
{ region: "bottom", contains: "Proceed, allow safe commands" },
|
|
503
|
+
{ region: "bottom", contains: "Proceed, allow all commands" },
|
|
504
|
+
],
|
|
505
|
+
},
|
|
506
|
+
working: {
|
|
507
|
+
any: [
|
|
508
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
509
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
510
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
511
|
+
],
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
const KILO: AgentManifest = {
|
|
517
|
+
id: "kilo",
|
|
518
|
+
commands: ["kilo", ".kilo", "kilocode"],
|
|
519
|
+
confidence: "conservative",
|
|
520
|
+
states: {
|
|
521
|
+
// conservative, but the blocked markers are VERBATIM from the shipped
|
|
522
|
+
// @kilocode/cli binary (v7.4.5, strings-extracted — no account to drive a
|
|
523
|
+
// live capture): the permission dialog renders a "△ Permission required"
|
|
524
|
+
// header over an "Allow once" / "Allow always" / "Reject" option list.
|
|
525
|
+
// NOTE: the CLI's npm launcher (`bin/kilo`, node) execs the platform
|
|
526
|
+
// binary installed as `bin/.kilo`, hence the ".kilo" command token.
|
|
527
|
+
blocked: {
|
|
528
|
+
any: [
|
|
529
|
+
{ region: "bottom", contains: "Permission required" },
|
|
530
|
+
{ region: "bottom", contains: "Allow once" },
|
|
531
|
+
{ region: "bottom", contains: "Allow always" },
|
|
532
|
+
],
|
|
533
|
+
},
|
|
534
|
+
// No working-state string was found in the binary (its status UI renders
|
|
535
|
+
// from dynamic parts) — only the shared spinner probe, so kilo may read
|
|
536
|
+
// idle while working until a live capture tunes it.
|
|
537
|
+
working: {
|
|
538
|
+
any: [
|
|
539
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
540
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
541
|
+
],
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
};
|
|
545
|
+
|
|
196
546
|
const SHELL: AgentManifest = {
|
|
197
547
|
id: "shell",
|
|
198
548
|
commands: ["bash", "zsh", "sh", "fish", "nu"],
|
|
549
|
+
confidence: "conservative",
|
|
199
550
|
states: {
|
|
200
551
|
// Catch-all: a raw shell is almost always idle. We only flag an explicit
|
|
201
552
|
// interactive confirmation as blocked; "working" is unreliable to read
|
|
@@ -222,5 +573,16 @@ export const BUNDLED_MANIFESTS: AgentManifest[] = [
|
|
|
222
573
|
GEMINI,
|
|
223
574
|
AIDER,
|
|
224
575
|
COPILOT,
|
|
576
|
+
CURSOR,
|
|
577
|
+
GOOSE,
|
|
578
|
+
AMP,
|
|
579
|
+
DEVIN,
|
|
580
|
+
KIMI,
|
|
581
|
+
PI,
|
|
582
|
+
GROK,
|
|
583
|
+
KIRO,
|
|
584
|
+
CLINE,
|
|
585
|
+
DROID,
|
|
586
|
+
KILO,
|
|
225
587
|
SHELL,
|
|
226
588
|
];
|
|
@@ -53,6 +53,16 @@ export function parsePsOutput(raw: string): ProcEntry[] {
|
|
|
53
53
|
* depth are guarded.
|
|
54
54
|
*/
|
|
55
55
|
export function subtreeCommands(entries: ProcEntry[], rootPid: number, maxDepth = 6): string[] {
|
|
56
|
+
return subtreeEntries(entries, rootPid, maxDepth).map((entry) => entry.command);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The full {@link ProcEntry} rows of the subtree rooted at `rootPid`, in the
|
|
61
|
+
* same deepest-first order as {@link subtreeCommands}. Callers that need PIDs
|
|
62
|
+
* (e.g. the session-id capture probing a pane's agent process by open files)
|
|
63
|
+
* use this; the command-only view above stays the common path.
|
|
64
|
+
*/
|
|
65
|
+
export function subtreeEntries(entries: ProcEntry[], rootPid: number, maxDepth = 6): ProcEntry[] {
|
|
56
66
|
const childrenByParent = new Map<number, ProcEntry[]>();
|
|
57
67
|
const byPid = new Map<number, ProcEntry>();
|
|
58
68
|
for (const entry of entries) {
|
|
@@ -65,7 +75,7 @@ export function subtreeCommands(entries: ProcEntry[], rootPid: number, maxDepth
|
|
|
65
75
|
const root = byPid.get(rootPid);
|
|
66
76
|
if (!root) return [];
|
|
67
77
|
|
|
68
|
-
const
|
|
78
|
+
const out: ProcEntry[] = [];
|
|
69
79
|
const visited = new Set<number>();
|
|
70
80
|
|
|
71
81
|
const walk = (pid: number, depth: number): void => {
|
|
@@ -76,11 +86,31 @@ export function subtreeCommands(entries: ProcEntry[], rootPid: number, maxDepth
|
|
|
76
86
|
walk(child.pid, depth + 1);
|
|
77
87
|
}
|
|
78
88
|
const self = byPid.get(pid);
|
|
79
|
-
if (self)
|
|
89
|
+
if (self) out.push(self);
|
|
80
90
|
};
|
|
81
91
|
|
|
82
92
|
walk(rootPid, 0);
|
|
83
|
-
return
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Summarize the executable-ish tokens seen in a pane's process subtree, in the
|
|
98
|
+
* order the tree-walk visits them (deepest first), de-duplicated. Pure.
|
|
99
|
+
*
|
|
100
|
+
* This is a DIAGNOSTIC for `agent explain`: when no manifest resolves, showing
|
|
101
|
+
* "process-tree saw: node, sleep" tells the user WHY nothing matched and what
|
|
102
|
+
* to point an `@agent_hint` at. Tokens are the same basenames
|
|
103
|
+
* `resolveAgentCommand` matches against, so the report reflects reality.
|
|
104
|
+
*/
|
|
105
|
+
export function describeSubtree(entries: ProcEntry[], rootPid: number, limit = 8): string[] {
|
|
106
|
+
const seen: string[] = [];
|
|
107
|
+
for (const command of subtreeCommands(entries, rootPid)) {
|
|
108
|
+
for (const token of commandTokens(command)) {
|
|
109
|
+
if (!seen.includes(token)) seen.push(token);
|
|
110
|
+
if (seen.length >= limit) return seen;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return seen;
|
|
84
114
|
}
|
|
85
115
|
|
|
86
116
|
/**
|