@nemus-cli/nemus 0.2.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 (320) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/LICENSE +21 -0
  3. package/README.md +275 -0
  4. package/assets/README.md +53 -0
  5. package/assets/banner.png +0 -0
  6. package/assets/banner.svg +66 -0
  7. package/assets/favicon-32.png +0 -0
  8. package/assets/favicon-64.png +0 -0
  9. package/assets/icon-256.png +0 -0
  10. package/assets/icon.svg +23 -0
  11. package/assets/logo-256.png +0 -0
  12. package/assets/logo-512.png +0 -0
  13. package/assets/logo-wordmark.svg +35 -0
  14. package/assets/logo.svg +32 -0
  15. package/bin/workspace.js +130 -0
  16. package/dist/cli/ai-prompt.js +421 -0
  17. package/dist/cli/dashboard/AgentList.js +61 -0
  18. package/dist/cli/dashboard/DashboardSidebar.js +280 -0
  19. package/dist/cli/dashboard/HelpFooter.js +73 -0
  20. package/dist/cli/dashboard/StatusBadge.js +50 -0
  21. package/dist/cli/dashboard/index.js +11 -0
  22. package/dist/cli/dashboard/types.js +2 -0
  23. package/dist/cli/tui.js +48 -0
  24. package/dist/commands/ai-prompt.js +22 -0
  25. package/dist/commands/analyze-deps.js +138 -0
  26. package/dist/commands/archive.js +113 -0
  27. package/dist/commands/branch/create.js +119 -0
  28. package/dist/commands/branch/index.js +104 -0
  29. package/dist/commands/branch/merge.js +90 -0
  30. package/dist/commands/branch/rebase.js +84 -0
  31. package/dist/commands/branch/switch.js +165 -0
  32. package/dist/commands/cache/index.js +80 -0
  33. package/dist/commands/cache/manager.js +198 -0
  34. package/dist/commands/cleanup.js +153 -0
  35. package/dist/commands/configure-claude.js +55 -0
  36. package/dist/commands/configure.js +231 -0
  37. package/dist/commands/create.js +204 -0
  38. package/dist/commands/dashboard/hook-handler.js +162 -0
  39. package/dist/commands/dashboard/index.js +258 -0
  40. package/dist/commands/dashboard/launcher.js +40 -0
  41. package/dist/commands/dashboard/session-picker.js +106 -0
  42. package/dist/commands/dashboard/sidebar.js +39 -0
  43. package/dist/commands/dashboard/workspace-picker.js +95 -0
  44. package/dist/commands/delete.js +194 -0
  45. package/dist/commands/deprecated-aliases.js +44 -0
  46. package/dist/commands/diff.js +129 -0
  47. package/dist/commands/doctor.js +141 -0
  48. package/dist/commands/generate-docs.js +77 -0
  49. package/dist/commands/ghq-status.js +72 -0
  50. package/dist/commands/go.js +154 -0
  51. package/dist/commands/history.js +124 -0
  52. package/dist/commands/list.js +191 -0
  53. package/dist/commands/mcp/index.js +69 -0
  54. package/dist/commands/migrate.js +296 -0
  55. package/dist/commands/remove-repo.js +217 -0
  56. package/dist/commands/report-bug.js +112 -0
  57. package/dist/commands/run.js +147 -0
  58. package/dist/commands/save-context.js +171 -0
  59. package/dist/commands/sessions.js +129 -0
  60. package/dist/commands/status.js +138 -0
  61. package/dist/commands/suite/create.js +212 -0
  62. package/dist/commands/suite/delete.js +64 -0
  63. package/dist/commands/suite/export.js +126 -0
  64. package/dist/commands/suite/import.js +128 -0
  65. package/dist/commands/suite/index.js +94 -0
  66. package/dist/commands/suite/list.js +49 -0
  67. package/dist/commands/suite/use.js +227 -0
  68. package/dist/commands/sync.js +117 -0
  69. package/dist/commands/update.js +203 -0
  70. package/dist/mcp/install.js +550 -0
  71. package/dist/mcp/server.js +398 -0
  72. package/dist/mcp/tools.js +922 -0
  73. package/dist/pi-extensions/permission-sync.ts.template +41 -0
  74. package/dist/pi-extensions/permissions-gate.ts.template +69 -0
  75. package/dist/pi-extensions/pr-fix-trigger.ts.template +124 -0
  76. package/dist/pi-extensions/review-reminder.ts.template +58 -0
  77. package/dist/pi-extensions/workspace-input-status.ts.template +919 -0
  78. package/dist/program.js +136 -0
  79. package/dist/scripts/workspace-table.py +394 -0
  80. package/dist/types/dashboard.js +11 -0
  81. package/dist/types/index.js +2 -0
  82. package/dist/utils/agent-config.js +263 -0
  83. package/dist/utils/agent-state.js +178 -0
  84. package/dist/utils/banner.js +24 -0
  85. package/dist/utils/branch-operations.js +232 -0
  86. package/dist/utils/bug-report.js +355 -0
  87. package/dist/utils/cache.js +168 -0
  88. package/dist/utils/claude-integration.js +617 -0
  89. package/dist/utils/claude-sessions.js +260 -0
  90. package/dist/utils/cleanup-operations.js +120 -0
  91. package/dist/utils/colors.js +26 -0
  92. package/dist/utils/command-helpers.js +44 -0
  93. package/dist/utils/config.js +150 -0
  94. package/dist/utils/context-file.js +33 -0
  95. package/dist/utils/dashboard-hooks.js +172 -0
  96. package/dist/utils/dependency-analyzer.js +234 -0
  97. package/dist/utils/diff-operations.js +80 -0
  98. package/dist/utils/doc-generator.js +184 -0
  99. package/dist/utils/ghq-integration.js +332 -0
  100. package/dist/utils/git-operations.js +237 -0
  101. package/dist/utils/git-status.js +187 -0
  102. package/dist/utils/github.js +68 -0
  103. package/dist/utils/health-checks.js +345 -0
  104. package/dist/utils/history.js +150 -0
  105. package/dist/utils/hooks.js +100 -0
  106. package/dist/utils/logger.js +40 -0
  107. package/dist/utils/permission-sync.js +681 -0
  108. package/dist/utils/pi-extensions.js +224 -0
  109. package/dist/utils/progress.js +92 -0
  110. package/dist/utils/prompts.js +279 -0
  111. package/dist/utils/repo-resolver.js +260 -0
  112. package/dist/utils/retry.js +65 -0
  113. package/dist/utils/run-operations.js +30 -0
  114. package/dist/utils/suite.js +159 -0
  115. package/dist/utils/sync-operations.js +107 -0
  116. package/dist/utils/tmux-dashboard.js +184 -0
  117. package/dist/utils/validation.js +153 -0
  118. package/dist/utils/version-check.js +121 -0
  119. package/dist/utils/workspace-meta.js +185 -0
  120. package/install-shell-integration.sh +532 -0
  121. package/install.sh +401 -0
  122. package/package.json +90 -0
  123. package/remove-shell-block.awk +44 -0
  124. package/scripts/postinstall.js +85 -0
  125. package/skills/analyze-deps.md +23 -0
  126. package/skills/archive-workspace.md +26 -0
  127. package/skills/branch-create.md +18 -0
  128. package/skills/create-workspace.md +39 -0
  129. package/skills/delete-workspace.md +37 -0
  130. package/skills/list-org-repos.md +26 -0
  131. package/skills/list-suites.md +24 -0
  132. package/skills/list-workspaces.md +27 -0
  133. package/skills/refresh-workspace-docs.md +89 -0
  134. package/skills/remove-repo.md +25 -0
  135. package/skills/run-command.md +23 -0
  136. package/skills/save-context.md +68 -0
  137. package/skills/search-repos.md +31 -0
  138. package/skills/snapshot-list.md +18 -0
  139. package/skills/snapshot-save.md +12 -0
  140. package/skills/switch-branch.md +18 -0
  141. package/skills/update-cache.md +21 -0
  142. package/skills/update-workspace.md +66 -0
  143. package/skills/workspace-cleanup.md +18 -0
  144. package/skills/workspace-diff.md +23 -0
  145. package/skills/workspace-doctor.md +23 -0
  146. package/skills/workspace-info.md +45 -0
  147. package/skills/workspace-manager/SKILL.md +201 -0
  148. package/skills/workspace-manager/references/ai-prompt.md +44 -0
  149. package/skills/workspace-manager/references/analyze-deps.md +45 -0
  150. package/skills/workspace-manager/references/archive-workspace.md +38 -0
  151. package/skills/workspace-manager/references/branch-create.md +31 -0
  152. package/skills/workspace-manager/references/branch-merge.md +22 -0
  153. package/skills/workspace-manager/references/branch-rebase.md +22 -0
  154. package/skills/workspace-manager/references/cache-clear.md +18 -0
  155. package/skills/workspace-manager/references/cache-info.md +22 -0
  156. package/skills/workspace-manager/references/configure-claude.md +31 -0
  157. package/skills/workspace-manager/references/configure.md +35 -0
  158. package/skills/workspace-manager/references/create-workspace.md +45 -0
  159. package/skills/workspace-manager/references/dashboard.md +61 -0
  160. package/skills/workspace-manager/references/delete-workspace.md +33 -0
  161. package/skills/workspace-manager/references/generate-docs.md +36 -0
  162. package/skills/workspace-manager/references/ghq-status.md +28 -0
  163. package/skills/workspace-manager/references/go.md +33 -0
  164. package/skills/workspace-manager/references/history.md +41 -0
  165. package/skills/workspace-manager/references/list-org-repos.md +26 -0
  166. package/skills/workspace-manager/references/list-suites.md +21 -0
  167. package/skills/workspace-manager/references/list-workspaces.md +27 -0
  168. package/skills/workspace-manager/references/mcp.md +49 -0
  169. package/skills/workspace-manager/references/remove-repo.md +26 -0
  170. package/skills/workspace-manager/references/run-command.md +44 -0
  171. package/skills/workspace-manager/references/search-repos.md +24 -0
  172. package/skills/workspace-manager/references/sessions.md +27 -0
  173. package/skills/workspace-manager/references/suite-create.md +22 -0
  174. package/skills/workspace-manager/references/suite-delete.md +21 -0
  175. package/skills/workspace-manager/references/suite-export.md +19 -0
  176. package/skills/workspace-manager/references/suite-import.md +19 -0
  177. package/skills/workspace-manager/references/suite-use.md +27 -0
  178. package/skills/workspace-manager/references/switch-branch.md +25 -0
  179. package/skills/workspace-manager/references/update-cache.md +22 -0
  180. package/skills/workspace-manager/references/update-workspace.md +50 -0
  181. package/skills/workspace-manager/references/workspace-cleanup.md +33 -0
  182. package/skills/workspace-manager/references/workspace-diff.md +27 -0
  183. package/skills/workspace-manager/references/workspace-doctor.md +27 -0
  184. package/skills/workspace-manager/references/workspace-info.md +33 -0
  185. package/skills/workspace-manager/references/workspace-status.md +43 -0
  186. package/skills/workspace-manager/references/workspace-sync.md +26 -0
  187. package/skills/workspace-status.md +26 -0
  188. package/skills/workspace-sync.md +23 -0
  189. package/src/cli/ai-prompt.test.ts +667 -0
  190. package/src/cli/ai-prompt.ts +421 -0
  191. package/src/cli/dashboard/AgentList.tsx +44 -0
  192. package/src/cli/dashboard/DashboardSidebar.tsx +291 -0
  193. package/src/cli/dashboard/HelpFooter.tsx +22 -0
  194. package/src/cli/dashboard/StatusBadge.tsx +23 -0
  195. package/src/cli/dashboard/index.ts +5 -0
  196. package/src/cli/dashboard/types.ts +16 -0
  197. package/src/cli/tui.tsx +51 -0
  198. package/src/cli/workspace-cli.test.ts +30 -0
  199. package/src/commands/ai-prompt.ts +21 -0
  200. package/src/commands/analyze-deps.ts +119 -0
  201. package/src/commands/archive.ts +114 -0
  202. package/src/commands/branch/create.ts +94 -0
  203. package/src/commands/branch/index.ts +74 -0
  204. package/src/commands/branch/merge.ts +61 -0
  205. package/src/commands/branch/rebase.ts +54 -0
  206. package/src/commands/branch/switch.ts +150 -0
  207. package/src/commands/cache/index.ts +51 -0
  208. package/src/commands/cache/manager.ts +168 -0
  209. package/src/commands/cleanup.ts +132 -0
  210. package/src/commands/configure-claude.ts +55 -0
  211. package/src/commands/configure.ts +204 -0
  212. package/src/commands/create.ts +196 -0
  213. package/src/commands/dashboard/hook-handler.ts +138 -0
  214. package/src/commands/dashboard/index.ts +248 -0
  215. package/src/commands/dashboard/launcher.ts +42 -0
  216. package/src/commands/dashboard/session-picker.ts +73 -0
  217. package/src/commands/dashboard/sidebar.js +39 -0
  218. package/src/commands/dashboard/workspace-picker.ts +62 -0
  219. package/src/commands/delete.test.ts +250 -0
  220. package/src/commands/delete.ts +171 -0
  221. package/src/commands/deprecated-aliases.ts +55 -0
  222. package/src/commands/diff.ts +106 -0
  223. package/src/commands/doctor.ts +110 -0
  224. package/src/commands/generate-docs.ts +45 -0
  225. package/src/commands/ghq-status.ts +70 -0
  226. package/src/commands/go.ts +125 -0
  227. package/src/commands/history.ts +136 -0
  228. package/src/commands/list.test.ts +219 -0
  229. package/src/commands/list.ts +176 -0
  230. package/src/commands/mcp/index.ts +39 -0
  231. package/src/commands/migrate.ts +277 -0
  232. package/src/commands/remove-repo.ts +194 -0
  233. package/src/commands/report-bug.ts +88 -0
  234. package/src/commands/run.ts +125 -0
  235. package/src/commands/save-context.test.ts +113 -0
  236. package/src/commands/save-context.ts +141 -0
  237. package/src/commands/sessions.ts +99 -0
  238. package/src/commands/status.ts +111 -0
  239. package/src/commands/suite/create.ts +240 -0
  240. package/src/commands/suite/delete.ts +65 -0
  241. package/src/commands/suite/export.ts +97 -0
  242. package/src/commands/suite/import.ts +100 -0
  243. package/src/commands/suite/index.ts +66 -0
  244. package/src/commands/suite/list.ts +54 -0
  245. package/src/commands/suite/use.ts +232 -0
  246. package/src/commands/sync.ts +97 -0
  247. package/src/commands/update.ts +197 -0
  248. package/src/mcp/install.ts +529 -0
  249. package/src/mcp/server.ts +579 -0
  250. package/src/mcp/tools.test.ts +545 -0
  251. package/src/mcp/tools.ts +1019 -0
  252. package/src/mcp/update-workspace.test.ts +220 -0
  253. package/src/pi-extensions/permission-sync.ts.template +41 -0
  254. package/src/pi-extensions/permissions-gate.ts.template +69 -0
  255. package/src/pi-extensions/pr-fix-trigger.ts.template +124 -0
  256. package/src/pi-extensions/review-reminder.ts.template +58 -0
  257. package/src/pi-extensions/workspace-input-status.ts.template +919 -0
  258. package/src/program.ts +112 -0
  259. package/src/scripts/workspace-table.py +394 -0
  260. package/src/types/dashboard.ts +29 -0
  261. package/src/types/index.ts +109 -0
  262. package/src/types/ink.d.ts +171 -0
  263. package/src/utils/agent-config.test.ts +303 -0
  264. package/src/utils/agent-config.ts +264 -0
  265. package/src/utils/agent-state.test.ts +166 -0
  266. package/src/utils/agent-state.ts +135 -0
  267. package/src/utils/banner.ts +24 -0
  268. package/src/utils/branch-operations.ts +237 -0
  269. package/src/utils/bug-report.test.ts +174 -0
  270. package/src/utils/bug-report.ts +355 -0
  271. package/src/utils/cache.ts +150 -0
  272. package/src/utils/claude-integration.test.ts +381 -0
  273. package/src/utils/claude-integration.ts +649 -0
  274. package/src/utils/claude-sessions.test.ts +262 -0
  275. package/src/utils/claude-sessions.ts +251 -0
  276. package/src/utils/cleanup-operations.ts +94 -0
  277. package/src/utils/colors.ts +25 -0
  278. package/src/utils/command-helpers.ts +46 -0
  279. package/src/utils/config.test.ts +203 -0
  280. package/src/utils/config.ts +133 -0
  281. package/src/utils/context-file.ts +36 -0
  282. package/src/utils/dashboard-hooks.test.ts +168 -0
  283. package/src/utils/dashboard-hooks.ts +163 -0
  284. package/src/utils/dependency-analyzer.ts +243 -0
  285. package/src/utils/diff-operations.ts +59 -0
  286. package/src/utils/doc-generator.ts +187 -0
  287. package/src/utils/ghq-integration.test.ts +195 -0
  288. package/src/utils/ghq-integration.ts +317 -0
  289. package/src/utils/git-operations.test.ts +265 -0
  290. package/src/utils/git-operations.ts +233 -0
  291. package/src/utils/git-status.ts +171 -0
  292. package/src/utils/github.ts +74 -0
  293. package/src/utils/health-checks.ts +359 -0
  294. package/src/utils/history.ts +140 -0
  295. package/src/utils/hooks.ts +92 -0
  296. package/src/utils/logger.ts +37 -0
  297. package/src/utils/permission-sync.test.ts +593 -0
  298. package/src/utils/permission-sync.ts +720 -0
  299. package/src/utils/pi-extensions.test.ts +182 -0
  300. package/src/utils/pi-extensions.ts +226 -0
  301. package/src/utils/progress.ts +73 -0
  302. package/src/utils/prompts.test.ts +169 -0
  303. package/src/utils/prompts.ts +296 -0
  304. package/src/utils/repo-resolver.test.ts +313 -0
  305. package/src/utils/repo-resolver.ts +294 -0
  306. package/src/utils/retry.test.ts +124 -0
  307. package/src/utils/retry.ts +85 -0
  308. package/src/utils/run-operations.ts +38 -0
  309. package/src/utils/suite.test.ts +256 -0
  310. package/src/utils/suite.ts +144 -0
  311. package/src/utils/sync-operations.ts +87 -0
  312. package/src/utils/tmux-dashboard.ts +193 -0
  313. package/src/utils/validation.test.ts +139 -0
  314. package/src/utils/validation.ts +128 -0
  315. package/src/utils/version-check.test.ts +108 -0
  316. package/src/utils/version-check.ts +96 -0
  317. package/src/utils/workspace-meta.ts +173 -0
  318. package/sync-permissions.sh +137 -0
  319. package/tsconfig.json +20 -0
  320. package/uninstall.sh +162 -0
