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.
Files changed (154) hide show
  1. package/README.md +36 -14
  2. package/bin/cli.js +4174 -1235
  3. package/bin/cli.ts +426 -72
  4. package/package.json +10 -6
  5. package/packages/contracts/src/__tests__/control.test.ts +154 -0
  6. package/packages/contracts/src/control.ts +217 -0
  7. package/packages/contracts/src/index.ts +1 -0
  8. package/packages/daemon/dist/agent-explain.d.ts +8 -1
  9. package/packages/daemon/dist/agent-explain.js +19 -3
  10. package/packages/daemon/dist/control/client.d.ts +23 -0
  11. package/packages/daemon/dist/control/client.js +105 -0
  12. package/packages/daemon/dist/control/dispatch.d.ts +34 -0
  13. package/packages/daemon/dist/control/dispatch.js +83 -0
  14. package/packages/daemon/dist/control/fanout.d.ts +19 -0
  15. package/packages/daemon/dist/control/fanout.js +37 -0
  16. package/packages/daemon/dist/control/frames.d.ts +23 -0
  17. package/packages/daemon/dist/control/frames.js +37 -0
  18. package/packages/daemon/dist/control/lifecycle.d.ts +45 -0
  19. package/packages/daemon/dist/control/lifecycle.js +114 -0
  20. package/packages/daemon/dist/control/server.d.ts +16 -0
  21. package/packages/daemon/dist/control/server.js +214 -0
  22. package/packages/daemon/dist/control/verbs.d.ts +11 -0
  23. package/packages/daemon/dist/control/verbs.js +91 -0
  24. package/packages/daemon/dist/doctor.d.ts +18 -0
  25. package/packages/daemon/dist/doctor.js +105 -15
  26. package/packages/daemon/dist/lib/agent-discovery.d.ts +27 -2
  27. package/packages/daemon/dist/lib/agent-discovery.js +29 -14
  28. package/packages/daemon/dist/lib/app-config.d.ts +106 -0
  29. package/packages/daemon/dist/lib/app-config.js +104 -5
  30. package/packages/daemon/dist/lib/manifest-pack.d.ts +79 -0
  31. package/packages/daemon/dist/lib/manifest-pack.js +232 -0
  32. package/packages/daemon/dist/lib/state-home.d.ts +2 -0
  33. package/packages/daemon/dist/lib/state-home.js +12 -0
  34. package/packages/daemon/dist/lib/tui-binary.d.ts +57 -0
  35. package/packages/daemon/dist/lib/tui-binary.js +130 -0
  36. package/packages/daemon/dist/lib/update-check.js +5 -0
  37. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +34 -0
  38. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
  39. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/PkgInfo +1 -0
  40. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/AppIcon.icns +0 -0
  41. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
  42. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +139 -0
  43. package/packages/daemon/dist/restore.d.ts +35 -8
  44. package/packages/daemon/dist/restore.js +52 -15
  45. package/packages/daemon/dist/send.d.ts +33 -1
  46. package/packages/daemon/dist/send.js +32 -19
  47. package/packages/daemon/dist/widgets/explorer/breadcrumbs.d.ts +1 -1
  48. package/packages/daemon/dist/widgets/explorer/footer.d.ts +1 -1
  49. package/packages/daemon/dist/widgets/explorer/tree.d.ts +1 -1
  50. package/packages/daemon/dist/widgets/lib/help-overlay.d.ts +1 -1
  51. package/packages/daemon/dist/widgets/setup/agent-naming.d.ts +1 -1
  52. package/packages/daemon/dist/widgets/setup/config-tree.d.ts +1 -1
  53. package/packages/daemon/dist/widgets/setup/detect-panel.d.ts +1 -1
  54. package/packages/daemon/dist/widgets/setup/field-editor.d.ts +1 -1
  55. package/packages/daemon/dist/widgets/setup/footer.d.ts +1 -1
  56. package/packages/daemon/dist/widgets/setup/layout-picker.d.ts +1 -1
  57. package/packages/daemon/src/agent-explain.ts +34 -6
  58. package/packages/daemon/src/control/client.ts +128 -0
  59. package/packages/daemon/src/control/dispatch.ts +107 -0
  60. package/packages/daemon/src/control/fanout.ts +44 -0
  61. package/packages/daemon/src/control/frames.ts +40 -0
  62. package/packages/daemon/src/control/lifecycle.ts +151 -0
  63. package/packages/daemon/src/control/server.ts +237 -0
  64. package/packages/daemon/src/control/verbs.ts +118 -0
  65. package/packages/daemon/src/doctor.ts +113 -28
  66. package/packages/daemon/src/lib/agent-discovery.ts +53 -13
  67. package/packages/daemon/src/lib/app-config.ts +194 -5
  68. package/packages/daemon/src/lib/manifest-pack.ts +255 -0
  69. package/packages/daemon/src/lib/state-home.ts +13 -0
  70. package/packages/daemon/src/lib/tui-binary.ts +165 -0
  71. package/packages/daemon/src/lib/update-check.ts +5 -0
  72. package/packages/daemon/src/restore.ts +53 -15
  73. package/packages/daemon/src/send.ts +55 -21
  74. package/packages/daemon/src/tui/chrome/events.ts +4 -4
  75. package/packages/daemon/src/tui/chrome/front-door.ts +39 -0
  76. package/packages/daemon/src/tui/chrome/notify-prefs.ts +58 -0
  77. package/packages/daemon/src/tui/chrome/notify-state.ts +76 -0
  78. package/packages/daemon/src/tui/chrome/notify.ts +737 -52
  79. package/packages/daemon/src/tui/chrome/updater.ts +318 -30
  80. package/packages/daemon/src/tui/compiled.ts +11 -3
  81. package/packages/daemon/src/tui/detect/classify.ts +49 -0
  82. package/packages/daemon/src/tui/detect/manifest-loader.ts +56 -6
  83. package/packages/daemon/src/tui/detect/manifest.ts +39 -3
  84. package/packages/daemon/src/tui/detect/manifests.ts +395 -33
  85. package/packages/daemon/src/tui/detect/process-tree.ts +33 -3
  86. package/packages/daemon/src/tui/detect/session-id.ts +503 -0
  87. package/packages/daemon/src/tui/integrations/opencode.ts +121 -0
  88. package/packages/daemon/src/tui/main.ts +13 -1
  89. package/packages/daemon/src/tui/mirror/ack-writer.ts +77 -0
  90. package/packages/daemon/src/tui/mirror/agent-chip.ts +126 -0
  91. package/packages/daemon/src/tui/mirror/agent-lifecycle.ts +437 -0
  92. package/packages/daemon/src/tui/mirror/agent-rows.ts +155 -0
  93. package/packages/daemon/src/tui/mirror/app-state.ts +342 -0
  94. package/packages/daemon/src/tui/mirror/app.tsx +7048 -0
  95. package/packages/daemon/src/tui/mirror/attention.ts +110 -0
  96. package/packages/daemon/src/tui/mirror/blit.ts +186 -0
  97. package/packages/daemon/src/tui/mirror/control-client.ts +80 -9
  98. package/packages/daemon/src/tui/mirror/dialog-model.ts +298 -0
  99. package/packages/daemon/src/tui/mirror/dialog-stack.ts +367 -0
  100. package/packages/daemon/src/tui/mirror/diff-model.ts +387 -0
  101. package/packages/daemon/src/tui/mirror/editor-buffer.ts +117 -0
  102. package/packages/daemon/src/tui/mirror/file-tree.ts +322 -0
  103. package/packages/daemon/src/tui/mirror/focus-border.ts +57 -0
  104. package/packages/daemon/src/tui/mirror/folder-picker.ts +124 -0
  105. package/packages/daemon/src/tui/mirror/home-model.ts +174 -0
  106. package/packages/daemon/src/tui/mirror/host-terminal.ts +49 -0
  107. package/packages/daemon/src/tui/mirror/hosted.ts +205 -0
  108. package/packages/daemon/src/tui/mirror/input-coalescer.ts +105 -0
  109. package/packages/daemon/src/tui/mirror/layout-parse.ts +154 -0
  110. package/packages/daemon/src/tui/mirror/menu-model.ts +210 -0
  111. package/packages/daemon/src/tui/mirror/palette.ts +564 -0
  112. package/packages/daemon/src/tui/mirror/pane-mirror.ts +578 -20
  113. package/packages/daemon/src/tui/mirror/pane-surface.tsx +422 -0
  114. package/packages/daemon/src/tui/mirror/perf-tap.ts +186 -0
  115. package/packages/daemon/src/tui/mirror/resize-model.ts +85 -0
  116. package/packages/daemon/src/tui/mirror/scrollbar-model.ts +88 -0
  117. package/packages/daemon/src/tui/mirror/search-model.ts +70 -0
  118. package/packages/daemon/src/tui/mirror/selection.ts +376 -0
  119. package/packages/daemon/src/tui/mirror/session-mirror.ts +724 -0
  120. package/packages/daemon/src/tui/mirror/settings-model.ts +425 -0
  121. package/packages/daemon/src/tui/mirror/sidebar.tsx +218 -0
  122. package/packages/daemon/src/tui/mirror/size-truth.ts +130 -0
  123. package/packages/daemon/src/tui/mirror/spans.ts +46 -0
  124. package/packages/daemon/src/tui/mirror/status-grammar.ts +32 -0
  125. package/packages/daemon/src/tui/mirror/theme.ts +45 -0
  126. package/packages/daemon/src/tui/team/entry.ts +34 -7
  127. package/packages/daemon/src/tui/team/fuzzy.ts +20 -0
  128. package/packages/daemon/src/tui/team/report.ts +11 -1
  129. package/packages/daemon/src/tui/team/sessions.ts +184 -17
  130. package/packages/daemon/src/tui/team/wait.ts +144 -0
  131. package/scripts/build-macos-notifier.mjs +160 -0
  132. package/scripts/build-tui.mjs +11 -4
  133. package/scripts/perf-mirror.mjs +313 -0
  134. package/scripts/postinstall.js +8 -1
  135. package/scripts/prepublish-check.mjs +37 -1
  136. package/scripts/publish-tap.sh +55 -0
  137. package/skill/SKILL.md +110 -2
  138. package/templates/AGENTS.md +14 -7
  139. package/templates/agent-team-monorepo.yml +8 -0
  140. package/templates/agent-team-nextjs.yml +8 -0
  141. package/templates/agent-team.yml +10 -0
  142. package/templates/convex.yml +2 -0
  143. package/templates/default.yml +11 -5
  144. package/templates/go.yml +4 -0
  145. package/templates/missions.yml +6 -0
  146. package/templates/nextjs.yml +4 -0
  147. package/templates/python.yml +4 -0
  148. package/templates/skills/backend.md +5 -12
  149. package/templates/skills/frontend.md +5 -12
  150. package/templates/skills/general-worker.md +5 -12
  151. package/templates/skills/researcher.md +7 -12
  152. package/templates/skills/reviewer.md +7 -16
  153. package/templates/vite.yml +4 -0
  154. package/packages/daemon/src/tui/mirror/viewer.tsx +0 -166
