tmux-ide 2.6.0 → 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 +1024 -519
- package/bin/cli.ts +63 -6
- package/bunfig.toml +4 -0
- package/package.json +18 -7
- package/packages/contracts/package.json +22 -0
- package/packages/contracts/src/__tests__/ide-config.test.ts +46 -0
- package/packages/contracts/src/__tests__/terminals.test.ts +87 -0
- package/packages/contracts/src/actions-contract.ts +310 -0
- package/packages/contracts/src/actions-errors.ts +41 -0
- package/packages/contracts/src/domain.ts +36 -0
- package/packages/contracts/src/ide-config.ts +170 -0
- package/packages/contracts/src/index.ts +24 -0
- package/packages/contracts/src/lib-internal/auth.ts +13 -0
- package/packages/contracts/src/lib-internal/hq.ts +38 -0
- package/packages/contracts/src/terminals.ts +116 -0
- package/packages/contracts/src/tmux.ts +60 -0
- package/packages/contracts/src/workspace.ts +67 -0
- 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 +298 -0
- package/packages/daemon/src/attach.ts +20 -0
- package/packages/daemon/src/bin.ts +4 -0
- package/packages/daemon/src/canonical.ts +7 -0
- package/packages/daemon/src/cli.ts +499 -0
- package/packages/daemon/src/command-center/actions/contract.ts +2 -0
- package/packages/daemon/src/command-center/actions/dispatcher.ts +137 -0
- package/packages/daemon/src/command-center/actions/errors.ts +105 -0
- package/packages/daemon/src/command-center/actions/handlers/_project-context.ts +30 -0
- package/packages/daemon/src/command-center/actions/handlers/_resolve-project.ts +78 -0
- package/packages/daemon/src/command-center/actions/handlers/app-set-remote-access.ts +118 -0
- package/packages/daemon/src/command-center/actions/handlers/config-actions.ts +113 -0
- package/packages/daemon/src/command-center/actions/handlers/daemon-shutdown.ts +38 -0
- package/packages/daemon/src/command-center/actions/handlers/project-activate.ts +30 -0
- package/packages/daemon/src/command-center/actions/handlers/project-launch.ts +70 -0
- package/packages/daemon/src/command-center/actions/handlers/project-open-terminal.ts +87 -0
- package/packages/daemon/src/command-center/actions/handlers/project-restart.ts +38 -0
- package/packages/daemon/src/command-center/actions/handlers/project-stop.ts +62 -0
- package/packages/daemon/src/command-center/actions/handlers/terminal-respawn.ts +119 -0
- package/packages/daemon/src/command-center/actions/handlers/terminal-stop.ts +35 -0
- package/packages/daemon/src/command-center/actions/registry.ts +149 -0
- package/packages/daemon/src/command-center/discovery.ts +96 -0
- package/packages/daemon/src/command-center/index.ts +31 -0
- package/packages/daemon/src/command-center/schemas.ts +85 -0
- package/packages/daemon/src/command-center/server.ts +1260 -0
- package/packages/daemon/src/command-center/ws-events.ts +316 -0
- package/packages/daemon/src/config.ts +549 -0
- package/packages/daemon/src/detect.ts +248 -0
- package/packages/daemon/src/doctor.ts +242 -0
- package/packages/daemon/src/embed.ts +5 -0
- package/packages/daemon/src/index.ts +12 -0
- package/packages/daemon/src/init.ts +211 -0
- package/packages/daemon/src/inspect.ts +178 -0
- package/packages/daemon/src/js-yaml.d.ts +10 -0
- package/packages/daemon/src/launch.ts +349 -0
- package/packages/daemon/src/lib/active-projects.ts +49 -0
- package/packages/daemon/src/lib/agent-discovery.ts +121 -0
- package/packages/daemon/src/lib/app-config.ts +427 -0
- package/packages/daemon/src/lib/app-settings.ts +53 -0
- package/packages/daemon/src/lib/auth/auth-service.ts +227 -0
- package/packages/daemon/src/lib/auth/middleware.ts +56 -0
- package/packages/daemon/src/lib/auth/types.ts +2 -0
- package/packages/daemon/src/lib/auth-token.ts +5 -0
- package/packages/daemon/src/lib/authorship.ts +280 -0
- package/packages/daemon/src/lib/canonical-daemon.ts +122 -0
- package/packages/daemon/src/lib/cli-action-bridge.ts +216 -0
- package/packages/daemon/src/lib/daemon-embed.ts +782 -0
- package/packages/daemon/src/lib/daemon-watchdog.ts +111 -0
- package/packages/daemon/src/lib/daemon.ts +79 -0
- package/packages/daemon/src/lib/dot-path.ts +17 -0
- package/packages/daemon/src/lib/errors.ts +67 -0
- package/packages/daemon/src/lib/filesystem-browser.ts +292 -0
- package/packages/daemon/src/lib/launch-plan.ts +90 -0
- package/packages/daemon/src/lib/log.ts +134 -0
- package/packages/daemon/src/lib/output.ts +76 -0
- package/packages/daemon/src/lib/project-init-runner.ts +150 -0
- package/packages/daemon/src/lib/project-inspect.ts +80 -0
- package/packages/daemon/src/lib/project-onboard.ts +149 -0
- package/packages/daemon/src/lib/project-probe.ts +92 -0
- package/packages/daemon/src/lib/project-registry.ts +296 -0
- package/packages/daemon/src/lib/session-monitor.ts +122 -0
- package/packages/daemon/src/lib/session-options.ts +100 -0
- package/packages/daemon/src/lib/shell.ts +8 -0
- package/packages/daemon/src/lib/sizes.ts +36 -0
- package/packages/daemon/src/lib/skill-sync.ts +155 -0
- package/packages/daemon/src/lib/slugify.ts +10 -0
- package/packages/daemon/src/lib/terminals-store.ts +125 -0
- package/packages/daemon/src/lib/tui-binary.ts +165 -0
- package/packages/daemon/src/lib/update-check.ts +298 -0
- package/packages/daemon/src/lib/update.ts +158 -0
- package/packages/daemon/src/lib/workspace-registry.ts +229 -0
- package/packages/daemon/src/lib/worktree.ts +289 -0
- package/packages/daemon/src/lib/yaml-io.ts +27 -0
- package/packages/daemon/src/ls.ts +40 -0
- package/packages/daemon/src/restart.ts +24 -0
- package/packages/daemon/src/restore.ts +514 -0
- package/packages/daemon/src/schemas/domain.ts +2 -0
- package/packages/daemon/src/schemas/filesystem.ts +34 -0
- package/packages/daemon/src/schemas/ide-config.ts +2 -0
- package/packages/daemon/src/schemas/index.ts +59 -0
- package/packages/daemon/src/schemas/inspect.ts +67 -0
- package/packages/daemon/src/schemas/registry.ts +55 -0
- package/packages/daemon/src/schemas/ws-events.ts +135 -0
- package/packages/daemon/src/send.ts +171 -0
- package/packages/daemon/src/server/README.md +15 -0
- package/packages/daemon/src/server/index.ts +74 -0
- package/packages/daemon/src/server/pty-bridge.ts +532 -0
- package/packages/daemon/src/server/standalone.ts +18 -0
- package/packages/daemon/src/server/ws-route.ts +483 -0
- package/packages/daemon/src/status.ts +59 -0
- package/packages/daemon/src/stop.ts +28 -0
- package/packages/daemon/src/terminal/NodePtyAdapter.ts +271 -0
- package/packages/daemon/src/terminal/PtyAdapter.ts +140 -0
- package/packages/daemon/src/terminal/README.md +92 -0
- package/packages/daemon/src/tui/chrome/cheatsheet.ts +260 -0
- package/packages/daemon/src/tui/chrome/chip.ts +30 -0
- package/packages/daemon/src/tui/chrome/events.ts +119 -0
- package/packages/daemon/src/tui/chrome/kitty-keys.ts +55 -0
- package/packages/daemon/src/tui/chrome/menu.ts +289 -0
- package/packages/daemon/src/tui/chrome/notify.ts +382 -0
- package/packages/daemon/src/tui/chrome/panels.ts +111 -0
- package/packages/daemon/src/tui/chrome/sidebar.ts +222 -0
- package/packages/daemon/src/tui/chrome/snapshot.ts +425 -0
- package/packages/daemon/src/tui/chrome/statusline.ts +595 -0
- package/packages/daemon/src/tui/chrome/updater.ts +510 -0
- package/packages/daemon/src/tui/chrome/welcome.ts +124 -0
- package/packages/daemon/src/tui/compiled.ts +121 -0
- package/packages/daemon/src/tui/detect/classify.ts +208 -0
- package/packages/daemon/src/tui/detect/manifest-loader.ts +193 -0
- package/packages/daemon/src/tui/detect/manifest.ts +199 -0
- package/packages/daemon/src/tui/detect/manifests.ts +354 -0
- package/packages/daemon/src/tui/detect/process-tree.ts +217 -0
- package/packages/daemon/src/tui/detect/snapshot.ts +70 -0
- package/packages/daemon/src/tui/integrations/claude.ts +176 -0
- package/packages/daemon/src/tui/integrations/offer.ts +145 -0
- package/packages/daemon/src/tui/main.ts +82 -0
- 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 +214 -0
- package/packages/daemon/src/tui/mirror/control.ts +97 -0
- 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 +561 -0
- 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/CONTROL.md +50 -0
- package/packages/daemon/src/tui/team/entry.ts +38 -0
- package/packages/daemon/src/tui/team/fuzzy.ts +133 -0
- package/packages/daemon/src/tui/team/home.ts +170 -0
- package/packages/daemon/src/tui/team/index.tsx +1521 -0
- package/packages/daemon/src/tui/team/input.ts +34 -0
- package/packages/daemon/src/tui/team/keymap.ts +127 -0
- package/packages/daemon/src/tui/team/mouse.ts +29 -0
- package/packages/daemon/src/tui/team/nav.ts +31 -0
- package/packages/daemon/src/tui/team/preview.ts +34 -0
- package/packages/daemon/src/tui/team/projects.ts +191 -0
- package/packages/daemon/src/tui/team/report.ts +83 -0
- package/packages/daemon/src/tui/team/sessions.ts +433 -0
- package/packages/daemon/src/tui/team/tree.ts +62 -0
- package/packages/daemon/src/types.ts +13 -0
- package/packages/daemon/src/ui/index.ts +32 -0
- package/packages/daemon/src/ui/terminal/index.ts +9 -0
- package/packages/daemon/src/ui/types.ts +91 -0
- package/packages/daemon/src/ui/web/base.css +80 -0
- package/packages/daemon/src/ui/web/components/Box.tsx +59 -0
- package/packages/daemon/src/ui/web/components/Input.tsx +32 -0
- package/packages/daemon/src/ui/web/components/ScrollBox.tsx +60 -0
- package/packages/daemon/src/ui/web/components/Text.tsx +28 -0
- package/packages/daemon/src/ui/web/hooks.ts +106 -0
- package/packages/daemon/src/ui/web/index.ts +27 -0
- package/packages/daemon/src/ui/web/render.ts +77 -0
- package/packages/daemon/src/ui/web/utils/color.ts +27 -0
- package/packages/daemon/src/validate.ts +217 -0
- package/packages/daemon/src/widgets/changes/README.md +3 -0
- package/packages/daemon/src/widgets/changes/index.tsx +691 -0
- package/packages/daemon/src/widgets/config/README.md +3 -0
- package/packages/daemon/src/widgets/config/index.tsx +481 -0
- package/packages/daemon/src/widgets/explorer/README.md +3 -0
- package/packages/daemon/src/widgets/explorer/breadcrumbs.tsx +77 -0
- package/packages/daemon/src/widgets/explorer/footer.tsx +20 -0
- package/packages/daemon/src/widgets/explorer/header.tsx +23 -0
- package/packages/daemon/src/widgets/explorer/index.tsx +456 -0
- package/packages/daemon/src/widgets/explorer/tree-model.ts +103 -0
- package/packages/daemon/src/widgets/explorer/tree.tsx +165 -0
- package/packages/daemon/src/widgets/lib/config-model.ts +116 -0
- package/packages/daemon/src/widgets/lib/files.ts +88 -0
- package/packages/daemon/src/widgets/lib/git.ts +88 -0
- package/packages/daemon/src/widgets/lib/grammar.ts +126 -0
- package/packages/daemon/src/widgets/lib/help-overlay.tsx +101 -0
- package/packages/daemon/src/widgets/lib/pane-comms.ts +209 -0
- package/packages/daemon/src/widgets/lib/theme.ts +194 -0
- package/packages/daemon/src/widgets/lib/watcher.ts +132 -0
- package/packages/daemon/src/widgets/preview/README.md +3 -0
- package/packages/daemon/src/widgets/preview/index.tsx +416 -0
- package/packages/daemon/src/widgets/resolve.ts +121 -0
- package/packages/daemon/src/widgets/setup/README.md +3 -0
- package/packages/daemon/src/widgets/setup/agent-naming.tsx +112 -0
- package/packages/daemon/src/widgets/setup/config-tree.tsx +246 -0
- package/packages/daemon/src/widgets/setup/detect-panel.tsx +72 -0
- package/packages/daemon/src/widgets/setup/field-editor.tsx +265 -0
- package/packages/daemon/src/widgets/setup/footer.tsx +107 -0
- package/packages/daemon/src/widgets/setup/index.tsx +341 -0
- package/packages/daemon/src/widgets/setup/layout-picker.tsx +96 -0
- package/packages/daemon/src/widgets/setup/orchestrator-panel.tsx +200 -0
- package/packages/daemon/src/widgets/setup/review-panel.tsx +140 -0
- package/packages/daemon/src/widgets/setup/setup-model.ts +188 -0
- package/packages/daemon/src/widgets/sidebar/index.tsx +527 -0
- package/packages/tmux-bridge/package.json +22 -0
- package/packages/tmux-bridge/src/errors.ts +28 -0
- package/packages/tmux-bridge/src/index.ts +31 -0
- package/packages/tmux-bridge/src/monitor.ts +77 -0
- package/packages/tmux-bridge/src/panes.ts +136 -0
- package/packages/tmux-bridge/src/runner.test.ts +501 -0
- package/packages/tmux-bridge/src/runner.ts +91 -0
- package/packages/tmux-bridge/src/sessions.ts +126 -0
- package/packages/tmux-bridge/src/targeting.test.ts +107 -0
- package/packages/tmux-bridge/src/targeting.ts +90 -0
- package/scripts/build-tui.mjs +11 -4
- package/scripts/perf-mirror.mjs +313 -0
- package/scripts/postinstall.js +26 -2
- 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
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declarative detection manifests — a pure, data-driven rule engine.
|
|
3
|
+
*
|
|
4
|
+
* A manifest describes, per agent command (claude, codex, shell, …), the
|
|
5
|
+
* textual evidence that indicates a `blocked`/`working`/`done` state. The
|
|
6
|
+
* evaluator resolves matcher regions from a `PaneSnapshot`, tests them, and
|
|
7
|
+
* returns the first state (by precedence) whose rule matches. Everything here
|
|
8
|
+
* is pure and never throws — invalid regexes simply fail to match. The 4-state
|
|
9
|
+
* classifier layers `idle` + seen-tracking on top of this.
|
|
10
|
+
*/
|
|
11
|
+
import type { PaneSnapshot } from "./snapshot.ts";
|
|
12
|
+
|
|
13
|
+
/** Which slice of a snapshot a matcher tests against. */
|
|
14
|
+
export type Region = "bottom" | "text" | "title";
|
|
15
|
+
|
|
16
|
+
/** A single evidence probe. Exactly one of `contains`/`regex` is required. */
|
|
17
|
+
export interface Matcher {
|
|
18
|
+
/** Region to resolve (default `bottom`). */
|
|
19
|
+
region?: Region;
|
|
20
|
+
/** Substring test. */
|
|
21
|
+
contains?: string;
|
|
22
|
+
/** Regex source string (compiled safely; invalid → no match). */
|
|
23
|
+
regex?: string;
|
|
24
|
+
/** Case-insensitive contains/regex (default false). */
|
|
25
|
+
caseInsensitive?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A boolean clause: `all` matchers AND-ed, `any` matchers OR-ed. */
|
|
29
|
+
export interface Rule {
|
|
30
|
+
/** Every matcher must match. */
|
|
31
|
+
all?: Matcher[];
|
|
32
|
+
/** At least one matcher must match. */
|
|
33
|
+
any?: Matcher[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Per-state rules. `idle` is the fallback and has no rule. */
|
|
37
|
+
export interface StateRules {
|
|
38
|
+
blocked?: Rule;
|
|
39
|
+
working?: Rule;
|
|
40
|
+
done?: Rule;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* How much trust the manifest's rules earn.
|
|
45
|
+
* - `tuned`: built from REAL captured screens (or the agent's own source
|
|
46
|
+
* strings) — the markers are verbatim, not guessed.
|
|
47
|
+
* - `conservative`: best-effort from public knowledge; high-precision only, so
|
|
48
|
+
* it can never false-positive on a plain prompt, but it may miss real states.
|
|
49
|
+
*/
|
|
50
|
+
export type ManifestConfidence = "tuned" | "conservative";
|
|
51
|
+
|
|
52
|
+
/** A detection manifest for one or more pane commands. */
|
|
53
|
+
export interface AgentManifest {
|
|
54
|
+
/** Stable manifest id. */
|
|
55
|
+
id: string;
|
|
56
|
+
/** Pane `current_command` names this manifest applies to. */
|
|
57
|
+
commands: string[];
|
|
58
|
+
/** Evidence rules per detectable state. */
|
|
59
|
+
states: StateRules;
|
|
60
|
+
/**
|
|
61
|
+
* Evidence confidence (default `conservative` when omitted). Surfaced by
|
|
62
|
+
* `agent explain` so a user can see whether a verdict rests on real evidence
|
|
63
|
+
* or a best-effort heuristic. Purely informational — never affects matching.
|
|
64
|
+
*/
|
|
65
|
+
confidence?: ManifestConfidence;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The states a manifest can positively detect (idle is inferred elsewhere). */
|
|
69
|
+
export type DetectedState = "blocked" | "working" | "done";
|
|
70
|
+
|
|
71
|
+
/** Snapshot plus the optional pane title used by the `title` region. */
|
|
72
|
+
type SnapshotWithTitle = PaneSnapshot & { title?: string };
|
|
73
|
+
|
|
74
|
+
/** Precedence order — the first matching state wins. */
|
|
75
|
+
const PRECEDENCE: DetectedState[] = ["blocked", "working", "done"];
|
|
76
|
+
|
|
77
|
+
/** Resolve the text a matcher tests against, tolerating an absent title. */
|
|
78
|
+
function resolveRegion(snapshot: SnapshotWithTitle, region: Region): string {
|
|
79
|
+
switch (region) {
|
|
80
|
+
case "text":
|
|
81
|
+
return snapshot.text;
|
|
82
|
+
case "title":
|
|
83
|
+
return snapshot.title ?? "";
|
|
84
|
+
case "bottom":
|
|
85
|
+
default:
|
|
86
|
+
return snapshot.bottomNonEmpty.join("\n");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Compile a regex safely — returns undefined on invalid source. */
|
|
91
|
+
function safeRegex(source: string, caseInsensitive?: boolean): RegExp | undefined {
|
|
92
|
+
try {
|
|
93
|
+
return new RegExp(source, caseInsensitive ? "i" : "");
|
|
94
|
+
} catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Test a single matcher against a snapshot. Pure — never throws.
|
|
101
|
+
* A matcher with neither `contains` nor `regex` never matches.
|
|
102
|
+
*/
|
|
103
|
+
export function matchMatcher(snapshot: SnapshotWithTitle, matcher: Matcher): boolean {
|
|
104
|
+
const haystack = resolveRegion(snapshot, matcher.region ?? "bottom");
|
|
105
|
+
|
|
106
|
+
if (matcher.contains !== undefined) {
|
|
107
|
+
if (matcher.caseInsensitive) {
|
|
108
|
+
return haystack.toLowerCase().includes(matcher.contains.toLowerCase());
|
|
109
|
+
}
|
|
110
|
+
return haystack.includes(matcher.contains);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (matcher.regex !== undefined) {
|
|
114
|
+
const re = safeRegex(matcher.regex, matcher.caseInsensitive);
|
|
115
|
+
return re ? re.test(haystack) : false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Test a rule: all present `all` matchers must match AND at least one present
|
|
123
|
+
* `any` matcher must match. An empty or absent rule never matches.
|
|
124
|
+
*/
|
|
125
|
+
export function matchRule(snapshot: SnapshotWithTitle, rule: Rule): boolean {
|
|
126
|
+
const hasAll = rule.all !== undefined && rule.all.length > 0;
|
|
127
|
+
const hasAny = rule.any !== undefined && rule.any.length > 0;
|
|
128
|
+
if (!hasAll && !hasAny) return false;
|
|
129
|
+
|
|
130
|
+
if (hasAll && !rule.all!.every((m) => matchMatcher(snapshot, m))) return false;
|
|
131
|
+
if (hasAny && !rule.any!.some((m) => matchMatcher(snapshot, m))) return false;
|
|
132
|
+
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Evaluate a manifest against a snapshot in precedence order
|
|
138
|
+
* (blocked → working → done). Returns the first matching state, or
|
|
139
|
+
* `{ state: null }` when no rule matches. Detection is strict: blocked
|
|
140
|
+
* requires explicit evidence.
|
|
141
|
+
*/
|
|
142
|
+
export function evaluateManifest(
|
|
143
|
+
snapshot: SnapshotWithTitle,
|
|
144
|
+
manifest: AgentManifest,
|
|
145
|
+
): { state: DetectedState | null; matched?: { state: DetectedState; matcher: Matcher } } {
|
|
146
|
+
for (const state of PRECEDENCE) {
|
|
147
|
+
const rule = manifest.states[state];
|
|
148
|
+
if (rule && matchRule(snapshot, rule)) {
|
|
149
|
+
const matcher = firstMatchingMatcher(snapshot, rule);
|
|
150
|
+
return matcher ? { state, matched: { state, matcher } } : { state };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { state: null };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Find the first matcher in a rule that matched, for reporting. */
|
|
157
|
+
function firstMatchingMatcher(snapshot: SnapshotWithTitle, rule: Rule): Matcher | undefined {
|
|
158
|
+
const matchers = [...(rule.all ?? []), ...(rule.any ?? [])];
|
|
159
|
+
return matchers.find((m) => matchMatcher(snapshot, m));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Debug helper — evaluate every state and report which matched, alongside the
|
|
164
|
+
* winning state (by precedence).
|
|
165
|
+
*/
|
|
166
|
+
export function explain(
|
|
167
|
+
snapshot: SnapshotWithTitle,
|
|
168
|
+
manifest: AgentManifest,
|
|
169
|
+
): { state: DetectedState | null; checked: Array<{ state: DetectedState; matched: boolean }> } {
|
|
170
|
+
const checked = PRECEDENCE.map((state) => {
|
|
171
|
+
const rule = manifest.states[state];
|
|
172
|
+
return { state, matched: rule ? matchRule(snapshot, rule) : false };
|
|
173
|
+
});
|
|
174
|
+
const winner = checked.find((c) => c.matched);
|
|
175
|
+
return { state: winner ? winner.state : null, checked };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Pick the manifest whose `commands` best match a pane's current command.
|
|
180
|
+
* Prefers an exact (case-insensitive) match, then a substring match in either
|
|
181
|
+
* direction. Returns undefined when nothing applies.
|
|
182
|
+
*/
|
|
183
|
+
export function pickManifest(
|
|
184
|
+
command: string,
|
|
185
|
+
manifests: AgentManifest[],
|
|
186
|
+
): AgentManifest | undefined {
|
|
187
|
+
const cmd = command.trim().toLowerCase();
|
|
188
|
+
if (cmd.length === 0) return undefined;
|
|
189
|
+
|
|
190
|
+
const exact = manifests.find((m) => m.commands.some((c) => c.toLowerCase() === cmd));
|
|
191
|
+
if (exact) return exact;
|
|
192
|
+
|
|
193
|
+
return manifests.find((m) =>
|
|
194
|
+
m.commands.some((c) => {
|
|
195
|
+
const name = c.toLowerCase();
|
|
196
|
+
return cmd.includes(name) || name.includes(cmd);
|
|
197
|
+
}),
|
|
198
|
+
);
|
|
199
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled detection manifests for known agent commands.
|
|
3
|
+
*
|
|
4
|
+
* These encode CONSERVATIVE, hand-tuned heuristics — the textual evidence
|
|
5
|
+
* seen at the bottom of a pane when an agent is blocked (waiting on input) or
|
|
6
|
+
* working (streaming/spinning). `done` is genuinely hard to read from a single
|
|
7
|
+
* snapshot, so it is left to the classifier's seen-tracking; the rules here
|
|
8
|
+
* stay minimal. Treat every matcher below as a heuristic to tune, not a
|
|
9
|
+
* contract — edit freely as agent UIs change.
|
|
10
|
+
*
|
|
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`). Every
|
|
20
|
+
* matcher is HIGH-PRECISION (esc-to-interrupt / spinner / explicit y-n /
|
|
21
|
+
* "Do you want") so it can never false-positive on a plain prompt, but it
|
|
22
|
+
* may miss real states until a live capture upgrades it. `opencode` was
|
|
23
|
+
* attempted live but its local auth DB errored and the TUI rendered blank,
|
|
24
|
+
* so it stays conservative.
|
|
25
|
+
*
|
|
26
|
+
* Users can override any of these with a JSON file in
|
|
27
|
+
* `~/.tmux-ide/agent-detection/` (see `manifest-loader.ts`).
|
|
28
|
+
*/
|
|
29
|
+
import type { AgentManifest } from "./manifest.ts";
|
|
30
|
+
|
|
31
|
+
// Braille spinner glyphs common to CLI TUIs (claude, codex, ora, …). Scoped to
|
|
32
|
+
// a single manifest's region, so it can only fire on that agent's pane.
|
|
33
|
+
const BRAILLE_SPINNER = "[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]";
|
|
34
|
+
|
|
35
|
+
const CLAUDE: AgentManifest = {
|
|
36
|
+
id: "claude",
|
|
37
|
+
commands: ["claude"],
|
|
38
|
+
confidence: "tuned",
|
|
39
|
+
states: {
|
|
40
|
+
// Approval / confirmation prompts — Claude is waiting on the user.
|
|
41
|
+
// Claude's approval UI is a bordered box asking a "Do you want …?" question
|
|
42
|
+
// with a numbered arrow menu ("❯ 1. Yes" / "3. No, and tell Claude …").
|
|
43
|
+
// These phrases are approval-specific and never appear in the idle chrome
|
|
44
|
+
// (a bare "❯ " input box) or the "How is Claude doing this session?" survey
|
|
45
|
+
// (which uses "1: Bad" colon-style options, not "❯ 1.").
|
|
46
|
+
blocked: {
|
|
47
|
+
any: [
|
|
48
|
+
// seen (approval dialogs): "Do you want to proceed?" / "Do you want to
|
|
49
|
+
// make this edit to …"
|
|
50
|
+
{ region: "bottom", contains: "Do you want" },
|
|
51
|
+
// seen: the highlighted first option of the numbered approval menu.
|
|
52
|
+
{ region: "bottom", contains: "❯ 1." },
|
|
53
|
+
// seen: "2. Yes, and don't ask again this session"
|
|
54
|
+
{ region: "bottom", contains: "Yes, and" },
|
|
55
|
+
// seen: "3. No, and tell Claude what to do differently"
|
|
56
|
+
{ region: "bottom", contains: "No, and tell Claude" },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
// Streaming / thinking indicators. While Claude works the bottom line shows
|
|
60
|
+
// a spinner + gerund + the interrupt hint, e.g.
|
|
61
|
+
// "✳ Cerebrating… (esc to interrupt · ctrl+t to hide todos)".
|
|
62
|
+
working: {
|
|
63
|
+
any: [
|
|
64
|
+
// seen: the interrupt hint is present for the entire duration of a turn
|
|
65
|
+
// — the single most reliable "working" invariant.
|
|
66
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
67
|
+
// seen: the animated status verb ("Thinking…", "Cerebrating…").
|
|
68
|
+
{ region: "bottom", contains: "Thinking" },
|
|
69
|
+
{ region: "bottom", contains: "Cerebrating" },
|
|
70
|
+
// The leading braille spinner glyph, in the body or (rarely) the title.
|
|
71
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
72
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
// done: intentionally omitted — inferred by the classifier's seen-tracking.
|
|
76
|
+
// NOTE (seen, NOT used): idle Claude shows a bordered "❯ " input box with a
|
|
77
|
+
// "⏵⏵ bypass permissions on (shift+tab to cycle) · ← for agents" or
|
|
78
|
+
// "? for shortcuts · ← for agents" footer, and finished turns leave a
|
|
79
|
+
// "✻ Brewed for 9s" summary — none of these are working/blocked evidence,
|
|
80
|
+
// so they are deliberately absent and fall through to idle.
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const CODEX: AgentManifest = {
|
|
85
|
+
id: "codex",
|
|
86
|
+
commands: ["codex", "codex.exe"],
|
|
87
|
+
confidence: "tuned",
|
|
88
|
+
states: {
|
|
89
|
+
// TUNED against real captures (codex-cli v0.142.5, driven through a turn).
|
|
90
|
+
// The command-approval dialog and the directory-trust prompt are the two
|
|
91
|
+
// "blocked" screens. Codex's approval menu uses a "› 1." numbered arrow —
|
|
92
|
+
// note the arrow is "›" (U+203A), NOT claude's "❯".
|
|
93
|
+
blocked: {
|
|
94
|
+
any: [
|
|
95
|
+
// seen (command approval): "Would you like to run the following
|
|
96
|
+
// command?" above a "$ <cmd>" preview and the numbered menu.
|
|
97
|
+
{ region: "bottom", contains: "Would you like to run", caseInsensitive: true },
|
|
98
|
+
// seen: the highlighted approval option "› 1. Yes, proceed".
|
|
99
|
+
{ region: "bottom", contains: "Yes, proceed" },
|
|
100
|
+
// seen: "3. No, and tell Codex what to do differently (esc)".
|
|
101
|
+
{ region: "bottom", contains: "No, and tell Codex" },
|
|
102
|
+
// seen: the confirm footer under the approval menu.
|
|
103
|
+
{ region: "bottom", contains: "Press enter to confirm", caseInsensitive: true },
|
|
104
|
+
// seen (directory-trust prompt on first launch in an untrusted dir):
|
|
105
|
+
// "Do you trust the contents of this directory?" + "1. Yes, continue".
|
|
106
|
+
{ region: "bottom", contains: "Do you trust the contents", caseInsensitive: true },
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
working: {
|
|
110
|
+
any: [
|
|
111
|
+
// seen (verbatim): the working status line is
|
|
112
|
+
// "• Working (6s • esc to interrupt)".
|
|
113
|
+
// Both the "Working (" prefix and the shared "esc to interrupt" hint
|
|
114
|
+
// are present for the whole turn.
|
|
115
|
+
{ region: "bottom", regex: "Working \\(\\d" },
|
|
116
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
117
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
118
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
// done: omitted. NOTE (seen, NOT used): a finished turn leaves the agent's
|
|
122
|
+
// answer above the idle "›" input box (placeholder "Find and fix a bug in
|
|
123
|
+
// @filename") and a "gpt-5.5 xhigh · <cwd>" status line; older builds also
|
|
124
|
+
// showed "Goal achieved (5m)". None are working/blocked evidence, so codex
|
|
125
|
+
// correctly falls through to idle and the classifier infers done.
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const OPENCODE: AgentManifest = {
|
|
130
|
+
id: "opencode",
|
|
131
|
+
commands: ["opencode", "opencode.exe"],
|
|
132
|
+
confidence: "conservative",
|
|
133
|
+
states: {
|
|
134
|
+
// conservative — a live capture was attempted (opencode v1.17.10) but its
|
|
135
|
+
// local auth DB errored ("no such column: name") and the TUI rendered
|
|
136
|
+
// blank, so these stay best-effort. High-precision only.
|
|
137
|
+
blocked: {
|
|
138
|
+
any: [
|
|
139
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
140
|
+
{ region: "bottom", contains: "[y/n]", caseInsensitive: true },
|
|
141
|
+
{ region: "bottom", contains: "Do you want" },
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
working: {
|
|
145
|
+
any: [
|
|
146
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
147
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
148
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const GEMINI: AgentManifest = {
|
|
155
|
+
id: "gemini",
|
|
156
|
+
commands: ["gemini"],
|
|
157
|
+
confidence: "conservative",
|
|
158
|
+
states: {
|
|
159
|
+
// conservative — gemini-cli needs a Google account/API key to reach a
|
|
160
|
+
// working state, so no live capture was taken. High-precision only.
|
|
161
|
+
blocked: {
|
|
162
|
+
any: [
|
|
163
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
164
|
+
{ region: "bottom", contains: "Apply this change", caseInsensitive: true },
|
|
165
|
+
{ region: "bottom", contains: "Allow execution", caseInsensitive: true },
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
working: {
|
|
169
|
+
any: [
|
|
170
|
+
// gemini-cli shows an "(esc to cancel)" hint during a turn.
|
|
171
|
+
{ region: "bottom", contains: "esc to cancel", caseInsensitive: true },
|
|
172
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
173
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
174
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const AIDER: AgentManifest = {
|
|
181
|
+
id: "aider",
|
|
182
|
+
commands: ["aider"],
|
|
183
|
+
confidence: "tuned",
|
|
184
|
+
states: {
|
|
185
|
+
// TUNED from aider's installed source (v0.86.2). Every confirmation renders
|
|
186
|
+
// through `io.confirm_ask` (io.py), which appends the literal option string
|
|
187
|
+
// " (Y)es/(N)o" (plus "/(A)ll/(S)kip all" or "/(D)on't ask again") and a
|
|
188
|
+
// "[Yes]:"/"[No]:" default — so "(Y)es/(N)o" is aider's exact, universal
|
|
189
|
+
// blocked marker. The specific questions below are verbatim from
|
|
190
|
+
// base_coder.py / commands.py.
|
|
191
|
+
blocked: {
|
|
192
|
+
any: [
|
|
193
|
+
{ region: "bottom", contains: "(Y)es/(N)o", caseInsensitive: true },
|
|
194
|
+
{ region: "bottom", contains: "Add file to the chat", caseInsensitive: true },
|
|
195
|
+
{ region: "bottom", contains: "Allow edits to file", caseInsensitive: true },
|
|
196
|
+
{ region: "bottom", contains: "Add command output to the chat", caseInsensitive: true },
|
|
197
|
+
{ region: "bottom", contains: "Run pip install", caseInsensitive: true },
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
// TUNED: while a turn runs aider shows a `WaitingSpinner` (waiting.py)
|
|
201
|
+
// rendered as "[░█ ] Waiting for <model>" — the text is literally
|
|
202
|
+
// "Waiting for LLM" or "Waiting for " + the model name (base_coder.py:1440).
|
|
203
|
+
// aider's spinner uses a "░█" scanner, NOT braille, so "Waiting for " is the
|
|
204
|
+
// real invariant; the braille probe is kept only as a harmless fallback.
|
|
205
|
+
working: {
|
|
206
|
+
any: [
|
|
207
|
+
{ region: "bottom", contains: "Waiting for ", caseInsensitive: false },
|
|
208
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const COPILOT: AgentManifest = {
|
|
215
|
+
id: "copilot",
|
|
216
|
+
commands: ["copilot", "github-copilot", "github-copilot-cli"],
|
|
217
|
+
confidence: "conservative",
|
|
218
|
+
states: {
|
|
219
|
+
// conservative — github-copilot-cli needs a GitHub account, so no live
|
|
220
|
+
// capture was taken. High-precision only.
|
|
221
|
+
blocked: {
|
|
222
|
+
any: [
|
|
223
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
224
|
+
{ region: "bottom", contains: "Select an option", caseInsensitive: true },
|
|
225
|
+
{ region: "bottom", contains: "Allow", caseInsensitive: false },
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
working: {
|
|
229
|
+
any: [
|
|
230
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
231
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
232
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const CURSOR: AgentManifest = {
|
|
239
|
+
id: "cursor",
|
|
240
|
+
commands: ["cursor-agent", "cursor"],
|
|
241
|
+
confidence: "conservative",
|
|
242
|
+
states: {
|
|
243
|
+
// conservative — cursor-agent (Cursor CLI) was launched live but sits on a
|
|
244
|
+
// "Press any key to log in…" pre-auth screen without an account, so no
|
|
245
|
+
// working/blocked turn could be captured. The pre-auth splash ("Cursor
|
|
246
|
+
// Agent" / "Press any key to log in") is idle chrome and deliberately NOT
|
|
247
|
+
// matched here. Markers below are high-precision guesses from public
|
|
248
|
+
// knowledge of its approval/streaming UI. NOTE: cursor-agent runs under
|
|
249
|
+
// `node`, so it resolves via the process-tree (argv0 basename), not the
|
|
250
|
+
// pane's `current_command`.
|
|
251
|
+
blocked: {
|
|
252
|
+
any: [
|
|
253
|
+
{ region: "bottom", contains: "Do you want", caseInsensitive: false },
|
|
254
|
+
{ region: "bottom", contains: "Run this command", caseInsensitive: true },
|
|
255
|
+
{ region: "bottom", contains: "Apply this edit", caseInsensitive: true },
|
|
256
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
working: {
|
|
260
|
+
any: [
|
|
261
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
262
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
263
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
264
|
+
],
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const GOOSE: AgentManifest = {
|
|
270
|
+
id: "goose",
|
|
271
|
+
commands: ["goose"],
|
|
272
|
+
confidence: "conservative",
|
|
273
|
+
states: {
|
|
274
|
+
// conservative — Block's goose CLI needs a configured provider, so no live
|
|
275
|
+
// capture was taken. High-precision only; markers are best-effort from
|
|
276
|
+
// public knowledge of its confirmation/streaming UI.
|
|
277
|
+
blocked: {
|
|
278
|
+
any: [
|
|
279
|
+
{ region: "bottom", contains: "Do you want", caseInsensitive: false },
|
|
280
|
+
{ region: "bottom", contains: "Allow this tool", caseInsensitive: true },
|
|
281
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
282
|
+
{ region: "bottom", contains: "[y/n]", caseInsensitive: true },
|
|
283
|
+
],
|
|
284
|
+
},
|
|
285
|
+
working: {
|
|
286
|
+
any: [
|
|
287
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
288
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
289
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
290
|
+
],
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const AMP: AgentManifest = {
|
|
296
|
+
id: "amp",
|
|
297
|
+
commands: ["amp"],
|
|
298
|
+
confidence: "conservative",
|
|
299
|
+
states: {
|
|
300
|
+
// conservative — Sourcegraph's amp CLI needs an account, so no live capture
|
|
301
|
+
// was taken. High-precision only; markers are best-effort from public
|
|
302
|
+
// knowledge of its approval/streaming UI.
|
|
303
|
+
blocked: {
|
|
304
|
+
any: [
|
|
305
|
+
{ region: "bottom", contains: "Do you want", caseInsensitive: false },
|
|
306
|
+
{ region: "bottom", contains: "Allow", caseInsensitive: false },
|
|
307
|
+
{ region: "bottom", contains: "(y/n)", caseInsensitive: true },
|
|
308
|
+
],
|
|
309
|
+
},
|
|
310
|
+
working: {
|
|
311
|
+
any: [
|
|
312
|
+
{ region: "bottom", contains: "esc to interrupt", caseInsensitive: true },
|
|
313
|
+
{ region: "bottom", regex: BRAILLE_SPINNER },
|
|
314
|
+
{ region: "title", regex: BRAILLE_SPINNER },
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
const SHELL: AgentManifest = {
|
|
321
|
+
id: "shell",
|
|
322
|
+
commands: ["bash", "zsh", "sh", "fish", "nu"],
|
|
323
|
+
confidence: "conservative",
|
|
324
|
+
states: {
|
|
325
|
+
// Catch-all: a raw shell is almost always idle. We only flag an explicit
|
|
326
|
+
// interactive confirmation as blocked; "working" is unreliable to read
|
|
327
|
+
// from a shell snapshot, so it stays absent (idle by default).
|
|
328
|
+
blocked: {
|
|
329
|
+
any: [
|
|
330
|
+
{ region: "bottom", contains: "[y/n]", caseInsensitive: true },
|
|
331
|
+
{ region: "bottom", contains: "(yes/no)", caseInsensitive: true },
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* All bundled manifests, in preference order for `pickManifest` and for the
|
|
339
|
+
* tree-walk priority in `resolveAgentCommand` (earlier = higher priority). The
|
|
340
|
+
* `shell` catch-all is intentionally LAST so a real agent that has spawned a
|
|
341
|
+
* transient shell child still resolves to the agent.
|
|
342
|
+
*/
|
|
343
|
+
export const BUNDLED_MANIFESTS: AgentManifest[] = [
|
|
344
|
+
CLAUDE,
|
|
345
|
+
CODEX,
|
|
346
|
+
OPENCODE,
|
|
347
|
+
GEMINI,
|
|
348
|
+
AIDER,
|
|
349
|
+
COPILOT,
|
|
350
|
+
CURSOR,
|
|
351
|
+
GOOSE,
|
|
352
|
+
AMP,
|
|
353
|
+
SHELL,
|
|
354
|
+
];
|