tmux-ide 2.6.0 → 2.6.1

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 (209) hide show
  1. package/bin/cli.js +13 -5
  2. package/bin/cli.ts +1 -1
  3. package/bunfig.toml +4 -0
  4. package/package.json +11 -3
  5. package/packages/contracts/package.json +22 -0
  6. package/packages/contracts/src/__tests__/ide-config.test.ts +46 -0
  7. package/packages/contracts/src/__tests__/terminals.test.ts +87 -0
  8. package/packages/contracts/src/actions-contract.ts +310 -0
  9. package/packages/contracts/src/actions-errors.ts +41 -0
  10. package/packages/contracts/src/domain.ts +36 -0
  11. package/packages/contracts/src/ide-config.ts +170 -0
  12. package/packages/contracts/src/index.ts +24 -0
  13. package/packages/contracts/src/lib-internal/auth.ts +13 -0
  14. package/packages/contracts/src/lib-internal/hq.ts +38 -0
  15. package/packages/contracts/src/terminals.ts +116 -0
  16. package/packages/contracts/src/tmux.ts +60 -0
  17. package/packages/contracts/src/workspace.ts +67 -0
  18. package/packages/daemon/src/agent-explain.ts +270 -0
  19. package/packages/daemon/src/attach.ts +20 -0
  20. package/packages/daemon/src/bin.ts +4 -0
  21. package/packages/daemon/src/canonical.ts +7 -0
  22. package/packages/daemon/src/cli.ts +499 -0
  23. package/packages/daemon/src/command-center/actions/contract.ts +2 -0
  24. package/packages/daemon/src/command-center/actions/dispatcher.ts +137 -0
  25. package/packages/daemon/src/command-center/actions/errors.ts +105 -0
  26. package/packages/daemon/src/command-center/actions/handlers/_project-context.ts +30 -0
  27. package/packages/daemon/src/command-center/actions/handlers/_resolve-project.ts +78 -0
  28. package/packages/daemon/src/command-center/actions/handlers/app-set-remote-access.ts +118 -0
  29. package/packages/daemon/src/command-center/actions/handlers/config-actions.ts +113 -0
  30. package/packages/daemon/src/command-center/actions/handlers/daemon-shutdown.ts +38 -0
  31. package/packages/daemon/src/command-center/actions/handlers/project-activate.ts +30 -0
  32. package/packages/daemon/src/command-center/actions/handlers/project-launch.ts +70 -0
  33. package/packages/daemon/src/command-center/actions/handlers/project-open-terminal.ts +87 -0
  34. package/packages/daemon/src/command-center/actions/handlers/project-restart.ts +38 -0
  35. package/packages/daemon/src/command-center/actions/handlers/project-stop.ts +62 -0
  36. package/packages/daemon/src/command-center/actions/handlers/terminal-respawn.ts +119 -0
  37. package/packages/daemon/src/command-center/actions/handlers/terminal-stop.ts +35 -0
  38. package/packages/daemon/src/command-center/actions/registry.ts +149 -0
  39. package/packages/daemon/src/command-center/discovery.ts +96 -0
  40. package/packages/daemon/src/command-center/index.ts +31 -0
  41. package/packages/daemon/src/command-center/schemas.ts +85 -0
  42. package/packages/daemon/src/command-center/server.ts +1260 -0
  43. package/packages/daemon/src/command-center/ws-events.ts +316 -0
  44. package/packages/daemon/src/config.ts +549 -0
  45. package/packages/daemon/src/detect.ts +248 -0
  46. package/packages/daemon/src/doctor.ts +242 -0
  47. package/packages/daemon/src/embed.ts +5 -0
  48. package/packages/daemon/src/index.ts +12 -0
  49. package/packages/daemon/src/init.ts +211 -0
  50. package/packages/daemon/src/inspect.ts +178 -0
  51. package/packages/daemon/src/js-yaml.d.ts +10 -0
  52. package/packages/daemon/src/launch.ts +349 -0
  53. package/packages/daemon/src/lib/active-projects.ts +49 -0
  54. package/packages/daemon/src/lib/agent-discovery.ts +121 -0
  55. package/packages/daemon/src/lib/app-config.ts +336 -0
  56. package/packages/daemon/src/lib/app-settings.ts +53 -0
  57. package/packages/daemon/src/lib/auth/auth-service.ts +227 -0
  58. package/packages/daemon/src/lib/auth/middleware.ts +56 -0
  59. package/packages/daemon/src/lib/auth/types.ts +2 -0
  60. package/packages/daemon/src/lib/auth-token.ts +5 -0
  61. package/packages/daemon/src/lib/authorship.ts +280 -0
  62. package/packages/daemon/src/lib/canonical-daemon.ts +122 -0
  63. package/packages/daemon/src/lib/cli-action-bridge.ts +216 -0
  64. package/packages/daemon/src/lib/daemon-embed.ts +782 -0
  65. package/packages/daemon/src/lib/daemon-watchdog.ts +111 -0
  66. package/packages/daemon/src/lib/daemon.ts +79 -0
  67. package/packages/daemon/src/lib/dot-path.ts +17 -0
  68. package/packages/daemon/src/lib/errors.ts +67 -0
  69. package/packages/daemon/src/lib/filesystem-browser.ts +292 -0
  70. package/packages/daemon/src/lib/launch-plan.ts +90 -0
  71. package/packages/daemon/src/lib/log.ts +134 -0
  72. package/packages/daemon/src/lib/output.ts +76 -0
  73. package/packages/daemon/src/lib/project-init-runner.ts +150 -0
  74. package/packages/daemon/src/lib/project-inspect.ts +80 -0
  75. package/packages/daemon/src/lib/project-onboard.ts +149 -0
  76. package/packages/daemon/src/lib/project-probe.ts +92 -0
  77. package/packages/daemon/src/lib/project-registry.ts +296 -0
  78. package/packages/daemon/src/lib/session-monitor.ts +122 -0
  79. package/packages/daemon/src/lib/session-options.ts +100 -0
  80. package/packages/daemon/src/lib/shell.ts +8 -0
  81. package/packages/daemon/src/lib/sizes.ts +36 -0
  82. package/packages/daemon/src/lib/skill-sync.ts +155 -0
  83. package/packages/daemon/src/lib/slugify.ts +10 -0
  84. package/packages/daemon/src/lib/terminals-store.ts +125 -0
  85. package/packages/daemon/src/lib/update-check.ts +298 -0
  86. package/packages/daemon/src/lib/update.ts +158 -0
  87. package/packages/daemon/src/lib/workspace-registry.ts +229 -0
  88. package/packages/daemon/src/lib/worktree.ts +289 -0
  89. package/packages/daemon/src/lib/yaml-io.ts +27 -0
  90. package/packages/daemon/src/ls.ts +40 -0
  91. package/packages/daemon/src/restart.ts +24 -0
  92. package/packages/daemon/src/restore.ts +514 -0
  93. package/packages/daemon/src/schemas/domain.ts +2 -0
  94. package/packages/daemon/src/schemas/filesystem.ts +34 -0
  95. package/packages/daemon/src/schemas/ide-config.ts +2 -0
  96. package/packages/daemon/src/schemas/index.ts +59 -0
  97. package/packages/daemon/src/schemas/inspect.ts +67 -0
  98. package/packages/daemon/src/schemas/registry.ts +55 -0
  99. package/packages/daemon/src/schemas/ws-events.ts +135 -0
  100. package/packages/daemon/src/send.ts +171 -0
  101. package/packages/daemon/src/server/README.md +15 -0
  102. package/packages/daemon/src/server/index.ts +74 -0
  103. package/packages/daemon/src/server/pty-bridge.ts +532 -0
  104. package/packages/daemon/src/server/standalone.ts +18 -0
  105. package/packages/daemon/src/server/ws-route.ts +483 -0
  106. package/packages/daemon/src/status.ts +59 -0
  107. package/packages/daemon/src/stop.ts +28 -0
  108. package/packages/daemon/src/terminal/NodePtyAdapter.ts +271 -0
  109. package/packages/daemon/src/terminal/PtyAdapter.ts +140 -0
  110. package/packages/daemon/src/terminal/README.md +92 -0
  111. package/packages/daemon/src/tui/chrome/cheatsheet.ts +260 -0
  112. package/packages/daemon/src/tui/chrome/chip.ts +30 -0
  113. package/packages/daemon/src/tui/chrome/events.ts +119 -0
  114. package/packages/daemon/src/tui/chrome/kitty-keys.ts +55 -0
  115. package/packages/daemon/src/tui/chrome/menu.ts +289 -0
  116. package/packages/daemon/src/tui/chrome/notify.ts +195 -0
  117. package/packages/daemon/src/tui/chrome/panels.ts +111 -0
  118. package/packages/daemon/src/tui/chrome/sidebar.ts +222 -0
  119. package/packages/daemon/src/tui/chrome/snapshot.ts +425 -0
  120. package/packages/daemon/src/tui/chrome/statusline.ts +595 -0
  121. package/packages/daemon/src/tui/chrome/updater.ts +428 -0
  122. package/packages/daemon/src/tui/chrome/welcome.ts +124 -0
  123. package/packages/daemon/src/tui/compiled.ts +113 -0
  124. package/packages/daemon/src/tui/detect/classify.ts +193 -0
  125. package/packages/daemon/src/tui/detect/manifest-loader.ts +192 -0
  126. package/packages/daemon/src/tui/detect/manifest.ts +184 -0
  127. package/packages/daemon/src/tui/detect/manifests.ts +226 -0
  128. package/packages/daemon/src/tui/detect/process-tree.ts +197 -0
  129. package/packages/daemon/src/tui/detect/snapshot.ts +70 -0
  130. package/packages/daemon/src/tui/integrations/claude.ts +176 -0
  131. package/packages/daemon/src/tui/integrations/offer.ts +145 -0
  132. package/packages/daemon/src/tui/main.ts +70 -0
  133. package/packages/daemon/src/tui/mirror/control-client.ts +143 -0
  134. package/packages/daemon/src/tui/mirror/control.ts +97 -0
  135. package/packages/daemon/src/tui/mirror/pane-mirror.ts +81 -0
  136. package/packages/daemon/src/tui/mirror/viewer.tsx +166 -0
  137. package/packages/daemon/src/tui/team/CONTROL.md +50 -0
  138. package/packages/daemon/src/tui/team/entry.ts +11 -0
  139. package/packages/daemon/src/tui/team/fuzzy.ts +133 -0
  140. package/packages/daemon/src/tui/team/home.ts +170 -0
  141. package/packages/daemon/src/tui/team/index.tsx +1521 -0
  142. package/packages/daemon/src/tui/team/input.ts +34 -0
  143. package/packages/daemon/src/tui/team/keymap.ts +127 -0
  144. package/packages/daemon/src/tui/team/mouse.ts +29 -0
  145. package/packages/daemon/src/tui/team/nav.ts +31 -0
  146. package/packages/daemon/src/tui/team/preview.ts +34 -0
  147. package/packages/daemon/src/tui/team/projects.ts +191 -0
  148. package/packages/daemon/src/tui/team/report.ts +73 -0
  149. package/packages/daemon/src/tui/team/sessions.ts +344 -0
  150. package/packages/daemon/src/tui/team/tree.ts +62 -0
  151. package/packages/daemon/src/types.ts +13 -0
  152. package/packages/daemon/src/ui/index.ts +32 -0
  153. package/packages/daemon/src/ui/terminal/index.ts +9 -0
  154. package/packages/daemon/src/ui/types.ts +91 -0
  155. package/packages/daemon/src/ui/web/base.css +80 -0
  156. package/packages/daemon/src/ui/web/components/Box.tsx +59 -0
  157. package/packages/daemon/src/ui/web/components/Input.tsx +32 -0
  158. package/packages/daemon/src/ui/web/components/ScrollBox.tsx +60 -0
  159. package/packages/daemon/src/ui/web/components/Text.tsx +28 -0
  160. package/packages/daemon/src/ui/web/hooks.ts +106 -0
  161. package/packages/daemon/src/ui/web/index.ts +27 -0
  162. package/packages/daemon/src/ui/web/render.ts +77 -0
  163. package/packages/daemon/src/ui/web/utils/color.ts +27 -0
  164. package/packages/daemon/src/validate.ts +217 -0
  165. package/packages/daemon/src/widgets/changes/README.md +3 -0
  166. package/packages/daemon/src/widgets/changes/index.tsx +691 -0
  167. package/packages/daemon/src/widgets/config/README.md +3 -0
  168. package/packages/daemon/src/widgets/config/index.tsx +481 -0
  169. package/packages/daemon/src/widgets/explorer/README.md +3 -0
  170. package/packages/daemon/src/widgets/explorer/breadcrumbs.tsx +77 -0
  171. package/packages/daemon/src/widgets/explorer/footer.tsx +20 -0
  172. package/packages/daemon/src/widgets/explorer/header.tsx +23 -0
  173. package/packages/daemon/src/widgets/explorer/index.tsx +456 -0
  174. package/packages/daemon/src/widgets/explorer/tree-model.ts +103 -0
  175. package/packages/daemon/src/widgets/explorer/tree.tsx +165 -0
  176. package/packages/daemon/src/widgets/lib/config-model.ts +116 -0
  177. package/packages/daemon/src/widgets/lib/files.ts +88 -0
  178. package/packages/daemon/src/widgets/lib/git.ts +88 -0
  179. package/packages/daemon/src/widgets/lib/grammar.ts +126 -0
  180. package/packages/daemon/src/widgets/lib/help-overlay.tsx +101 -0
  181. package/packages/daemon/src/widgets/lib/pane-comms.ts +209 -0
  182. package/packages/daemon/src/widgets/lib/theme.ts +194 -0
  183. package/packages/daemon/src/widgets/lib/watcher.ts +132 -0
  184. package/packages/daemon/src/widgets/preview/README.md +3 -0
  185. package/packages/daemon/src/widgets/preview/index.tsx +416 -0
  186. package/packages/daemon/src/widgets/resolve.ts +121 -0
  187. package/packages/daemon/src/widgets/setup/README.md +3 -0
  188. package/packages/daemon/src/widgets/setup/agent-naming.tsx +112 -0
  189. package/packages/daemon/src/widgets/setup/config-tree.tsx +246 -0
  190. package/packages/daemon/src/widgets/setup/detect-panel.tsx +72 -0
  191. package/packages/daemon/src/widgets/setup/field-editor.tsx +265 -0
  192. package/packages/daemon/src/widgets/setup/footer.tsx +107 -0
  193. package/packages/daemon/src/widgets/setup/index.tsx +341 -0
  194. package/packages/daemon/src/widgets/setup/layout-picker.tsx +96 -0
  195. package/packages/daemon/src/widgets/setup/orchestrator-panel.tsx +200 -0
  196. package/packages/daemon/src/widgets/setup/review-panel.tsx +140 -0
  197. package/packages/daemon/src/widgets/setup/setup-model.ts +188 -0
  198. package/packages/daemon/src/widgets/sidebar/index.tsx +527 -0
  199. package/packages/tmux-bridge/package.json +22 -0
  200. package/packages/tmux-bridge/src/errors.ts +28 -0
  201. package/packages/tmux-bridge/src/index.ts +31 -0
  202. package/packages/tmux-bridge/src/monitor.ts +77 -0
  203. package/packages/tmux-bridge/src/panes.ts +136 -0
  204. package/packages/tmux-bridge/src/runner.test.ts +501 -0
  205. package/packages/tmux-bridge/src/runner.ts +91 -0
  206. package/packages/tmux-bridge/src/sessions.ts +126 -0
  207. package/packages/tmux-bridge/src/targeting.test.ts +107 -0
  208. package/packages/tmux-bridge/src/targeting.ts +90 -0
  209. package/scripts/postinstall.js +26 -2
