@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,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AGENT_ORDER = void 0;
|
|
37
|
+
exports.getAgentPaths = getAgentPaths;
|
|
38
|
+
exports.getActiveAgents = getActiveAgents;
|
|
39
|
+
exports.getPrimaryAgent = getPrimaryAgent;
|
|
40
|
+
exports.isAgentCliAvailable = isAgentCliAvailable;
|
|
41
|
+
exports.resetAgentConfigCache = resetAgentConfigCache;
|
|
42
|
+
exports.getSkillsTargetDirs = getSkillsTargetDirs;
|
|
43
|
+
exports.getContextFileNames = getContextFileNames;
|
|
44
|
+
exports.getHookTargetFiles = getHookTargetFiles;
|
|
45
|
+
exports.getMcpAgents = getMcpAgents;
|
|
46
|
+
exports.getAllKnownContextFileNames = getAllKnownContextFileNames;
|
|
47
|
+
const os = __importStar(require("os"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const child_process_1 = require("child_process");
|
|
50
|
+
const config_1 = require("./config");
|
|
51
|
+
const HOME = os.homedir();
|
|
52
|
+
/**
|
|
53
|
+
* Detection order for `auto` / `both` / `getPrimaryAgent`. Also the order agents
|
|
54
|
+
* appear in `getActiveAgents()`. Add a new agent's config below and to this list.
|
|
55
|
+
*/
|
|
56
|
+
exports.AGENT_ORDER = ['claude', 'pi', 'opencode', 'codex', 'gemini'];
|
|
57
|
+
const CLAUDE_PATHS = {
|
|
58
|
+
type: 'claude',
|
|
59
|
+
skillsDir: path.join(HOME, '.claude', 'skills'),
|
|
60
|
+
settingsFile: path.join(HOME, '.claude', 'settings.json'),
|
|
61
|
+
// Must be exactly 'CLAUDE.md': Claude Code only auto-discovers CLAUDE.md /
|
|
62
|
+
// AGENTS.md up the cwd/ancestor chain. A hidden lowercase '.claude.md' is
|
|
63
|
+
// invisible to its project-memory loader, so the workspace context (and its
|
|
64
|
+
// 'Saved Context' pointer) would never surface automatically (issue #186).
|
|
65
|
+
contextFileName: 'CLAUDE.md',
|
|
66
|
+
launchCommand: 'claude',
|
|
67
|
+
resumeCommand: (id) => `claude --resume ${id} --fork-session`,
|
|
68
|
+
supportsMcp: true,
|
|
69
|
+
supportsHooks: true,
|
|
70
|
+
sessionProjectsDir: path.join(HOME, '.claude', 'projects'),
|
|
71
|
+
};
|
|
72
|
+
const PI_PATHS = {
|
|
73
|
+
type: 'pi',
|
|
74
|
+
skillsDir: path.join(HOME, '.pi', 'agent', 'skills'),
|
|
75
|
+
settingsFile: path.join(HOME, '.pi', 'agent', 'settings.json'),
|
|
76
|
+
contextFileName: 'AGENTS.md',
|
|
77
|
+
launchCommand: 'pi',
|
|
78
|
+
resumeCommand: (id) => `pi --session ${id} --fork`,
|
|
79
|
+
supportsMcp: false,
|
|
80
|
+
supportsHooks: false,
|
|
81
|
+
sessionProjectsDir: path.join(HOME, '.pi', 'agent', 'sessions'),
|
|
82
|
+
};
|
|
83
|
+
// OpenCode uses XDG paths: ~/.local/share/opencode for data, ~/.config/opencode for config
|
|
84
|
+
// It reads skills from ~/.claude/skills/ automatically (Claude-compatible)
|
|
85
|
+
// Project config lives in .opencode/ within the project directory
|
|
86
|
+
const OPENCODE_DATA_DIR = path.join(process.env.XDG_DATA_HOME || path.join(HOME, '.local', 'share'), 'opencode');
|
|
87
|
+
const OPENCODE_CONFIG_DIR = path.join(process.env.XDG_CONFIG_HOME || path.join(HOME, '.config'), 'opencode');
|
|
88
|
+
const OPENCODE_PATHS = {
|
|
89
|
+
type: 'opencode',
|
|
90
|
+
// OpenCode reads skills from ~/.claude/skills/ natively — no separate install needed
|
|
91
|
+
skillsDir: path.join(HOME, '.claude', 'skills'),
|
|
92
|
+
settingsFile: path.join(OPENCODE_CONFIG_DIR, 'opencode.jsonc'),
|
|
93
|
+
contextFileName: 'AGENTS.md',
|
|
94
|
+
launchCommand: 'opencode',
|
|
95
|
+
resumeCommand: (_id) => `opencode --continue`,
|
|
96
|
+
supportsMcp: true,
|
|
97
|
+
supportsHooks: false,
|
|
98
|
+
sessionProjectsDir: OPENCODE_DATA_DIR,
|
|
99
|
+
};
|
|
100
|
+
// Codex (OpenAI Codex CLI) — config under ~/.codex, reads AGENTS.md for context,
|
|
101
|
+
// supports MCP servers via ~/.codex/config.toml. No native skills or hooks.
|
|
102
|
+
const CODEX_HOME = process.env.CODEX_HOME || path.join(HOME, '.codex');
|
|
103
|
+
const CODEX_PATHS = {
|
|
104
|
+
type: 'codex',
|
|
105
|
+
skillsDir: path.join(CODEX_HOME, 'skills'),
|
|
106
|
+
settingsFile: path.join(CODEX_HOME, 'config.toml'),
|
|
107
|
+
contextFileName: 'AGENTS.md',
|
|
108
|
+
launchCommand: 'codex',
|
|
109
|
+
resumeCommand: (id) => `codex resume ${id}`,
|
|
110
|
+
supportsMcp: true,
|
|
111
|
+
supportsHooks: false,
|
|
112
|
+
sessionProjectsDir: path.join(CODEX_HOME, 'sessions'),
|
|
113
|
+
};
|
|
114
|
+
// Gemini (Google Gemini CLI) — config under ~/.gemini, reads GEMINI.md for
|
|
115
|
+
// context, supports MCP servers via ~/.gemini/settings.json. No hooks.
|
|
116
|
+
const GEMINI_HOME = path.join(HOME, '.gemini');
|
|
117
|
+
const GEMINI_PATHS = {
|
|
118
|
+
type: 'gemini',
|
|
119
|
+
skillsDir: path.join(GEMINI_HOME, 'skills'),
|
|
120
|
+
settingsFile: path.join(GEMINI_HOME, 'settings.json'),
|
|
121
|
+
contextFileName: 'GEMINI.md',
|
|
122
|
+
launchCommand: 'gemini',
|
|
123
|
+
resumeCommand: (_id) => `gemini`,
|
|
124
|
+
supportsMcp: true,
|
|
125
|
+
supportsHooks: false,
|
|
126
|
+
sessionProjectsDir: path.join(GEMINI_HOME, 'tmp'),
|
|
127
|
+
};
|
|
128
|
+
const AGENT_REGISTRY = {
|
|
129
|
+
claude: CLAUDE_PATHS,
|
|
130
|
+
pi: PI_PATHS,
|
|
131
|
+
opencode: OPENCODE_PATHS,
|
|
132
|
+
codex: CODEX_PATHS,
|
|
133
|
+
gemini: GEMINI_PATHS,
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Get AgentPaths for a specific agent type.
|
|
137
|
+
*/
|
|
138
|
+
function getAgentPaths(agent) {
|
|
139
|
+
return { ...AGENT_REGISTRY[agent] };
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Resolve 'auto' to a concrete agent type by detecting installed CLIs.
|
|
143
|
+
* Cached after first call to avoid repeated shell exec.
|
|
144
|
+
*/
|
|
145
|
+
let resolvedAutoAgent = null;
|
|
146
|
+
function resolveAutoAgent() {
|
|
147
|
+
if (resolvedAutoAgent)
|
|
148
|
+
return resolvedAutoAgent;
|
|
149
|
+
const available = exports.AGENT_ORDER.filter(a => isAgentCliAvailable(a));
|
|
150
|
+
// If multiple agents detected, use 'both' (meaning: all available agents).
|
|
151
|
+
if (available.length > 1)
|
|
152
|
+
resolvedAutoAgent = 'both';
|
|
153
|
+
else if (available.length === 1)
|
|
154
|
+
resolvedAutoAgent = available[0];
|
|
155
|
+
else
|
|
156
|
+
resolvedAutoAgent = 'claude'; // default to claude even if not installed
|
|
157
|
+
return resolvedAutoAgent;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Get all active agent configurations based on user config.
|
|
161
|
+
* Resolves 'auto' by detecting installed CLIs. 'both' means every available agent.
|
|
162
|
+
*/
|
|
163
|
+
function getActiveAgents() {
|
|
164
|
+
let { aiAgent } = (0, config_1.getUserConfig)();
|
|
165
|
+
if (aiAgent === 'auto')
|
|
166
|
+
aiAgent = resolveAutoAgent();
|
|
167
|
+
if (aiAgent === 'both') {
|
|
168
|
+
const agents = exports.AGENT_ORDER.filter(a => isAgentCliAvailable(a)).map(a => ({ ...AGENT_REGISTRY[a] }));
|
|
169
|
+
// Fallback to Claude if nothing detected (shouldn't happen in 'both' mode)
|
|
170
|
+
return agents.length > 0 ? agents : [{ ...CLAUDE_PATHS }];
|
|
171
|
+
}
|
|
172
|
+
return [{ ...AGENT_REGISTRY[aiAgent] }];
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get the preferred (primary) agent — the one used for launching, dashboards, etc.
|
|
176
|
+
* Uses the `primaryAgent` config. When 'auto', picks the first available CLI.
|
|
177
|
+
*/
|
|
178
|
+
function getPrimaryAgent() {
|
|
179
|
+
const { primaryAgent } = (0, config_1.getUserConfig)();
|
|
180
|
+
if (primaryAgent !== 'auto')
|
|
181
|
+
return { ...AGENT_REGISTRY[primaryAgent] };
|
|
182
|
+
// 'auto' — pick first available in detection order
|
|
183
|
+
const first = exports.AGENT_ORDER.find(a => isAgentCliAvailable(a));
|
|
184
|
+
return { ...AGENT_REGISTRY[first ?? 'claude'] };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check if a specific agent CLI is available on the system.
|
|
188
|
+
* Result is cached per agent to avoid repeated execSync overhead.
|
|
189
|
+
*/
|
|
190
|
+
const cliAvailabilityCache = new Map();
|
|
191
|
+
function isAgentCliAvailable(agent) {
|
|
192
|
+
if (cliAvailabilityCache.has(agent))
|
|
193
|
+
return cliAvailabilityCache.get(agent);
|
|
194
|
+
const command = `${AGENT_REGISTRY[agent].launchCommand} --version`;
|
|
195
|
+
let available;
|
|
196
|
+
try {
|
|
197
|
+
(0, child_process_1.execSync)(command, { stdio: 'pipe' });
|
|
198
|
+
available = true;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
available = false;
|
|
202
|
+
}
|
|
203
|
+
cliAvailabilityCache.set(agent, available);
|
|
204
|
+
return available;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Reset all cached values. For use in tests to ensure clean state.
|
|
208
|
+
* @internal
|
|
209
|
+
*/
|
|
210
|
+
function resetAgentConfigCache() {
|
|
211
|
+
resolvedAutoAgent = null;
|
|
212
|
+
cliAvailabilityCache.clear();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Get all skills directories that should be written to.
|
|
216
|
+
*/
|
|
217
|
+
function getSkillsTargetDirs() {
|
|
218
|
+
const dirs = getActiveAgents().map(a => a.skillsDir);
|
|
219
|
+
return [...new Set(dirs)];
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Get all context file names that should be generated in workspaces.
|
|
223
|
+
* Deduplicates if multiple agents use the same file.
|
|
224
|
+
*/
|
|
225
|
+
function getContextFileNames() {
|
|
226
|
+
const names = getActiveAgents().map(a => a.contextFileName);
|
|
227
|
+
return [...new Set(names)];
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get all settings files where hooks should be installed.
|
|
231
|
+
* Only returns agents that support hooks.
|
|
232
|
+
*/
|
|
233
|
+
function getHookTargetFiles() {
|
|
234
|
+
return getActiveAgents()
|
|
235
|
+
.filter(a => a.supportsHooks)
|
|
236
|
+
.map(a => a.settingsFile);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Get all agents that support MCP.
|
|
240
|
+
*/
|
|
241
|
+
function getMcpAgents() {
|
|
242
|
+
return getActiveAgents().filter(a => a.supportsMcp);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get every context file name that any known agent can produce, regardless
|
|
246
|
+
* of which agents are currently configured. Used when scanning repos for
|
|
247
|
+
* existing context files (e.g. a Pi workspace may contain repos that were
|
|
248
|
+
* previously set up for Claude Code, and vice-versa).
|
|
249
|
+
*
|
|
250
|
+
* Also includes legacy '.claude.md' — the filename generated for Claude before
|
|
251
|
+
* issue #186 — so repos/workspaces that still contain one are still recognized.
|
|
252
|
+
*/
|
|
253
|
+
function getAllKnownContextFileNames() {
|
|
254
|
+
// Order matters: this is the per-repo scan PRIORITY (findContextFilesInDir
|
|
255
|
+
// embeds the first existing candidate per directory). AGENTS.md must come
|
|
256
|
+
// before CLAUDE.md — in dual-agent repos CLAUDE.md is commonly a thin
|
|
257
|
+
// "@AGENTS.md" shim, so AGENTS.md holds the real content. '.claude.md' is the
|
|
258
|
+
// legacy output (pre-#186), kept last so any lingering files are still
|
|
259
|
+
// recognized.
|
|
260
|
+
const priority = ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md', '.claude.md'];
|
|
261
|
+
const fromAgents = exports.AGENT_ORDER.map(a => AGENT_REGISTRY[a].contextFileName);
|
|
262
|
+
return [...new Set([...priority, ...fromAgents])];
|
|
263
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AGENTS_STATE_DIR = void 0;
|
|
37
|
+
exports.ensureStateDir = ensureStateDir;
|
|
38
|
+
exports.writeAgentState = writeAgentState;
|
|
39
|
+
exports.readAgentState = readAgentState;
|
|
40
|
+
exports.readAllAgentStates = readAllAgentStates;
|
|
41
|
+
exports.removeAgentState = removeAgentState;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const os = __importStar(require("os"));
|
|
45
|
+
const dashboard_1 = require("../types/dashboard");
|
|
46
|
+
exports.AGENTS_STATE_DIR = path.join(os.homedir(), '.workspace-manager', 'agents');
|
|
47
|
+
function ensureStateDir() {
|
|
48
|
+
fs.mkdirSync(exports.AGENTS_STATE_DIR, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validate that a session ID is safe to use as a filename.
|
|
52
|
+
* Allows only alphanumeric characters and hyphens, max 100 chars.
|
|
53
|
+
*/
|
|
54
|
+
function isValidSessionId(id) {
|
|
55
|
+
return typeof id === 'string' && /^[a-zA-Z0-9\-]{1,100}$/.test(id);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Write agent state atomically (write tmp + rename).
|
|
59
|
+
*/
|
|
60
|
+
function writeAgentState(state) {
|
|
61
|
+
if (!isValidSessionId(state.sessionId))
|
|
62
|
+
return; // reject invalid IDs silently
|
|
63
|
+
ensureStateDir();
|
|
64
|
+
const filePath = path.join(exports.AGENTS_STATE_DIR, `${state.sessionId}.json`);
|
|
65
|
+
const tmpPath = filePath + '.tmp';
|
|
66
|
+
try {
|
|
67
|
+
fs.writeFileSync(tmpPath, JSON.stringify(state, null, 2), 'utf-8');
|
|
68
|
+
fs.renameSync(tmpPath, filePath);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Clean up tmp on failure
|
|
72
|
+
try {
|
|
73
|
+
fs.unlinkSync(tmpPath);
|
|
74
|
+
}
|
|
75
|
+
catch { /* ignore */ }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Read a single agent state by session ID.
|
|
80
|
+
*/
|
|
81
|
+
function readAgentState(sessionId) {
|
|
82
|
+
if (!isValidSessionId(sessionId))
|
|
83
|
+
return null;
|
|
84
|
+
try {
|
|
85
|
+
const filePath = path.join(exports.AGENTS_STATE_DIR, `${sessionId}.json`);
|
|
86
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
87
|
+
return JSON.parse(content);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Check if a process is alive by sending signal 0.
|
|
95
|
+
*/
|
|
96
|
+
function isProcessAlive(pid) {
|
|
97
|
+
try {
|
|
98
|
+
process.kill(pid, 0);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Read all agent states, check PID liveness, and clean stale entries.
|
|
107
|
+
* Returns active states sorted by workspace, then startedAt.
|
|
108
|
+
*/
|
|
109
|
+
function readAllAgentStates() {
|
|
110
|
+
ensureStateDir();
|
|
111
|
+
let entries;
|
|
112
|
+
try {
|
|
113
|
+
entries = fs.readdirSync(exports.AGENTS_STATE_DIR);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
const now = Date.now();
|
|
119
|
+
const states = [];
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
if (!entry.endsWith('.json') || entry.startsWith('.'))
|
|
122
|
+
continue;
|
|
123
|
+
const sessionId = entry.replace('.json', '');
|
|
124
|
+
const state = readAgentState(sessionId);
|
|
125
|
+
if (!state) {
|
|
126
|
+
// Malformed file — remove it
|
|
127
|
+
try {
|
|
128
|
+
fs.unlinkSync(path.join(exports.AGENTS_STATE_DIR, entry));
|
|
129
|
+
}
|
|
130
|
+
catch { /* ignore */ }
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
// PID liveness check for non-stopped states
|
|
134
|
+
// Skip the check if the state was updated very recently (within 10s)
|
|
135
|
+
// to allow hooks time to update the PID on session resume
|
|
136
|
+
if (state.status !== 'stopped' && state.pid > 0) {
|
|
137
|
+
const age = now - new Date(state.lastUpdatedAt).getTime();
|
|
138
|
+
if (age > 10000 && !isProcessAlive(state.pid)) {
|
|
139
|
+
state.status = 'stopped';
|
|
140
|
+
state.lastUpdatedAt = new Date().toISOString();
|
|
141
|
+
writeAgentState(state);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Filter out stale stopped states
|
|
145
|
+
if (state.status === 'stopped') {
|
|
146
|
+
const age = now - new Date(state.lastUpdatedAt).getTime();
|
|
147
|
+
if (age > dashboard_1.DASHBOARD_DEFAULTS.staleTtlMs) {
|
|
148
|
+
try {
|
|
149
|
+
fs.unlinkSync(path.join(exports.AGENTS_STATE_DIR, entry));
|
|
150
|
+
}
|
|
151
|
+
catch { /* ignore */ }
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
states.push(state);
|
|
156
|
+
}
|
|
157
|
+
// Sort by workspace ascending, then startedAt ascending
|
|
158
|
+
states.sort((a, b) => {
|
|
159
|
+
const wsCmp = a.workspace.localeCompare(b.workspace);
|
|
160
|
+
if (wsCmp !== 0)
|
|
161
|
+
return wsCmp;
|
|
162
|
+
return new Date(a.startedAt).getTime() - new Date(b.startedAt).getTime();
|
|
163
|
+
});
|
|
164
|
+
return states;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Remove a single agent state file.
|
|
168
|
+
*/
|
|
169
|
+
function removeAgentState(sessionId) {
|
|
170
|
+
if (!isValidSessionId(sessionId))
|
|
171
|
+
return;
|
|
172
|
+
try {
|
|
173
|
+
fs.unlinkSync(path.join(exports.AGENTS_STATE_DIR, `${sessionId}.json`));
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// File doesn't exist — fine
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printBanner = exports.BANNER_PLAIN = exports.BANNER = void 0;
|
|
4
|
+
const colors_1 = require("./colors");
|
|
5
|
+
const g = colors_1.colors.green;
|
|
6
|
+
const d = colors_1.colors.dim;
|
|
7
|
+
const b = colors_1.colors.bright;
|
|
8
|
+
const r = colors_1.colors.reset;
|
|
9
|
+
exports.BANNER = `
|
|
10
|
+
${d} ╭──────────────────────────────────────╮${r}
|
|
11
|
+
${d} │${r} ${g}>_${r} ${b}Nemus${r} ${d}│${r}
|
|
12
|
+
${d} │${r} ${d}multi-repo workspaces${r} ${d}│${r}
|
|
13
|
+
${d} ╰──────────────────────────────────────╯${r}
|
|
14
|
+
`;
|
|
15
|
+
exports.BANNER_PLAIN = `
|
|
16
|
+
╭──────────────────────────────────────╮
|
|
17
|
+
│ >_ Nemus │
|
|
18
|
+
│ multi-repo workspaces │
|
|
19
|
+
╰──────────────────────────────────────╯
|
|
20
|
+
`;
|
|
21
|
+
const printBanner = () => {
|
|
22
|
+
console.log(exports.BANNER);
|
|
23
|
+
};
|
|
24
|
+
exports.printBanner = printBanner;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.rebaseBranch = exports.mergeBranch = exports.createBranch = exports.switchBranch = void 0;
|
|
37
|
+
const child_process_1 = require("child_process");
|
|
38
|
+
const util_1 = require("util");
|
|
39
|
+
const fs = __importStar(require("fs/promises"));
|
|
40
|
+
const git_status_1 = require("./git-status");
|
|
41
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
42
|
+
const GIT_TIMEOUT = 30000;
|
|
43
|
+
const switchBranch = async (repoPath, repoName, targetBranch) => {
|
|
44
|
+
try {
|
|
45
|
+
// Check if directory exists
|
|
46
|
+
try {
|
|
47
|
+
await fs.access(repoPath);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return {
|
|
51
|
+
repo: repoName,
|
|
52
|
+
status: 'skipped',
|
|
53
|
+
message: 'Directory not found',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
// Check if it's a git repository
|
|
57
|
+
try {
|
|
58
|
+
await execAsync('git rev-parse --git-dir', { cwd: repoPath });
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return {
|
|
62
|
+
repo: repoName,
|
|
63
|
+
status: 'skipped',
|
|
64
|
+
message: 'Not a git repository',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Get current branch
|
|
68
|
+
const { stdout: currentBranchOutput } = await execAsync('git branch --show-current', { cwd: repoPath });
|
|
69
|
+
const currentBranch = currentBranchOutput.trim();
|
|
70
|
+
if (currentBranch === targetBranch) {
|
|
71
|
+
return {
|
|
72
|
+
repo: repoName,
|
|
73
|
+
status: 'skipped',
|
|
74
|
+
message: 'Already on target branch',
|
|
75
|
+
previousBranch: currentBranch,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Check for uncommitted changes
|
|
79
|
+
const { stdout: statusOutput } = await execAsync('git status --porcelain', { cwd: repoPath });
|
|
80
|
+
if (statusOutput.trim().length > 0) {
|
|
81
|
+
return {
|
|
82
|
+
repo: repoName,
|
|
83
|
+
status: 'skipped',
|
|
84
|
+
message: 'Uncommitted changes',
|
|
85
|
+
previousBranch: currentBranch,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// Fetch to ensure we have latest branches
|
|
89
|
+
await execAsync('git fetch', { cwd: repoPath, timeout: GIT_TIMEOUT });
|
|
90
|
+
// Check if branch exists locally
|
|
91
|
+
try {
|
|
92
|
+
await execAsync(`git rev-parse --verify ${targetBranch}`, { cwd: repoPath });
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Branch doesn't exist locally, check remote
|
|
96
|
+
try {
|
|
97
|
+
await execAsync(`git rev-parse --verify origin/${targetBranch}`, { cwd: repoPath });
|
|
98
|
+
// Branch exists on remote, create local tracking branch
|
|
99
|
+
await execAsync(`git checkout -b ${targetBranch} origin/${targetBranch}`, { cwd: repoPath });
|
|
100
|
+
return {
|
|
101
|
+
repo: repoName,
|
|
102
|
+
status: 'success',
|
|
103
|
+
previousBranch: currentBranch,
|
|
104
|
+
newBranch: targetBranch,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return {
|
|
109
|
+
repo: repoName,
|
|
110
|
+
status: 'failed',
|
|
111
|
+
message: `Branch ${targetBranch} not found`,
|
|
112
|
+
previousBranch: currentBranch,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Switch to existing local branch
|
|
117
|
+
await execAsync(`git checkout ${targetBranch}`, { cwd: repoPath });
|
|
118
|
+
return {
|
|
119
|
+
repo: repoName,
|
|
120
|
+
status: 'success',
|
|
121
|
+
previousBranch: currentBranch,
|
|
122
|
+
newBranch: targetBranch,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
127
|
+
return {
|
|
128
|
+
repo: repoName,
|
|
129
|
+
status: 'failed',
|
|
130
|
+
message: errorMessage,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
exports.switchBranch = switchBranch;
|
|
135
|
+
const createBranch = async (repoPath, repoName, branchName, baseBranch, force) => {
|
|
136
|
+
try {
|
|
137
|
+
// Check for uncommitted changes
|
|
138
|
+
if (!force) {
|
|
139
|
+
const hasChanges = await (0, git_status_1.hasUncommittedChanges)(repoPath);
|
|
140
|
+
if (hasChanges) {
|
|
141
|
+
return {
|
|
142
|
+
repo: repoName,
|
|
143
|
+
success: false,
|
|
144
|
+
message: 'Skipped (uncommitted changes)',
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Checkout base branch if specified
|
|
149
|
+
if (baseBranch) {
|
|
150
|
+
await execAsync(`git checkout ${baseBranch}`, {
|
|
151
|
+
cwd: repoPath,
|
|
152
|
+
timeout: GIT_TIMEOUT,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// Create and checkout new branch
|
|
156
|
+
await execAsync(`git checkout -b ${branchName}`, {
|
|
157
|
+
cwd: repoPath,
|
|
158
|
+
timeout: GIT_TIMEOUT,
|
|
159
|
+
});
|
|
160
|
+
return {
|
|
161
|
+
repo: repoName,
|
|
162
|
+
success: true,
|
|
163
|
+
message: `Created branch ${branchName}`,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
return {
|
|
168
|
+
repo: repoName,
|
|
169
|
+
success: false,
|
|
170
|
+
message: 'Failed',
|
|
171
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
exports.createBranch = createBranch;
|
|
176
|
+
const mergeBranch = async (repoPath, repoName, sourceBranch, targetBranch, options) => {
|
|
177
|
+
try {
|
|
178
|
+
// Checkout target branch
|
|
179
|
+
await execAsync(`git checkout ${targetBranch}`, {
|
|
180
|
+
cwd: repoPath,
|
|
181
|
+
timeout: GIT_TIMEOUT,
|
|
182
|
+
});
|
|
183
|
+
// Build merge command
|
|
184
|
+
let mergeCmd = `git merge ${sourceBranch}`;
|
|
185
|
+
if (options?.noFf)
|
|
186
|
+
mergeCmd += ' --no-ff';
|
|
187
|
+
if (options?.ffOnly)
|
|
188
|
+
mergeCmd += ' --ff-only';
|
|
189
|
+
if (options?.squash)
|
|
190
|
+
mergeCmd += ' --squash';
|
|
191
|
+
await execAsync(mergeCmd, {
|
|
192
|
+
cwd: repoPath,
|
|
193
|
+
timeout: GIT_TIMEOUT,
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
repo: repoName,
|
|
197
|
+
success: true,
|
|
198
|
+
message: `Merged ${sourceBranch} into ${targetBranch}`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
return {
|
|
203
|
+
repo: repoName,
|
|
204
|
+
success: false,
|
|
205
|
+
message: 'Merge failed',
|
|
206
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
exports.mergeBranch = mergeBranch;
|
|
211
|
+
const rebaseBranch = async (repoPath, repoName, targetBranch) => {
|
|
212
|
+
try {
|
|
213
|
+
await execAsync(`git rebase ${targetBranch}`, {
|
|
214
|
+
cwd: repoPath,
|
|
215
|
+
timeout: GIT_TIMEOUT,
|
|
216
|
+
});
|
|
217
|
+
return {
|
|
218
|
+
repo: repoName,
|
|
219
|
+
success: true,
|
|
220
|
+
message: `Rebased onto ${targetBranch}`,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
return {
|
|
225
|
+
repo: repoName,
|
|
226
|
+
success: false,
|
|
227
|
+
message: 'Rebase failed',
|
|
228
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
exports.rebaseBranch = rebaseBranch;
|