@@ -30,9 +30,20 @@ import { join } from "node:path";
30
30
  import type { AgentManifest, Rule, StateRules } from "./manifest.ts";
31
31
  import { BUNDLED_MANIFESTS } from "./manifests.ts";
32
32
 
33
- /** Directory scanned for user override manifests. */
33
+ /** Directory scanned for user override manifests. Honors `TMUX_IDE_HOME` (the
34
+ * state-home override every other `~/.tmux-ide` consumer respects) so tests
35
+ * never read a real user's overrides. */
34
36
  export function overrideDir(): string {
35
- return join(homedir(), ".tmux-ide", "agent-detection");
37
+ const home = process.env.TMUX_IDE_HOME ?? join(homedir(), ".tmux-ide");
38
+ return join(home, "agent-detection");
39
+ }
40
+
41
+ /** The fetched manifest-pack file (`tmux-ide update --manifests` installs it;
42
+ * see `lib/manifest-pack.ts` for the format + publish contract). Lives in a
43
+ * SUBDIRECTORY so {@link readOverrideManifests}'s top-level `*.json` scan
44
+ * never sees it — that is what keeps user files ABOVE the pack. */
45
+ export function packFile(dir = overrideDir()): string {
46
+ return join(dir, "pack", "manifest-pack.json");
36
47
  }
