@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,260 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getWorkspaceSessions = getWorkspaceSessions;
37
+ exports.pathToProjectDirName = pathToProjectDirName;
38
+ exports.extractWorkspaceName = extractWorkspaceName;
39
+ exports.relativeTime = relativeTime;
40
+ const fs = __importStar(require("fs/promises"));
41
+ const path = __importStar(require("path"));
42
+ const config_1 = require("./config");
43
+ const agent_config_1 = require("./agent-config");
44
+ /**
45
+ * Scan session directories for workspace-related session files.
46
+ * Checks Claude (~/.claude/projects/) and/or Pi (~/.pi/agent/sessions/) based on config.
47
+ * Returns sessions sorted by last activity (most recent first).
48
+ *
49
+ * Note: Pi session discovery has limitations. Pi uses a different naming convention
50
+ * (--path--with--dashes--) vs Claude's (-path-with-dashes). This scanner currently
51
+ * only reliably finds Claude sessions. Pi session discovery may require updates
52
+ * based on Pi's actual session directory structure.
53
+ */
54
+ async function getWorkspaceSessions() {
55
+ const agents = (0, agent_config_1.getActiveAgents)();
56
+ const allSessions = [];
57
+ // Scan each active agent's session directory
58
+ for (const agent of agents) {
59
+ const sessions = await scanProjectsDir(agent.sessionProjectsDir, agent.type);
60
+ allSessions.push(...sessions);
61
+ }
62
+ // Deduplicate by workspace + session ID, sort by most recent
63
+ const seen = new Set();
64
+ const deduped = allSessions.filter(s => {
65
+ const key = `${s.workspacePath}:${s.sessionId}`;
66
+ if (seen.has(key))
67
+ return false;
68
+ seen.add(key);
69
+ return true;
70
+ });
71
+ deduped.sort((a, b) => b.lastActiveAt.getTime() - a.lastActiveAt.getTime());
72
+ return deduped;
73
+ }
74
+ /**
75
+ * Scan a single projects/sessions directory for workspace sessions.
76
+ */
77
+ async function scanProjectsDir(projectsDir, agentType) {
78
+ // Only Claude Code and Pi expose a scannable per-project session directory
79
+ // layout. OpenCode stores sessions in SQLite; Codex/Gemini use their own
80
+ // formats that aren't scanned here — skip them.
81
+ if (agentType !== 'claude' && agentType !== 'pi')
82
+ return [];
83
+ const workspacesPrefix = pathToProjectDirName(config_1.WORKSPACES_DIR, agentType);
84
+ let projectDirs;
85
+ try {
86
+ projectDirs = await fs.readdir(projectsDir);
87
+ }
88
+ catch {
89
+ return [];
90
+ }
91
+ // Filter to only workspace project directories
92
+ const workspaceProjectDirs = projectDirs.filter(dir => {
93
+ if (agentType === 'pi') {
94
+ // Pi dirs: "--{base}-{name}--", so strip trailing -- from prefix to match base + '-'
95
+ const prefixBase = workspacesPrefix.slice(0, -2); // remove trailing --
96
+ return dir.startsWith(prefixBase + '-') && dir !== workspacesPrefix;
97
+ }
98
+ // Claude: prefix + dash separator
99
+ return dir.startsWith(workspacesPrefix + '-') && dir !== workspacesPrefix;
100
+ });
101
+ const sessions = [];
102
+ await Promise.all(workspaceProjectDirs.map(async (projDir) => {
103
+ const workspaceName = extractWorkspaceName(projDir, workspacesPrefix, agentType);
104
+ if (!workspaceName)
105
+ return;
106
+ const workspacePath = path.join(config_1.WORKSPACES_DIR, workspaceName);
107
+ // Check workspace still exists on disk
108
+ try {
109
+ await fs.access(workspacePath);
110
+ }
111
+ catch {
112
+ return; // Workspace deleted, skip
113
+ }
114
+ const fullProjDir = path.join(projectsDir, projDir);
115
+ const session = await getMostRecentSession(fullProjDir);
116
+ if (!session)
117
+ return;
118
+ sessions.push({
119
+ workspaceName,
120
+ workspacePath,
121
+ sessionId: session.sessionId,
122
+ lastActiveAt: session.lastActiveAt,
123
+ lastActiveLabel: relativeTime(session.lastActiveAt),
124
+ agentType,
125
+ });
126
+ }));
127
+ // Sort by most recently active first
128
+ sessions.sort((a, b) => b.lastActiveAt.getTime() - a.lastActiveAt.getTime());
129
+ return sessions;
130
+ }
131
+ /** @internal exported for testing */
132
+ function pathToProjectDirName(absPath, agentType = 'claude') {
133
+ if (agentType === 'pi') {
134
+ // Pi format: --Users-yotambloom-Work-workspaces-- (leading --, single - between segments, trailing --)
135
+ // Strip leading /, then replace remaining / with -
136
+ const inner = absPath.replace(/^\//, '').replace(/\//g, '-');
137
+ return '--' + inner + '--';
138
+ }
139
+ // Claude format: -Users-yotambloom-Work-workspaces (/ replaced by -)
140
+ return absPath.replace(/\//g, '-');
141
+ }
142
+ /** @internal exported for testing */
143
+ function extractWorkspaceName(projDir, prefix, agentType = 'claude') {
144
+ if (agentType === 'pi') {
145
+ // Pi: prefix is "--Users-...-workspaces--", dir is "--Users-...-workspaces-{name}--"
146
+ // The prefix ends with --, but workspace name is between prefix (minus trailing --) + - and trailing --
147
+ const prefixBase = prefix.slice(0, -2); // remove trailing --
148
+ if (!projDir.startsWith(prefixBase + '-'))
149
+ return null;
150
+ // Ensure dir is longer than just prefixBase + '--' (which would be the prefix itself)
151
+ if (projDir === prefix)
152
+ return null;
153
+ const rest = projDir.slice(prefixBase.length + 1); // skip the - separator
154
+ // rest should be "{workspace-name}--" — reject malformed dirs that don't end with --
155
+ if (!rest.endsWith('--'))
156
+ return null;
157
+ const name = rest.slice(0, -2);
158
+ return name || null;
159
+ }
160
+ // Claude: remove prefix + the separator dash
161
+ const rest = projDir.slice(prefix.length + 1);
162
+ if (!rest)
163
+ return null;
164
+ return rest;
165
+ }
166
+ /**
167
+ * Find the most recently active session in a project directory.
168
+ * Reads .jsonl files and checks the last line's timestamp.
169
+ */
170
+ async function getMostRecentSession(projDir) {
171
+ let entries;
172
+ try {
173
+ entries = await fs.readdir(projDir);
174
+ }
175
+ catch {
176
+ return null;
177
+ }
178
+ const jsonlFiles = entries.filter(f => f.endsWith('.jsonl'));
179
+ if (jsonlFiles.length === 0)
180
+ return null;
181
+ let bestSession = null;
182
+ // For performance, check file modification times first to find candidates
183
+ const fileStats = await Promise.all(jsonlFiles.map(async (f) => {
184
+ try {
185
+ const stat = await fs.stat(path.join(projDir, f));
186
+ return { file: f, mtime: stat.mtime };
187
+ }
188
+ catch {
189
+ return null;
190
+ }
191
+ }));
192
+ // Sort by mtime descending, only check the most recent files
193
+ const sorted = fileStats
194
+ .filter((s) => s !== null)
195
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
196
+ // Only read the most recent session file to get accurate timestamp
197
+ for (const { file, mtime } of sorted.slice(0, 1)) {
198
+ const sessionId = file.replace('.jsonl', '');
199
+ const timestamp = await getLastTimestamp(path.join(projDir, file));
200
+ bestSession = {
201
+ sessionId,
202
+ lastActiveAt: timestamp || mtime,
203
+ };
204
+ }
205
+ return bestSession;
206
+ }
207
+ /**
208
+ * Read the last few lines of a .jsonl file and extract the most recent timestamp.
209
+ */
210
+ async function getLastTimestamp(filePath) {
211
+ try {
212
+ const handle = await fs.open(filePath, 'r');
213
+ try {
214
+ const stat = await handle.stat();
215
+ // Read last 4KB to find the last line with a timestamp
216
+ const readSize = Math.min(4096, stat.size);
217
+ const buffer = Buffer.alloc(readSize);
218
+ await handle.read(buffer, 0, readSize, Math.max(0, stat.size - readSize));
219
+ const content = buffer.toString('utf-8');
220
+ // Find the last line with a timestamp
221
+ const lines = content.split('\n').filter(l => l.trim());
222
+ for (let i = lines.length - 1; i >= 0; i--) {
223
+ try {
224
+ const parsed = JSON.parse(lines[i]);
225
+ if (parsed.timestamp) {
226
+ return new Date(parsed.timestamp);
227
+ }
228
+ }
229
+ catch {
230
+ continue;
231
+ }
232
+ }
233
+ }
234
+ finally {
235
+ await handle.close();
236
+ }
237
+ }
238
+ catch {
239
+ // ignore
240
+ }
241
+ return null;
242
+ }
243
+ /** @internal exported for testing */
244
+ function relativeTime(date) {
245
+ const now = Date.now();
246
+ const diff = now - date.getTime();
247
+ const minutes = Math.floor(diff / 60000);
248
+ if (minutes < 1)
249
+ return 'just now';
250
+ if (minutes < 60)
251
+ return `${minutes}m ago`;
252
+ const hours = Math.floor(minutes / 60);
253
+ if (hours < 24)
254
+ return `${hours}h ago`;
255
+ const days = Math.floor(hours / 24);
256
+ if (days < 30)
257
+ return `${days}d ago`;
258
+ const months = Math.floor(days / 30);
259
+ return `${months}mo ago`;
260
+ }
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.gitClean = exports.removeBuildArtifacts = exports.removeNodeModules = exports.calculateDirSize = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const util_1 = require("util");
39
+ const fs = __importStar(require("fs/promises"));
40
+ const path = __importStar(require("path"));
41
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
42
+ const calculateDirSize = async (dirPath) => {
43
+ try {
44
+ const { stdout } = await execAsync(`du -sk "${dirPath}"`);
45
+ const sizeInKB = parseInt(stdout.split('\t')[0], 10);
46
+ return sizeInKB;
47
+ }
48
+ catch {
49
+ return 0;
50
+ }
51
+ };
52
+ exports.calculateDirSize = calculateDirSize;
53
+ const removeNodeModules = async (repoPath) => {
54
+ const nodeModulesPath = path.join(repoPath, 'node_modules');
55
+ try {
56
+ const sizeBefore = await (0, exports.calculateDirSize)(nodeModulesPath);
57
+ await fs.rm(nodeModulesPath, { recursive: true, force: true });
58
+ return {
59
+ operation: 'Remove node_modules',
60
+ filesRemoved: 1,
61
+ spaceFreed: `${(sizeBefore / 1024).toFixed(2)} MB`,
62
+ success: true,
63
+ };
64
+ }
65
+ catch (error) {
66
+ return {
67
+ operation: 'Remove node_modules',
68
+ filesRemoved: 0,
69
+ spaceFreed: '0 MB',
70
+ success: false,
71
+ };
72
+ }
73
+ };
74
+ exports.removeNodeModules = removeNodeModules;
75
+ const removeBuildArtifacts = async (repoPath) => {
76
+ const artifacts = ['dist', 'build', '.next', 'coverage', 'out'];
77
+ let totalSize = 0;
78
+ let removed = 0;
79
+ for (const artifact of artifacts) {
80
+ const artifactPath = path.join(repoPath, artifact);
81
+ try {
82
+ const size = await (0, exports.calculateDirSize)(artifactPath);
83
+ await fs.rm(artifactPath, { recursive: true, force: true });
84
+ totalSize += size;
85
+ removed++;
86
+ }
87
+ catch {
88
+ // Ignore if doesn't exist
89
+ }
90
+ }
91
+ return {
92
+ operation: 'Remove build artifacts',
93
+ filesRemoved: removed,
94
+ spaceFreed: `${(totalSize / 1024).toFixed(2)} MB`,
95
+ success: true,
96
+ };
97
+ };
98
+ exports.removeBuildArtifacts = removeBuildArtifacts;
99
+ const gitClean = async (repoPath, dryRun = false) => {
100
+ try {
101
+ const cmd = dryRun ? 'git clean -fdxn' : 'git clean -fdx';
102
+ const { stdout } = await execAsync(cmd, { cwd: repoPath });
103
+ const lines = stdout.split('\n').filter(l => l.trim());
104
+ return {
105
+ operation: 'Git clean',
106
+ filesRemoved: lines.length,
107
+ spaceFreed: 'Unknown',
108
+ success: true,
109
+ };
110
+ }
111
+ catch {
112
+ return {
113
+ operation: 'Git clean',
114
+ filesRemoved: 0,
115
+ spaceFreed: '0 MB',
116
+ success: false,
117
+ };
118
+ }
119
+ };
120
+ exports.gitClean = gitClean;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.colorize = exports.colors = void 0;
4
+ exports.colors = {
5
+ reset: '\x1b[0m',
6
+ bright: '\x1b[1m',
7
+ dim: '\x1b[2m',
8
+ // Foreground colors
9
+ red: '\x1b[31m',
10
+ green: '\x1b[32m',
11
+ yellow: '\x1b[33m',
12
+ blue: '\x1b[34m',
13
+ magenta: '\x1b[35m',
14
+ cyan: '\x1b[36m',
15
+ white: '\x1b[37m',
16
+ gray: '\x1b[90m',
17
+ // Background colors
18
+ bgRed: '\x1b[41m',
19
+ bgGreen: '\x1b[42m',
20
+ bgYellow: '\x1b[43m',
21
+ bgBlue: '\x1b[44m',
22
+ };
23
+ const colorize = (text, color) => {
24
+ return `${exports.colors[color]}${text}${exports.colors.reset}`;
25
+ };
26
+ exports.colorize = colorize;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getGlobalOpts = getGlobalOpts;
4
+ exports.resolveWorkspace = resolveWorkspace;
5
+ exports.parseList = parseList;
6
+ const workspace_meta_1 = require("./workspace-meta");
7
+ const prompts_1 = require("./prompts");
8
+ /**
9
+ * Extract global options (--force-refresh, --yes) from the root program.
10
+ * Commander doesn't auto-inherit parent options to subcommands,
11
+ * so we walk up the parent chain to find them.
12
+ */
13
+ function getGlobalOpts(cmd) {
14
+ let root = cmd;
15
+ while (root.parent)
16
+ root = root.parent;
17
+ const opts = root.opts();
18
+ return {
19
+ forceRefresh: opts.forceRefresh ?? false,
20
+ yes: opts.yes ?? false,
21
+ };
22
+ }
23
+ /**
24
+ * Resolve workspace name: use provided value, or prompt interactively.
25
+ * In non-interactive mode (no TTY), throws if no name provided.
26
+ */
27
+ async function resolveWorkspace(name) {
28
+ if (name)
29
+ return name;
30
+ if (!process.stdout.isTTY) {
31
+ throw new Error('Workspace name required in non-interactive mode. Use --workspace <name> or provide as argument.');
32
+ }
33
+ const workspaces = await (0, workspace_meta_1.listWorkspaces)();
34
+ if (workspaces.length === 0) {
35
+ throw new Error('No workspaces found. Create one first with: nemus create');
36
+ }
37
+ return (0, prompts_1.promptWorkspaceSelection)(workspaces);
38
+ }
39
+ /**
40
+ * Parse comma-separated list from a flag value.
41
+ */
42
+ function parseList(value) {
43
+ return value.split(',').map(s => s.trim()).filter(Boolean);
44
+ }
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.config = exports.CLONE_MAX_BUFFER = exports.CLONE_TIMEOUT_MS = exports.CONFIG_PATH = exports.META_FILENAME = exports.SUITES_FILE = exports.HISTORY_FILE = exports.CACHE_DIR = exports.WORKSPACES_DIR = void 0;
37
+ exports.getUserConfig = getUserConfig;
38
+ exports.getPackageVersion = getPackageVersion;
39
+ exports.getCloneUrl = getCloneUrl;
40
+ exports.saveUserConfig = saveUserConfig;
41
+ const os = __importStar(require("os"));
42
+ const path = __importStar(require("path"));
43
+ const fs = __importStar(require("fs"));
44
+ const HOME_DIR = os.homedir();
45
+ // Config file lives outside CACHE_DIR so we can read it to determine CACHE_DIR
46
+ const CONFIG_FILE = path.join(HOME_DIR, '.workspace-manager-cache', 'config.json');
47
+ const DEFAULTS = {
48
+ workspacesDir: path.join(HOME_DIR, 'workspaces'),
49
+ githubOrg: '',
50
+ autoLaunchClaude: true,
51
+ generateClaudeContext: true,
52
+ cloneProtocol: 'ssh',
53
+ installMcp: true,
54
+ aiAgent: 'auto',
55
+ primaryAgent: 'auto',
56
+ piWorkspaceInputStatus: true,
57
+ claudeWorkspaceStatusLine: true,
58
+ autoReportBugs: false,
59
+ };
60
+ function loadConfigFileSync() {
61
+ try {
62
+ const content = fs.readFileSync(CONFIG_FILE, 'utf-8');
63
+ const raw = JSON.parse(content);
64
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
65
+ return {};
66
+ const result = {};
67
+ if (typeof raw.workspacesDir === 'string' && raw.workspacesDir)
68
+ result.workspacesDir = raw.workspacesDir;
69
+ if (typeof raw.githubOrg === 'string' && raw.githubOrg)
70
+ result.githubOrg = raw.githubOrg;
71
+ if (typeof raw.autoLaunchClaude === 'boolean')
72
+ result.autoLaunchClaude = raw.autoLaunchClaude;
73
+ if (typeof raw.generateClaudeContext === 'boolean')
74
+ result.generateClaudeContext = raw.generateClaudeContext;
75
+ if (raw.cloneProtocol === 'ssh' || raw.cloneProtocol === 'https')
76
+ result.cloneProtocol = raw.cloneProtocol;
77
+ if (typeof raw.installMcp === 'boolean')
78
+ result.installMcp = raw.installMcp;
79
+ const agentValues = ['claude', 'pi', 'opencode', 'codex', 'gemini'];
80
+ if (agentValues.includes(raw.aiAgent) || raw.aiAgent === 'both' || raw.aiAgent === 'auto')
81
+ result.aiAgent = raw.aiAgent;
82
+ if (agentValues.includes(raw.primaryAgent) || raw.primaryAgent === 'auto')
83
+ result.primaryAgent = raw.primaryAgent;
84
+ if (typeof raw.piWorkspaceInputStatus === 'boolean')
85
+ result.piWorkspaceInputStatus = raw.piWorkspaceInputStatus;
86
+ if (typeof raw.claudeWorkspaceStatusLine === 'boolean')
87
+ result.claudeWorkspaceStatusLine = raw.claudeWorkspaceStatusLine;
88
+ if (typeof raw.autoReportBugs === 'boolean')
89
+ result.autoReportBugs = raw.autoReportBugs;
90
+ return result;
91
+ }
92
+ catch {
93
+ return {};
94
+ }
95
+ }
96
+ /** Get the full resolved user config (defaults + file overrides). Always reads from disk. */
97
+ function getUserConfig() {
98
+ return { ...DEFAULTS, ...loadConfigFileSync() };
99
+ }
100
+ // Bootstrap constants from initial config read (env vars take precedence)
101
+ const initialConfig = getUserConfig();
102
+ exports.WORKSPACES_DIR = process.env.WORKSPACE_MANAGER_DIR || initialConfig.workspacesDir;
103
+ exports.CACHE_DIR = process.env.WORKSPACE_MANAGER_CACHE_DIR || path.join(HOME_DIR, '.workspace-manager-cache');
104
+ exports.HISTORY_FILE = path.join(exports.CACHE_DIR, 'history.jsonl');
105
+ exports.SUITES_FILE = path.join(exports.CACHE_DIR, 'suites.json');
106
+ exports.META_FILENAME = '.workspace-meta.json';
107
+ exports.CONFIG_PATH = CONFIG_FILE;
108
+ // ── Clone execution limits ──────────────────────────────────────────────────
109
+ // Git clone runs via child_process.exec, which (a) kills the process on
110
+ // timeout and (b) has a small default maxBuffer (1 MB) that a large repo's
111
+ // progress output can blow past. Both surface as confusing "Command failed"
112
+ // errors, so we set generous, overridable limits here.
113
+ exports.CLONE_TIMEOUT_MS = (() => {
114
+ const parsed = Number(process.env.WORKSPACE_CLONE_TIMEOUT_MS);
115
+ // Invalid or non-positive values (incl. 0) fall back to the 15-min default.
116
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 15 * 60 * 1000;
117
+ })();
118
+ exports.CLONE_MAX_BUFFER = 64 * 1024 * 1024; // 64 MB
119
+ exports.config = {
120
+ workspacesDir: exports.WORKSPACES_DIR,
121
+ cacheDir: exports.CACHE_DIR,
122
+ historyFile: exports.HISTORY_FILE,
123
+ suitesFile: exports.SUITES_FILE,
124
+ metaFilename: exports.META_FILENAME,
125
+ };
126
+ /** Read the package version from package.json. */
127
+ function getPackageVersion() {
128
+ try {
129
+ const pkgPath = path.join(__dirname, '..', '..', 'package.json');
130
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
131
+ return pkg.version;
132
+ }
133
+ catch {
134
+ return '0.0.0';
135
+ }
136
+ }
137
+ /** Get the clone URL for a repo based on the configured protocol. */
138
+ function getCloneUrl(repo) {
139
+ const { cloneProtocol } = getUserConfig();
140
+ if (cloneProtocol === 'https') {
141
+ return repo.url.endsWith('.git') ? repo.url : `${repo.url}.git`;
142
+ }
143
+ return repo.sshUrl;
144
+ }
145
+ /** Save user config to disk. */
146
+ function saveUserConfig(cfg) {
147
+ const dir = path.dirname(CONFIG_FILE);
148
+ fs.mkdirSync(dir, { recursive: true });
149
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + '\n', 'utf-8');
150
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /**
3
+ * Shared utilities for CONTEXT.md file formatting.
4
+ * Used by both the CLI (save-context command) and MCP tool (handleSaveContext).
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.formatContextFile = formatContextFile;
8
+ exports.appendToContextFile = appendToContextFile;
9
+ /**
10
+ * Format a fresh CONTEXT.md file with header and body.
11
+ */
12
+ function formatContextFile(workspaceName, body, timestamp) {
13
+ const ts = timestamp || new Date().toISOString().replace('T', ' ').replace(/\.\d+Z$/, 'Z');
14
+ return `# Workspace Context: ${workspaceName}
15
+
16
+ > This file contains saved progress and context for AI agents.
17
+ > It persists across \`/clear\` and session restarts.
18
+ > Last updated: ${ts}
19
+
20
+ ${body}
21
+ `;
22
+ }
23
+ /**
24
+ * Append content to an existing CONTEXT.md, adding a timestamped separator.
25
+ * If the existing file can't be read, returns a fresh formatted file.
26
+ */
27
+ function appendToContextFile(existingContent, workspaceName, newContent, timestamp) {
28
+ const ts = timestamp || new Date().toISOString().replace('T', ' ').replace(/\.\d+Z$/, 'Z');
29
+ if (existingContent) {
30
+ return existingContent.trimEnd() + `\n\n---\n\n### Update (${ts})\n\n${newContent}`;
31
+ }
32
+ return formatContextFile(workspaceName, newContent, ts);
33
+ }