@@ -0,0 +1,211 @@
1
+ import {
2
+ existsSync,
3
+ readFileSync,
4
+ writeFileSync,
5
+ renameSync,
6
+ mkdirSync,
7
+ readdirSync,
8
+ copyFileSync,
9
+ } from "node:fs";
10
+ import { resolve, join, basename, dirname } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ import { detectStack, suggestConfig } from "./detect.ts";
15
+ import { outputError, printLayout } from "./lib/output.ts";
16
+ import type { IdeConfig } from "./types.ts";
17
+
18
+ function copyTemplateSkills(targetDir: string): string[] {
19
+ const created: string[] = [];
20
+ const templateSkillsDir = resolve(__dirname, "..", "..", "..", "templates", "skills");
21
+ if (!existsSync(templateSkillsDir)) return created;
22
+
23
+ mkdirSync(targetDir, { recursive: true });
24
+ for (const file of readdirSync(templateSkillsDir)) {
25
+ if (!file.endsWith(".md")) continue;
26
+ const destination = join(targetDir, file);
27
+ copyFileSync(join(templateSkillsDir, file), destination);
28
+ created.push(destination);
29
+ }
30
+ return created;
31
+ }
32
+
33
+ function scaffoldLibraryStubs(dir: string): string[] {
34
+ const created: string[] = [];
35
+ const libraryDir = join(dir, ".tmux-ide", "library");
36
+ if (!existsSync(libraryDir)) {
37
+ mkdirSync(libraryDir, { recursive: true });
38
+ created.push(libraryDir);
39
+ }
40
+
41
+ const archPath = join(libraryDir, "architecture.md");
42
+ if (!existsSync(archPath)) {
43
+ writeFileSync(
44
+ archPath,
45
+ "# Architecture\n\n<!-- Describe your project's architecture here. This context is injected into agent dispatch prompts. -->\n",
46
+ );
47
+ created.push(archPath);
48
+ }
49
+
50
+ const learningsPath = join(libraryDir, "learnings.md");
51
+ if (!existsSync(learningsPath)) {
52
+ writeFileSync(
53
+ learningsPath,
54
+ "# Learnings\n\n<!-- Task summaries are automatically appended here by the orchestrator. -->\n",
55
+ );
56
+ created.push(learningsPath);
57
+ }
58
+
59
+ return created;
60
+ }
61
+
62
+ function scaffoldValidationContract(dir: string): string[] {
63
+ const created: string[] = [];
64
+ const tasksDir = join(dir, ".tasks");
65
+ if (!existsSync(tasksDir)) {
66
+ mkdirSync(tasksDir, { recursive: true });
67
+ }
68
+
69
+ const contractPath = join(tasksDir, "validation-contract.md");
70
+ if (!existsSync(contractPath)) {
71
+ writeFileSync(
72
+ contractPath,
73
+ "# Validation Contract\n\n<!-- Define assertions that the validator agent will verify. Example: -->\n<!-- - VAL-001: All tests pass -->\n<!-- - VAL-002: No TypeScript errors -->\n<!-- - VAL-003: Lint passes with zero warnings -->\n",
74
+ );
75
+ created.push(contractPath);
76
+ }
77
+
78
+ return created;
79
+ }
80
+
81
+ function scaffoldAgentsMd(dir: string, name: string): string[] {
82
+ const created: string[] = [];
83
+ const agentsTemplatePath = resolve(__dirname, "..", "..", "..", "templates", "AGENTS.md");
84
+ if (existsSync(agentsTemplatePath)) {
85
+ const agentsPath = join(dir, "AGENTS.md");
86
+ if (!existsSync(agentsPath)) {
87
+ const content = readFileSync(agentsTemplatePath, "utf-8").replace(/{{name}}/g, name);
88
+ writeFileSync(agentsPath, content);
89
+ created.push(agentsPath);
90
+ }
91
+ }
92
+ return created;
93
+ }
94
+
95
+ function isTeamTemplate(templateName: string): boolean {
96
+ return templateName === "missions" || templateName.startsWith("agent-team");
97
+ }
98
+
99
+ function scaffoldTeamWorkspace(dir: string, name: string): string[] {
100
+ const created: string[] = [];
101
+ created.push(...scaffoldLibraryStubs(dir));
102
+ created.push(...scaffoldValidationContract(dir));
103
+ created.push(...scaffoldAgentsMd(dir, name));
104
+ return created;
105
+ }
106
+
107
+ function scaffoldMissionsWorkspace(dir: string, name: string): string[] {
108
+ const created: string[] = [];
109
+ const skillsDir = join(dir, ".tmux-ide", "skills");
110
+ created.push(...copyTemplateSkills(skillsDir));
111
+ created.push(...scaffoldTeamWorkspace(dir, name));
112
+ return created;
113
+ }
114
+
115
+ export async function init({
116
+ template,
117
+ json,
118
+ }: { template?: string; json?: boolean } = {}): Promise<void> {
119
+ const dir = process.cwd();
120
+ const configPath = resolve(dir, "ide.yml");
121
+
122
+ if (existsSync(configPath)) {
123
+ outputError("ide.yml already exists in this directory", "EXISTS");
124
+ }
125
+
126
+ // If a specific template is requested, use it
127
+ if (template) {
128
+ const templatePath = resolve(__dirname, "..", "..", "..", "templates", `${template}.yml`);
129
+ if (!existsSync(templatePath)) {
130
+ outputError(`Template "${template}" not found`, "NOT_FOUND");
131
+ }
132
+
133
+ let content = readFileSync(templatePath, "utf-8");
134
+ const name = basename(dir);
135
+ content = content.replace(/^name: .+/m, `name: ${name}`);
136
+ const tmpPath = configPath + ".tmp";
137
+ writeFileSync(tmpPath, content);
138
+ renameSync(tmpPath, configPath);
139
+ let created: string[];
140
+ if (template === "missions") {
141
+ created = scaffoldMissionsWorkspace(dir, name);
142
+ } else if (isTeamTemplate(template)) {
143
+ created = [
144
+ ...copyTemplateSkills(join(dir, ".tmux-ide", "skills")),
145
+ ...scaffoldTeamWorkspace(dir, name),
146
+ ];
147
+ } else {
148
+ created = copyTemplateSkills(join(dir, ".tmux-ide", "skills"));
149
+ }
150
+
151
+ if (json) {
152
+ console.log(JSON.stringify({ created: true, template, name, paths: created }));
153
+ } else {
154
+ console.log(`Created ide.yml from "${template}" template for "${name}"`);
155
+ const yaml = (await import("js-yaml")).default;
156
+ printLayout(yaml.load(content) as IdeConfig);
157
+ for (const createdPath of created) {
158
+ console.log(`Created ${createdPath.replace(dir + "/", "")}`);
159
+ }
160
+ }
161
+ return;
162
+ }
163
+
164
+ // Smart detection
165
+ const detected = detectStack(dir);
166
+ const name = basename(dir);
167
+
168
+ if (detected.frameworks.length > 0) {
169
+ // Use detected stack to generate config
170
+ const config = suggestConfig(dir, detected);
171
+ const yaml = (await import("js-yaml")).default;
172
+ const tmpPath2 = configPath + ".tmp";
173
+ writeFileSync(tmpPath2, yaml.dump(config, { lineWidth: -1, noRefs: true, quotingType: '"' }));
174
+ renameSync(tmpPath2, configPath);
175
+
176
+ const desc = detected.frameworks.join(" + ");
177
+ if (json) {
178
+ console.log(JSON.stringify({ created: true, detected: detected.frameworks, name }));
179
+ } else {
180
+ console.log(`Detected ${desc}. Created ide.yml for "${name}".`);
181
+ printLayout(config);
182
+ console.log("Edit it to customize, then run: tmux-ide");
183
+ }
184
+ } else {
185
+ // Fallback to default template
186
+ const templatePath = resolve(__dirname, "..", "..", "..", "templates", "default.yml");
187
+ let content = readFileSync(templatePath, "utf-8");
188
+ content = content.replace(/^name: .+/m, `name: ${name}`);
189
+ const tmpPath3 = configPath + ".tmp";
190
+ writeFileSync(tmpPath3, content);
191
+ renameSync(tmpPath3, configPath);
192
+
193
+ if (json) {
194
+ console.log(JSON.stringify({ created: true, template: "default", name }));
195
+ } else {
196
+ console.log(`Created ide.yml for "${name}"`);
197
+ const yaml = (await import("js-yaml")).default;
198
+ printLayout(yaml.load(content) as IdeConfig);
199
+ console.log("Edit it to configure your workspace, then run: tmux-ide");
200
+ }
201
+ }
202
+
203
+ // Copy built-in skills if .tmux-ide/skills/ doesn't exist
204
+ const skillsDir = join(dir, ".tmux-ide", "skills");
205
+ if (!existsSync(skillsDir)) {
206
+ const created = copyTemplateSkills(skillsDir);
207
+ if (created.length > 0 && !json) {
208
+ console.log("Copied built-in skill templates to .tmux-ide/skills/");
209
+ }
210
+ }
211
+ }
@@ -0,0 +1,178 @@
1
+ import { resolve, basename } from "node:path";
2
+ import { readConfig } from "./lib/yaml-io.ts";
3
+ import { validateConfig } from "./validate.ts";
4
+ import { outputError } from "./lib/output.ts";
5
+ import { getSessionState, listPanes } from "@tmux-ide/tmux-bridge";
6
+ import type { IdeConfig } from "./types.ts";
7
+
8
+ interface ResolvedPane {
9
+ index: number;
10
+ title: string | null;
11
+ command: string | null;
12
+ dir: string;
13
+ size: string | null;
14
+ focus: boolean;
15
+ role: string | null;
16
+ task: string | null;
17
+ env: Record<string, unknown>;
18
+ }
19
+
20
+ interface ResolvedRow {
21
+ index: number;
22
+ size: string | null;
23
+ panes: ResolvedPane[];
24
+ }
25
+
26
+ interface Inspection {
27
+ dir: string;
28
+ configPath: string;
29
+ valid: boolean;
30
+ errors: string[];
31
+ session: string;
32
+ before: string | null;
33
+ summary: { rows: number; panes: number; focus: string | null };
34
+ team: IdeConfig["team"] | null;
35
+ theme: IdeConfig["theme"] | null;
36
+ focus: { row: number; pane: number; title: string | null } | null;
37
+ rows: ResolvedRow[];
38
+ rawConfig: IdeConfig;
39
+ tmux: { running: boolean; panes: ReturnType<typeof listPanes> };
40
+ }
41
+
42
+ export function buildInspection(
43
+ dir: string,
44
+ {
45
+ config,
46
+ configPath,
47
+ running,
48
+ panes,
49
+ }: {
50
+ config: IdeConfig;
51
+ configPath: string;
52
+ running: boolean;
53
+ panes: ReturnType<typeof listPanes>;
54
+ },
55
+ ): Inspection {
56
+ const errors = validateConfig(config);
57
+ const rows = Array.isArray(config?.rows) ? config.rows : [];
58
+ const resolvedRows: ResolvedRow[] = rows.map((row, rowIndex) => ({
59
+ index: rowIndex,
60
+ size: row.size ?? null,
61
+ panes: (Array.isArray(row?.panes) ? row.panes : []).map((pane, paneIndex) => ({
62
+ index: paneIndex,
63
+ title: pane.title ?? null,
64
+ command: pane.command ?? null,
65
+ dir: pane.dir ?? ".",
66
+ size: pane.size ?? null,
67
+ focus: pane.focus === true,
68
+ role: pane.role ?? null,
69
+ task: pane.task ?? null,
70
+ env: pane.env ?? {},
71
+ })),
72
+ }));
73
+
74
+ const focusPane =
75
+ resolvedRows
76
+ .flatMap((row) => row.panes.map((pane) => ({ row: row.index, pane })))
77
+ .find(({ pane }) => pane.focus) ?? null;
78
+ const session = config?.name ?? basename(dir);
79
+
80
+ return {
81
+ dir,
82
+ configPath,
83
+ valid: errors.length === 0,
84
+ errors,
85
+ session,
86
+ before: config?.before ?? null,
87
+ summary: {
88
+ rows: resolvedRows.length,
89
+ panes: resolvedRows.reduce((sum, row) => sum + row.panes.length, 0),
90
+ focus: focusPane ? `rows.${focusPane.row}.panes.${focusPane.pane.index}` : null,
91
+ },
92
+ team: config?.team ?? null,
93
+ theme: config?.theme ?? null,
94
+ focus: focusPane
95
+ ? {
96
+ row: focusPane.row,
97
+ pane: focusPane.pane.index,
98
+ title: focusPane.pane.title,
99
+ }
100
+ : null,
101
+ rows: resolvedRows,
102
+ rawConfig: config,
103
+ tmux: {
104
+ running,
105
+ panes,
106
+ },
107
+ };
108
+ }
109
+
110
+ export async function inspect(
111
+ targetDir: string | undefined,
112
+ { json }: { json?: boolean } = {},
113
+ ): Promise<void> {
114
+ const dir = resolve(targetDir ?? ".");
115
+
116
+ let config;
117
+ let configPath: string;
118
+ try {
119
+ ({ config, configPath } = readConfig(dir));
120
+ } catch (error) {
121
+ outputError(`Cannot read ide.yml: ${(error as Error).message}`, "READ_ERROR");
122
+ return;
123
+ }
124
+
125
+ const session = config?.name ?? basename(dir);
126
+ const state = getSessionState(session);
127
+ const panes = state.running ? listPanes(session) : [];
128
+ const data = buildInspection(dir, { config, configPath, running: state.running, panes });
129
+
130
+ if (json) {
131
+ console.log(JSON.stringify(data, null, 2));
132
+ return;
133
+ }
134
+
135
+ console.log(`Directory: ${data.dir}`);
136
+ console.log(`Config: ${data.configPath}`);
137
+ console.log(`Valid: ${data.valid ? "yes" : "no"}`);
138
+ console.log(`Session: ${data.session}`);
139
+ console.log(`Running: ${data.tmux.running ? "yes" : "no"}`);
140
+ console.log(`Rows: ${data.summary.rows}`);
141
+ console.log(`Panes: ${data.summary.panes}`);
142
+ console.log(`Team: ${data.team ? data.team.name : "disabled"}`);
143
+
144
+ if (data.focus) {
145
+ console.log(
146
+ `Focus: row ${data.focus.row}, pane ${data.focus.pane}${data.focus.title ? ` (${data.focus.title})` : ""}`,
147
+ );
148
+ }
149
+
150
+ if (!data.valid) {
151
+ console.log("\nValidation Errors:");
152
+ for (const error of data.errors) {
153
+ console.log(` - ${error}`);
154
+ }
155
+ }
156
+
157
+ console.log("\nResolved Layout:");
158
+ for (const row of data.rows) {
159
+ console.log(` Row ${row.index}${row.size ? ` (${row.size})` : ""}`);
160
+ for (const pane of row.panes) {
161
+ const parts: string[] = [];
162
+ if (pane.title) parts.push(pane.title);
163
+ if (pane.command) parts.push(`cmd=${pane.command}`);
164
+ if (pane.dir && pane.dir !== ".") parts.push(`dir=${pane.dir}`);
165
+ if (pane.role) parts.push(`role=${pane.role}`);
166
+ if (pane.focus) parts.push("focus");
167
+ console.log(` - pane ${pane.index}: ${parts.join(" | ") || "shell"}`);
168
+ }
169
+ }
170
+
171
+ if (data.tmux.running && data.tmux.panes.length > 0) {
172
+ console.log("\nLive Panes:");
173
+ for (const pane of data.tmux.panes) {
174
+ const active = pane.active ? " (active)" : "";
175
+ console.log(` ${pane.index}: ${pane.title} [${pane.width}x${pane.height}]${active}`);
176
+ }
177
+ }
178
+ }
@@ -0,0 +1,10 @@
1
+ declare module "js-yaml" {
2
+ function load(input: string): unknown;
3
+ function dump(
4
+ input: unknown,
5
+ options?: { lineWidth?: number; noRefs?: boolean; quotingType?: string },
6
+ ): string;
7
+ const _default: { load: typeof load; dump: typeof dump };
8
+ export default _default;
9
+ export { load, dump };
10
+ }