37
48
 
38
49
  /**
@@ -156,7 +167,8 @@ function normalizeStates(m: AgentManifest): AgentManifest {
156
167
  if (m.states.blocked) states.blocked = m.states.blocked;
157
168
  if (m.states.working) states.working = m.states.working;
158
169
  if (m.states.done) states.done = m.states.done;
159
- return { id: m.id, commands: m.commands, states };
170
+ const confidence = m.confidence === "tuned" ? "tuned" : "conservative";
171
+ return { id: m.id, commands: m.commands, states, confidence };
160
172
  }
161
173
 
162
174
  function warnOnce(path: string, reason: string): void {
@@ -166,11 +178,49 @@ function warnOnce(path: string, reason: string): void {
166
178
  }
167
179
 
168
180
  /**
169
- * Load the full manifest set: bundled + user overrides, merged. Does io on
170
- * every call — prefer {@link getManifests} for hot paths.
181
+ * Read the installed manifest PACK (fetched by `tmux-ide update --manifests`),
182
+ * or `[]` when none is installed. Thin io — a malformed pack file is skipped
183
+ * with a one-time stderr warning, never a throw (the pack was schema-validated
184
+ * at install time; this guards a hand-edited/corrupted file). Each entry gets
185
+ * the same structural validation + normalization as a user override file.
186
+ */
187
+ export function readPackManifests(dir = overrideDir()): AgentManifest[] {
188
+ const path = packFile(dir);
189
+ let raw: string;
190
+ try {
191
+ raw = readFileSync(path, "utf8");
192
+ } catch {
193
+ // No pack installed (the common case).
194
+ return [];
195
+ }
196
+ try {
197
+ const parsed: unknown = JSON.parse(raw);
198
+ const manifests = (parsed as { manifests?: unknown })?.manifests;
199
+ if (!Array.isArray(manifests)) {
200
+ warnOnce(path, "not a manifest pack (missing manifests[])");
201
+ return [];
202
+ }
203
+ const valid: AgentManifest[] = [];
204
+ for (const entry of manifests) {
205
+ if (validateManifestShape(entry)) valid.push(normalizeStates(entry));
206
+ else warnOnce(path, "pack entry is not a valid AgentManifest — entry skipped");
207
+ }
208
+ return valid;
209
+ } catch (err) {
210
+ warnOnce(path, err instanceof Error ? err.message : String(err));
211
+ return [];
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Load the full manifest set: bundled + pack + user overrides, merged in that
217
+ * PRECEDENCE order — a fetched pack refines the bundled set, and a user's own
218
+ * `agent-detection/*.json` file always beats both (it is merged LAST). Does io
219
+ * on every call — prefer {@link getManifests} for hot paths.
171
220
  */
172
221
  export function loadManifests(): AgentManifest[] {
173
- return mergeManifests(BUNDLED_MANIFESTS, readOverrideManifests());
222
+ const withPack = mergeManifests(BUNDLED_MANIFESTS, readPackManifests());
223
+ return mergeManifests(withPack, readOverrideManifests());
174
224
  }
175
225
 
176
226
  /** Process-lifetime cache for the merged manifest set. */
@@ -40,6 +40,15 @@ export interface StateRules {
40
40
  done?: Rule;
41
41
  }
42
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
+
43
52
  /** A detection manifest for one or more pane commands. */
44
53
  export interface AgentManifest {
45
54
  /** Stable manifest id. */
@@ -48,6 +57,12 @@ export interface AgentManifest {
48
57
  commands: string[];
49
58
  /** Evidence rules per detectable state. */
50
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;
51
66
  }
52
67
 
53
68
  /** The states a manifest can positively detect (idle is inferred elsewhere). */
@@ -160,10 +175,31 @@ export function explain(
160
175
  return { state: winner ? winner.state : null, checked };
161
176
  }
162
177
 
178
+ /** Escape a string for literal use inside a RegExp source. */
179
+ function escapeRegex(s: string): string {
180
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
181
+ }
182
+
183
+ /**
184
+ * Does `haystack` contain `needle` as a WHOLE alphanumeric segment (delimited
185
+ * by start/end or any non-alphanumeric)? `grok-build` contains the segment
186
+ * `grok`; `pip` does NOT contain the segment `pi`. This is what keeps short
187
+ * manifest command tokens (pi, devin, kimi — M25.4) from substring-matching
188
+ * unrelated commands (`pip`, `vi`, `api-server`).
189
+ */
190
+ function containsSegment(haystack: string, needle: string): boolean {
191
+ if (!haystack.includes(needle)) return false;
192
+ return new RegExp(`(^|[^a-z0-9])${escapeRegex(needle)}([^a-z0-9]|$)`).test(haystack);
193
+ }
194
+
163
195
  /**
164
196
  * Pick the manifest whose `commands` best match a pane's current command.
165
- * Prefers an exact (case-insensitive) match, then a substring match in either
166
- * direction. Returns undefined when nothing applies.
197
+ * Prefers an exact (case-insensitive) match, then a SEGMENT match in either
198
+ * direction — the command contains a manifest token as a whole segment
199
+ * (`grok-build` → `grok`, `.kilo` → `kilo`) or vice versa (`cursor` →
200
+ * `cursor-agent`). Raw substring matching was retired in M25.4: it made every
201
+ * short token a false-positive machine (`pi` ⊂ `pip`, `vi` ⊂ `devin`).
202
+ * Returns undefined when nothing applies.
167
203
  */
168
204
  export function pickManifest(
169
205
  command: string,
@@ -178,7 +214,7 @@ export function pickManifest(
178
214
  return manifests.find((m) =>
179
215
  m.commands.some((c) => {
180
216
  const name = c.toLowerCase();
181
- return cmd.includes(name) || name.includes(cmd);
217
+ return containsSegment(cmd, name) || containsSegment(name, cmd);
182
218
  }),
183
219
  );
184
220
  }