@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,106 @@
1
+ import { Command } from 'commander';
2
+ import * as path from 'path';
3
+ import { WORKSPACES_DIR } from '../utils/config';
4
+ import { loadMetadata } from '../utils/workspace-meta';
5
+ import { logError } from '../utils/logger';
6
+ import { colorize } from '../utils/colors';
7
+ import { resolveWorkspace } from '../utils/command-helpers';
8
+ import { getRepoDiff, RepoDiff } from '../utils/diff-operations';
9
+ import { exec } from 'child_process';
10
+ import { promisify } from 'util';
11
+
12
+ const execAsync = promisify(exec);
13
+
14
+ export function registerDiffCommand(parent: Command) {
15
+ parent
16
+ .command('diff')
17
+ .alias('di')
18
+ .description('Show diff summary across all repos')
19
+ .argument('[workspace]', 'Workspace name')
20
+ .option('--full', 'Show full diff output')
21
+ .action(async (workspace, opts) => {
22
+ await handleDiff(workspace, opts);
23
+ });
24
+ }
25
+
26
+ async function handleDiff(workspaceArg?: string, opts: { full?: boolean } = {}) {
27
+ try {
28
+ const selectedWorkspace = await resolveWorkspace(workspaceArg);
29
+ const workspacePath = path.join(WORKSPACES_DIR, selectedWorkspace);
30
+ const metadata = await loadMetadata(workspacePath);
31
+
32
+ if (!metadata) {
33
+ logError(`Workspace metadata not found for: ${selectedWorkspace}`);
34
+ process.exit(1);
35
+ }
36
+
37
+ const repos = metadata.repositories.filter(r => r.status === 'success');
38
+
39
+ console.log('\n' + colorize('Workspace Diff Summary', 'bright'));
40
+ console.log(colorize('═'.repeat(40), 'gray') + '\n');
41
+
42
+ const diffs: RepoDiff[] = [];
43
+ let totalStaged = 0;
44
+ let totalUnstaged = 0;
45
+ let reposWithChanges = 0;
46
+
47
+ for (const repo of repos) {
48
+ const repoPath = path.join(workspacePath, repo.directoryName);
49
+ const displayName = repo.directoryName !== repo.name
50
+ ? `${repo.directoryName} (${repo.name})`
51
+ : repo.name;
52
+
53
+ const diff = await getRepoDiff(repoPath, displayName);
54
+ diffs.push(diff);
55
+
56
+ if (diff.error) {
57
+ console.log(colorize(diff.repo, 'cyan'));
58
+ console.log(` ${colorize('⚠', 'yellow')} ${diff.error}\n`);
59
+ continue;
60
+ }
61
+
62
+ const hasChanges = diff.staged.files > 0 || diff.unstaged.files > 0;
63
+
64
+ if (hasChanges) {
65
+ reposWithChanges++;
66
+ totalStaged += diff.staged.files;
67
+ totalUnstaged += diff.unstaged.files;
68
+
69
+ console.log(colorize(diff.repo, 'cyan'));
70
+ if (diff.staged.files > 0) {
71
+ console.log(` Staged: ${diff.staged.files} file(s), ${colorize(`+${diff.staged.insertions}`, 'green')}, ${colorize(`-${diff.staged.deletions}`, 'red')}`);
72
+ }
73
+ if (diff.unstaged.files > 0) {
74
+ console.log(` Unstaged: ${diff.unstaged.files} file(s), ${colorize(`+${diff.unstaged.insertions}`, 'green')}, ${colorize(`-${diff.unstaged.deletions}`, 'red')}`);
75
+ }
76
+ console.log('');
77
+
78
+ if (opts.full) {
79
+ try {
80
+ const { stdout: fullDiff } = await execAsync('git diff', { cwd: path.join(workspacePath, repo.directoryName) });
81
+ if (fullDiff.trim()) {
82
+ console.log(colorize('--- Full diff ---', 'gray'));
83
+ console.log(fullDiff);
84
+ console.log('');
85
+ }
86
+ } catch {
87
+ // Ignore errors getting full diff
88
+ }
89
+ }
90
+ } else {
91
+ console.log(colorize(diff.repo, 'cyan'));
92
+ console.log(` ${colorize('✓', 'green')} Clean\n`);
93
+ }
94
+ }
95
+
96
+ console.log(colorize('═'.repeat(40), 'gray'));
97
+ console.log(`Total: ${colorize(String(totalStaged), 'green')} staged, ${colorize(String(totalUnstaged), 'yellow')} unstaged across ${reposWithChanges} repo(s)`);
98
+ console.log('');
99
+ } catch (error) {
100
+ logError('Failed to get workspace diff');
101
+ if (error instanceof Error) {
102
+ logError(error.message);
103
+ }
104
+ process.exit(1);
105
+ }
106
+ }
@@ -0,0 +1,110 @@
1
+ import { Command } from 'commander';
2
+ import * as path from 'path';
3
+ import { WORKSPACES_DIR } from '../utils/config';
4
+ import { loadMetadata } from '../utils/workspace-meta';
5
+ import { runAllHealthChecks, calculateHealthScore } from '../utils/health-checks';
6
+ import { logError, logInfo, logStep, logSuccess, logWarning } from '../utils/logger';
7
+ import { colorize } from '../utils/colors';
8
+ import { HealthCheckResult } from '../types';
9
+ import { resolveWorkspace } from '../utils/command-helpers';
10
+
11
+ const displayHealthCheck = (result: HealthCheckResult) => {
12
+ let statusIcon = '';
13
+ let statusColor: 'green' | 'yellow' | 'red' = 'green';
14
+
15
+ switch (result.status) {
16
+ case 'healthy':
17
+ statusIcon = '✓';
18
+ statusColor = 'green';
19
+ break;
20
+ case 'warning':
21
+ statusIcon = '⚠';
22
+ statusColor = 'yellow';
23
+ break;
24
+ case 'error':
25
+ statusIcon = '✗';
26
+ statusColor = 'red';
27
+ break;
28
+ }
29
+
30
+ console.log(`\n${colorize(statusIcon, statusColor)} ${colorize(result.category, 'bright')}`);
31
+ console.log(` ${result.message}`);
32
+ if (result.details) {
33
+ console.log(` ${colorize('Details:', 'gray')} ${result.details}`);
34
+ }
35
+ if (result.actionable) {
36
+ console.log(` ${colorize('→', 'cyan')} ${result.actionable}`);
37
+ }
38
+ };
39
+
40
+ const displayHealthScore = (score: number) => {
41
+ let scoreColor: 'green' | 'yellow' | 'red' = 'green';
42
+ let rating = 'Excellent';
43
+
44
+ if (score >= 80) { scoreColor = 'green'; rating = 'Excellent'; }
45
+ else if (score >= 60) { scoreColor = 'yellow'; rating = 'Good'; }
46
+ else if (score >= 40) { scoreColor = 'yellow'; rating = 'Fair'; }
47
+ else { scoreColor = 'red'; rating = 'Poor'; }
48
+
49
+ console.log('\n' + colorize('═'.repeat(60), 'gray'));
50
+ console.log(
51
+ `${colorize('Overall Health Score:', 'bright')} ${colorize(String(score), scoreColor)}/100 (${colorize(rating, scoreColor)})`
52
+ );
53
+ console.log(colorize('═'.repeat(60), 'gray') + '\n');
54
+ };
55
+
56
+ export function registerDoctorCommand(parent: Command) {
57
+ parent
58
+ .command('doctor')
59
+ .alias('doc')
60
+ .description('Run comprehensive health checks')
61
+ .argument('[workspace]', 'Workspace name')
62
+ .action(async (workspace) => {
63
+ await handleDoctor(workspace);
64
+ });
65
+ }
66
+
67
+ async function handleDoctor(workspaceArg?: string) {
68
+ try {
69
+ const selectedWorkspace = await resolveWorkspace(workspaceArg);
70
+ const workspacePath = path.join(WORKSPACES_DIR, selectedWorkspace);
71
+ const metadata = await loadMetadata(workspacePath);
72
+
73
+ if (!metadata) {
74
+ logError(`Workspace metadata not found for: ${selectedWorkspace}`);
75
+ process.exit(1);
76
+ }
77
+
78
+ logStep(`Running health checks for workspace: ${colorize(selectedWorkspace, 'cyan')}`);
79
+ logInfo('This may take a moment...');
80
+
81
+ const results = await runAllHealthChecks(workspacePath, metadata);
82
+ const score = calculateHealthScore(results);
83
+
84
+ console.log('\n' + colorize('Health Check Results', 'bright'));
85
+ console.log(colorize('═'.repeat(60), 'gray'));
86
+
87
+ for (const result of results) {
88
+ displayHealthCheck(result);
89
+ }
90
+
91
+ displayHealthScore(score);
92
+
93
+ const hasErrors = results.some(r => r.status === 'error');
94
+ const hasWarnings = results.some(r => r.status === 'warning');
95
+
96
+ if (hasErrors) {
97
+ logError('Critical issues detected. Please address error items above.');
98
+ } else if (hasWarnings) {
99
+ logWarning('Some warnings detected. Review and address when possible.');
100
+ } else {
101
+ logSuccess('All health checks passed! Your workspace is in good shape.');
102
+ }
103
+ } catch (error) {
104
+ logError('Failed to run health checks');
105
+ if (error instanceof Error) {
106
+ logError(error.message);
107
+ }
108
+ process.exit(1);
109
+ }
110
+ }
@@ -0,0 +1,45 @@
1
+ import { Command } from 'commander';
2
+ import * as path from 'path';
3
+ import { WORKSPACES_DIR } from '../utils/config';
4
+ import { loadMetadata } from '../utils/workspace-meta';
5
+ import { writeDocs } from '../utils/doc-generator';
6
+ import { logError, logStep, logSuccess } from '../utils/logger';
7
+ import { colorize } from '../utils/colors';
8
+ import { resolveWorkspace } from '../utils/command-helpers';
9
+
10
+ export function registerGenerateDocsCommand(parent: Command) {
11
+ parent
12
+ .command('generate-docs')
13
+ .alias('gd')
14
+ .description('Generate workspace documentation')
15
+ .argument('[workspace]', 'Workspace name')
16
+ .action(async (workspace) => {
17
+ await handleGenerateDocs(workspace);
18
+ });
19
+ }
20
+
21
+ async function handleGenerateDocs(workspaceArg?: string) {
22
+ try {
23
+ const workspaceName = await resolveWorkspace(workspaceArg);
24
+ const workspacePath = path.join(WORKSPACES_DIR, workspaceName);
25
+ const metadata = await loadMetadata(workspacePath);
26
+
27
+ if (!metadata) {
28
+ logError(`Workspace not found: ${workspaceName}`);
29
+ process.exit(1);
30
+ }
31
+
32
+ logStep(`Generating documentation for ${colorize(workspaceName, 'cyan')}...`);
33
+
34
+ await writeDocs(workspacePath, metadata);
35
+
36
+ logSuccess('Documentation generated:');
37
+ console.log(` • WORKSPACE-README.md`);
38
+ console.log(` • DEPENDENCY-GRAPH.md`);
39
+ console.log(` • REPOSITORY-INDEX.md`);
40
+ } catch (error) {
41
+ logError('Failed to generate documentation');
42
+ if (error instanceof Error) { logError(error.message); }
43
+ process.exit(1);
44
+ }
45
+ }
@@ -0,0 +1,70 @@
1
+ import { Command } from 'commander';
2
+ import { getGhqStatus, displayGhqInfo, ghqList } from '../utils/ghq-integration';
3
+ import { logSuccess, logWarning, logError } from '../utils/logger';
4
+ import { colorize } from '../utils/colors';
5
+ import inquirer from 'inquirer';
6
+
7
+ export function registerGhqStatusCommand(parent: Command) {
8
+ parent
9
+ .command('ghq-status')
10
+ .description('Check ghq integration status')
11
+ .action(async () => {
12
+ await handleGhqStatus();
13
+ });
14
+ }
15
+
16
+ async function handleGhqStatus() {
17
+ console.log('\n' + '='.repeat(60));
18
+ console.log(colorize('ghq Integration Status', 'bright'));
19
+ console.log('='.repeat(60) + '\n');
20
+
21
+ try {
22
+ const status = await getGhqStatus();
23
+
24
+ if (status.installed) {
25
+ logSuccess('ghq is installed and active');
26
+ console.log('');
27
+ console.log(colorize('Configuration:', 'cyan'));
28
+ console.log(` Root directory: ${colorize(status.root || 'unknown', 'gray')}`);
29
+ console.log(` Managed repositories: ${colorize(String(status.repoCount || 0), 'yellow')}`);
30
+ console.log('');
31
+
32
+ if (status.repoCount && status.repoCount > 0) {
33
+ const { showRepos } = await inquirer.prompt([{
34
+ type: 'confirm', name: 'showRepos',
35
+ message: `View all ${status.repoCount} managed repositories?`, default: false,
36
+ }]);
37
+
38
+ if (showRepos) {
39
+ const repos = await ghqList();
40
+ console.log('');
41
+ console.log(colorize('Managed Repositories:', 'cyan'));
42
+ repos.forEach((repo, index) => { console.log(` ${index + 1}. ${repo}`); });
43
+ console.log('');
44
+ }
45
+ }
46
+ } else {
47
+ logWarning('ghq is not installed');
48
+ console.log('');
49
+ console.log('The workspace manager will use direct git clones.');
50
+
51
+ const { showInfo } = await inquirer.prompt([{
52
+ type: 'confirm', name: 'showInfo',
53
+ message: 'Show ghq installation information?', default: true,
54
+ }]);
55
+
56
+ if (showInfo) { displayGhqInfo(); }
57
+ }
58
+
59
+ console.log(colorize('Workspace Manager Behavior:', 'cyan'));
60
+ console.log(' • Auto-detects ghq installation');
61
+ console.log(' • Uses ghq when available');
62
+ console.log(' • Falls back to git clone if needed');
63
+ console.log(' • Always maintains workspace isolation');
64
+ console.log('');
65
+ } catch (error) {
66
+ logError('Failed to check ghq status');
67
+ if (error instanceof Error) { logError(error.message); }
68
+ process.exit(1);
69
+ }
70
+ }
@@ -0,0 +1,125 @@
1
+ import { Command } from 'commander';
2
+ import * as path from 'path';
3
+ import { WORKSPACES_DIR } from '../utils/config';
4
+ import * as fs from 'fs/promises';
5
+ import * as os from 'os';
6
+ import { listWorkspaces } from '../utils/workspace-meta';
7
+ import { getWorkspaceSessions } from '../utils/claude-sessions';
8
+ import { logError } from '../utils/logger';
9
+ import { colorize } from '../utils/colors';
10
+ import inquirer from 'inquirer';
11
+ import autocompletePrompt from 'inquirer-autocomplete-prompt';
12
+ import * as fuzzy from 'fuzzy';
13
+
14
+ inquirer.registerPrompt('autocomplete', autocompletePrompt);
15
+
16
+ const TEMP_FILE = path.join(os.homedir(), '.workspace-last-go');
17
+ const RESUME_FLAG_FILE = path.join(os.homedir(), '.workspace-resume-session');
18
+
19
+ export function registerGoCommand(parent: Command) {
20
+ parent
21
+ .command('go')
22
+ .description('Quick navigate to workspace')
23
+ .argument('[workspace]', 'Workspace name')
24
+ .action(async (workspace) => {
25
+ await handleGo(workspace);
26
+ });
27
+ }
28
+
29
+ async function handleGo(workspaceArg?: string) {
30
+ try {
31
+ let selectedWorkspace: string;
32
+
33
+ if (workspaceArg) {
34
+ selectedWorkspace = workspaceArg;
35
+ } else {
36
+ const [workspaces, sessions] = await Promise.all([
37
+ listWorkspaces(),
38
+ getWorkspaceSessions(),
39
+ ]);
40
+
41
+ if (workspaces.length === 0) {
42
+ logError('No workspaces found');
43
+ process.exit(1);
44
+ return;
45
+ }
46
+
47
+ // Build session map, keeping most-recent session per workspace
48
+ // (sessions are sorted most-recent-first, so first entry wins)
49
+ const sessionMap = new Map<string, typeof sessions[0]>();
50
+ for (const session of sessions) {
51
+ if (!sessionMap.has(session.workspaceName)) {
52
+ sessionMap.set(session.workspaceName, session);
53
+ }
54
+ }
55
+
56
+ const items = workspaces.map(ws => ({
57
+ name: ws.name,
58
+ repoCount: ws.metadata?.repositories?.length ?? 0,
59
+ lastActiveLabel: sessionMap.get(ws.name)?.lastActiveLabel ?? null,
60
+ lastActiveAt: sessionMap.get(ws.name)?.lastActiveAt.getTime() ?? 0,
61
+ hasSession: sessionMap.has(ws.name),
62
+ }));
63
+
64
+ items.sort((a, b) => {
65
+ if (a.hasSession && b.hasSession) return b.lastActiveAt - a.lastActiveAt;
66
+ if (a.hasSession) return -1;
67
+ if (b.hasSession) return 1;
68
+ return 0;
69
+ });
70
+
71
+ const { workspaceName } = await inquirer.prompt([
72
+ {
73
+ type: 'autocomplete',
74
+ name: 'workspaceName',
75
+ message: 'Select workspace to navigate to:',
76
+ source: async (_answersSoFar: any, input: string | undefined) => {
77
+ const searchInput = input || '';
78
+ const source = items.map(item => ({
79
+ name: `${item.name} ${item.lastActiveLabel ? colorize(item.lastActiveLabel, 'dim') : colorize('no session', 'dim')} ${colorize(`${item.repoCount} repos`, 'dim')}`,
80
+ value: item.name,
81
+ }));
82
+ if (!searchInput) return source;
83
+ const results = fuzzy.filter(searchInput, items, { extract: (item) => item.name });
84
+ return results.map(result => ({
85
+ name: `${result.original.name} ${result.original.lastActiveLabel ? colorize(result.original.lastActiveLabel, 'dim') : colorize('no session', 'dim')} ${colorize(`${result.original.repoCount} repos`, 'dim')}`,
86
+ value: result.original.name,
87
+ }));
88
+ },
89
+ pageSize: 15,
90
+ } as any,
91
+ ]);
92
+
93
+ selectedWorkspace = workspaceName;
94
+ }
95
+
96
+ const workspacePath = path.join(WORKSPACES_DIR, selectedWorkspace);
97
+
98
+ try {
99
+ await fs.access(workspacePath);
100
+ } catch {
101
+ logError(`Workspace not found: ${selectedWorkspace}`);
102
+ process.exit(1);
103
+ }
104
+
105
+ await fs.writeFile(TEMP_FILE, workspacePath, 'utf-8');
106
+
107
+ const sessions = await getWorkspaceSessions();
108
+ const session = sessions.find(s => s.workspaceName === selectedWorkspace);
109
+ if (session) {
110
+ // Write JSON with session ID and agent type for correct resume
111
+ await fs.writeFile(RESUME_FLAG_FILE, JSON.stringify({
112
+ sessionId: session.sessionId,
113
+ agentType: session.agentType,
114
+ }), 'utf-8');
115
+ }
116
+
117
+ console.log(workspacePath);
118
+ } catch (error) {
119
+ logError('Failed to navigate to workspace');
120
+ if (error instanceof Error) {
121
+ logError(error.message);
122
+ }
123
+ process.exit(1);
124
+ }
125
+ }
@@ -0,0 +1,136 @@
1
+ import { Command } from 'commander';
2
+ import { readHistory, getOperationStats, filterHistory, clearHistory } from '../utils/history';
3
+ import { logError, logInfo, logStep, logSuccess } from '../utils/logger';
4
+ import { colorize } from '../utils/colors';
5
+ import inquirer from 'inquirer';
6
+ import { getGlobalOpts } from '../utils/command-helpers';
7
+
8
+ const displayHistoryTable = (records: Array<{ timestamp: string; command: string; workspace?: string; duration: number; success: boolean; error?: string }>) => {
9
+ if (records.length === 0) {
10
+ logInfo('No operation history found');
11
+ return;
12
+ }
13
+
14
+ console.log('\n' + colorize('═'.repeat(120), 'gray'));
15
+ console.log(
16
+ colorize('Time', 'bright').padEnd(25) +
17
+ colorize('Command', 'bright').padEnd(25) +
18
+ colorize('Workspace', 'bright').padEnd(25) +
19
+ colorize('Duration', 'bright').padEnd(15) +
20
+ colorize('Status', 'bright')
21
+ );
22
+ console.log(colorize('─'.repeat(120), 'gray'));
23
+
24
+ for (const record of records) {
25
+ const time = new Date(record.timestamp).toLocaleString().padEnd(15);
26
+ const command = record.command.padEnd(15);
27
+ const workspace = (record.workspace || '-').padEnd(15);
28
+ const duration = `${(record.duration / 1000).toFixed(1)}s`.padEnd(10);
29
+ const status = record.success
30
+ ? colorize('✓ Success', 'green')
31
+ : colorize('✗ Failed', 'red') + (record.error ? ` (${record.error})` : '');
32
+
33
+ console.log(`${time} ${command} ${workspace} ${duration} ${status}`);
34
+ }
35
+
36
+ console.log(colorize('═'.repeat(120), 'gray') + '\n');
37
+ };
38
+
39
+ const displayStats = (stats: { totalOperations: number; successRate: number; avgDuration: number; commandCounts: Record<string, number> }) => {
40
+ console.log(colorize('Operation Statistics', 'bright'));
41
+ console.log(colorize('─'.repeat(60), 'gray'));
42
+ console.log(`Total Operations: ${colorize(String(stats.totalOperations), 'cyan')}`);
43
+ console.log(`Success Rate: ${colorize(`${stats.successRate.toFixed(1)}%`, 'green')}`);
44
+ console.log(`Average Duration: ${colorize(`${(stats.avgDuration / 1000).toFixed(1)}s`, 'yellow')}`);
45
+
46
+ console.log(`\n${colorize('Command Usage', 'bright')}`);
47
+ const sortedCommands = Object.entries(stats.commandCounts).sort((a, b) => b[1] - a[1]);
48
+
49
+ for (const [command, count] of sortedCommands) {
50
+ console.log(` ${command.padEnd(20)} ${colorize(String(count), 'cyan')} times`);
51
+ }
52
+ console.log('');
53
+ };
54
+
55
+ export function registerHistoryCommand(parent: Command) {
56
+ const history = parent
57
+ .command('history')
58
+ .alias('h')
59
+ .description('View operation history');
60
+
61
+ history
62
+ .command('show', { isDefault: true })
63
+ .description('Show recent operations')
64
+ .argument('[limit]', 'Number of records to show', '20')
65
+ .option('--command <cmd>', 'Filter by command')
66
+ .option('--workspace <name>', 'Filter by workspace')
67
+ .action(async (limit, opts) => {
68
+ await handleHistoryShow(parseInt(limit) || 20, opts);
69
+ });
70
+
71
+ history
72
+ .command('stats')
73
+ .description('Show detailed statistics')
74
+ .action(async () => {
75
+ logStep('Calculating statistics...');
76
+ const stats = await getOperationStats();
77
+ displayStats(stats);
78
+ });
79
+
80
+ history
81
+ .command('clear')
82
+ .description('Clear all history')
83
+ .action(async (opts, cmd) => {
84
+ const globalOpts = getGlobalOpts(cmd);
85
+ if (globalOpts.yes) {
86
+ await clearHistory();
87
+ logSuccess('Operation history cleared');
88
+ return;
89
+ }
90
+
91
+ const { confirm } = await inquirer.prompt([
92
+ {
93
+ type: 'confirm',
94
+ name: 'confirm',
95
+ message: 'Are you sure you want to clear operation history?',
96
+ default: false,
97
+ },
98
+ ]);
99
+
100
+ if (confirm) {
101
+ await clearHistory();
102
+ logSuccess('Operation history cleared');
103
+ } else {
104
+ logInfo('Operation cancelled');
105
+ }
106
+ });
107
+ }
108
+
109
+ async function handleHistoryShow(limit: number, opts: { command?: string; workspace?: string }) {
110
+ try {
111
+ logStep(`Fetching operation history (last ${limit} records)...`);
112
+
113
+ let records;
114
+ if (opts.command || opts.workspace) {
115
+ records = await filterHistory(opts.command, opts.workspace);
116
+ records = records.slice(0, limit);
117
+ } else {
118
+ records = await readHistory(limit);
119
+ }
120
+
121
+ displayHistoryTable(records);
122
+
123
+ const stats = await getOperationStats();
124
+ console.log(colorize('Quick Stats:', 'bright'));
125
+ console.log(` Total Operations: ${stats.totalOperations}`);
126
+ console.log(` Success Rate: ${stats.successRate.toFixed(1)}%`);
127
+ console.log(` Average Duration: ${(stats.avgDuration / 1000).toFixed(1)}s`);
128
+ console.log('');
129
+ } catch (error) {
130
+ logError('Failed to retrieve history');
131
+ if (error instanceof Error) {
132
+ logError(error.message);
133
+ }
134
+ process.exit(1);
135
+ }
136
+ }