@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,187 @@
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.isGitRepository = exports.hasUncommittedChanges = exports.getAllReposStatus = exports.getAheadBehindCounts = exports.getUncommittedChanges = exports.getCurrentBranch = exports.getGitStatus = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const util_1 = require("util");
39
+ const path = __importStar(require("path"));
40
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
41
+ const GIT_TIMEOUT = 10000; // 10 seconds per git operation
42
+ const getGitStatus = async (repoPath, repoName) => {
43
+ try {
44
+ const results = await Promise.allSettled([
45
+ (0, exports.getCurrentBranch)(repoPath),
46
+ (0, exports.getUncommittedChanges)(repoPath),
47
+ (0, exports.getAheadBehindCounts)(repoPath),
48
+ ]);
49
+ const branch = results[0].status === 'fulfilled' ? results[0].value : 'unknown';
50
+ const changes = results[1].status === 'fulfilled' ? results[1].value : { clean: false, modifiedFiles: 0, untrackedFiles: 0 };
51
+ const tracking = results[2].status === 'fulfilled' ? results[2].value : { ahead: 0, behind: 0, hasRemote: false };
52
+ return {
53
+ repo: repoName,
54
+ branch,
55
+ clean: changes.clean,
56
+ ahead: tracking.ahead,
57
+ behind: tracking.behind,
58
+ modifiedFiles: changes.modifiedFiles,
59
+ untrackedFiles: changes.untrackedFiles,
60
+ hasRemote: tracking.hasRemote,
61
+ detachedHead: branch.startsWith('HEAD detached'),
62
+ };
63
+ }
64
+ catch (error) {
65
+ // Return error state
66
+ return {
67
+ repo: repoName,
68
+ branch: 'unknown',
69
+ clean: false,
70
+ ahead: 0,
71
+ behind: 0,
72
+ modifiedFiles: 0,
73
+ untrackedFiles: 0,
74
+ hasRemote: false,
75
+ detachedHead: false,
76
+ };
77
+ }
78
+ };
79
+ exports.getGitStatus = getGitStatus;
80
+ const getCurrentBranch = async (repoPath) => {
81
+ try {
82
+ const { stdout } = await execAsync('git rev-parse --abbrev-ref HEAD', {
83
+ cwd: repoPath,
84
+ timeout: GIT_TIMEOUT,
85
+ });
86
+ const branch = stdout.trim();
87
+ // Check for detached HEAD
88
+ if (branch === 'HEAD') {
89
+ const { stdout: commitHash } = await execAsync('git rev-parse --short HEAD', {
90
+ cwd: repoPath,
91
+ timeout: GIT_TIMEOUT,
92
+ });
93
+ return `HEAD detached at ${commitHash.trim()}`;
94
+ }
95
+ return branch;
96
+ }
97
+ catch (error) {
98
+ return 'unknown';
99
+ }
100
+ };
101
+ exports.getCurrentBranch = getCurrentBranch;
102
+ const getUncommittedChanges = async (repoPath) => {
103
+ try {
104
+ const { stdout } = await execAsync('git status --porcelain', {
105
+ cwd: repoPath,
106
+ timeout: GIT_TIMEOUT,
107
+ });
108
+ if (!stdout.trim()) {
109
+ return { clean: true, modifiedFiles: 0, untrackedFiles: 0 };
110
+ }
111
+ const lines = stdout.trim().split('\n');
112
+ let modifiedFiles = 0;
113
+ let untrackedFiles = 0;
114
+ for (const line of lines) {
115
+ const status = line.substring(0, 2);
116
+ if (status.includes('?')) {
117
+ untrackedFiles++;
118
+ }
119
+ else {
120
+ modifiedFiles++;
121
+ }
122
+ }
123
+ return { clean: false, modifiedFiles, untrackedFiles };
124
+ }
125
+ catch (error) {
126
+ return { clean: false, modifiedFiles: 0, untrackedFiles: 0 };
127
+ }
128
+ };
129
+ exports.getUncommittedChanges = getUncommittedChanges;
130
+ const getAheadBehindCounts = async (repoPath) => {
131
+ try {
132
+ // First check if there's a remote tracking branch
133
+ const { stdout: upstreamBranch } = await execAsync('git rev-parse --abbrev-ref @{upstream}', {
134
+ cwd: repoPath,
135
+ timeout: GIT_TIMEOUT,
136
+ });
137
+ if (!upstreamBranch.trim()) {
138
+ return { ahead: 0, behind: 0, hasRemote: false };
139
+ }
140
+ // Get ahead/behind counts
141
+ const { stdout } = await execAsync('git rev-list --count --left-right @{upstream}...HEAD', {
142
+ cwd: repoPath,
143
+ timeout: GIT_TIMEOUT,
144
+ });
145
+ const counts = stdout.trim().split('\t');
146
+ const behind = parseInt(counts[0], 10) || 0;
147
+ const ahead = parseInt(counts[1], 10) || 0;
148
+ return { ahead, behind, hasRemote: true };
149
+ }
150
+ catch (error) {
151
+ // No remote tracking branch
152
+ return { ahead: 0, behind: 0, hasRemote: false };
153
+ }
154
+ };
155
+ exports.getAheadBehindCounts = getAheadBehindCounts;
156
+ const getAllReposStatus = async (workspacePath, repoNames, concurrency = 3) => {
157
+ const results = [];
158
+ // Process in chunks for controlled parallelism
159
+ for (let i = 0; i < repoNames.length; i += concurrency) {
160
+ const chunk = repoNames.slice(i, i + concurrency);
161
+ const chunkResults = await Promise.all(chunk.map(repoName => {
162
+ const repoPath = path.join(workspacePath, repoName);
163
+ return (0, exports.getGitStatus)(repoPath, repoName);
164
+ }));
165
+ results.push(...chunkResults);
166
+ }
167
+ return results;
168
+ };
169
+ exports.getAllReposStatus = getAllReposStatus;
170
+ const hasUncommittedChanges = async (repoPath) => {
171
+ const { clean } = await (0, exports.getUncommittedChanges)(repoPath);
172
+ return !clean;
173
+ };
174
+ exports.hasUncommittedChanges = hasUncommittedChanges;
175
+ const isGitRepository = async (repoPath) => {
176
+ try {
177
+ await execAsync('git rev-parse --git-dir', {
178
+ cwd: repoPath,
179
+ timeout: GIT_TIMEOUT,
180
+ });
181
+ return true;
182
+ }
183
+ catch (error) {
184
+ return false;
185
+ }
186
+ };
187
+ exports.isGitRepository = isGitRepository;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.displayAuthInstructions = exports.fetchOrgRepos = exports.verifyGhAuth = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const util_1 = require("util");
6
+ const logger_1 = require("./logger");
7
+ const cache_1 = require("./cache");
8
+ const config_1 = require("./config");
9
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
10
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
11
+ const verifyGhAuth = async () => {
12
+ try {
13
+ const { stdout } = await execAsync('gh auth status');
14
+ return stdout.includes('Logged in') || stdout.includes('✓');
15
+ }
16
+ catch (error) {
17
+ return false;
18
+ }
19
+ };
20
+ exports.verifyGhAuth = verifyGhAuth;
21
+ const fetchOrgRepos = async (options = {}) => {
22
+ const { forceRefresh = false } = options;
23
+ try {
24
+ const { githubOrg } = (0, config_1.getUserConfig)();
25
+ // Try to get cached repos first
26
+ if (!forceRefresh) {
27
+ const cachedRepos = await (0, cache_1.getCachedRepos)({ org: githubOrg });
28
+ if (cachedRepos) {
29
+ return cachedRepos.sort((a, b) => a.name.localeCompare(b.name));
30
+ }
31
+ }
32
+ (0, logger_1.logInfo)(githubOrg
33
+ ? `Fetching ${githubOrg} repositories from GitHub...`
34
+ : 'Fetching your GitHub repositories... (set an org with `nemus configure` to list an organization)');
35
+ // With no configured org, `gh repo list` (no positional) lists the
36
+ // authenticated user's own repos. With an org, it lists that org's repos.
37
+ const args = [
38
+ 'repo', 'list',
39
+ ...(githubOrg ? [githubOrg] : []),
40
+ '--limit', '1000',
41
+ '--json', 'name,url,sshUrl,owner,description,isPrivate',
42
+ ];
43
+ const { stdout } = await execFileAsync('gh', args);
44
+ const repos = JSON.parse(stdout);
45
+ (0, logger_1.logInfo)(`Found ${repos.length} repositories`);
46
+ // Cache the results (24 hour TTL)
47
+ await (0, cache_1.setCachedRepos)(repos, 86400000, githubOrg);
48
+ return repos.sort((a, b) => a.name.localeCompare(b.name));
49
+ }
50
+ catch (error) {
51
+ (0, logger_1.logError)('Failed to fetch repositories from GitHub');
52
+ if (error instanceof Error) {
53
+ (0, logger_1.logError)(error.message);
54
+ }
55
+ throw new Error('Unable to fetch repositories. Please ensure gh CLI is authenticated.');
56
+ }
57
+ };
58
+ exports.fetchOrgRepos = fetchOrgRepos;
59
+ const displayAuthInstructions = () => {
60
+ console.log('\n' + '='.repeat(60));
61
+ console.log('GitHub CLI Authentication Required');
62
+ console.log('='.repeat(60));
63
+ console.log('\nPlease authenticate with GitHub CLI by running:');
64
+ console.log('\n gh auth login\n');
65
+ console.log('Then try running this command again.');
66
+ console.log('='.repeat(60) + '\n');
67
+ };
68
+ exports.displayAuthInstructions = displayAuthInstructions;
@@ -0,0 +1,345 @@
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.calculateHealthScore = exports.runAllHealthChecks = exports.checkGhqInstalled = exports.checkMetadataIntegrity = exports.checkDiskSpace = exports.checkDependencies = exports.checkAuthentication = exports.checkUncommittedChanges = exports.checkGitHealth = exports.checkMissingRepositories = 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 git_status_1 = require("./git-status");
42
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
43
+ const checkMissingRepositories = async (workspacePath, metadata) => {
44
+ const missingRepos = [];
45
+ for (const repo of metadata.repositories) {
46
+ const repoPath = path.join(workspacePath, repo.directoryName);
47
+ try {
48
+ await fs.access(repoPath);
49
+ }
50
+ catch {
51
+ missingRepos.push(repo.directoryName);
52
+ }
53
+ }
54
+ if (missingRepos.length === 0) {
55
+ return {
56
+ category: 'Missing Repositories',
57
+ status: 'healthy',
58
+ message: 'All repositories are present',
59
+ };
60
+ }
61
+ return {
62
+ category: 'Missing Repositories',
63
+ status: 'error',
64
+ message: `${missingRepos.length} repository(ies) missing`,
65
+ details: missingRepos.join(', '),
66
+ actionable: `Run 'workspace sync ${metadata.workspaceName}' to clone missing repositories`,
67
+ };
68
+ };
69
+ exports.checkMissingRepositories = checkMissingRepositories;
70
+ const checkGitHealth = async (workspacePath, repoNames) => {
71
+ const issues = [];
72
+ for (const repoName of repoNames) {
73
+ const repoPath = path.join(workspacePath, repoName);
74
+ try {
75
+ await fs.access(repoPath);
76
+ }
77
+ catch {
78
+ continue; // Skip missing repos (handled by checkMissingRepositories)
79
+ }
80
+ const isRepo = await (0, git_status_1.isGitRepository)(repoPath);
81
+ if (!isRepo) {
82
+ issues.push(`${repoName}: not a git repository`);
83
+ continue;
84
+ }
85
+ // Check for corrupted repo
86
+ try {
87
+ await execAsync('git fsck --no-progress', {
88
+ cwd: repoPath,
89
+ timeout: 30000,
90
+ });
91
+ }
92
+ catch (error) {
93
+ issues.push(`${repoName}: repository may be corrupted`);
94
+ }
95
+ }
96
+ if (issues.length === 0) {
97
+ return {
98
+ category: 'Git Health',
99
+ status: 'healthy',
100
+ message: 'All repositories are healthy',
101
+ };
102
+ }
103
+ return {
104
+ category: 'Git Health',
105
+ status: 'error',
106
+ message: `${issues.length} repository(ies) have issues`,
107
+ details: issues.join('\n'),
108
+ actionable: 'Check repository integrity and consider re-cloning problematic repos',
109
+ };
110
+ };
111
+ exports.checkGitHealth = checkGitHealth;
112
+ const checkUncommittedChanges = async (workspacePath, repoNames) => {
113
+ const dirtyRepos = [];
114
+ for (const repoName of repoNames) {
115
+ const repoPath = path.join(workspacePath, repoName);
116
+ try {
117
+ await fs.access(repoPath);
118
+ }
119
+ catch {
120
+ continue;
121
+ }
122
+ const hasChanges = await (0, git_status_1.hasUncommittedChanges)(repoPath);
123
+ if (hasChanges) {
124
+ dirtyRepos.push(repoName);
125
+ }
126
+ }
127
+ if (dirtyRepos.length === 0) {
128
+ return {
129
+ category: 'Uncommitted Changes',
130
+ status: 'healthy',
131
+ message: 'No uncommitted changes',
132
+ };
133
+ }
134
+ return {
135
+ category: 'Uncommitted Changes',
136
+ status: 'warning',
137
+ message: `${dirtyRepos.length} repository(ies) have uncommitted changes`,
138
+ details: dirtyRepos.join(', '),
139
+ actionable: `Review and commit or stash changes before switching branches`,
140
+ };
141
+ };
142
+ exports.checkUncommittedChanges = checkUncommittedChanges;
143
+ const checkAuthentication = async () => {
144
+ const issues = [];
145
+ // Check GitHub CLI
146
+ try {
147
+ await execAsync('gh auth status', { timeout: 5000 });
148
+ }
149
+ catch {
150
+ issues.push('GitHub CLI (gh) not authenticated');
151
+ }
152
+ // Check SSH keys
153
+ try {
154
+ await execAsync('ssh -T git@github.com', { timeout: 5000 });
155
+ }
156
+ catch (error) {
157
+ const errorMessage = error instanceof Error ? error.message : '';
158
+ if (!errorMessage.includes('successfully authenticated')) {
159
+ issues.push('SSH key not configured for GitHub');
160
+ }
161
+ }
162
+ if (issues.length === 0) {
163
+ return {
164
+ category: 'Authentication',
165
+ status: 'healthy',
166
+ message: 'Authentication configured correctly',
167
+ };
168
+ }
169
+ return {
170
+ category: 'Authentication',
171
+ status: 'warning',
172
+ message: 'Authentication issues detected',
173
+ details: issues.join('\n'),
174
+ actionable: 'Run "gh auth login" or configure SSH keys',
175
+ };
176
+ };
177
+ exports.checkAuthentication = checkAuthentication;
178
+ const checkDependencies = async (workspacePath, repoNames) => {
179
+ const missingDeps = [];
180
+ for (const repoName of repoNames) {
181
+ const repoPath = path.join(workspacePath, repoName);
182
+ try {
183
+ await fs.access(repoPath);
184
+ }
185
+ catch {
186
+ continue;
187
+ }
188
+ // Check for package.json
189
+ const packageJsonPath = path.join(repoPath, 'package.json');
190
+ try {
191
+ await fs.access(packageJsonPath);
192
+ // Check if node_modules exists
193
+ const nodeModulesPath = path.join(repoPath, 'node_modules');
194
+ try {
195
+ await fs.access(nodeModulesPath);
196
+ }
197
+ catch {
198
+ missingDeps.push(`${repoName}: missing node_modules`);
199
+ }
200
+ }
201
+ catch {
202
+ // No package.json, skip
203
+ }
204
+ }
205
+ if (missingDeps.length === 0) {
206
+ return {
207
+ category: 'Dependencies',
208
+ status: 'healthy',
209
+ message: 'All dependencies installed',
210
+ };
211
+ }
212
+ return {
213
+ category: 'Dependencies',
214
+ status: 'warning',
215
+ message: `${missingDeps.length} repository(ies) missing dependencies`,
216
+ details: missingDeps.join('\n'),
217
+ actionable: 'Run "npm install" or "yarn install" in affected repositories',
218
+ };
219
+ };
220
+ exports.checkDependencies = checkDependencies;
221
+ const checkDiskSpace = async (workspacePath) => {
222
+ try {
223
+ // Get disk usage for workspace
224
+ const { stdout } = await execAsync(`du -sh "${workspacePath}"`, { timeout: 30000 });
225
+ const sizeStr = stdout.trim().split('\t')[0];
226
+ // Parse size (rough check for > 10GB)
227
+ const sizeValue = parseFloat(sizeStr);
228
+ const unit = sizeStr.replace(/[0-9.]/g, '').trim();
229
+ const isLarge = (unit === 'G' && sizeValue > 10) || unit === 'T';
230
+ if (isLarge) {
231
+ return {
232
+ category: 'Disk Space',
233
+ status: 'warning',
234
+ message: `Workspace is large (${sizeStr})`,
235
+ actionable: 'Consider running "workspace cleanup" to free up space',
236
+ };
237
+ }
238
+ return {
239
+ category: 'Disk Space',
240
+ status: 'healthy',
241
+ message: `Workspace size: ${sizeStr}`,
242
+ };
243
+ }
244
+ catch (error) {
245
+ return {
246
+ category: 'Disk Space',
247
+ status: 'warning',
248
+ message: 'Could not calculate disk usage',
249
+ };
250
+ }
251
+ };
252
+ exports.checkDiskSpace = checkDiskSpace;
253
+ const checkMetadataIntegrity = async (workspacePath, metadata) => {
254
+ const issues = [];
255
+ // Check required fields
256
+ if (!metadata.workspaceName) {
257
+ issues.push('Missing workspace name');
258
+ }
259
+ if (!metadata.createdAt) {
260
+ issues.push('Missing creation timestamp');
261
+ }
262
+ if (!metadata.repositories || metadata.repositories.length === 0) {
263
+ issues.push('No repositories in metadata');
264
+ }
265
+ // Check for duplicate directory names
266
+ const dirNames = metadata.repositories.map(r => r.directoryName);
267
+ const uniqueDirNames = new Set(dirNames);
268
+ if (dirNames.length !== uniqueDirNames.size) {
269
+ issues.push('Duplicate directory names in metadata');
270
+ }
271
+ // Check for extra directories not in metadata
272
+ try {
273
+ const entries = await fs.readdir(workspacePath, { withFileTypes: true });
274
+ const directories = entries.filter(e => e.isDirectory()).map(e => e.name);
275
+ const metadataDirNames = new Set(dirNames);
276
+ const extraDirs = directories.filter(dir => !metadataDirNames.has(dir) && !dir.startsWith('.'));
277
+ if (extraDirs.length > 0) {
278
+ issues.push(`${extraDirs.length} directories not tracked in metadata: ${extraDirs.join(', ')}`);
279
+ }
280
+ }
281
+ catch (error) {
282
+ // Ignore read errors
283
+ }
284
+ if (issues.length === 0) {
285
+ return {
286
+ category: 'Metadata Integrity',
287
+ status: 'healthy',
288
+ message: 'Metadata is valid',
289
+ };
290
+ }
291
+ return {
292
+ category: 'Metadata Integrity',
293
+ status: 'warning',
294
+ message: 'Metadata issues detected',
295
+ details: issues.join('\n'),
296
+ actionable: 'Review workspace metadata file',
297
+ };
298
+ };
299
+ exports.checkMetadataIntegrity = checkMetadataIntegrity;
300
+ const checkGhqInstalled = async () => {
301
+ try {
302
+ await execAsync('which ghq', { timeout: 5000 });
303
+ return {
304
+ category: 'ghq',
305
+ status: 'healthy',
306
+ message: 'ghq is installed (fast cached clones enabled)',
307
+ };
308
+ }
309
+ catch {
310
+ return {
311
+ category: 'ghq',
312
+ status: 'warning',
313
+ message: 'ghq is not installed',
314
+ details: 'Without ghq, repos are cloned directly with git — slower, no shared cache, and large repos can hit clone timeouts.',
315
+ actionable: 'Install ghq: brew install ghq (macOS) — https://github.com/x-motemen/ghq#installation',
316
+ };
317
+ }
318
+ };
319
+ exports.checkGhqInstalled = checkGhqInstalled;
320
+ const runAllHealthChecks = async (workspacePath, metadata) => {
321
+ const repoNames = metadata.repositories.map(r => r.directoryName);
322
+ const checks = await Promise.all([
323
+ (0, exports.checkMissingRepositories)(workspacePath, metadata),
324
+ (0, exports.checkGitHealth)(workspacePath, repoNames),
325
+ (0, exports.checkUncommittedChanges)(workspacePath, repoNames),
326
+ (0, exports.checkAuthentication)(),
327
+ (0, exports.checkGhqInstalled)(),
328
+ (0, exports.checkDependencies)(workspacePath, repoNames),
329
+ (0, exports.checkDiskSpace)(workspacePath),
330
+ (0, exports.checkMetadataIntegrity)(workspacePath, metadata),
331
+ ]);
332
+ return checks;
333
+ };
334
+ exports.runAllHealthChecks = runAllHealthChecks;
335
+ const calculateHealthScore = (results) => {
336
+ const weights = {
337
+ healthy: 100,
338
+ warning: 60,
339
+ error: 0,
340
+ };
341
+ const totalWeight = results.reduce((sum, result) => sum + weights[result.status], 0);
342
+ const maxWeight = results.length * 100;
343
+ return Math.round((totalWeight / maxWeight) * 100);
344
+ };
345
+ exports.calculateHealthScore = calculateHealthScore;