commandmate 0.1.10 → 0.1.12
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/.env.example +8 -3
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +10 -10
- package/.next/app-path-routes-manifest.json +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/1.pack +0 -0
- package/.next/cache/webpack/client-production/2.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route.js +11 -12
- package/.next/server/app/api/external-apps/[id]/route.js +14 -15
- package/.next/server/app/api/external-apps/route.js +12 -13
- package/.next/server/app/api/hooks/claude-done/route.js +6 -6
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/slash-commands.body +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route.js +7 -7
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/index.html +2 -2
- package/.next/server/app/index.rsc +2 -2
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route.js +12 -13
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +4 -4
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/simple-terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +8 -8
- package/.next/server/chunks/1318.js +4 -4
- package/.next/server/chunks/2597.js +1 -0
- package/.next/server/chunks/2648.js +1 -0
- package/.next/server/chunks/7425.js +97 -48
- package/.next/server/chunks/9723.js +1 -1
- package/.next/server/functions-config-manifest.json +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/server/src/middleware.js +2 -2
- package/.next/server/src/middleware.js.map +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-58fcf2e63c056743.js +1 -0
- package/.next/trace +5 -5
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +6 -4
- package/dist/cli/commands/start.d.ts +2 -0
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +64 -17
- package/dist/cli/commands/status.d.ts +4 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +95 -6
- package/dist/cli/commands/stop.d.ts +2 -0
- package/dist/cli/commands/stop.d.ts.map +1 -1
- package/dist/cli/commands/stop.js +27 -10
- package/dist/cli/index.js +16 -2
- package/dist/cli/types/index.d.ts +20 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/utils/daemon-factory.d.ts +105 -0
- package/dist/cli/utils/daemon-factory.d.ts.map +1 -0
- package/dist/cli/utils/daemon-factory.js +117 -0
- package/dist/cli/utils/daemon.d.ts.map +1 -1
- package/dist/cli/utils/daemon.js +4 -0
- package/dist/cli/utils/env-setup.d.ts +24 -12
- package/dist/cli/utils/env-setup.d.ts.map +1 -1
- package/dist/cli/utils/env-setup.js +64 -43
- package/dist/cli/utils/input-validators.d.ts +103 -0
- package/dist/cli/utils/input-validators.d.ts.map +1 -0
- package/dist/cli/utils/input-validators.js +163 -0
- package/dist/cli/utils/install-context.d.ts +53 -0
- package/dist/cli/utils/install-context.d.ts.map +1 -0
- package/dist/cli/utils/install-context.js +96 -0
- package/dist/cli/utils/pid-manager.d.ts +34 -0
- package/dist/cli/utils/pid-manager.d.ts.map +1 -1
- package/dist/cli/utils/pid-manager.js +43 -0
- package/dist/cli/utils/port-allocator.d.ts +108 -0
- package/dist/cli/utils/port-allocator.d.ts.map +1 -0
- package/dist/cli/utils/port-allocator.js +166 -0
- package/dist/cli/utils/resource-resolvers.d.ts +92 -0
- package/dist/cli/utils/resource-resolvers.d.ts.map +1 -0
- package/dist/cli/utils/resource-resolvers.js +175 -0
- package/dist/cli/utils/worktree-detector.d.ts +82 -0
- package/dist/cli/utils/worktree-detector.d.ts.map +1 -0
- package/dist/cli/utils/worktree-detector.js +221 -0
- package/dist/lib/errors.d.ts +111 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +153 -0
- package/dist/server/server.js +3 -0
- package/dist/server/src/cli/utils/install-context.js +96 -0
- package/dist/server/src/config/system-directories.js +40 -0
- package/dist/server/src/lib/auto-yes-manager.js +324 -0
- package/dist/server/src/lib/auto-yes-resolver.js +34 -0
- package/dist/server/src/lib/claude-session.js +134 -28
- package/dist/server/src/lib/cli-patterns.js +6 -1
- package/dist/server/src/lib/db-instance.js +12 -2
- package/dist/server/src/lib/db-migrations.js +68 -1
- package/dist/server/src/lib/db-path-resolver.js +99 -0
- package/dist/server/src/lib/db.js +21 -0
- package/dist/server/src/lib/env.js +52 -3
- package/dist/server/src/lib/worktrees.js +36 -1
- package/dist/server/src/types/external-apps.js +20 -0
- package/package.json +1 -1
- package/.next/server/chunks/1528.js +0 -1
- package/.next/server/chunks/7213.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-aea2d5e7e28955be.js +0 -1
- /package/.next/static/{8o5rUyZun0GklIHWDgkmv → 564GHwluX5xIv9qpqLJV2}/_buildManifest.js +0 -0
- /package/.next/static/{8o5rUyZun0GklIHWDgkmv → 564GHwluX5xIv9qpqLJV2}/_ssgManifest.js +0 -0
- /package/.next/static/chunks/app/{page-96a8aa2ec30a44e9.js → page-fe35d61f14b90a51.js} +0 -0
|
@@ -19,7 +19,7 @@ const db_1 = require("./db");
|
|
|
19
19
|
* Current schema version
|
|
20
20
|
* Increment this when adding new migrations
|
|
21
21
|
*/
|
|
22
|
-
exports.CURRENT_SCHEMA_VERSION =
|
|
22
|
+
exports.CURRENT_SCHEMA_VERSION = 16;
|
|
23
23
|
/**
|
|
24
24
|
* Migration registry
|
|
25
25
|
* All migrations should be added to this array in order
|
|
@@ -650,6 +650,73 @@ const migrations = [
|
|
|
650
650
|
`);
|
|
651
651
|
console.log('✓ Removed initial_branch column from worktrees table');
|
|
652
652
|
}
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
version: 16,
|
|
656
|
+
name: 'add-issue-no-to-external-apps',
|
|
657
|
+
up: (db) => {
|
|
658
|
+
// Issue #136: Add issue_no column to external_apps table
|
|
659
|
+
// This column identifies which worktree/issue an external app belongs to
|
|
660
|
+
// NULL = main app (non-worktree), integer = issue number
|
|
661
|
+
db.exec(`
|
|
662
|
+
ALTER TABLE external_apps ADD COLUMN issue_no INTEGER;
|
|
663
|
+
`);
|
|
664
|
+
// Create index for efficient querying by issue_no
|
|
665
|
+
db.exec(`
|
|
666
|
+
CREATE INDEX IF NOT EXISTS idx_external_apps_issue_no ON external_apps(issue_no);
|
|
667
|
+
`);
|
|
668
|
+
console.log('✓ Added issue_no column to external_apps table');
|
|
669
|
+
console.log('✓ Created index idx_external_apps_issue_no');
|
|
670
|
+
},
|
|
671
|
+
down: (db) => {
|
|
672
|
+
// SQLite doesn't support DROP COLUMN directly
|
|
673
|
+
// Recreate table without issue_no column
|
|
674
|
+
db.exec(`
|
|
675
|
+
-- 1. Create backup table without issue_no
|
|
676
|
+
CREATE TABLE external_apps_backup AS
|
|
677
|
+
SELECT id, name, display_name, description, path_prefix, target_port,
|
|
678
|
+
target_host, app_type, websocket_enabled, websocket_path_pattern,
|
|
679
|
+
enabled, created_at, updated_at
|
|
680
|
+
FROM external_apps;
|
|
681
|
+
|
|
682
|
+
-- 2. Drop original table (this also drops indexes)
|
|
683
|
+
DROP TABLE external_apps;
|
|
684
|
+
|
|
685
|
+
-- 3. Recreate table without issue_no
|
|
686
|
+
CREATE TABLE external_apps (
|
|
687
|
+
id TEXT PRIMARY KEY,
|
|
688
|
+
name TEXT NOT NULL UNIQUE,
|
|
689
|
+
display_name TEXT NOT NULL,
|
|
690
|
+
description TEXT,
|
|
691
|
+
path_prefix TEXT NOT NULL UNIQUE,
|
|
692
|
+
target_port INTEGER NOT NULL,
|
|
693
|
+
target_host TEXT DEFAULT 'localhost',
|
|
694
|
+
app_type TEXT NOT NULL CHECK(app_type IN ('sveltekit', 'streamlit', 'nextjs', 'other')),
|
|
695
|
+
websocket_enabled INTEGER DEFAULT 0,
|
|
696
|
+
websocket_path_pattern TEXT,
|
|
697
|
+
enabled INTEGER DEFAULT 1,
|
|
698
|
+
created_at INTEGER NOT NULL,
|
|
699
|
+
updated_at INTEGER NOT NULL
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
-- 4. Restore data
|
|
703
|
+
INSERT INTO external_apps (id, name, display_name, description, path_prefix,
|
|
704
|
+
target_port, target_host, app_type, websocket_enabled, websocket_path_pattern,
|
|
705
|
+
enabled, created_at, updated_at)
|
|
706
|
+
SELECT id, name, display_name, description, path_prefix,
|
|
707
|
+
target_port, target_host, app_type, websocket_enabled, websocket_path_pattern,
|
|
708
|
+
enabled, created_at, updated_at
|
|
709
|
+
FROM external_apps_backup;
|
|
710
|
+
|
|
711
|
+
-- 5. Drop backup
|
|
712
|
+
DROP TABLE external_apps_backup;
|
|
713
|
+
|
|
714
|
+
-- 6. Recreate original indexes
|
|
715
|
+
CREATE INDEX idx_external_apps_path_prefix ON external_apps(path_prefix);
|
|
716
|
+
CREATE INDEX idx_external_apps_enabled ON external_apps(enabled);
|
|
717
|
+
`);
|
|
718
|
+
console.log('✓ Removed issue_no column from external_apps table');
|
|
719
|
+
}
|
|
653
720
|
}
|
|
654
721
|
];
|
|
655
722
|
/**
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DB Path Resolver
|
|
4
|
+
* Issue #135: DB path resolution logic fix
|
|
5
|
+
* Issue #136: Import from install-context.ts to avoid circular imports
|
|
6
|
+
*
|
|
7
|
+
* Provides consistent DB path resolution for both global and local installs.
|
|
8
|
+
* This module centralizes DB path logic to follow SRP (Single Responsibility Principle).
|
|
9
|
+
*
|
|
10
|
+
* @module db-path-resolver
|
|
11
|
+
*/
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.getDefaultDbPath = getDefaultDbPath;
|
|
17
|
+
exports.getIssueDbPath = getIssueDbPath;
|
|
18
|
+
exports.validateDbPath = validateDbPath;
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const os_1 = require("os");
|
|
21
|
+
const install_context_1 = require("../cli/utils/install-context");
|
|
22
|
+
const system_directories_1 = require("../config/system-directories");
|
|
23
|
+
/**
|
|
24
|
+
* Get the default database path based on install type
|
|
25
|
+
*
|
|
26
|
+
* For global installs: ~/.commandmate/data/cm.db
|
|
27
|
+
* For local installs: <cwd>/data/cm.db (as absolute path)
|
|
28
|
+
*
|
|
29
|
+
* @returns Absolute path to the default database file
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const dbPath = getDefaultDbPath();
|
|
34
|
+
* // Global: /Users/username/.commandmate/data/cm.db
|
|
35
|
+
* // Local: /path/to/project/data/cm.db
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
function getDefaultDbPath() {
|
|
39
|
+
if ((0, install_context_1.isGlobalInstall)()) {
|
|
40
|
+
return path_1.default.join((0, os_1.homedir)(), '.commandmate', 'data', 'cm.db');
|
|
41
|
+
}
|
|
42
|
+
return path_1.default.resolve(process.cwd(), 'data', 'cm.db');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the database path for a specific issue (worktree)
|
|
46
|
+
* Issue #136: Worktree-specific DB path
|
|
47
|
+
*
|
|
48
|
+
* @param issueNo - The issue number
|
|
49
|
+
* @returns Absolute path to the issue-specific database file
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const dbPath = getIssueDbPath(135);
|
|
54
|
+
* // Returns: /Users/username/.commandmate/data/cm-135.db
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
function getIssueDbPath(issueNo) {
|
|
58
|
+
const configDir = (0, install_context_1.getConfigDir)();
|
|
59
|
+
return path_1.default.join(configDir, 'data', `cm-${issueNo}.db`);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Validate database path for security
|
|
63
|
+
*
|
|
64
|
+
* SEC-001: Protects against writing to system directories
|
|
65
|
+
* - Global install: DB path must be within home directory
|
|
66
|
+
* - Local install: DB path must not be in system directories
|
|
67
|
+
*
|
|
68
|
+
* @param dbPath - The database path to validate
|
|
69
|
+
* @returns The resolved absolute path if valid
|
|
70
|
+
* @throws Error if path is in a forbidden location
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // Valid paths
|
|
75
|
+
* validateDbPath('~/.commandmate/data/cm.db'); // Global
|
|
76
|
+
* validateDbPath('./data/cm.db'); // Local
|
|
77
|
+
*
|
|
78
|
+
* // Invalid paths (throws)
|
|
79
|
+
* validateDbPath('/etc/cm.db');
|
|
80
|
+
* validateDbPath('/var/lib/cm.db');
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
function validateDbPath(dbPath) {
|
|
84
|
+
const resolvedPath = path_1.default.resolve(dbPath);
|
|
85
|
+
if ((0, install_context_1.isGlobalInstall)()) {
|
|
86
|
+
// Global install: DB path must be within home directory
|
|
87
|
+
const homeDir = (0, os_1.homedir)();
|
|
88
|
+
if (!resolvedPath.startsWith(homeDir)) {
|
|
89
|
+
throw new Error(`Security error: DB path must be within home directory: ${resolvedPath}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// Local install: DB path must not be in system directories (SEC-001)
|
|
94
|
+
if ((0, system_directories_1.isSystemDirectory)(resolvedPath)) {
|
|
95
|
+
throw new Error(`Security error: DB path cannot be in system directory: ${resolvedPath}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return resolvedPath;
|
|
99
|
+
}
|
|
@@ -41,6 +41,7 @@ exports.saveInitialBranch = saveInitialBranch;
|
|
|
41
41
|
exports.getInitialBranch = getInitialBranch;
|
|
42
42
|
exports.getWorktreeIdsByRepository = getWorktreeIdsByRepository;
|
|
43
43
|
exports.deleteRepositoryWorktrees = deleteRepositoryWorktrees;
|
|
44
|
+
exports.deleteWorktreesByIds = deleteWorktreesByIds;
|
|
44
45
|
const crypto_1 = require("crypto");
|
|
45
46
|
function mapChatMessage(row) {
|
|
46
47
|
return {
|
|
@@ -875,3 +876,23 @@ function deleteRepositoryWorktrees(db, repositoryPath) {
|
|
|
875
876
|
const result = stmt.run(repositoryPath);
|
|
876
877
|
return { deletedCount: result.changes };
|
|
877
878
|
}
|
|
879
|
+
/**
|
|
880
|
+
* Delete worktrees by their IDs
|
|
881
|
+
* Related data (chat_messages, session_states, worktree_memos) will be
|
|
882
|
+
* automatically deleted via CASCADE foreign key constraints.
|
|
883
|
+
*
|
|
884
|
+
* @param db - Database instance
|
|
885
|
+
* @param worktreeIds - Array of worktree IDs to delete
|
|
886
|
+
* @returns Object containing the count of deleted worktrees
|
|
887
|
+
*/
|
|
888
|
+
function deleteWorktreesByIds(db, worktreeIds) {
|
|
889
|
+
if (worktreeIds.length === 0) {
|
|
890
|
+
return { deletedCount: 0 };
|
|
891
|
+
}
|
|
892
|
+
const placeholders = worktreeIds.map(() => '?').join(',');
|
|
893
|
+
const stmt = db.prepare(`
|
|
894
|
+
DELETE FROM worktrees WHERE id IN (${placeholders})
|
|
895
|
+
`);
|
|
896
|
+
const result = stmt.run(...worktreeIds);
|
|
897
|
+
return { deletedCount: result.changes };
|
|
898
|
+
}
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Issue #76: Environment variable fallback support
|
|
7
7
|
* Supports both new (CM_*) and legacy (MCBD_*) environment variable names
|
|
8
|
+
*
|
|
9
|
+
* Issue #135: DB path resolution fix
|
|
10
|
+
* Uses getDefaultDbPath() from db-path-resolver.ts for consistent DB path handling
|
|
8
11
|
*/
|
|
9
12
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
13
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
@@ -14,11 +17,14 @@ exports.ENV_MAPPING = void 0;
|
|
|
14
17
|
exports.resetWarnedKeys = resetWarnedKeys;
|
|
15
18
|
exports.getEnvWithFallback = getEnvWithFallback;
|
|
16
19
|
exports.getEnvByKey = getEnvByKey;
|
|
20
|
+
exports.resetDatabasePathWarning = resetDatabasePathWarning;
|
|
21
|
+
exports.getDatabasePathWithDeprecationWarning = getDatabasePathWithDeprecationWarning;
|
|
17
22
|
exports.getLogConfig = getLogConfig;
|
|
18
23
|
exports.getEnv = getEnv;
|
|
19
24
|
exports.validateEnv = validateEnv;
|
|
20
25
|
exports.isAuthRequired = isAuthRequired;
|
|
21
26
|
const path_1 = __importDefault(require("path"));
|
|
27
|
+
const db_path_resolver_1 = require("./db-path-resolver");
|
|
22
28
|
// ============================================================
|
|
23
29
|
// Environment Variable Mapping (for fallback support)
|
|
24
30
|
// Issue #76: CommandMate rename - Phase 1
|
|
@@ -79,6 +85,36 @@ function getEnvWithFallback(newKey, oldKey) {
|
|
|
79
85
|
function getEnvByKey(key) {
|
|
80
86
|
return getEnvWithFallback(key, exports.ENV_MAPPING[key]);
|
|
81
87
|
}
|
|
88
|
+
// ============================================================
|
|
89
|
+
// [Issue #135] DATABASE_PATH Deprecation Support
|
|
90
|
+
// ============================================================
|
|
91
|
+
/**
|
|
92
|
+
* Set to track warned keys for DATABASE_PATH deprecation (separate from ENV_MAPPING warnings)
|
|
93
|
+
*/
|
|
94
|
+
let databasePathWarned = false;
|
|
95
|
+
/**
|
|
96
|
+
* Reset DATABASE_PATH warning state (for testing purposes)
|
|
97
|
+
*/
|
|
98
|
+
function resetDatabasePathWarning() {
|
|
99
|
+
databasePathWarned = false;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get DATABASE_PATH with deprecation warning
|
|
103
|
+
*
|
|
104
|
+
* SEC-004: Logs security event when deprecated DATABASE_PATH is used
|
|
105
|
+
*
|
|
106
|
+
* @returns DATABASE_PATH value if set, undefined otherwise
|
|
107
|
+
*/
|
|
108
|
+
function getDatabasePathWithDeprecationWarning() {
|
|
109
|
+
const dbPath = process.env.DATABASE_PATH;
|
|
110
|
+
if (dbPath) {
|
|
111
|
+
if (!databasePathWarned) {
|
|
112
|
+
console.warn('[DEPRECATED] DATABASE_PATH is deprecated. Use CM_DB_PATH instead.');
|
|
113
|
+
databasePathWarned = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return dbPath;
|
|
117
|
+
}
|
|
82
118
|
/**
|
|
83
119
|
* Validate log level
|
|
84
120
|
*/
|
|
@@ -125,9 +161,11 @@ function getEnv() {
|
|
|
125
161
|
const port = parseInt(getEnvByKey('CM_PORT') || '3000', 10);
|
|
126
162
|
const bind = getEnvByKey('CM_BIND') || '127.0.0.1';
|
|
127
163
|
const authToken = getEnvByKey('CM_AUTH_TOKEN');
|
|
164
|
+
// Issue #135: DB path resolution with proper fallback chain
|
|
165
|
+
// Priority: CM_DB_PATH > DATABASE_PATH (deprecated) > getDefaultDbPath()
|
|
128
166
|
const databasePath = getEnvByKey('CM_DB_PATH')
|
|
129
|
-
||
|
|
130
|
-
||
|
|
167
|
+
|| getDatabasePathWithDeprecationWarning()
|
|
168
|
+
|| (0, db_path_resolver_1.getDefaultDbPath)();
|
|
131
169
|
// Validate values
|
|
132
170
|
if (!rootDir) {
|
|
133
171
|
throw new Error('CM_ROOT_DIR (or MCBD_ROOT_DIR) is required');
|
|
@@ -142,12 +180,23 @@ function getEnv() {
|
|
|
142
180
|
if (bind === '0.0.0.0' && !authToken) {
|
|
143
181
|
throw new Error('CM_AUTH_TOKEN (or MCBD_AUTH_TOKEN) is required when CM_BIND=0.0.0.0');
|
|
144
182
|
}
|
|
183
|
+
// Issue #135: Validate DB path for security (SEC-001)
|
|
184
|
+
let validatedDbPath;
|
|
185
|
+
try {
|
|
186
|
+
validatedDbPath = (0, db_path_resolver_1.validateDbPath)(databasePath);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// If validation fails, fall back to default path
|
|
190
|
+
// This can happen if DATABASE_PATH points to a system directory
|
|
191
|
+
console.warn(`[Security] Invalid DB path "${databasePath}", using default.`);
|
|
192
|
+
validatedDbPath = (0, db_path_resolver_1.validateDbPath)((0, db_path_resolver_1.getDefaultDbPath)());
|
|
193
|
+
}
|
|
145
194
|
return {
|
|
146
195
|
CM_ROOT_DIR: path_1.default.resolve(rootDir),
|
|
147
196
|
CM_PORT: port,
|
|
148
197
|
CM_BIND: bind,
|
|
149
198
|
CM_AUTH_TOKEN: authToken,
|
|
150
|
-
CM_DB_PATH:
|
|
199
|
+
CM_DB_PATH: validatedDbPath,
|
|
151
200
|
};
|
|
152
201
|
}
|
|
153
202
|
/**
|
|
@@ -215,6 +215,11 @@ async function scanMultipleRepositories(repositoryPaths) {
|
|
|
215
215
|
/**
|
|
216
216
|
* Sync scanned worktrees to database
|
|
217
217
|
*
|
|
218
|
+
* This function performs a true sync:
|
|
219
|
+
* 1. Groups worktrees by repository
|
|
220
|
+
* 2. For each repository, removes worktrees that no longer exist
|
|
221
|
+
* 3. Upserts all current worktrees
|
|
222
|
+
*
|
|
218
223
|
* @param db - Database instance
|
|
219
224
|
* @param worktrees - Array of worktrees to sync
|
|
220
225
|
*
|
|
@@ -225,7 +230,37 @@ async function scanMultipleRepositories(repositoryPaths) {
|
|
|
225
230
|
* ```
|
|
226
231
|
*/
|
|
227
232
|
function syncWorktreesToDB(db, worktrees) {
|
|
233
|
+
// If no worktrees provided, do nothing (avoid accidentally deleting all data)
|
|
234
|
+
if (worktrees.length === 0) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
// Group worktrees by repository path
|
|
238
|
+
const worktreesByRepo = new Map();
|
|
228
239
|
for (const worktree of worktrees) {
|
|
229
|
-
|
|
240
|
+
const repoPath = worktree.repositoryPath || '';
|
|
241
|
+
if (!worktreesByRepo.has(repoPath)) {
|
|
242
|
+
worktreesByRepo.set(repoPath, []);
|
|
243
|
+
}
|
|
244
|
+
worktreesByRepo.get(repoPath).push(worktree);
|
|
245
|
+
}
|
|
246
|
+
// Process each repository
|
|
247
|
+
for (const [repoPath, repoWorktrees] of worktreesByRepo) {
|
|
248
|
+
if (!repoPath)
|
|
249
|
+
continue;
|
|
250
|
+
// Get current worktree IDs in DB for this repository
|
|
251
|
+
const existingIds = (0, db_1.getWorktreeIdsByRepository)(db, repoPath);
|
|
252
|
+
// Get new worktree IDs from scan
|
|
253
|
+
const newIds = new Set(repoWorktrees.map(wt => wt.id));
|
|
254
|
+
// Find worktrees that no longer exist (deleted)
|
|
255
|
+
const deletedIds = existingIds.filter(id => !newIds.has(id));
|
|
256
|
+
// Delete removed worktrees from DB
|
|
257
|
+
if (deletedIds.length > 0) {
|
|
258
|
+
const result = (0, db_1.deleteWorktreesByIds)(db, deletedIds);
|
|
259
|
+
console.log(`Removed ${result.deletedCount} deleted worktree(s) from ${repoPath}`);
|
|
260
|
+
}
|
|
261
|
+
// Upsert current worktrees
|
|
262
|
+
for (const worktree of repoWorktrees) {
|
|
263
|
+
(0, db_1.upsertWorktree)(db, worktree);
|
|
264
|
+
}
|
|
230
265
|
}
|
|
231
266
|
}
|
|
@@ -4,3 +4,23 @@
|
|
|
4
4
|
* Issue #42: Proxy routing for multiple frontend applications
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.isWorktreeExternalApp = isWorktreeExternalApp;
|
|
8
|
+
/**
|
|
9
|
+
* Type guard to check if an ExternalApp is a WorktreeExternalApp
|
|
10
|
+
* Issue #136: Helper for type narrowing
|
|
11
|
+
*
|
|
12
|
+
* @param app - The external app to check
|
|
13
|
+
* @returns true if the app has a valid issueNo
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const app = getExternalAppById('some-id');
|
|
18
|
+
* if (isWorktreeExternalApp(app)) {
|
|
19
|
+
* // TypeScript knows app.issueNo is number
|
|
20
|
+
* console.log(`Worktree for issue #${app.issueNo}`);
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
function isWorktreeExternalApp(app) {
|
|
25
|
+
return typeof app.issueNo === 'number' && app.issueNo > 0;
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";exports.id=1528,exports.ids=[1528],exports.modules={19377:(t,e,n)=>{n.d(e,{Wg:()=>c,bs:()=>function t(e){switch(e){case"claude":return{promptPattern:i,separatorPattern:a,thinkingPattern:o,skipPatterns:[/^─{10,}$/,/^[>❯]\s*$/,o,/^\s*[⎿⏋]\s+Tip:/,/^\s*Tip:/,/^\s*\?\s*for shortcuts/,/to interrupt\)/]};case"codex":return{promptPattern:l,separatorPattern:p,thinkingPattern:s,skipPatterns:[/^─.*─+$/,/^›\s*$/,/^›\s+(Implement|Find and fix|Type)/,s,/^\s*\d+%\s+context left/,/^\s*for shortcuts$/,/╭─+╮/,/╰─+╯/]};case"gemini":return{promptPattern:u,separatorPattern:/^gemini\s+--\s+/m,thinkingPattern:/(?!)/m,skipPatterns:[/^gemini\s+--\s+/,u,/^\s*$/]};default:return t("claude")}},vp:()=>d});let r=(0,n(43895).h)("cli-patterns"),o=RegExp(`[✻✽⏺\xb7∴✢✳✶⦿◉●○◌◎⊙⊚⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]\\s+.+…|to interrupt\\)`,"m"),s=/•\s*(Planning|Searching|Exploring|Running|Thinking|Working|Reading|Writing|Analyzing)/m,i=/^[>❯]\s*$/m,a=/^─{10,}$/m,l=/^›\s+.+/m,p=/^─.*Worked for.*─+$/m,u=/^(%|\$|.*@.*[%$#])\s*$/m;function c(t,e){let n;let i=r.withContext({cliToolId:t});switch(i.debug("detectThinking:check",{contentLength:e.length}),t){case"claude":default:n=o.test(e);break;case"codex":n=s.test(e);break;case"gemini":n=!1}return i.debug("detectThinking:result",{isThinking:n}),n}let m=/\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\[[0-9;]*m/g;function d(t){return t.replace(m,"")}},89194:(t,e,n)=>{n.d(e,{Lg:()=>i,NA:()=>a});var r=n(10927),o=n(97213);let s=(0,n(43895).h)("cli-session");async function i(t,e){let n=o.g.getInstance().getTool(e).getSessionName(t);return await (0,r.Hk)(n)}async function a(t,e,n=1e3){let i=s.withContext({worktreeId:t,cliToolId:e});i.debug("captureSessionOutput:start",{requestedLines:n});let a=o.g.getInstance().getTool(e),l=a.getSessionName(t);if(!await (0,r.Hk)(l))throw i.debug("captureSessionOutput:sessionNotFound",{sessionName:l}),Error(`${a.name} session ${l} does not exist`);try{let t=await (0,r.xq)(l,{startLine:-n}),e=t.split("\n").length;return i.debug("captureSessionOutput:success",{actualLines:e,lastFewLines:t.split("\n").slice(-3).join(" | ")}),t}catch(e){let t=e instanceof Error?e.message:String(e);throw i.error("captureSessionOutput:failed",{error:t}),Error(`Failed to capture ${a.name} output: ${t}`)}}},37848:(t,e,n)=>{n.d(e,{Hb:()=>a,LI:()=>l,dU:()=>p});var r=n(55315),o=n.n(r);let s={CM_ROOT_DIR:"MCBD_ROOT_DIR",CM_PORT:"MCBD_PORT",CM_BIND:"MCBD_BIND",CM_AUTH_TOKEN:"MCBD_AUTH_TOKEN",CM_LOG_LEVEL:"MCBD_LOG_LEVEL",CM_LOG_FORMAT:"MCBD_LOG_FORMAT",CM_LOG_DIR:"MCBD_LOG_DIR",CM_DB_PATH:"MCBD_DB_PATH"},i=new Set;function a(t){return function(t,e){let n=process.env[t];if(void 0!==n)return n;let r=process.env[e];if(void 0!==r)return i.has(e)||(console.warn(`[DEPRECATED] ${e} is deprecated, use ${t} instead`),i.add(e)),r}(t,s[t])}function l(){let t=a("CM_LOG_LEVEL")?.toLowerCase(),e=a("CM_LOG_FORMAT")?.toLowerCase();return{level:void 0!==t&&["debug","info","warn","error"].includes(t)?t:"info",format:"json"===e?"json":"text"}}function p(){let t=a("CM_ROOT_DIR")||process.cwd(),e=parseInt(a("CM_PORT")||"3000",10),n=a("CM_BIND")||"127.0.0.1",r=a("CM_AUTH_TOKEN"),s=a("CM_DB_PATH")||process.env.DATABASE_PATH||o().join(process.cwd(),"data","cm.db");if(!t)throw Error("CM_ROOT_DIR (or MCBD_ROOT_DIR) is required");if(isNaN(e)||e<1||e>65535)throw Error(`Invalid CM_PORT: ${a("CM_PORT")}. Must be between 1 and 65535.`);if("127.0.0.1"!==n&&"0.0.0.0"!==n&&"localhost"!==n)throw Error(`Invalid CM_BIND: ${n}. Must be '127.0.0.1', '0.0.0.0', or 'localhost'.`);if("0.0.0.0"===n&&!r)throw Error("CM_AUTH_TOKEN (or MCBD_AUTH_TOKEN) is required when CM_BIND=0.0.0.0");return{CM_ROOT_DIR:o().resolve(t),CM_PORT:e,CM_BIND:n,CM_AUTH_TOKEN:r,CM_DB_PATH:o().resolve(s)}}},43895:(t,e,n)=>{n.d(e,{Y:()=>l,h:()=>p});var r=n(37848);let o=[{pattern:/Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi,replacement:"Bearer [REDACTED]"},{pattern:/(password|passwd|pwd)[=:]\s*\S+/gi,replacement:"$1=[REDACTED]"},{pattern:/(token|secret|api_key|apikey|auth)[=:]\s*\S+/gi,replacement:"$1=[REDACTED]"},{pattern:/CM_AUTH_TOKEN=\S+/gi,replacement:"CM_AUTH_TOKEN=[REDACTED]"},{pattern:/MCBD_AUTH_TOKEN=\S+/gi,replacement:"MCBD_AUTH_TOKEN=[REDACTED]"},{pattern:/Authorization:\s*\S+/gi,replacement:"Authorization: [REDACTED]"},{pattern:/-----BEGIN\s+\w+\s+PRIVATE\s+KEY-----[\s\S]*?-----END\s+\w+\s+PRIVATE\s+KEY-----/g,replacement:"[SSH_KEY_REDACTED]"}],s=/password|secret|token|key|auth/i,i={debug:0,info:1,warn:2,error:3};function a(t,e,n,a,l){let p=(0,r.LI)().level;if(i[t]<i[p])return;let u=a?function t(e){if("string"==typeof e){let t=e;for(let{pattern:e,replacement:n}of o)t=t.replace(e,n);return t}if("object"==typeof e&&null!==e){if(Array.isArray(e))return e.map(t);let n={};for(let[r,o]of Object.entries(e))s.test(r)?n[r]="[REDACTED]":n[r]=t(o);return n}return e}(a):void 0,c=function(t){if("json"===(0,r.LI)().format)return JSON.stringify(t);let{timestamp:e,level:n,module:o,action:s,data:i,worktreeId:a,cliToolId:l,requestId:p}=t,u=[a,l].filter(Boolean),c=u.length>0?` [${u.join(":")}]`:"",m=p?` (${p.slice(0,8)})`:"",d=i?` ${JSON.stringify(i)}`:"";return`[${e}] [${n.toUpperCase()}] [${o}]${c}${m} ${s}${d}`}({level:t,module:e,action:n,timestamp:new Date().toISOString(),...l,...u&&{data:u}});switch(t){case"error":console.error(c);break;case"warn":console.warn(c);break;default:console.log(c)}}function l(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{let e=16*Math.random()|0;return("x"===t?e:3&e|8).toString(16)})}function p(t){let e=n=>({debug:(e,r)=>a("debug",t,e,r,n),info:(e,r)=>a("info",t,e,r,n),warn:(e,r)=>a("warn",t,e,r,n),error:(e,r)=>a("error",t,e,r,n),withContext:t=>e({...n,...t})});return e()}},63661:(t,e,n)=>{n.d(e,{F:()=>o,J:()=>i});let r=(0,n(43895).h)("prompt-detector");function o(t){r.debug("detectPrompt:start",{outputLength:t.length});let e=t.split("\n").slice(-10).join("\n"),n=function(t){let e=t.split("\n"),n=/^\s*([❯ ]\s*)?(\d+)\.\s*(.+)$/,r=[],o=-1,i=-1;for(let t=e.length-1;t>=0&&t>=e.length-50;t--){let s=e[t].trim(),a=e[t],l=s.match(n);if(l){let e=!!(l[1]&&l[1].includes("❯")),n=parseInt(l[2],10),o=l[3].trim();r.unshift({number:n,label:o,isDefault:e}),-1===i&&(i=t)}else if(r.length>0&&s&&!s.match(/^[-─]+$/)){let e=a.match(/^\s{2,}[^\d]/)&&!a.match(/^\s*\d+\./),n=s.length<5&&!s.endsWith("?");if(e||n)continue;o=t;break}}let a=r.some(t=>t.isDefault);if(r.length<2||!a)return{isPrompt:!1,cleanContent:t.trim()};let l="";if(o>=0){let t=[];for(let n=Math.max(0,o-5);n<=o;n++){let r=e[n].trim();r&&!r.match(/^[-─]+$/)&&t.push(r)}l=t.join(" ")}else l="Please select an option:";return{isPrompt:!0,promptData:{type:"multiple_choice",question:l.trim(),options:r.map(t=>{let e=s.some(e=>e.test(t.label));return{number:t.number,label:t.label,isDefault:t.isDefault,requiresTextInput:e}}),status:"pending"},cleanContent:l.trim()}}(t);if(n.isPrompt)return r.info("detectPrompt:multipleChoice",{isPrompt:!0,question:n.promptData?.question,optionsCount:n.promptData?.options?.length}),n;let o=e.match(/^(.+)\s+\(y\/n\)\s*$/m);if(o)return{isPrompt:!0,promptData:{type:"yes_no",question:o[1].trim(),options:["yes","no"],status:"pending"},cleanContent:o[1].trim()};let i=e.match(/^(.+)\s+\[y\/N\]\s*$/m);if(i)return{isPrompt:!0,promptData:{type:"yes_no",question:i[1].trim(),options:["yes","no"],status:"pending",defaultOption:"no"},cleanContent:i[1].trim()};let a=e.match(/^(.+)\s+\[Y\/n\]\s*$/m);if(a)return{isPrompt:!0,promptData:{type:"yes_no",question:a[1].trim(),options:["yes","no"],status:"pending",defaultOption:"yes"},cleanContent:a[1].trim()};let l=e.match(/^(.+)\s+\(yes\/no\)\s*$/m);if(l)return{isPrompt:!0,promptData:{type:"yes_no",question:l[1].trim(),options:["yes","no"],status:"pending"},cleanContent:l[1].trim()};let p=e.match(/^(.*?)Approve\?\s*$/m);if(p){let t=p[1].trim();return{isPrompt:!0,promptData:{type:"yes_no",question:t?`${t} Approve?`:"Approve?",options:["yes","no"],status:"pending"},cleanContent:t||"Approve?"}}return r.debug("detectPrompt:complete",{isPrompt:!1}),{isPrompt:!1,cleanContent:t.trim()}}let s=[/type\s+here/i,/tell\s+(me|claude)/i,/enter\s+/i,/custom/i,/differently/i];function i(t,e="yes_no"){let n=t.toLowerCase().trim();if("multiple_choice"===e){if(/^\d+$/.test(n))return n;throw Error(`Invalid answer for multiple choice: ${t}. Expected a number.`)}if("yes"===n||"y"===n)return"y";if("no"===n||"n"===n)return"n";throw Error(`Invalid answer: ${t}. Expected 'yes', 'no', 'y', or 'n'.`)}}};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";exports.id=7213,exports.ids=[7213],exports.modules={62648:(t,e,s)=>{s.d(e,{Lm:()=>w,Uv:()=>c,YI:()=>m,_f:()=>h,xd:()=>d,ym:()=>u});var i=s(10927),o=s(61282);let n=(0,s(21764).promisify)(o.exec),a=null;async function r(){if(a)return a;if(process.env.CLAUDE_PATH)return a=process.env.CLAUDE_PATH;try{let{stdout:t}=await n("which claude",{timeout:5e3});return a=t.trim()}catch{for(let t of["/opt/homebrew/bin/claude","/usr/local/bin/claude","/usr/bin/claude"])try{return await n(`test -x "${t}"`,{timeout:1e3}),a=t}catch{}throw Error("Claude CLI not found. Set CLAUDE_PATH environment variable or install Claude CLI.")}}function l(t){return`mcbd-claude-${t}`}async function c(){try{return await n("which claude",{timeout:5e3}),!0}catch{return!1}}async function m(t){let e=l(t);return await (0,i.Hk)(e)}async function u(t){let{worktreeId:e,worktreePath:s}=t;if(!await c())throw Error("Claude CLI is not installed or not in PATH");let o=l(e);if(await (0,i.Hk)(o)){console.log(`Claude session ${o} already exists`);return}try{await (0,i.ed)({sessionName:o,workingDirectory:s,historyLimit:5e4});let t=await r();await (0,i.Is)(o,t,!0);let e=Date.now();for(;Date.now()-e<1e4;){await new Promise(t=>setTimeout(t,500));try{let t=await (0,i.xq)(o,{startLine:-50});if(/^>\s*$/m.test(t)||/^─{10,}$/m.test(t)){console.log(`✓ Claude initialized in ${Date.now()-e}ms`);break}}catch{}}console.log(`✓ Started Claude session: ${o}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to start Claude session: ${t}`)}}async function d(t,e){let s=l(t);if(!await (0,i.Hk)(s))throw Error(`Claude session ${s} does not exist. Start the session first.`);try{await (0,i.Is)(s,e,!1),await new Promise(t=>setTimeout(t,100)),await n(`tmux send-keys -t "${s}" C-m`),await new Promise(t=>setTimeout(t,200)),console.log(`✓ Sent message to Claude session: ${s}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to send message to Claude: ${t}`)}}async function w(t,e=1e3){let s=l(t);if(!await (0,i.Hk)(s))throw Error(`Claude session ${s} does not exist`);try{return await (0,i.xq)(s,{startLine:-e})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to capture Claude output: ${t}`)}}async function h(t){let e=l(t);try{await (0,i.Hk)(e)&&(await (0,i.Is)(e,"",!1),await n(`tmux send-keys -t "${e}" C-d`),await new Promise(t=>setTimeout(t,500)));let t=await (0,i.AJ)(e);return t&&console.log(`✓ Stopped Claude session: ${e}`),t}catch(e){let t=e instanceof Error?e.message:String(e);return console.error(`Error stopping Claude session: ${t}`),!1}}},97213:(t,e,s)=>{s.d(e,{g:()=>h});var i=s(61282),o=s(21764),n=s(10927);let a=(0,o.promisify)(i.exec);class r{async isInstalled(){try{return await a(`which ${this.command}`,{timeout:5e3}),!0}catch{return!1}}getSessionName(t){return`mcbd-${this.id}-${t}`}async interrupt(t){let e=this.getSessionName(t);await (0,n.ZV)(e,"Escape")}}var l=s(62648);class c extends r{async isInstalled(){return await (0,l.Uv)()}async isRunning(t){return await (0,l.YI)(t)}async startSession(t,e){await (0,l.ym)({worktreeId:t,worktreePath:e})}async sendMessage(t,e){await (0,l.xd)(t,e)}async killSession(t){await (0,l._f)(t)}constructor(...t){super(...t),this.id="claude",this.name="Claude Code",this.command="claude"}}let m=(0,o.promisify)(i.exec);class u extends r{async isRunning(t){let e=this.getSessionName(t);return await (0,n.Hk)(e)}async startSession(t,e){if(!await this.isInstalled())throw Error("Codex CLI is not installed or not in PATH");let s=this.getSessionName(t);if(await (0,n.Hk)(s)){console.log(`Codex session ${s} already exists`);return}try{await (0,n.ed)({sessionName:s,workingDirectory:e,historyLimit:5e4}),await new Promise(t=>setTimeout(t,100)),await (0,n.Is)(s,"codex",!0),await new Promise(t=>setTimeout(t,3e3)),await (0,n.Is)(s,"2",!0),await new Promise(t=>setTimeout(t,500)),console.log(`✓ Started Codex session: ${s}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to start Codex session: ${t}`)}}async sendMessage(t,e){let s=this.getSessionName(t);if(!await (0,n.Hk)(s))throw Error(`Codex session ${s} does not exist. Start the session first.`);try{await (0,n.Is)(s,e,!1),await new Promise(t=>setTimeout(t,100)),await m(`tmux send-keys -t "${s}" C-m`),await new Promise(t=>setTimeout(t,200)),console.log(`✓ Sent message to Codex session: ${s}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to send message to Codex: ${t}`)}}async killSession(t){let e=this.getSessionName(t);try{await (0,n.Hk)(e)&&(await m(`tmux send-keys -t "${e}" C-d`),await new Promise(t=>setTimeout(t,500))),await (0,n.AJ)(e)&&console.log(`✓ Stopped Codex session: ${e}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw console.error(`Error stopping Codex session: ${t}`),e}}constructor(...t){super(...t),this.id="codex",this.name="Codex CLI",this.command="codex"}}let d=(0,o.promisify)(i.exec);class w extends r{async isRunning(t){let e=this.getSessionName(t);return await (0,n.Hk)(e)}async startSession(t,e){if(!await this.isInstalled())throw Error("Gemini CLI is not installed or not in PATH");let s=this.getSessionName(t);if(await (0,n.Hk)(s)){console.log(`Gemini session ${s} already exists`);return}try{await (0,n.ed)({sessionName:s,workingDirectory:e,historyLimit:5e4}),console.log(`✓ Started Gemini session: ${s}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to start Gemini session: ${t}`)}}async sendMessage(t,e){let s=this.getSessionName(t);if(!await (0,n.Hk)(s))throw Error(`Gemini session ${s} does not exist. Start the session first.`);try{let t=e.replace(/'/g,"'\\''");await (0,n.Is)(s,`echo '${t}' | gemini`,!0),console.log(`✓ Sent message to Gemini session: ${s}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to send message to Gemini: ${t}`)}}async killSession(t){let e=this.getSessionName(t);try{await (0,n.Hk)(e)&&(await d(`tmux send-keys -t "${e}" C-d`),await new Promise(t=>setTimeout(t,500))),await (0,n.AJ)(e)&&console.log(`✓ Stopped Gemini session: ${e}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw console.error(`Error stopping Gemini session: ${t}`),e}}constructor(...t){super(...t),this.id="gemini",this.name="Gemini CLI",this.command="gemini"}}class h{constructor(){this.tools=new Map,this.tools.set("claude",new c),this.tools.set("codex",new u),this.tools.set("gemini",new w)}static getInstance(){return h.instance||(h.instance=new h),h.instance}getTool(t){let e=this.tools.get(t);if(!e)throw Error(`CLI tool '${t}' not found`);return e}getAllTools(){return Array.from(this.tools.values())}async getToolInfo(t){let e=this.getTool(t),s=await e.isInstalled();return{id:e.id,name:e.name,command:e.command,installed:s}}async getAllToolsInfo(){return Promise.all(this.getAllTools().map(async t=>{let e=await t.isInstalled();return{id:t.id,name:t.name,command:t.command,installed:e}}))}async getInstalledTools(){return(await this.getAllToolsInfo()).filter(t=>t.installed)}}},10927:(t,e,s)=>{s.d(e,{AJ:()=>c,Hk:()=>n,Is:()=>r,ZV:()=>m,ed:()=>a,xq:()=>l});var i=s(61282);let o=(0,s(21764).promisify)(i.exec);async function n(t){try{return await o(`tmux has-session -t "${t}"`,{timeout:5e3}),!0}catch{return!1}}async function a(t,e){let s,i,n;"string"==typeof t?(s=t,i=e,n=5e4):(s=t.sessionName,i=t.workingDirectory,n=t.historyLimit||5e4);try{await o(`tmux new-session -d -s "${s}" -c "${i}"`,{timeout:5e3}),await o(`tmux set-option -t "${s}" history-limit ${n}`,{timeout:5e3})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to create tmux session: ${t}`)}}async function r(t,e,s=!0){let i=e.replace(/'/g,"'\\''"),n=s?`tmux send-keys -t "${t}" '${i}' C-m`:`tmux send-keys -t "${t}" '${i}'`;try{await o(n,{timeout:5e3})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to send keys to tmux session: ${t}`)}}async function l(t,e){let s,i;"number"==typeof e?(s=-e,i="-"):e?(s=e.startLine??-1e4,i=e.endLine??"-"):(s=-1e3,i="-");try{let{stdout:e}=await o(`tmux capture-pane -t "${t}" -p -e -S ${s} -E ${i}`,{timeout:5e3,maxBuffer:10485760});return e}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to capture pane: ${t}`)}}async function c(t){try{return await o(`tmux kill-session -t "${t}"`,{timeout:5e3}),!0}catch(e){let t=e instanceof Error?e.message:String(e);if(t?.includes("no server running")||t?.includes("can't find session"))return!1;throw Error(`Failed to kill tmux session: ${t}`)}}async function m(t,e){try{await o(`tmux send-keys -t "${t}" ${e}`,{timeout:5e3})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to send special key: ${t}`)}}}};
|