@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
package/src/program.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import { colors } from './utils/colors';
|
|
5
|
+
|
|
6
|
+
// Read version from package.json
|
|
7
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
8
|
+
|
|
9
|
+
const g = colors.green;
|
|
10
|
+
const d = colors.dim;
|
|
11
|
+
const b = colors.bright;
|
|
12
|
+
const r = colors.reset;
|
|
13
|
+
|
|
14
|
+
const INNER = 38;
|
|
15
|
+
const titleLine = `>_ Nemus`;
|
|
16
|
+
const titlePad = ' '.repeat(Math.max(0, INNER - 2 - titleLine.length));
|
|
17
|
+
const versionLine = `v${pkg.version} · multi-repo workspaces`;
|
|
18
|
+
const versionPad = ' '.repeat(Math.max(0, INNER - 7 - versionLine.length));
|
|
19
|
+
const bar = '─'.repeat(INNER);
|
|
20
|
+
const bannerText = `
|
|
21
|
+
${d} ╭${bar}╮${r}
|
|
22
|
+
${d} │${r} ${g}>_${r} ${b}Nemus${r}${titlePad}${d}│${r}
|
|
23
|
+
${d} │${r} ${d}${versionLine}${r}${versionPad}${d}│${r}
|
|
24
|
+
${d} ╰${bar}╯${r}
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
export const program = new Command();
|
|
28
|
+
|
|
29
|
+
program
|
|
30
|
+
.name('workspace')
|
|
31
|
+
.description('Multi-repo workspace manager')
|
|
32
|
+
.version(pkg.version, '-V, --version')
|
|
33
|
+
.option('-f, --force-refresh', 'Force refresh GitHub repos (skip cache)')
|
|
34
|
+
.option('-y, --yes', 'Skip confirmations')
|
|
35
|
+
.addHelpText('before', bannerText);
|
|
36
|
+
|
|
37
|
+
// Register top-level commands
|
|
38
|
+
import { registerCreateCommand } from './commands/create';
|
|
39
|
+
import { registerListCommand } from './commands/list';
|
|
40
|
+
import { registerUpdateCommand } from './commands/update';
|
|
41
|
+
import { registerDeleteCommand } from './commands/delete';
|
|
42
|
+
import { registerSyncCommand } from './commands/sync';
|
|
43
|
+
import { registerStatusCommand } from './commands/status';
|
|
44
|
+
import { registerDiffCommand } from './commands/diff';
|
|
45
|
+
import { registerRunCommand } from './commands/run';
|
|
46
|
+
import { registerGoCommand } from './commands/go';
|
|
47
|
+
import { registerDoctorCommand } from './commands/doctor';
|
|
48
|
+
import { registerAnalyzeDepsCommand } from './commands/analyze-deps';
|
|
49
|
+
import { registerHistoryCommand } from './commands/history';
|
|
50
|
+
import { registerCleanupCommand } from './commands/cleanup';
|
|
51
|
+
import { registerRemoveRepoCommand } from './commands/remove-repo';
|
|
52
|
+
import { registerArchiveCommand } from './commands/archive';
|
|
53
|
+
import { registerSessionsCommand } from './commands/sessions';
|
|
54
|
+
import { registerGenerateDocsCommand } from './commands/generate-docs';
|
|
55
|
+
import { registerConfigureCommand } from './commands/configure';
|
|
56
|
+
import { registerConfigureClaudeCommand } from './commands/configure-claude';
|
|
57
|
+
import { registerGhqStatusCommand } from './commands/ghq-status';
|
|
58
|
+
import { registerSaveContextCommand } from './commands/save-context';
|
|
59
|
+
import { registerMigrateCommand } from './commands/migrate';
|
|
60
|
+
import { registerReportBugCommand } from './commands/report-bug';
|
|
61
|
+
|
|
62
|
+
registerCreateCommand(program);
|
|
63
|
+
registerListCommand(program);
|
|
64
|
+
registerUpdateCommand(program);
|
|
65
|
+
registerDeleteCommand(program);
|
|
66
|
+
registerSyncCommand(program);
|
|
67
|
+
registerStatusCommand(program);
|
|
68
|
+
registerDiffCommand(program);
|
|
69
|
+
registerRunCommand(program);
|
|
70
|
+
registerGoCommand(program);
|
|
71
|
+
registerDoctorCommand(program);
|
|
72
|
+
registerAnalyzeDepsCommand(program);
|
|
73
|
+
registerHistoryCommand(program);
|
|
74
|
+
registerCleanupCommand(program);
|
|
75
|
+
registerRemoveRepoCommand(program);
|
|
76
|
+
registerArchiveCommand(program);
|
|
77
|
+
registerSessionsCommand(program);
|
|
78
|
+
registerGenerateDocsCommand(program);
|
|
79
|
+
registerConfigureCommand(program);
|
|
80
|
+
registerConfigureClaudeCommand(program);
|
|
81
|
+
registerGhqStatusCommand(program);
|
|
82
|
+
registerSaveContextCommand(program);
|
|
83
|
+
registerMigrateCommand(program);
|
|
84
|
+
registerReportBugCommand(program);
|
|
85
|
+
|
|
86
|
+
// Register TUI (delegates to existing Ink/React implementation)
|
|
87
|
+
program
|
|
88
|
+
.command('tui')
|
|
89
|
+
.description('Launch interactive terminal UI')
|
|
90
|
+
.action(async () => {
|
|
91
|
+
const { main } = await import('./cli/tui');
|
|
92
|
+
await main();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Register dashboard command
|
|
96
|
+
import { registerDashboardCommand } from './commands/dashboard';
|
|
97
|
+
registerDashboardCommand(program);
|
|
98
|
+
|
|
99
|
+
// Register grouped commands
|
|
100
|
+
import { registerSuiteCommands } from './commands/suite';
|
|
101
|
+
import { registerBranchCommands } from './commands/branch';
|
|
102
|
+
import { registerCacheCommands } from './commands/cache';
|
|
103
|
+
import { registerMcpCommands } from './commands/mcp';
|
|
104
|
+
|
|
105
|
+
registerSuiteCommands(program);
|
|
106
|
+
registerBranchCommands(program);
|
|
107
|
+
registerCacheCommands(program);
|
|
108
|
+
registerMcpCommands(program);
|
|
109
|
+
|
|
110
|
+
// Register deprecated aliases
|
|
111
|
+
import { registerDeprecatedAliases } from './commands/deprecated-aliases';
|
|
112
|
+
registerDeprecatedAliases(program);
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
workspace-table.py — Workspace Manager status-line extension for Claude Code
|
|
4
|
+
(auto-generated by workspace-manager)
|
|
5
|
+
|
|
6
|
+
Outputs ONLY the workspace repo table (branch · PR · CI · dirty state).
|
|
7
|
+
Claude Code already renders model / cost / context itself, so this script
|
|
8
|
+
adds only what it doesn't show.
|
|
9
|
+
|
|
10
|
+
Configure in ~/.claude/settings.json:
|
|
11
|
+
"statusLine": {
|
|
12
|
+
"type": "command",
|
|
13
|
+
"command": "~/.workspace-manager-table.py",
|
|
14
|
+
"padding": 0
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Claude Code passes session data as JSON on stdin.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import hashlib
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import subprocess
|
|
24
|
+
import sys
|
|
25
|
+
import time
|
|
26
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
# ─── Cache ───────────────────────────────────────────────────────────────────
|
|
30
|
+
CACHE_FILE = Path.home() / ".workspace-manager-cache" / "pr-status.json"
|
|
31
|
+
CACHE_TTL = 300 # seconds
|
|
32
|
+
|
|
33
|
+
# ─── ANSI helpers ─────────────────────────────────────────────────────────────
|
|
34
|
+
RESET = "\033[0m"
|
|
35
|
+
BOLD = "\033[1m"
|
|
36
|
+
DIM = "\033[2m"
|
|
37
|
+
CYAN = "\033[38;5;87m"
|
|
38
|
+
BLUE = "\033[38;5;75m"
|
|
39
|
+
MAGENTA = "\033[38;5;213m"
|
|
40
|
+
GREEN = "\033[38;5;120m"
|
|
41
|
+
YELLOW = "\033[38;5;221m"
|
|
42
|
+
RED = "\033[38;5;203m"
|
|
43
|
+
GRAY = "\033[38;5;245m"
|
|
44
|
+
PURPLE = "\033[38;5;141m"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def color(text: str, c: str) -> str:
|
|
48
|
+
return f"{c}{text}{RESET}"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def hyperlink(text: str, url: str | None) -> str:
|
|
52
|
+
if not url:
|
|
53
|
+
return text
|
|
54
|
+
return f"\033]8;;{url}\033\\{text}\033]8;;\033\\"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# ─── Cache helpers ────────────────────────────────────────────────────────────
|
|
58
|
+
def load_cache() -> dict:
|
|
59
|
+
try:
|
|
60
|
+
return json.loads(CACHE_FILE.read_text())
|
|
61
|
+
except Exception:
|
|
62
|
+
return {}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def save_cache(cache: dict) -> None:
|
|
66
|
+
try:
|
|
67
|
+
CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
CACHE_FILE.write_text(json.dumps(cache))
|
|
69
|
+
except Exception:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ─── Git / PR helpers ─────────────────────────────────────────────────────────
|
|
74
|
+
def repo_web_url(cwd: str) -> str | None:
|
|
75
|
+
try:
|
|
76
|
+
r = subprocess.run(
|
|
77
|
+
["git", "-C", cwd, "remote", "get-url", "origin"],
|
|
78
|
+
capture_output=True, text=True, timeout=0.3,
|
|
79
|
+
)
|
|
80
|
+
if r.returncode != 0:
|
|
81
|
+
return None
|
|
82
|
+
url = r.stdout.strip()
|
|
83
|
+
if url.startswith("git@"):
|
|
84
|
+
host, path = url[4:].split(":", 1)
|
|
85
|
+
url = f"https://{host}/{path}"
|
|
86
|
+
if url.endswith(".git"):
|
|
87
|
+
url = url[:-4]
|
|
88
|
+
return url
|
|
89
|
+
except Exception:
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def pr_info(cwd: str, branch: str) -> dict:
|
|
94
|
+
"""Return cached PR info, firing a background refresh if stale."""
|
|
95
|
+
repo_url = repo_web_url(cwd)
|
|
96
|
+
if not repo_url:
|
|
97
|
+
return {"url": None, "fallback": None, "number": None,
|
|
98
|
+
"state": None, "isDraft": None, "ci": None}
|
|
99
|
+
key = hashlib.sha1(f"{repo_url}@{branch}".encode()).hexdigest()
|
|
100
|
+
cache = load_cache()
|
|
101
|
+
entry = cache.get(key)
|
|
102
|
+
now = time.time()
|
|
103
|
+
fresh = entry and now - entry.get("ts", 0) < CACHE_TTL
|
|
104
|
+
fallback = f"{repo_url}/tree/{branch}"
|
|
105
|
+
if not fresh:
|
|
106
|
+
try:
|
|
107
|
+
subprocess.Popen(
|
|
108
|
+
[sys.executable, str(Path(__file__).resolve()), "--refresh-pr", key, cwd, branch],
|
|
109
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
110
|
+
stdin=subprocess.DEVNULL, start_new_session=True,
|
|
111
|
+
)
|
|
112
|
+
except Exception:
|
|
113
|
+
pass
|
|
114
|
+
e = entry or {}
|
|
115
|
+
return {
|
|
116
|
+
"url": e.get("url"),
|
|
117
|
+
"fallback": fallback,
|
|
118
|
+
"number": e.get("number"),
|
|
119
|
+
"state": e.get("state"),
|
|
120
|
+
"isDraft": e.get("isDraft"),
|
|
121
|
+
"ci": e.get("ci"),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def refresh_pr(key: str, cwd: str, branch: str) -> None:
|
|
126
|
+
info: dict = {"url": None, "number": None, "state": None,
|
|
127
|
+
"isDraft": None, "ci": None}
|
|
128
|
+
try:
|
|
129
|
+
r = subprocess.run(
|
|
130
|
+
["gh", "pr", "view", "--json",
|
|
131
|
+
"url,number,state,isDraft,statusCheckRollup"],
|
|
132
|
+
capture_output=True, text=True, timeout=10, cwd=cwd,
|
|
133
|
+
)
|
|
134
|
+
if r.returncode == 0 and r.stdout.strip():
|
|
135
|
+
data = json.loads(r.stdout)
|
|
136
|
+
info["url"] = data.get("url")
|
|
137
|
+
info["number"] = data.get("number")
|
|
138
|
+
info["state"] = data.get("state")
|
|
139
|
+
info["isDraft"] = data.get("isDraft")
|
|
140
|
+
# Summarise CI rollup
|
|
141
|
+
rollup = data.get("statusCheckRollup") or []
|
|
142
|
+
info["ci"] = _rollup_ci(rollup)
|
|
143
|
+
except Exception:
|
|
144
|
+
pass
|
|
145
|
+
cache = load_cache()
|
|
146
|
+
cache[key] = {**info, "ts": time.time()}
|
|
147
|
+
save_cache(cache)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _rollup_ci(rollup: list) -> str | None:
|
|
151
|
+
if not rollup:
|
|
152
|
+
return None
|
|
153
|
+
has_fail = has_pending = False
|
|
154
|
+
for c in rollup:
|
|
155
|
+
s = (c.get("state") or c.get("conclusion") or c.get("status") or "").upper()
|
|
156
|
+
if s in ("FAILURE", "TIMED_OUT", "CANCELLED", "ERROR"):
|
|
157
|
+
has_fail = True
|
|
158
|
+
elif s in ("PENDING", "IN_PROGRESS", "QUEUED", "", "EXPECTED"):
|
|
159
|
+
has_pending = True
|
|
160
|
+
if has_fail:
|
|
161
|
+
return "failure"
|
|
162
|
+
if has_pending:
|
|
163
|
+
return "pending"
|
|
164
|
+
return "success"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def git_info(cwd: str) -> tuple[str, int | None] | None:
|
|
168
|
+
try:
|
|
169
|
+
r = subprocess.run(
|
|
170
|
+
["git", "-C", cwd, "rev-parse", "--abbrev-ref", "HEAD"],
|
|
171
|
+
capture_output=True, text=True, timeout=1.0,
|
|
172
|
+
)
|
|
173
|
+
if r.returncode != 0:
|
|
174
|
+
return None
|
|
175
|
+
branch = r.stdout.strip()
|
|
176
|
+
except Exception:
|
|
177
|
+
return None
|
|
178
|
+
dirty: int | None = None
|
|
179
|
+
try:
|
|
180
|
+
s = subprocess.run(
|
|
181
|
+
["git", "-C", cwd, "status", "--porcelain"],
|
|
182
|
+
capture_output=True, text=True, timeout=2.0,
|
|
183
|
+
)
|
|
184
|
+
if s.returncode == 0:
|
|
185
|
+
dirty = len([l for l in s.stdout.splitlines() if l.strip()])
|
|
186
|
+
except Exception:
|
|
187
|
+
pass
|
|
188
|
+
return branch, dirty
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# ─── Workspace discovery ───────────────────────────────────────────────────────
|
|
192
|
+
DEFAULT_BRANCHES = {"main", "master", "develop", "production", "trunk"}
|
|
193
|
+
|
|
194
|
+
CI_ICON = {"success": "✓", "failure": "✗", "pending": "◌"}
|
|
195
|
+
CI_COLOR = {"success": GREEN, "failure": RED, "pending": YELLOW}
|
|
196
|
+
PR_ICONS = {
|
|
197
|
+
"open": "\ue725", # nf-cod-git_pull_request
|
|
198
|
+
"draft": "\uea64", # nf-oct-git_pull_request_draft
|
|
199
|
+
"merged": "\uea6a", # nf-oct-git_merge
|
|
200
|
+
"closed": "\ue729", # nf-cod-git_pull_request_closed
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def find_workspace(cwd: str) -> tuple[Path, dict] | None:
|
|
205
|
+
p = Path(cwd).resolve()
|
|
206
|
+
for candidate in [p, *p.parents]:
|
|
207
|
+
meta = candidate / ".workspace-meta.json"
|
|
208
|
+
if meta.is_file():
|
|
209
|
+
try:
|
|
210
|
+
data = json.loads(meta.read_text())
|
|
211
|
+
return candidate, data
|
|
212
|
+
except Exception:
|
|
213
|
+
return None
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def gather_repo_row(repo_path: Path, is_active: bool) -> dict:
|
|
218
|
+
cwd_str = str(repo_path)
|
|
219
|
+
g = git_info(cwd_str)
|
|
220
|
+
name = repo_path.name
|
|
221
|
+
if not g:
|
|
222
|
+
return {"active": is_active, "name": name, "branch": None,
|
|
223
|
+
"branch_url": None, "pr": None, "dirty": None, "no_git": True}
|
|
224
|
+
branch, dirty = g
|
|
225
|
+
pi = pr_info(cwd_str, branch)
|
|
226
|
+
return {
|
|
227
|
+
"active": is_active,
|
|
228
|
+
"name": name,
|
|
229
|
+
"branch": branch,
|
|
230
|
+
"branch_url": pi.get("url") or pi.get("fallback"),
|
|
231
|
+
"pr_num": pi.get("number"),
|
|
232
|
+
"pr_url": pi.get("url"),
|
|
233
|
+
"pr_state": pi.get("state"),
|
|
234
|
+
"pr_draft": pi.get("isDraft"),
|
|
235
|
+
"pr_ci": pi.get("ci"),
|
|
236
|
+
"dirty": dirty,
|
|
237
|
+
"no_git": False,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# ─── Cell renderers ────────────────────────────────────────────────────────────
|
|
242
|
+
def pr_plain(row: dict) -> str:
|
|
243
|
+
if row["no_git"] or not row.get("pr_num"):
|
|
244
|
+
return "—" if not row["no_git"] and row.get("branch") else ""
|
|
245
|
+
s = f"#{row['pr_num']}"
|
|
246
|
+
if row.get("pr_draft"): s += " draft"
|
|
247
|
+
elif row.get("pr_state") == "MERGED": s += " merged"
|
|
248
|
+
elif row.get("pr_state") == "CLOSED": s += " closed"
|
|
249
|
+
ci = row.get("pr_ci")
|
|
250
|
+
if ci:
|
|
251
|
+
s += f" {CI_ICON.get(ci, '')}"
|
|
252
|
+
return s
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def pr_styled(row: dict) -> str:
|
|
256
|
+
if row["no_git"] or not row.get("pr_num"):
|
|
257
|
+
return color("—", GRAY) if not row["no_git"] and row.get("branch") else ""
|
|
258
|
+
num = f"#{row['pr_num']}"
|
|
259
|
+
draft = row.get("pr_draft")
|
|
260
|
+
state = row.get("pr_state", "OPEN")
|
|
261
|
+
icon = PR_ICONS.get("draft" if draft else state.lower(), PR_ICONS["open"])
|
|
262
|
+
if draft: c = GRAY
|
|
263
|
+
elif state == "MERGED": c = PURPLE
|
|
264
|
+
elif state == "CLOSED": c = RED
|
|
265
|
+
else: c = GREEN
|
|
266
|
+
label = f"{icon} {num} {'draft' if draft else state.lower()}"
|
|
267
|
+
ci = row.get("pr_ci")
|
|
268
|
+
ci_part = f" {color(CI_ICON.get(ci,''), CI_COLOR.get(ci, GRAY))}" if ci else ""
|
|
269
|
+
return hyperlink(color(label, c), row.get("pr_url")) + ci_part
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def dirty_plain(row: dict) -> str:
|
|
273
|
+
if row["no_git"]: return "(no git)"
|
|
274
|
+
if row["dirty"] is None: return "*?"
|
|
275
|
+
return f"*{row['dirty']}" if row["dirty"] > 0 else ""
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def dirty_styled(row: dict) -> str:
|
|
279
|
+
if row["no_git"]: return color("(no git)", GRAY)
|
|
280
|
+
if row["dirty"] is None: return color("*?", GRAY)
|
|
281
|
+
return color(f"*{row['dirty']}", RED) if row["dirty"] > 0 else ""
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
# ─── Table renderer ────────────────────────────────────────────────────────────
|
|
285
|
+
def render_repo_table(rows: list[dict]) -> list[str]:
|
|
286
|
+
headers = ("REPO", "BRANCH", "PR", "DIRTY")
|
|
287
|
+
plain_rows = [(r["name"], r.get("branch") or "", pr_plain(r), dirty_plain(r))
|
|
288
|
+
for r in rows]
|
|
289
|
+
plain_rows.insert(0, headers)
|
|
290
|
+
widths = [max(len(p[i]) for p in plain_rows) for i in range(4)]
|
|
291
|
+
|
|
292
|
+
def pad(s: str, w: int) -> str:
|
|
293
|
+
return s + " " * (w - len(s))
|
|
294
|
+
|
|
295
|
+
header = " " + " ".join(
|
|
296
|
+
color(pad(h, widths[i]), DIM + GRAY) for i, h in enumerate(headers)
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
result = [header]
|
|
300
|
+
for r in rows:
|
|
301
|
+
marker = color("▸ ", PURPLE) if r["active"] else " "
|
|
302
|
+
name_c = PURPLE + BOLD if r["active"] else BLUE
|
|
303
|
+
name_cell = color(r["name"], name_c if not r["no_git"] else DIM + GRAY)
|
|
304
|
+
name_cell += " " * (widths[0] - len(r["name"]))
|
|
305
|
+
|
|
306
|
+
br_plain = r.get("branch") or ""
|
|
307
|
+
br_styled = (hyperlink(color(br_plain, MAGENTA), r.get("branch_url"))
|
|
308
|
+
if br_plain else "")
|
|
309
|
+
br_cell = br_styled + " " * (widths[1] - len(br_plain))
|
|
310
|
+
|
|
311
|
+
pp = pr_plain(r)
|
|
312
|
+
pr_cell = pr_styled(r) + " " * (widths[2] - len(pp))
|
|
313
|
+
|
|
314
|
+
dp = dirty_plain(r)
|
|
315
|
+
d_cell = dirty_styled(r) + " " * (widths[3] - len(dp))
|
|
316
|
+
|
|
317
|
+
result.append(f"{marker}{name_cell} {br_cell} {pr_cell} {d_cell}".rstrip())
|
|
318
|
+
return result
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
# ─── Main ─────────────────────────────────────────────────────────────────────
|
|
322
|
+
def main() -> None:
|
|
323
|
+
"""
|
|
324
|
+
Claude Code calls this script and shows whatever we write to stdout
|
|
325
|
+
as the statusLine — an *additional* line shown below the editor.
|
|
326
|
+
Claude Code's own header (model · tokens · cost) is always shown
|
|
327
|
+
regardless of what we output here.
|
|
328
|
+
|
|
329
|
+
Behaviour contract:
|
|
330
|
+
- Not in a workspace → output nothing (CC shows its default footer)
|
|
331
|
+
- In workspace, all repos on default branch → output nothing
|
|
332
|
+
- In workspace with feature-branch repos → output the repo table only
|
|
333
|
+
|
|
334
|
+
We NEVER output model info, cost, or anything that duplicates CC's
|
|
335
|
+
own display. We NEVER modify the user's existing styling.
|
|
336
|
+
"""
|
|
337
|
+
try:
|
|
338
|
+
data = json.load(sys.stdin)
|
|
339
|
+
except Exception:
|
|
340
|
+
data = {}
|
|
341
|
+
|
|
342
|
+
cwd = (data.get("workspace") or {}).get("current_dir") or os.getcwd()
|
|
343
|
+
ws = find_workspace(cwd)
|
|
344
|
+
|
|
345
|
+
if not ws:
|
|
346
|
+
# Not in a workspace — output nothing so CC's default footer is shown unchanged.
|
|
347
|
+
return
|
|
348
|
+
|
|
349
|
+
ws_path, ws_meta = ws
|
|
350
|
+
|
|
351
|
+
# Find which repo (if any) is currently active
|
|
352
|
+
active_repo: str | None = None
|
|
353
|
+
try:
|
|
354
|
+
rel = Path(cwd).resolve().relative_to(ws_path)
|
|
355
|
+
if rel.parts:
|
|
356
|
+
active_repo = rel.parts[0]
|
|
357
|
+
except ValueError:
|
|
358
|
+
pass
|
|
359
|
+
|
|
360
|
+
repo_tasks = []
|
|
361
|
+
for repo in ws_meta.get("repositories") or []:
|
|
362
|
+
dir_name = repo.get("directoryName") or repo.get("name")
|
|
363
|
+
if not dir_name:
|
|
364
|
+
continue
|
|
365
|
+
repo_path = ws_path / dir_name
|
|
366
|
+
if not repo_path.is_dir():
|
|
367
|
+
continue
|
|
368
|
+
repo_tasks.append((repo_path, dir_name == active_repo))
|
|
369
|
+
|
|
370
|
+
if not repo_tasks:
|
|
371
|
+
return
|
|
372
|
+
|
|
373
|
+
max_workers = min(8, len(repo_tasks))
|
|
374
|
+
with ThreadPoolExecutor(max_workers=max_workers) as ex:
|
|
375
|
+
rows = list(ex.map(lambda t: gather_repo_row(*t), repo_tasks))
|
|
376
|
+
|
|
377
|
+
# Filter: only repos on non-default branches (toggle not available in CC)
|
|
378
|
+
rows = [r for r in rows if r.get("branch")
|
|
379
|
+
and r["branch"] not in DEFAULT_BRANCHES]
|
|
380
|
+
|
|
381
|
+
if not rows:
|
|
382
|
+
# All repos are on default branches — nothing interesting to show.
|
|
383
|
+
# Output nothing to preserve CC's default footer.
|
|
384
|
+
return
|
|
385
|
+
|
|
386
|
+
# Output ONLY the table. CC's model/cost header is shown above this independently.
|
|
387
|
+
sys.stdout.write("\n".join(render_repo_table(rows)))
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
if __name__ == "__main__":
|
|
391
|
+
if len(sys.argv) >= 4 and sys.argv[1] == "--refresh-pr":
|
|
392
|
+
refresh_pr(sys.argv[2], sys.argv[3], sys.argv[4] if len(sys.argv) > 4 else "")
|
|
393
|
+
else:
|
|
394
|
+
main()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type AgentStatus = 'idle' | 'working' | 'waiting' | 'stopped';
|
|
2
|
+
|
|
3
|
+
export interface AgentState {
|
|
4
|
+
sessionId: string;
|
|
5
|
+
workspace: string;
|
|
6
|
+
workspacePath: string;
|
|
7
|
+
pid: number;
|
|
8
|
+
status: AgentStatus;
|
|
9
|
+
startedAt: string;
|
|
10
|
+
lastUpdatedAt: string;
|
|
11
|
+
tmuxPane?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DashboardConfig {
|
|
15
|
+
sessionName: string;
|
|
16
|
+
sidebarWidthPercent: number;
|
|
17
|
+
pollIntervalMs: number;
|
|
18
|
+
staleTtlMs: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const DASHBOARD_DEFAULTS: DashboardConfig = {
|
|
22
|
+
sessionName: 'ws-dashboard',
|
|
23
|
+
sidebarWidthPercent: 20,
|
|
24
|
+
pollIntervalMs: 2000,
|
|
25
|
+
staleTtlMs: 300_000,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Marker string embedded in hook commands for detection/removal */
|
|
29
|
+
export const HOOK_MARKER = 'workspace-dashboard-hook';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export interface GitHubRepo {
|
|
2
|
+
name: string;
|
|
3
|
+
url: string;
|
|
4
|
+
sshUrl: string;
|
|
5
|
+
owner: {
|
|
6
|
+
login: string;
|
|
7
|
+
};
|
|
8
|
+
description: string;
|
|
9
|
+
isPrivate: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CloneResult {
|
|
13
|
+
repo: GitHubRepo;
|
|
14
|
+
directoryName: string;
|
|
15
|
+
status: 'success' | 'failed';
|
|
16
|
+
error?: string;
|
|
17
|
+
clonedAt?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CloneProgress {
|
|
21
|
+
total: number;
|
|
22
|
+
completed: number;
|
|
23
|
+
current: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RepositoryMetadata {
|
|
27
|
+
name: string;
|
|
28
|
+
directoryName: string;
|
|
29
|
+
owner: string;
|
|
30
|
+
clonedAt: string;
|
|
31
|
+
cloneUrl: string;
|
|
32
|
+
status: 'success' | 'failed';
|
|
33
|
+
error?: string;
|
|
34
|
+
// Enhanced tracking
|
|
35
|
+
lastSynced?: string;
|
|
36
|
+
lastBranchSwitch?: string;
|
|
37
|
+
lastHealthCheck?: string;
|
|
38
|
+
healthStatus?: 'healthy' | 'warning' | 'error';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface DependencyInfo {
|
|
42
|
+
dependsOn: string[];
|
|
43
|
+
dependedBy: string[];
|
|
44
|
+
lastAnalyzed: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface WorkspaceMetadata {
|
|
48
|
+
workspaceName: string;
|
|
49
|
+
createdAt: string;
|
|
50
|
+
lastModified?: string;
|
|
51
|
+
repositories: RepositoryMetadata[];
|
|
52
|
+
// Original prompt used to create the workspace (from 'nemus -- <prompt>')
|
|
53
|
+
prompt?: string;
|
|
54
|
+
// Dependency information
|
|
55
|
+
dependencies?: {
|
|
56
|
+
[repoName: string]: DependencyInfo;
|
|
57
|
+
};
|
|
58
|
+
// Custom metadata
|
|
59
|
+
tags?: string[];
|
|
60
|
+
description?: string;
|
|
61
|
+
// Archive support
|
|
62
|
+
archivedAt?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface HealthCheckResult {
|
|
66
|
+
category: string;
|
|
67
|
+
status: 'healthy' | 'warning' | 'error';
|
|
68
|
+
message: string;
|
|
69
|
+
details?: string;
|
|
70
|
+
actionable?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface GitStatus {
|
|
74
|
+
repo: string;
|
|
75
|
+
branch: string;
|
|
76
|
+
clean: boolean;
|
|
77
|
+
ahead: number;
|
|
78
|
+
behind: number;
|
|
79
|
+
modifiedFiles: number;
|
|
80
|
+
untrackedFiles: number;
|
|
81
|
+
hasRemote: boolean;
|
|
82
|
+
detachedHead: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface SuiteEntry {
|
|
86
|
+
repoName: string;
|
|
87
|
+
directoryName: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface PostCloneHook {
|
|
91
|
+
repoName?: string; // If omitted, runs for all repos
|
|
92
|
+
commands: string[]; // Run sequentially in repo dir
|
|
93
|
+
description?: string;
|
|
94
|
+
continueOnError?: boolean; // Default false
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface WorkspaceSuite {
|
|
98
|
+
name: string;
|
|
99
|
+
description: string;
|
|
100
|
+
entries: SuiteEntry[];
|
|
101
|
+
createdAt: string;
|
|
102
|
+
updatedAt: string;
|
|
103
|
+
postCloneHooks?: PostCloneHook[];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface SuitesStore {
|
|
107
|
+
version: 1;
|
|
108
|
+
suites: WorkspaceSuite[];
|
|
109
|
+
}
|