@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.
- package/CHANGELOG.md +86 -0
- package/LICENSE +21 -0
- package/README.md +275 -0
- package/assets/README.md +53 -0
- package/assets/banner.png +0 -0
- package/assets/banner.svg +66 -0
- package/assets/favicon-32.png +0 -0
- package/assets/favicon-64.png +0 -0
- package/assets/icon-256.png +0 -0
- package/assets/icon.svg +23 -0
- package/assets/logo-256.png +0 -0
- package/assets/logo-512.png +0 -0
- package/assets/logo-wordmark.svg +35 -0
- package/assets/logo.svg +32 -0
- package/bin/workspace.js +130 -0
- package/dist/cli/ai-prompt.js +421 -0
- package/dist/cli/dashboard/AgentList.js +61 -0
- package/dist/cli/dashboard/DashboardSidebar.js +280 -0
- package/dist/cli/dashboard/HelpFooter.js +73 -0
- package/dist/cli/dashboard/StatusBadge.js +50 -0
- package/dist/cli/dashboard/index.js +11 -0
- package/dist/cli/dashboard/types.js +2 -0
- package/dist/cli/tui.js +48 -0
- package/dist/commands/ai-prompt.js +22 -0
- package/dist/commands/analyze-deps.js +138 -0
- package/dist/commands/archive.js +113 -0
- package/dist/commands/branch/create.js +119 -0
- package/dist/commands/branch/index.js +104 -0
- package/dist/commands/branch/merge.js +90 -0
- package/dist/commands/branch/rebase.js +84 -0
- package/dist/commands/branch/switch.js +165 -0
- package/dist/commands/cache/index.js +80 -0
- package/dist/commands/cache/manager.js +198 -0
- package/dist/commands/cleanup.js +153 -0
- package/dist/commands/configure-claude.js +55 -0
- package/dist/commands/configure.js +231 -0
- package/dist/commands/create.js +204 -0
- package/dist/commands/dashboard/hook-handler.js +162 -0
- package/dist/commands/dashboard/index.js +258 -0
- package/dist/commands/dashboard/launcher.js +40 -0
- package/dist/commands/dashboard/session-picker.js +106 -0
- package/dist/commands/dashboard/sidebar.js +39 -0
- package/dist/commands/dashboard/workspace-picker.js +95 -0
- package/dist/commands/delete.js +194 -0
- package/dist/commands/deprecated-aliases.js +44 -0
- package/dist/commands/diff.js +129 -0
- package/dist/commands/doctor.js +141 -0
- package/dist/commands/generate-docs.js +77 -0
- package/dist/commands/ghq-status.js +72 -0
- package/dist/commands/go.js +154 -0
- package/dist/commands/history.js +124 -0
- package/dist/commands/list.js +191 -0
- package/dist/commands/mcp/index.js +69 -0
- package/dist/commands/migrate.js +296 -0
- package/dist/commands/remove-repo.js +217 -0
- package/dist/commands/report-bug.js +112 -0
- package/dist/commands/run.js +147 -0
- package/dist/commands/save-context.js +171 -0
- package/dist/commands/sessions.js +129 -0
- package/dist/commands/status.js +138 -0
- package/dist/commands/suite/create.js +212 -0
- package/dist/commands/suite/delete.js +64 -0
- package/dist/commands/suite/export.js +126 -0
- package/dist/commands/suite/import.js +128 -0
- package/dist/commands/suite/index.js +94 -0
- package/dist/commands/suite/list.js +49 -0
- package/dist/commands/suite/use.js +227 -0
- package/dist/commands/sync.js +117 -0
- package/dist/commands/update.js +203 -0
- package/dist/mcp/install.js +550 -0
- package/dist/mcp/server.js +398 -0
- package/dist/mcp/tools.js +922 -0
- package/dist/pi-extensions/permission-sync.ts.template +41 -0
- package/dist/pi-extensions/permissions-gate.ts.template +69 -0
- package/dist/pi-extensions/pr-fix-trigger.ts.template +124 -0
- package/dist/pi-extensions/review-reminder.ts.template +58 -0
- package/dist/pi-extensions/workspace-input-status.ts.template +919 -0
- package/dist/program.js +136 -0
- package/dist/scripts/workspace-table.py +394 -0
- package/dist/types/dashboard.js +11 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/agent-config.js +263 -0
- package/dist/utils/agent-state.js +178 -0
- package/dist/utils/banner.js +24 -0
- package/dist/utils/branch-operations.js +232 -0
- package/dist/utils/bug-report.js +355 -0
- package/dist/utils/cache.js +168 -0
- package/dist/utils/claude-integration.js +617 -0
- package/dist/utils/claude-sessions.js +260 -0
- package/dist/utils/cleanup-operations.js +120 -0
- package/dist/utils/colors.js +26 -0
- package/dist/utils/command-helpers.js +44 -0
- package/dist/utils/config.js +150 -0
- package/dist/utils/context-file.js +33 -0
- package/dist/utils/dashboard-hooks.js +172 -0
- package/dist/utils/dependency-analyzer.js +234 -0
- package/dist/utils/diff-operations.js +80 -0
- package/dist/utils/doc-generator.js +184 -0
- package/dist/utils/ghq-integration.js +332 -0
- package/dist/utils/git-operations.js +237 -0
- package/dist/utils/git-status.js +187 -0
- package/dist/utils/github.js +68 -0
- package/dist/utils/health-checks.js +345 -0
- package/dist/utils/history.js +150 -0
- package/dist/utils/hooks.js +100 -0
- package/dist/utils/logger.js +40 -0
- package/dist/utils/permission-sync.js +681 -0
- package/dist/utils/pi-extensions.js +224 -0
- package/dist/utils/progress.js +92 -0
- package/dist/utils/prompts.js +279 -0
- package/dist/utils/repo-resolver.js +260 -0
- package/dist/utils/retry.js +65 -0
- package/dist/utils/run-operations.js +30 -0
- package/dist/utils/suite.js +159 -0
- package/dist/utils/sync-operations.js +107 -0
- package/dist/utils/tmux-dashboard.js +184 -0
- package/dist/utils/validation.js +153 -0
- package/dist/utils/version-check.js +121 -0
- package/dist/utils/workspace-meta.js +185 -0
- package/install-shell-integration.sh +532 -0
- package/install.sh +401 -0
- package/package.json +90 -0
- package/remove-shell-block.awk +44 -0
- package/scripts/postinstall.js +85 -0
- package/skills/analyze-deps.md +23 -0
- package/skills/archive-workspace.md +26 -0
- package/skills/branch-create.md +18 -0
- package/skills/create-workspace.md +39 -0
- package/skills/delete-workspace.md +37 -0
- package/skills/list-org-repos.md +26 -0
- package/skills/list-suites.md +24 -0
- package/skills/list-workspaces.md +27 -0
- package/skills/refresh-workspace-docs.md +89 -0
- package/skills/remove-repo.md +25 -0
- package/skills/run-command.md +23 -0
- package/skills/save-context.md +68 -0
- package/skills/search-repos.md +31 -0
- package/skills/snapshot-list.md +18 -0
- package/skills/snapshot-save.md +12 -0
- package/skills/switch-branch.md +18 -0
- package/skills/update-cache.md +21 -0
- package/skills/update-workspace.md +66 -0
- package/skills/workspace-cleanup.md +18 -0
- package/skills/workspace-diff.md +23 -0
- package/skills/workspace-doctor.md +23 -0
- package/skills/workspace-info.md +45 -0
- package/skills/workspace-manager/SKILL.md +201 -0
- package/skills/workspace-manager/references/ai-prompt.md +44 -0
- package/skills/workspace-manager/references/analyze-deps.md +45 -0
- package/skills/workspace-manager/references/archive-workspace.md +38 -0
- package/skills/workspace-manager/references/branch-create.md +31 -0
- package/skills/workspace-manager/references/branch-merge.md +22 -0
- package/skills/workspace-manager/references/branch-rebase.md +22 -0
- package/skills/workspace-manager/references/cache-clear.md +18 -0
- package/skills/workspace-manager/references/cache-info.md +22 -0
- package/skills/workspace-manager/references/configure-claude.md +31 -0
- package/skills/workspace-manager/references/configure.md +35 -0
- package/skills/workspace-manager/references/create-workspace.md +45 -0
- package/skills/workspace-manager/references/dashboard.md +61 -0
- package/skills/workspace-manager/references/delete-workspace.md +33 -0
- package/skills/workspace-manager/references/generate-docs.md +36 -0
- package/skills/workspace-manager/references/ghq-status.md +28 -0
- package/skills/workspace-manager/references/go.md +33 -0
- package/skills/workspace-manager/references/history.md +41 -0
- package/skills/workspace-manager/references/list-org-repos.md +26 -0
- package/skills/workspace-manager/references/list-suites.md +21 -0
- package/skills/workspace-manager/references/list-workspaces.md +27 -0
- package/skills/workspace-manager/references/mcp.md +49 -0
- package/skills/workspace-manager/references/remove-repo.md +26 -0
- package/skills/workspace-manager/references/run-command.md +44 -0
- package/skills/workspace-manager/references/search-repos.md +24 -0
- package/skills/workspace-manager/references/sessions.md +27 -0
- package/skills/workspace-manager/references/suite-create.md +22 -0
- package/skills/workspace-manager/references/suite-delete.md +21 -0
- package/skills/workspace-manager/references/suite-export.md +19 -0
- package/skills/workspace-manager/references/suite-import.md +19 -0
- package/skills/workspace-manager/references/suite-use.md +27 -0
- package/skills/workspace-manager/references/switch-branch.md +25 -0
- package/skills/workspace-manager/references/update-cache.md +22 -0
- package/skills/workspace-manager/references/update-workspace.md +50 -0
- package/skills/workspace-manager/references/workspace-cleanup.md +33 -0
- package/skills/workspace-manager/references/workspace-diff.md +27 -0
- package/skills/workspace-manager/references/workspace-doctor.md +27 -0
- package/skills/workspace-manager/references/workspace-info.md +33 -0
- package/skills/workspace-manager/references/workspace-status.md +43 -0
- package/skills/workspace-manager/references/workspace-sync.md +26 -0
- package/skills/workspace-status.md +26 -0
- package/skills/workspace-sync.md +23 -0
- package/src/cli/ai-prompt.test.ts +667 -0
- package/src/cli/ai-prompt.ts +421 -0
- package/src/cli/dashboard/AgentList.tsx +44 -0
- package/src/cli/dashboard/DashboardSidebar.tsx +291 -0
- package/src/cli/dashboard/HelpFooter.tsx +22 -0
- package/src/cli/dashboard/StatusBadge.tsx +23 -0
- package/src/cli/dashboard/index.ts +5 -0
- package/src/cli/dashboard/types.ts +16 -0
- package/src/cli/tui.tsx +51 -0
- package/src/cli/workspace-cli.test.ts +30 -0
- package/src/commands/ai-prompt.ts +21 -0
- package/src/commands/analyze-deps.ts +119 -0
- package/src/commands/archive.ts +114 -0
- package/src/commands/branch/create.ts +94 -0
- package/src/commands/branch/index.ts +74 -0
- package/src/commands/branch/merge.ts +61 -0
- package/src/commands/branch/rebase.ts +54 -0
- package/src/commands/branch/switch.ts +150 -0
- package/src/commands/cache/index.ts +51 -0
- package/src/commands/cache/manager.ts +168 -0
- package/src/commands/cleanup.ts +132 -0
- package/src/commands/configure-claude.ts +55 -0
- package/src/commands/configure.ts +204 -0
- package/src/commands/create.ts +196 -0
- package/src/commands/dashboard/hook-handler.ts +138 -0
- package/src/commands/dashboard/index.ts +248 -0
- package/src/commands/dashboard/launcher.ts +42 -0
- package/src/commands/dashboard/session-picker.ts +73 -0
- package/src/commands/dashboard/sidebar.js +39 -0
- package/src/commands/dashboard/workspace-picker.ts +62 -0
- package/src/commands/delete.test.ts +250 -0
- package/src/commands/delete.ts +171 -0
- package/src/commands/deprecated-aliases.ts +55 -0
- package/src/commands/diff.ts +106 -0
- package/src/commands/doctor.ts +110 -0
- package/src/commands/generate-docs.ts +45 -0
- package/src/commands/ghq-status.ts +70 -0
- package/src/commands/go.ts +125 -0
- package/src/commands/history.ts +136 -0
- package/src/commands/list.test.ts +219 -0
- package/src/commands/list.ts +176 -0
- package/src/commands/mcp/index.ts +39 -0
- package/src/commands/migrate.ts +277 -0
- package/src/commands/remove-repo.ts +194 -0
- package/src/commands/report-bug.ts +88 -0
- package/src/commands/run.ts +125 -0
- package/src/commands/save-context.test.ts +113 -0
- package/src/commands/save-context.ts +141 -0
- package/src/commands/sessions.ts +99 -0
- package/src/commands/status.ts +111 -0
- package/src/commands/suite/create.ts +240 -0
- package/src/commands/suite/delete.ts +65 -0
- package/src/commands/suite/export.ts +97 -0
- package/src/commands/suite/import.ts +100 -0
- package/src/commands/suite/index.ts +66 -0
- package/src/commands/suite/list.ts +54 -0
- package/src/commands/suite/use.ts +232 -0
- package/src/commands/sync.ts +97 -0
- package/src/commands/update.ts +197 -0
- package/src/mcp/install.ts +529 -0
- package/src/mcp/server.ts +579 -0
- package/src/mcp/tools.test.ts +545 -0
- package/src/mcp/tools.ts +1019 -0
- package/src/mcp/update-workspace.test.ts +220 -0
- package/src/pi-extensions/permission-sync.ts.template +41 -0
- package/src/pi-extensions/permissions-gate.ts.template +69 -0
- package/src/pi-extensions/pr-fix-trigger.ts.template +124 -0
- package/src/pi-extensions/review-reminder.ts.template +58 -0
- package/src/pi-extensions/workspace-input-status.ts.template +919 -0
- package/src/program.ts +112 -0
- package/src/scripts/workspace-table.py +394 -0
- package/src/types/dashboard.ts +29 -0
- package/src/types/index.ts +109 -0
- package/src/types/ink.d.ts +171 -0
- package/src/utils/agent-config.test.ts +303 -0
- package/src/utils/agent-config.ts +264 -0
- package/src/utils/agent-state.test.ts +166 -0
- package/src/utils/agent-state.ts +135 -0
- package/src/utils/banner.ts +24 -0
- package/src/utils/branch-operations.ts +237 -0
- package/src/utils/bug-report.test.ts +174 -0
- package/src/utils/bug-report.ts +355 -0
- package/src/utils/cache.ts +150 -0
- package/src/utils/claude-integration.test.ts +381 -0
- package/src/utils/claude-integration.ts +649 -0
- package/src/utils/claude-sessions.test.ts +262 -0
- package/src/utils/claude-sessions.ts +251 -0
- package/src/utils/cleanup-operations.ts +94 -0
- package/src/utils/colors.ts +25 -0
- package/src/utils/command-helpers.ts +46 -0
- package/src/utils/config.test.ts +203 -0
- package/src/utils/config.ts +133 -0
- package/src/utils/context-file.ts +36 -0
- package/src/utils/dashboard-hooks.test.ts +168 -0
- package/src/utils/dashboard-hooks.ts +163 -0
- package/src/utils/dependency-analyzer.ts +243 -0
- package/src/utils/diff-operations.ts +59 -0
- package/src/utils/doc-generator.ts +187 -0
- package/src/utils/ghq-integration.test.ts +195 -0
- package/src/utils/ghq-integration.ts +317 -0
- package/src/utils/git-operations.test.ts +265 -0
- package/src/utils/git-operations.ts +233 -0
- package/src/utils/git-status.ts +171 -0
- package/src/utils/github.ts +74 -0
- package/src/utils/health-checks.ts +359 -0
- package/src/utils/history.ts +140 -0
- package/src/utils/hooks.ts +92 -0
- package/src/utils/logger.ts +37 -0
- package/src/utils/permission-sync.test.ts +593 -0
- package/src/utils/permission-sync.ts +720 -0
- package/src/utils/pi-extensions.test.ts +182 -0
- package/src/utils/pi-extensions.ts +226 -0
- package/src/utils/progress.ts +73 -0
- package/src/utils/prompts.test.ts +169 -0
- package/src/utils/prompts.ts +296 -0
- package/src/utils/repo-resolver.test.ts +313 -0
- package/src/utils/repo-resolver.ts +294 -0
- package/src/utils/retry.test.ts +124 -0
- package/src/utils/retry.ts +85 -0
- package/src/utils/run-operations.ts +38 -0
- package/src/utils/suite.test.ts +256 -0
- package/src/utils/suite.ts +144 -0
- package/src/utils/sync-operations.ts +87 -0
- package/src/utils/tmux-dashboard.ts +193 -0
- package/src/utils/validation.test.ts +139 -0
- package/src/utils/validation.ts +128 -0
- package/src/utils/version-check.test.ts +108 -0
- package/src/utils/version-check.ts +96 -0
- package/src/utils/workspace-meta.ts +173 -0
- package/sync-permissions.sh +137 -0
- package/tsconfig.json +20 -0
- 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
|
+
}
|