@@ -0,0 +1,532 @@
1
+ #!/bin/bash
2
+
3
+ # Nemus - Shell Integration Installer
4
+ # This script installs a shell function that allows automatic CD after workspace creation
5
+
6
+ set -e
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
9
+ SHELL_TYPE="${1:-detect}"
10
+
11
+ # Detect shell if not specified
12
+ if [ "$SHELL_TYPE" = "detect" ]; then
13
+ if [ -n "$BASH_VERSION" ]; then
14
+ SHELL_TYPE="bash"
15
+ elif [ -n "$ZSH_VERSION" ]; then
16
+ SHELL_TYPE="zsh"
17
+ else
18
+ SHELL_CURRENT=$(basename "$SHELL")
19
+ if [ "$SHELL_CURRENT" = "bash" ]; then
20
+ SHELL_TYPE="bash"
21
+ elif [ "$SHELL_CURRENT" = "zsh" ]; then
22
+ SHELL_TYPE="zsh"
23
+ else
24
+ echo "Could not detect shell type. Please specify: bash or zsh"
25
+ exit 1
26
+ fi
27
+ fi
28
+ fi
29
+
30
+ # Shell function to wrap workspace command
31
+ read -r -d '' SHELL_FUNCTION << 'EOF' || true
32
+
33
+ # Nemus - Shell Integration (v50)
34
+ # Wraps both 'nemus' and 'nem' commands so the shell can CD after
35
+ # create / list / go commands. A child process cannot change the parent
36
+ # shell's directory, so we need shell functions that read a temp file
37
+ # written by the Node CLI and call `cd` in the current shell.
38
+
39
+ # ── helper: deterministic tab color from workspace name ──────────────
40
+ # Hashes the workspace name and maps it to a color on the full HSL hue
41
+ # wheel, then converts to RGB. This gives ~360 distinct, readable colors
42
+ # (vs the old 16-entry palette that repeated after a handful of
43
+ # workspaces). Always produces the same color for the same name.
44
+ # Output: "R G B" as three space-separated integers (0-255).
45
+ __workspace_hash_color() {
46
+ local str="$1"
47
+ local hash=0
48
+ local i char
49
+ for (( i=0; i<${#str}; i++ )); do
50
+ char="${str:$i:1}"
51
+ # 24-bit accumulator for a wide, well-distributed hash.
52
+ hash=$(( (hash * 31 + $(printf '%d' "'$char")) % 16777216 ))
53
+ done
54
+
55
+ # Derive hue across the whole wheel (0-359) plus small saturation/lightness
56
+ # variation from independent slices of the hash for extra distinctness,
57
+ # kept within a tasteful, readable band (not neon, not muddy).
58
+ local hue=$(( hash % 360 ))
59
+ local sat=$(( 58 + (hash / 360) % 15 )) # 58-72 %
60
+ local light=$(( 60 + (hash / 97) % 11 )) # 60-70 %
61
+
62
+ # HSL -> RGB. Prefer awk (handles the float math) for a smooth spectrum;
63
+ # fall back to a small built-in palette if awk is somehow unavailable.
64
+ if command -v awk >/dev/null 2>&1; then
65
+ awk -v h="$hue" -v s="$sat" -v l="$light" 'BEGIN{
66
+ s/=100.0; l/=100.0;
67
+ c=(1-((2*l-1)<0?-(2*l-1):(2*l-1)))*s;
68
+ hp=h/60.0;
69
+ m2=hp-2*int(hp/2); d=m2-1; if(d<0)d=-d;
70
+ x=c*(1-d);
71
+ if(hp<1){r=c;g=x;b=0} else if(hp<2){r=x;g=c;b=0}
72
+ else if(hp<3){r=0;g=c;b=x} else if(hp<4){r=0;g=x;b=c}
73
+ else if(hp<5){r=x;g=0;b=c} else {r=c;g=0;b=x}
74
+ m=l-c/2.0;
75
+ printf "%d %d %d", (r+m)*255+0.5, (g+m)*255+0.5, (b+m)*255+0.5;
76
+ }'
77
+ else
78
+ # Fallback palette (only used if awk is missing).
79
+ local palette=(
80
+ "86 156 214" "106 153 85" "214 157 133" "156 220 254"
81
+ "220 220 170" "197 134 192" "78 201 176" "255 198 109"
82
+ "229 192 123" "97 175 239" "224 108 117" "152 195 121"
83
+ "180 100 166" "209 154 102" "129 162 190" "140 170 175"
84
+ )
85
+ local idx=$(( hash % ${#palette[@]} ))
86
+ if [ -n "${ZSH_VERSION:-}" ]; then idx=$(( idx + 1 )); fi
87
+ echo "${palette[$idx]}"
88
+ fi
89
+ }
90
+
91
+ # ── helper: set iTerm2 tab background color ────────────────────────
92
+ # Uses the xterm OSC 6 escape sequence that iTerm2 supports.
93
+ # Silently does nothing in terminals that don't understand it.
94
+ __workspace_set_tab_color() {
95
+ local r="$1" g="$2" b="$3"
96
+ if [ "${TERM_PROGRAM:-}" = "iTerm.app" ] || [ "${TERM_PROGRAM:-}" = "iTerm2" ]; then
97
+ printf '\033]6;1;bg;red;brightness;%d\007' "$r"
98
+ printf '\033]6;1;bg;green;brightness;%d\007' "$g"
99
+ printf '\033]6;1;bg;blue;brightness;%d\007' "$b"
100
+ fi
101
+ }
102
+
103
+ # ── helper: reset iTerm2 tab color to the profile default ─────────
104
+ __workspace_reset_tab_color() {
105
+ if [ "${TERM_PROGRAM:-}" = "iTerm.app" ] || [ "${TERM_PROGRAM:-}" = "iTerm2" ]; then
106
+ printf '\033]6;1;bg;*;default\007'
107
+ fi
108
+ }
109
+
110
+ # ── helper: set terminal tab title + iTerm2 tab color ────────────────
111
+ # Uses both OSC 0 (window title) and OSC 1 (tab title) for broad
112
+ # terminal support. Installs a precmd/PROMPT_COMMAND hook so the title
113
+ # and color persist across prompts (zsh/oh-my-zsh and bash reset titles
114
+ # on each prompt). The hook auto-clears when the user CDs out of the
115
+ # workspace, restoring the default tab color.
116
+ __workspace_set_tab_title() {
117
+ local title="$1"
118
+ # Set title immediately
119
+ printf '\033]0;%s\007' "$title"
120
+ printf '\033]1;%s\007' "$title"
121
+
122
+ # Compute and apply a deterministic color for this workspace
123
+ local color r g b
124
+ color=$(__workspace_hash_color "$title")
125
+ read -r r g b <<< "$color"
126
+ __workspace_set_tab_color "$r" "$g" "$b"
127
+
128
+ # Persist state for the hook
129
+ export __WORKSPACE_TAB_TITLE="$title"
130
+ export __WORKSPACE_TAB_DIR="$PWD"
131
+ export __WORKSPACE_TAB_R="$r"
132
+ export __WORKSPACE_TAB_G="$g"
133
+ export __WORKSPACE_TAB_B="$b"
134
+
135
+ if [ -n "${ZSH_VERSION:-}" ]; then
136
+ # Disable oh-my-zsh auto-title so it doesn't overwrite ours
137
+ if [ -z "${__WORKSPACE_PREV_DISABLE_AUTO_TITLE+x}" ]; then
138
+ export __WORKSPACE_PREV_DISABLE_AUTO_TITLE="${DISABLE_AUTO_TITLE:-}"
139
+ fi
140
+ export DISABLE_AUTO_TITLE=true
141
+ __workspace_precmd() {
142
+ if [ -n "$__WORKSPACE_TAB_TITLE" ]; then
143
+ if [ "$PWD" = "$__WORKSPACE_TAB_DIR" ] || [[ "$PWD" = "$__WORKSPACE_TAB_DIR"/* ]]; then
144
+ printf '\033]0;%s\007' "$__WORKSPACE_TAB_TITLE"
145
+ printf '\033]1;%s\007' "$__WORKSPACE_TAB_TITLE"
146
+ __workspace_set_tab_color "$__WORKSPACE_TAB_R" "$__WORKSPACE_TAB_G" "$__WORKSPACE_TAB_B"
147
+ else
148
+ # Left the workspace — clear everything and restore defaults
149
+ unset __WORKSPACE_TAB_TITLE __WORKSPACE_TAB_DIR
150
+ unset __WORKSPACE_TAB_R __WORKSPACE_TAB_G __WORKSPACE_TAB_B
151
+ __workspace_reset_tab_color
152
+ # Restore previous DISABLE_AUTO_TITLE value
153
+ if [ -n "${__WORKSPACE_PREV_DISABLE_AUTO_TITLE:-}" ]; then
154
+ export DISABLE_AUTO_TITLE="$__WORKSPACE_PREV_DISABLE_AUTO_TITLE"
155
+ else
156
+ unset DISABLE_AUTO_TITLE
157
+ fi
158
+ unset __WORKSPACE_PREV_DISABLE_AUTO_TITLE
159
+ # Remove ourselves from precmd_functions
160
+ precmd_functions=(${precmd_functions:#__workspace_precmd})
161
+ fi
162
+ fi
163
+ }
164
+ # Add to precmd_functions array (zsh hook mechanism)
165
+ if (( ! ${precmd_functions[(Ie)__workspace_precmd]} )); then
166
+ precmd_functions+=(__workspace_precmd)
167
+ fi
168
+ elif [ -n "${BASH_VERSION:-}" ]; then
169
+ __workspace_prompt_command() {
170
+ if [ -n "$__WORKSPACE_TAB_TITLE" ]; then
171
+ if [ "$PWD" = "$__WORKSPACE_TAB_DIR" ] || [[ "$PWD" == "$__WORKSPACE_TAB_DIR"/* ]]; then
172
+ printf '\033]0;%s\007' "$__WORKSPACE_TAB_TITLE"
173
+ printf '\033]1;%s\007' "$__WORKSPACE_TAB_TITLE"
174
+ __workspace_set_tab_color "$__WORKSPACE_TAB_R" "$__WORKSPACE_TAB_G" "$__WORKSPACE_TAB_B"
175
+ else
176
+ # Left the workspace — clear everything and restore defaults
177
+ unset __WORKSPACE_TAB_TITLE __WORKSPACE_TAB_DIR
178
+ unset __WORKSPACE_TAB_R __WORKSPACE_TAB_G __WORKSPACE_TAB_B
179
+ __workspace_reset_tab_color
180
+ PROMPT_COMMAND="${PROMPT_COMMAND//__workspace_prompt_command;/}" 2>/dev/null || true
181
+ fi
182
+ fi
183
+ }
184
+ case "${PROMPT_COMMAND:-}" in
185
+ *__workspace_prompt_command*) ;;
186
+ *) PROMPT_COMMAND="__workspace_prompt_command;${PROMPT_COMMAND:-}" ;;
187
+ esac
188
+ fi
189
+ }
190
+
191
+ # ── helper: detect configured AI agent ───────────────────────────────
192
+ # Reads primaryAgent from Nemus config and returns the CLI command.
193
+ # Falls back to 'claude' if config is missing or unreadable.
194
+ # Caches result for the shell session to avoid repeated node invocations.
195
+ __ws_cached_agent=""
196
+ __workspace_detect_agent() {
197
+ # Return cached result if available
198
+ if [ -n "$__ws_cached_agent" ]; then
199
+ echo "$__ws_cached_agent"
200
+ return
201
+ fi
202
+ # An explicit primaryAgent from config wins; otherwise we auto-detect.
203
+ local agent=""
204
+ if command -v node >/dev/null 2>&1; then
205
+ local cfg="$HOME/.workspace-manager-cache/config.json"
206
+ if [ -f "$cfg" ]; then
207
+ local ai
208
+ ai=$(CFG="$cfg" node -e '
209
+ try {
210
+ const c = JSON.parse(require("fs").readFileSync(process.env.CFG, "utf-8"));
211
+ console.log(c.primaryAgent || "auto");
212
+ } catch { console.log("auto"); }
213
+ ' 2>/dev/null)
214
+ case "$ai" in
215
+ pi) agent="pi" ;;
216
+ opencode) agent="opencode" ;;
217
+ codex) agent="codex" ;;
218
+ gemini) agent="gemini" ;;
219
+ claude) agent="claude" ;;
220
+ # 'auto' (or anything else) falls through to the probe below.
221
+ esac
222
+ fi
223
+ fi
224
+ # No explicit choice (no config file, node unavailable, or primaryAgent=auto):
225
+ # probe for an installed agent in the same order as the TS getPrimaryAgent()
226
+ # 'auto' path (claude → pi → opencode → codex → gemini). This is what lets a
227
+ # Pi-only machine with no config actually launch the seeded prompt instead of
228
+ # silently defaulting to a claude that isn't installed.
229
+ if [ -z "$agent" ]; then
230
+ if command -v claude >/dev/null 2>&1; then
231
+ agent="claude"
232
+ elif command -v pi >/dev/null 2>&1; then
233
+ agent="pi"
234
+ elif command -v opencode >/dev/null 2>&1; then
235
+ agent="opencode"
236
+ elif command -v codex >/dev/null 2>&1; then
237
+ agent="codex"
238
+ elif command -v gemini >/dev/null 2>&1; then
239
+ agent="gemini"
240
+ else
241
+ agent="claude"
242
+ fi
243
+ fi
244
+ __ws_cached_agent="$agent"
245
+ echo "$agent"
246
+ }
247
+
248
+ # ── helper: run the real binary then handle CD ──────────────────────
249
+ __workspace_run() {
250
+ local bin="$1" # "nemus" or "nem"
251
+ shift
252
+ if [ $# -eq 0 ]; then
253
+ command "$bin"
254
+ return $?
255
+ fi
256
+ local cmd="$1"
257
+ shift
258
+
259
+ local temp_file="$HOME/.workspace-last-created"
260
+ local go_file="$HOME/.workspace-last-go"
261
+ local resume_file="$HOME/.workspace-resume-session"
262
+ local ai_prompt_file="$HOME/.workspace-ai-prompt"
263
+
264
+ # Classify the command
265
+ local is_create=false
266
+ local is_go=false
267
+ local is_ai=false
268
+ case "$cmd" in
269
+ create|c) is_create=true ;;
270
+ # Deprecated flat aliases that create workspaces
271
+ from-template|ft|from-suite|fs) is_create=true ;;
272
+ # AI prompt creates workspaces via MCP tools, then hands off
273
+ --) is_create=true; is_ai=true ;;
274
+ list|l|go|sessions|ses) is_go=true ;;
275
+ esac
276
+ # Grouped commands: "nemus suite use" / "nemus template use" — check second arg
277
+ if [ "$is_create" = false ]; then
278
+ case "$cmd" in
279
+ suite|template)
280
+ local sub="${1:-}"
281
+ if [ "$sub" = "use" ]; then
282
+ is_create=true
283
+ fi
284
+ ;;
285
+ esac
286
+ fi
287
+
288
+ # Detect --yes/-y (non-interactive mode): skip auto-launching the agent
289
+ # after workspace creation so one-shot/scripted invocations don't hang
290
+ # waiting on an interactive claude/pi/opencode session. Does not apply
291
+ # to the AI-prompt path ("nemus -- <prompt>"), which is interactive-only.
292
+ local skip_agent_launch=false
293
+ if [ "$is_create" = true ] && [ "$is_ai" = false ]; then
294
+ local arg
295
+ for arg in "$@"; do
296
+ case "$arg" in
297
+ -y|--yes) skip_agent_launch=true ;;
298
+ esac
299
+ done
300
+ fi
301
+
302
+ # Remove stale temp files before running
303
+ if [ "$is_create" = true ]; then
304
+ [ -f "$temp_file" ] && rm -f "$temp_file"
305
+ [ -f "$ai_prompt_file" ] && rm -f "$ai_prompt_file"
306
+ fi
307
+ if [ "$is_go" = true ]; then
308
+ [ -f "$go_file" ] && rm -f "$go_file"
309
+ [ -f "$resume_file" ] && rm -f "$resume_file"
310
+ fi
311
+
312
+ # Run the real binary
313
+ command "$bin" "$cmd" "$@"
314
+ local exit_code=$?
315
+
316
+ # For go-like commands, always CD if the go file exists (even after
317
+ # Ctrl+C in the fuzzy picker, which exits non-zero).
318
+ if [ "$is_go" = true ] && [ -f "$go_file" ]; then
319
+ local workspace_path
320
+ workspace_path=$(cat "$go_file")
321
+ rm -f "$go_file"
322
+ if [ -d "$workspace_path" ]; then
323
+ local ws_name
324
+ ws_name="$(basename "$workspace_path")"
325
+ echo ""
326
+ echo "📁 Changing directory to: $workspace_path"
327
+ cd "$workspace_path" || return 1
328
+ __workspace_set_tab_title "$ws_name"
329
+ local __ws_agent
330
+ __ws_agent=$(__workspace_detect_agent)
331
+ if command -v "$__ws_agent" >/dev/null 2>&1; then
332
+ # Check for resume file and read session's agent type.
333
+ # Resume file format: JSON { sessionId, agentType } (new) or plain sessionId (old).
334
+ # Old format gracefully falls back to configured agent type.
335
+ local resume_agent=""
336
+ if [ -f "$resume_file" ]; then
337
+ local resume_data
338
+ resume_data=$(cat "$resume_file")
339
+ rm -f "$resume_file"
340
+ # Default to configured agent (ensures --continue for old-format files)
341
+ resume_agent="$__ws_agent"
342
+ # Extract agentType from JSON resume file
343
+ local resume_agent_type
344
+ resume_agent_type=$(RESUME_DATA="$resume_data" node -e '
345
+ try {
346
+ const d = JSON.parse(process.env.RESUME_DATA);
347
+ console.log(d.agentType || "");
348
+ } catch { console.log(""); }
349
+ ' 2>/dev/null)
350
+ # Override with session's agent type if available
351
+ if [ "$resume_agent_type" = "pi" ] && command -v pi >/dev/null 2>&1; then
352
+ resume_agent="pi"
353
+ elif [ "$resume_agent_type" = "opencode" ] && command -v opencode >/dev/null 2>&1; then
354
+ resume_agent="opencode"
355
+ elif [ "$resume_agent_type" = "claude" ] && command -v claude >/dev/null 2>&1; then
356
+ resume_agent="claude"
357
+ fi
358
+ fi
359
+
360
+ # Launch agent: with resume if file existed, otherwise fresh session
361
+ if [ -n "$resume_agent" ]; then
362
+ if [ "$resume_agent" = "pi" ]; then
363
+ pi --continue
364
+ elif [ "$resume_agent" = "opencode" ]; then
365
+ opencode --continue
366
+ else
367
+ claude --continue --name "$ws_name"
368
+ fi
369
+ else
370
+ # No resume file - fresh session
371
+ if [ "$__ws_agent" = "pi" ]; then
372
+ pi
373
+ elif [ "$__ws_agent" = "opencode" ]; then
374
+ opencode
375
+ else
376
+ claude --name "$ws_name"
377
+ fi
378
+ fi
379
+ fi
380
+ # Re-apply tab title after agent exits
381
+ __workspace_set_tab_title "$ws_name"
382
+ fi
383
+ fi
384
+
385
+ # For create-like commands, only CD on success
386
+ if [ $exit_code -eq 0 ] && [ "$is_create" = true ] && [ -f "$temp_file" ]; then
387
+ local workspace_path
388
+ workspace_path=$(cat "$temp_file")
389
+ rm -f "$temp_file"
390
+ if [ -d "$workspace_path" ]; then
391
+ local ws_name
392
+ ws_name="$(basename "$workspace_path")"
393
+ echo ""
394
+ echo "📁 Changing directory to: $workspace_path"
395
+ cd "$workspace_path" || return 1
396
+ __workspace_set_tab_title "$ws_name"
397
+ local __ws_agent
398
+ __ws_agent=$(__workspace_detect_agent)
399
+ if [ "$skip_agent_launch" = true ]; then
400
+ echo "🤖 Skipping agent auto-launch (--yes / non-interactive mode)"
401
+ elif command -v "$__ws_agent" >/dev/null 2>&1; then
402
+ if [ "$__ws_agent" = "claude" ]; then
403
+ if [ "$is_ai" = true ] && [ -f "$ai_prompt_file" ]; then
404
+ local original_prompt
405
+ original_prompt=$(cat "$ai_prompt_file")
406
+ rm -f "$ai_prompt_file"
407
+ claude --name "$ws_name" "$original_prompt"
408
+ else
409
+ claude --name "$ws_name"
410
+ fi
411
+ elif [ "$__ws_agent" = "opencode" ]; then
412
+ if [ "$is_ai" = true ] && [ -f "$ai_prompt_file" ]; then
413
+ local original_prompt
414
+ original_prompt=$(cat "$ai_prompt_file")
415
+ rm -f "$ai_prompt_file"
416
+ opencode run "$original_prompt"
417
+ else
418
+ opencode
419
+ fi
420
+ else
421
+ if [ "$is_ai" = true ] && [ -f "$ai_prompt_file" ]; then
422
+ local original_prompt
423
+ original_prompt=$(cat "$ai_prompt_file")
424
+ rm -f "$ai_prompt_file"
425
+ # Interactive mode with initial prompt (positional arg)
426
+ pi "$original_prompt"
427
+ else
428
+ pi
429
+ fi
430
+ fi
431
+ fi
432
+ # Re-apply tab title after agent exits
433
+ __workspace_set_tab_title "$ws_name"
434
+ fi
435
+ fi
436
+
437
+ # Clean up ai prompt file if it wasn't consumed (e.g. no workspace was created)
438
+ [ -f "$ai_prompt_file" ] && rm -f "$ai_prompt_file"
439
+
440
+ return $exit_code
441
+ }
442
+
443
+ # ── public wrappers ─────────────────────────────────────────────────
444
+ nemus() { __workspace_run nemus "$@"; }
445
+ nem() { __workspace_run nem "$@"; }
446
+
447
+ # Quick navigation to workspace with fuzzy search
448
+ nemgo() {
449
+ local temp_file="$HOME/.workspace-last-go"
450
+ [ -f "$temp_file" ] && rm -f "$temp_file"
451
+
452
+ command nemus go "$@"
453
+ local exit_code=$?
454
+
455
+ if [ -f "$temp_file" ]; then
456
+ local workspace_path
457
+ workspace_path=$(cat "$temp_file")
458
+ rm -f "$temp_file"
459
+ if [ -d "$workspace_path" ]; then
460
+ local ws_name
461
+ ws_name="$(basename "$workspace_path")"
462
+ cd "$workspace_path" || return 1
463
+ __workspace_set_tab_title "$ws_name"
464
+ local __ws_agent
465
+ __ws_agent=$(__workspace_detect_agent)
466
+ if command -v "$__ws_agent" >/dev/null 2>&1; then
467
+ if [ "$__ws_agent" = "claude" ]; then
468
+ claude --name "$ws_name"
469
+ elif [ "$__ws_agent" = "opencode" ]; then
470
+ opencode
471
+ else
472
+ pi
473
+ fi
474
+ fi
475
+ __workspace_set_tab_title "$ws_name"
476
+ fi
477
+ fi
478
+
479
+ return $exit_code
480
+ }
481
+ EOF
482
+
483
+ # Determine RC file based on shell type
484
+ if [ "$SHELL_TYPE" = "bash" ]; then
485
+ RC_FILE="$HOME/.bashrc"
486
+ elif [ "$SHELL_TYPE" = "zsh" ]; then
487
+ RC_FILE="$HOME/.zshrc"
488
+ else
489
+ echo "❌ Unsupported shell: $SHELL_TYPE"
490
+ echo "Supported shells: bash, zsh"
491
+ exit 1
492
+ fi
493
+
494
+ # Check if already installed (version marker is in the grep below)
495
+ if grep -q "Shell Integration (v50)" "$RC_FILE" 2>/dev/null; then
496
+ echo "✅ Shell integration already up to date in $RC_FILE"
497
+ elif grep -q "# Nemus - Shell Integration" "$RC_FILE" 2>/dev/null; then
498
+ # Old version installed — remove it and install new version
499
+ echo "🔄 Upgrading shell integration in $RC_FILE..."
500
+ TMPFILE=$(mktemp)
501
+ awk -f "$SCRIPT_DIR/remove-shell-block.awk" "$RC_FILE" > "$TMPFILE"
502
+ # Append new shell integration to temp file so mv is atomic
503
+ echo "" >> "$TMPFILE"
504
+ echo "$SHELL_FUNCTION" >> "$TMPFILE"
505
+ cp "$RC_FILE" "${RC_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
506
+ chmod --reference="$RC_FILE" "$TMPFILE" 2>/dev/null || chmod "$(stat -f '%Lp' "$RC_FILE")" "$TMPFILE" 2>/dev/null || true
507
+ mv "$TMPFILE" "$RC_FILE"
508
+
509
+ echo "✅ Shell integration upgraded in $RC_FILE"
510
+ else
511
+ # Backup RC file if it exists
512
+ if [ -f "$RC_FILE" ]; then
513
+ cp "$RC_FILE" "${RC_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
514
+ echo "📦 Backup created: ${RC_FILE}.backup.*"
515
+ fi
516
+
517
+ # Append shell function (creates the file if it doesn't exist)
518
+ echo "" >> "$RC_FILE"
519
+ echo "$SHELL_FUNCTION" >> "$RC_FILE"
520
+
521
+ echo "✅ Shell integration installed in $RC_FILE"
522
+ echo ""
523
+ echo "To activate, run:"
524
+ echo " source $RC_FILE"
525
+ echo ""
526
+ echo "Or restart your terminal."
527
+ fi
528
+
529
+ echo ""
530
+ echo "After activation:"
531
+ echo " • 'nemus create' / 'nemus list' will automatically CD to the workspace! 🚀"
532
+ echo " • 'nemgo' command for quick navigation with fuzzy search! 🔍"