commandmate 0.1.10 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.env.example +8 -3
  2. package/.next/BUILD_ID +1 -1
  3. package/.next/app-build-manifest.json +11 -11
  4. package/.next/app-path-routes-manifest.json +1 -1
  5. package/.next/build-manifest.json +2 -2
  6. package/.next/cache/.tsbuildinfo +1 -1
  7. package/.next/cache/config.json +3 -3
  8. package/.next/cache/webpack/client-production/0.pack +0 -0
  9. package/.next/cache/webpack/client-production/1.pack +0 -0
  10. package/.next/cache/webpack/client-production/2.pack +0 -0
  11. package/.next/cache/webpack/client-production/index.pack +0 -0
  12. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  13. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  14. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  15. package/.next/cache/webpack/server-production/0.pack +0 -0
  16. package/.next/cache/webpack/server-production/index.pack +0 -0
  17. package/.next/next-server.js.nft.json +1 -1
  18. package/.next/prerender-manifest.json +1 -1
  19. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/.next/server/app/_not-found.html +1 -1
  21. package/.next/server/app/_not-found.rsc +1 -1
  22. package/.next/server/app/api/external-apps/[id]/health/route.js +11 -12
  23. package/.next/server/app/api/external-apps/[id]/route.js +14 -15
  24. package/.next/server/app/api/external-apps/route.js +12 -13
  25. package/.next/server/app/api/hooks/claude-done/route.js +1 -1
  26. package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
  27. package/.next/server/app/api/repositories/clone/route.js +1 -1
  28. package/.next/server/app/api/repositories/route.js +1 -1
  29. package/.next/server/app/api/repositories/scan/route.js +1 -1
  30. package/.next/server/app/api/repositories/sync/route.js +1 -1
  31. package/.next/server/app/api/slash-commands.body +1 -1
  32. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  33. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  34. package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -1
  35. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  36. package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js +1 -1
  37. package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
  38. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  39. package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
  40. package/.next/server/app/api/worktrees/[id]/logs/route.js +7 -7
  41. package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js +1 -1
  42. package/.next/server/app/api/worktrees/[id]/memos/route.js +1 -1
  43. package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -1
  44. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  45. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  46. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  47. package/.next/server/app/api/worktrees/[id]/search/route.js +1 -1
  48. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  49. package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
  50. package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -1
  51. package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js +1 -1
  52. package/.next/server/app/api/worktrees/[id]/tree/route.js +1 -1
  53. package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js +1 -1
  54. package/.next/server/app/api/worktrees/[id]/viewed/route.js +1 -1
  55. package/.next/server/app/api/worktrees/route.js +1 -1
  56. package/.next/server/app/index.html +2 -2
  57. package/.next/server/app/index.rsc +2 -2
  58. package/.next/server/app/page_client-reference-manifest.js +1 -1
  59. package/.next/server/app/proxy/[...path]/route.js +12 -13
  60. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  61. package/.next/server/app/worktrees/[id]/page.js +3 -3
  62. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  63. package/.next/server/app/worktrees/[id]/simple-terminal/page_client-reference-manifest.js +1 -1
  64. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  65. package/.next/server/app-paths-manifest.json +10 -10
  66. package/.next/server/chunks/1318.js +4 -4
  67. package/.next/server/chunks/1528.js +1 -1
  68. package/.next/server/chunks/7425.js +97 -48
  69. package/.next/server/chunks/9723.js +1 -1
  70. package/.next/server/functions-config-manifest.json +1 -1
  71. package/.next/server/middleware-manifest.json +5 -5
  72. package/.next/server/pages/404.html +1 -1
  73. package/.next/server/pages/500.html +1 -1
  74. package/.next/server/server-reference-manifest.json +1 -1
  75. package/.next/server/src/middleware.js +2 -2
  76. package/.next/server/src/middleware.js.map +1 -1
  77. package/.next/static/chunks/app/worktrees/[id]/page-720605c2fb074444.js +1 -0
  78. package/.next/trace +5 -5
  79. package/dist/cli/commands/init.d.ts.map +1 -1
  80. package/dist/cli/commands/init.js +6 -4
  81. package/dist/cli/commands/start.d.ts +2 -0
  82. package/dist/cli/commands/start.d.ts.map +1 -1
  83. package/dist/cli/commands/start.js +64 -17
  84. package/dist/cli/commands/status.d.ts +4 -1
  85. package/dist/cli/commands/status.d.ts.map +1 -1
  86. package/dist/cli/commands/status.js +95 -6
  87. package/dist/cli/commands/stop.d.ts +2 -0
  88. package/dist/cli/commands/stop.d.ts.map +1 -1
  89. package/dist/cli/commands/stop.js +27 -10
  90. package/dist/cli/index.js +16 -2
  91. package/dist/cli/types/index.d.ts +20 -0
  92. package/dist/cli/types/index.d.ts.map +1 -1
  93. package/dist/cli/utils/daemon-factory.d.ts +105 -0
  94. package/dist/cli/utils/daemon-factory.d.ts.map +1 -0
  95. package/dist/cli/utils/daemon-factory.js +117 -0
  96. package/dist/cli/utils/daemon.d.ts.map +1 -1
  97. package/dist/cli/utils/daemon.js +4 -0
  98. package/dist/cli/utils/env-setup.d.ts +24 -12
  99. package/dist/cli/utils/env-setup.d.ts.map +1 -1
  100. package/dist/cli/utils/env-setup.js +64 -43
  101. package/dist/cli/utils/input-validators.d.ts +103 -0
  102. package/dist/cli/utils/input-validators.d.ts.map +1 -0
  103. package/dist/cli/utils/input-validators.js +163 -0
  104. package/dist/cli/utils/install-context.d.ts +53 -0
  105. package/dist/cli/utils/install-context.d.ts.map +1 -0
  106. package/dist/cli/utils/install-context.js +96 -0
  107. package/dist/cli/utils/pid-manager.d.ts +34 -0
  108. package/dist/cli/utils/pid-manager.d.ts.map +1 -1
  109. package/dist/cli/utils/pid-manager.js +43 -0
  110. package/dist/cli/utils/port-allocator.d.ts +108 -0
  111. package/dist/cli/utils/port-allocator.d.ts.map +1 -0
  112. package/dist/cli/utils/port-allocator.js +166 -0
  113. package/dist/cli/utils/resource-resolvers.d.ts +92 -0
  114. package/dist/cli/utils/resource-resolvers.d.ts.map +1 -0
  115. package/dist/cli/utils/resource-resolvers.js +175 -0
  116. package/dist/cli/utils/worktree-detector.d.ts +82 -0
  117. package/dist/cli/utils/worktree-detector.d.ts.map +1 -0
  118. package/dist/cli/utils/worktree-detector.js +221 -0
  119. package/dist/lib/errors.d.ts +111 -0
  120. package/dist/lib/errors.d.ts.map +1 -0
  121. package/dist/lib/errors.js +153 -0
  122. package/dist/server/server.js +3 -0
  123. package/dist/server/src/cli/utils/install-context.js +96 -0
  124. package/dist/server/src/config/system-directories.js +40 -0
  125. package/dist/server/src/lib/auto-yes-manager.js +325 -0
  126. package/dist/server/src/lib/auto-yes-resolver.js +34 -0
  127. package/dist/server/src/lib/cli-patterns.js +6 -1
  128. package/dist/server/src/lib/db-instance.js +12 -2
  129. package/dist/server/src/lib/db-migrations.js +68 -1
  130. package/dist/server/src/lib/db-path-resolver.js +99 -0
  131. package/dist/server/src/lib/db.js +21 -0
  132. package/dist/server/src/lib/env.js +52 -3
  133. package/dist/server/src/lib/worktrees.js +36 -1
  134. package/dist/server/src/types/external-apps.js +20 -0
  135. package/package.json +1 -1
  136. package/.next/static/chunks/app/worktrees/[id]/page-aea2d5e7e28955be.js +0 -1
  137. /package/.next/static/chunks/app/{page-96a8aa2ec30a44e9.js → page-fe35d61f14b90a51.js} +0 -0
  138. /package/.next/static/{8o5rUyZun0GklIHWDgkmv → gRNW5YXY43KqCKbCdaJoJ}/_buildManifest.js +0 -0
  139. /package/.next/static/{8o5rUyZun0GklIHWDgkmv → gRNW5YXY43KqCKbCdaJoJ}/_ssgManifest.js +0 -0
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ /**
3
+ * Worktree Detector
4
+ * Issue #136: Phase 1 - Task 1.3
5
+ *
6
+ * Provides utilities for detecting git worktrees and extracting issue numbers.
7
+ * Used to automatically identify which worktree environment the CLI is running in.
8
+ *
9
+ * Security: Uses execFile instead of exec to prevent command injection.
10
+ *
11
+ * @module worktree-detector
12
+ */
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.extractIssueNoFromPath = extractIssueNoFromPath;
18
+ exports.extractIssueNoFromBranch = extractIssueNoFromBranch;
19
+ exports.isWorktreeDirectory = isWorktreeDirectory;
20
+ exports.detectCurrentWorktree = detectCurrentWorktree;
21
+ exports.detectWorktreeContext = detectWorktreeContext;
22
+ const child_process_1 = require("child_process");
23
+ const util_1 = require("util");
24
+ const path_1 = __importDefault(require("path"));
25
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
26
+ /**
27
+ * Pattern for extracting issue number from worktree directory name
28
+ * Matches: commandmate-issue-{number}
29
+ */
30
+ const WORKTREE_PATH_PATTERN = /commandmate-issue-(\d+)/;
31
+ /**
32
+ * Pattern for extracting issue number from branch name
33
+ * Matches:
34
+ * - feature/{number}-description
35
+ * - fix/{number}-description
36
+ * - hotfix/{number}-description
37
+ * - {number}-description (without prefix)
38
+ */
39
+ const BRANCH_ISSUE_PATTERN = /^(?:feature\/|fix\/|hotfix\/)?(\d+)-/;
40
+ /**
41
+ * Extract issue number from a worktree directory path
42
+ *
43
+ * @param dirPath - Directory path to check
44
+ * @returns Issue number or null if not a worktree path
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * extractIssueNoFromPath('/home/user/repos/commandmate-issue-135'); // 135
49
+ * extractIssueNoFromPath('/home/user/repos/commandmate'); // null
50
+ * ```
51
+ */
52
+ function extractIssueNoFromPath(dirPath) {
53
+ // Normalize path and remove trailing slash
54
+ const normalizedPath = path_1.default.normalize(dirPath).replace(/\/$/, '');
55
+ const match = normalizedPath.match(WORKTREE_PATH_PATTERN);
56
+ if (!match) {
57
+ return null;
58
+ }
59
+ const issueNo = parseInt(match[1], 10);
60
+ // Validate: must be positive integer
61
+ if (!Number.isInteger(issueNo) || issueNo <= 0) {
62
+ return null;
63
+ }
64
+ return issueNo;
65
+ }
66
+ /**
67
+ * Extract issue number from a git branch name
68
+ *
69
+ * @param branchName - Git branch name
70
+ * @returns Issue number or null if not found
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * extractIssueNoFromBranch('feature/135-add-worktree'); // 135
75
+ * extractIssueNoFromBranch('main'); // null
76
+ * ```
77
+ */
78
+ function extractIssueNoFromBranch(branchName) {
79
+ const match = branchName.match(BRANCH_ISSUE_PATTERN);
80
+ if (!match) {
81
+ return null;
82
+ }
83
+ const issueNo = parseInt(match[1], 10);
84
+ // Validate: must be positive integer
85
+ if (!Number.isInteger(issueNo) || issueNo <= 0) {
86
+ return null;
87
+ }
88
+ return issueNo;
89
+ }
90
+ /**
91
+ * Check if a directory is inside a git worktree
92
+ *
93
+ * @param dirPath - Directory path to check
94
+ * @returns true if directory is inside a git repository
95
+ */
96
+ async function isWorktreeDirectory(dirPath) {
97
+ try {
98
+ // Use execFile for security (no shell interpretation)
99
+ const { stdout } = await execFileAsync('git', ['rev-parse', '--is-inside-work-tree'], {
100
+ cwd: dirPath,
101
+ timeout: 5000, // 5 second timeout
102
+ });
103
+ return stdout.trim() === 'true';
104
+ }
105
+ catch {
106
+ return false;
107
+ }
108
+ }
109
+ /**
110
+ * Get the git repository root for a directory
111
+ *
112
+ * @param dirPath - Directory path
113
+ * @returns Repository root path or null
114
+ */
115
+ async function getGitRoot(dirPath) {
116
+ try {
117
+ const { stdout } = await execFileAsync('git', ['rev-parse', '--show-toplevel'], {
118
+ cwd: dirPath,
119
+ timeout: 5000,
120
+ });
121
+ return stdout.trim();
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ /**
128
+ * Get the current git branch name
129
+ *
130
+ * @param dirPath - Directory path within git repository
131
+ * @returns Branch name or null
132
+ */
133
+ async function getCurrentBranch(dirPath) {
134
+ try {
135
+ const { stdout } = await execFileAsync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
136
+ cwd: dirPath,
137
+ timeout: 5000,
138
+ });
139
+ const branch = stdout.trim();
140
+ // Handle detached HEAD
141
+ if (branch === 'HEAD') {
142
+ return null;
143
+ }
144
+ return branch;
145
+ }
146
+ catch {
147
+ return null;
148
+ }
149
+ }
150
+ /**
151
+ * Detect if the current directory is within a CommandMate worktree
152
+ *
153
+ * Attempts to extract issue number from:
154
+ * 1. Directory path (e.g., commandmate-issue-135)
155
+ * 2. Git branch name (e.g., feature/135-description)
156
+ *
157
+ * @param dirPath - Directory path to check (defaults to cwd)
158
+ * @returns WorktreeInfo if detected, null otherwise
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * const info = await detectCurrentWorktree();
163
+ * if (info) {
164
+ * console.log(`Running in worktree for Issue #${info.issueNo}`);
165
+ * }
166
+ * ```
167
+ */
168
+ async function detectCurrentWorktree(dirPath) {
169
+ const targetPath = dirPath || process.cwd();
170
+ try {
171
+ // Get git root
172
+ const gitRoot = await getGitRoot(targetPath);
173
+ if (!gitRoot) {
174
+ return null;
175
+ }
176
+ // Get current branch
177
+ const branch = await getCurrentBranch(targetPath);
178
+ // Try to extract issue number from path first
179
+ let issueNo = extractIssueNoFromPath(gitRoot);
180
+ // If not found in path, try branch name
181
+ if (issueNo === null && branch) {
182
+ issueNo = extractIssueNoFromBranch(branch);
183
+ }
184
+ // If no issue number found, this is not a worktree for an issue
185
+ if (issueNo === null) {
186
+ return null;
187
+ }
188
+ return {
189
+ issueNo,
190
+ path: gitRoot,
191
+ branch: branch || 'unknown',
192
+ };
193
+ }
194
+ catch {
195
+ return null;
196
+ }
197
+ }
198
+ /**
199
+ * Detect worktree from environment or current directory
200
+ * Convenience function that checks CM_ISSUE_NO env var first
201
+ *
202
+ * @returns WorktreeInfo if detected, null otherwise
203
+ */
204
+ async function detectWorktreeContext() {
205
+ // Check environment variable first
206
+ const envIssueNo = process.env.CM_ISSUE_NO;
207
+ if (envIssueNo) {
208
+ const issueNo = parseInt(envIssueNo, 10);
209
+ if (Number.isInteger(issueNo) && issueNo > 0) {
210
+ const gitRoot = await getGitRoot(process.cwd());
211
+ const branch = await getCurrentBranch(process.cwd());
212
+ return {
213
+ issueNo,
214
+ path: gitRoot || process.cwd(),
215
+ branch: branch || 'unknown',
216
+ };
217
+ }
218
+ }
219
+ // Fall back to detection
220
+ return detectCurrentWorktree();
221
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Error Definitions
3
+ * Issue #136: Phase 1 - Foundation
4
+ *
5
+ * Centralized error handling for the application.
6
+ * SF-SEC-003: Separates client-facing and internal error messages.
7
+ *
8
+ * @module errors
9
+ */
10
+ /**
11
+ * Standard error codes used throughout the application
12
+ * These codes are safe to expose to clients.
13
+ */
14
+ export declare const ErrorCode: {
15
+ readonly INVALID_ISSUE_NO: "INVALID_ISSUE_NO";
16
+ readonly ISSUE_NO_OUT_OF_RANGE: "ISSUE_NO_OUT_OF_RANGE";
17
+ readonly INVALID_BRANCH_NAME: "INVALID_BRANCH_NAME";
18
+ readonly BRANCH_NAME_TOO_LONG: "BRANCH_NAME_TOO_LONG";
19
+ readonly INVALID_PORT: "INVALID_PORT";
20
+ readonly PORT_EXHAUSTED: "PORT_EXHAUSTED";
21
+ readonly PORT_IN_USE: "PORT_IN_USE";
22
+ readonly WORKTREE_NOT_FOUND: "WORKTREE_NOT_FOUND";
23
+ readonly WORKTREE_ALREADY_EXISTS: "WORKTREE_ALREADY_EXISTS";
24
+ readonly PID_FILE_EXISTS: "PID_FILE_EXISTS";
25
+ readonly PROCESS_NOT_RUNNING: "PROCESS_NOT_RUNNING";
26
+ readonly PATH_TRAVERSAL: "PATH_TRAVERSAL";
27
+ readonly UNAUTHORIZED: "UNAUTHORIZED";
28
+ readonly FORBIDDEN: "FORBIDDEN";
29
+ readonly DATABASE_ERROR: "DATABASE_ERROR";
30
+ readonly FILESYSTEM_ERROR: "FILESYSTEM_ERROR";
31
+ readonly GIT_ERROR: "GIT_ERROR";
32
+ readonly TIMEOUT: "TIMEOUT";
33
+ readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
34
+ };
35
+ export type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
36
+ /**
37
+ * Application-specific error class
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * throw new AppError('INVALID_ISSUE_NO', 'Issue number must be a positive integer', { received: -1 });
42
+ * ```
43
+ */
44
+ export declare class AppError extends Error {
45
+ /**
46
+ * Error code - safe to expose to clients
47
+ */
48
+ readonly code: string;
49
+ /**
50
+ * Additional details - may contain sensitive info, log only
51
+ */
52
+ readonly details?: Record<string, unknown>;
53
+ /**
54
+ * Timestamp when error occurred
55
+ */
56
+ readonly timestamp: string;
57
+ constructor(code: string, message: string, details?: Record<string, unknown>);
58
+ /**
59
+ * Create a client-safe representation (excludes sensitive details)
60
+ */
61
+ toClientError(): {
62
+ code: string;
63
+ message: string;
64
+ };
65
+ /**
66
+ * Create a log-safe representation (includes details for debugging)
67
+ */
68
+ toLogError(): {
69
+ code: string;
70
+ message: string;
71
+ details?: Record<string, unknown>;
72
+ timestamp: string;
73
+ };
74
+ }
75
+ /**
76
+ * Factory function to create AppError
77
+ *
78
+ * @param code - Error code from ErrorCode enum
79
+ * @param message - Human-readable error message
80
+ * @param details - Optional additional details (logged, not sent to client)
81
+ * @returns AppError instance
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * throw createAppError(ErrorCode.PORT_EXHAUSTED, 'No available ports in range', { range: [3001, 3100] });
86
+ * ```
87
+ */
88
+ export declare function createAppError(code: string, message: string, details?: Record<string, unknown>): AppError;
89
+ /**
90
+ * Type guard to check if an error is an AppError
91
+ *
92
+ * @param error - Error to check
93
+ * @returns true if error is an AppError
94
+ */
95
+ export declare function isAppError(error: unknown): error is AppError;
96
+ /**
97
+ * Wrap unknown error into AppError
98
+ *
99
+ * @param error - Unknown error
100
+ * @param defaultCode - Default error code if error is not AppError
101
+ * @returns AppError instance
102
+ */
103
+ export declare function wrapError(error: unknown, defaultCode?: string): AppError;
104
+ /**
105
+ * Get error message from unknown error
106
+ *
107
+ * @param error - Unknown error
108
+ * @returns Error message string
109
+ */
110
+ export declare function getErrorMessage(error: unknown): string;
111
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;CA6BZ,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAEvE;;;;;;;GAOG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE3C;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAGzB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAcnC;;OAEG;IACH,aAAa,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAOlD;;OAEG;IACH,UAAU,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;CAQtG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,QAAQ,CAEV;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,GAAE,MAAgC,GAAG,QAAQ,CAUjG;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKtD"}
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ /**
3
+ * Error Definitions
4
+ * Issue #136: Phase 1 - Foundation
5
+ *
6
+ * Centralized error handling for the application.
7
+ * SF-SEC-003: Separates client-facing and internal error messages.
8
+ *
9
+ * @module errors
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.AppError = exports.ErrorCode = void 0;
13
+ exports.createAppError = createAppError;
14
+ exports.isAppError = isAppError;
15
+ exports.wrapError = wrapError;
16
+ exports.getErrorMessage = getErrorMessage;
17
+ /**
18
+ * Standard error codes used throughout the application
19
+ * These codes are safe to expose to clients.
20
+ */
21
+ exports.ErrorCode = {
22
+ // Input validation errors
23
+ INVALID_ISSUE_NO: 'INVALID_ISSUE_NO',
24
+ ISSUE_NO_OUT_OF_RANGE: 'ISSUE_NO_OUT_OF_RANGE',
25
+ INVALID_BRANCH_NAME: 'INVALID_BRANCH_NAME',
26
+ BRANCH_NAME_TOO_LONG: 'BRANCH_NAME_TOO_LONG',
27
+ INVALID_PORT: 'INVALID_PORT',
28
+ // Resource errors
29
+ PORT_EXHAUSTED: 'PORT_EXHAUSTED',
30
+ PORT_IN_USE: 'PORT_IN_USE',
31
+ WORKTREE_NOT_FOUND: 'WORKTREE_NOT_FOUND',
32
+ WORKTREE_ALREADY_EXISTS: 'WORKTREE_ALREADY_EXISTS',
33
+ PID_FILE_EXISTS: 'PID_FILE_EXISTS',
34
+ PROCESS_NOT_RUNNING: 'PROCESS_NOT_RUNNING',
35
+ // Security errors
36
+ PATH_TRAVERSAL: 'PATH_TRAVERSAL',
37
+ UNAUTHORIZED: 'UNAUTHORIZED',
38
+ FORBIDDEN: 'FORBIDDEN',
39
+ // System errors
40
+ DATABASE_ERROR: 'DATABASE_ERROR',
41
+ FILESYSTEM_ERROR: 'FILESYSTEM_ERROR',
42
+ GIT_ERROR: 'GIT_ERROR',
43
+ TIMEOUT: 'TIMEOUT',
44
+ // Generic errors
45
+ UNKNOWN_ERROR: 'UNKNOWN_ERROR',
46
+ };
47
+ /**
48
+ * Application-specific error class
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * throw new AppError('INVALID_ISSUE_NO', 'Issue number must be a positive integer', { received: -1 });
53
+ * ```
54
+ */
55
+ class AppError extends Error {
56
+ /**
57
+ * Error code - safe to expose to clients
58
+ */
59
+ code;
60
+ /**
61
+ * Additional details - may contain sensitive info, log only
62
+ */
63
+ details;
64
+ /**
65
+ * Timestamp when error occurred
66
+ */
67
+ timestamp;
68
+ constructor(code, message, details) {
69
+ super(message);
70
+ this.name = 'AppError';
71
+ this.code = code;
72
+ this.details = details;
73
+ this.timestamp = new Date().toISOString();
74
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
75
+ if (Error.captureStackTrace) {
76
+ Error.captureStackTrace(this, AppError);
77
+ }
78
+ }
79
+ /**
80
+ * Create a client-safe representation (excludes sensitive details)
81
+ */
82
+ toClientError() {
83
+ return {
84
+ code: this.code,
85
+ message: this.message,
86
+ };
87
+ }
88
+ /**
89
+ * Create a log-safe representation (includes details for debugging)
90
+ */
91
+ toLogError() {
92
+ return {
93
+ code: this.code,
94
+ message: this.message,
95
+ details: this.details,
96
+ timestamp: this.timestamp,
97
+ };
98
+ }
99
+ }
100
+ exports.AppError = AppError;
101
+ /**
102
+ * Factory function to create AppError
103
+ *
104
+ * @param code - Error code from ErrorCode enum
105
+ * @param message - Human-readable error message
106
+ * @param details - Optional additional details (logged, not sent to client)
107
+ * @returns AppError instance
108
+ *
109
+ * @example
110
+ * ```typescript
111
+ * throw createAppError(ErrorCode.PORT_EXHAUSTED, 'No available ports in range', { range: [3001, 3100] });
112
+ * ```
113
+ */
114
+ function createAppError(code, message, details) {
115
+ return new AppError(code, message, details);
116
+ }
117
+ /**
118
+ * Type guard to check if an error is an AppError
119
+ *
120
+ * @param error - Error to check
121
+ * @returns true if error is an AppError
122
+ */
123
+ function isAppError(error) {
124
+ return error instanceof AppError;
125
+ }
126
+ /**
127
+ * Wrap unknown error into AppError
128
+ *
129
+ * @param error - Unknown error
130
+ * @param defaultCode - Default error code if error is not AppError
131
+ * @returns AppError instance
132
+ */
133
+ function wrapError(error, defaultCode = exports.ErrorCode.UNKNOWN_ERROR) {
134
+ if (isAppError(error)) {
135
+ return error;
136
+ }
137
+ if (error instanceof Error) {
138
+ return new AppError(defaultCode, error.message, { originalError: error.name });
139
+ }
140
+ return new AppError(defaultCode, String(error));
141
+ }
142
+ /**
143
+ * Get error message from unknown error
144
+ *
145
+ * @param error - Unknown error
146
+ * @returns Error message string
147
+ */
148
+ function getErrorMessage(error) {
149
+ if (error instanceof Error) {
150
+ return error.message;
151
+ }
152
+ return String(error);
153
+ }
@@ -34,6 +34,7 @@ const ws_server_1 = require("./src/lib/ws-server");
34
34
  const worktrees_1 = require("./src/lib/worktrees");
35
35
  const db_instance_1 = require("./src/lib/db-instance");
36
36
  const response_poller_1 = require("./src/lib/response-poller");
37
+ const auto_yes_manager_1 = require("./src/lib/auto-yes-manager");
37
38
  const db_migrations_1 = require("./src/lib/db-migrations");
38
39
  const env_1 = require("./src/lib/env");
39
40
  const dev = process.env.NODE_ENV !== 'production';
@@ -104,6 +105,8 @@ app.prepare().then(() => {
104
105
  console.log(`${signal} received: shutting down...`);
105
106
  // Stop polling first
106
107
  (0, response_poller_1.stopAllPolling)();
108
+ // Issue #138: Stop all auto-yes pollers
109
+ (0, auto_yes_manager_1.stopAllAutoYesPolling)();
107
110
  // Close WebSocket connections immediately (don't wait)
108
111
  (0, ws_server_1.closeWebSocket)();
109
112
  // Force exit after 3 seconds if graceful shutdown fails
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ /**
3
+ * Install Context Utility
4
+ * Issue #136: DRY refactoring - extract common install detection functions
5
+ *
6
+ * This module provides centralized functions for detecting the install type
7
+ * (global vs local npm install) and resolving configuration directories.
8
+ *
9
+ * Extracted from env-setup.ts to solve circular import issues and follow DRY principle.
10
+ *
11
+ * @module install-context
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.isGlobalInstall = isGlobalInstall;
15
+ exports.getConfigDir = getConfigDir;
16
+ exports.ensureConfigDir = ensureConfigDir;
17
+ const fs_1 = require("fs");
18
+ const path_1 = require("path");
19
+ const os_1 = require("os");
20
+ /**
21
+ * Check if running as global npm package
22
+ * Issue #119: Determine .env location based on install type
23
+ * Issue #136: Extracted to avoid circular imports
24
+ *
25
+ * @returns true if running as global npm package
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * if (isGlobalInstall()) {
30
+ * // Use ~/.commandmate for config
31
+ * } else {
32
+ * // Use current working directory
33
+ * }
34
+ * ```
35
+ */
36
+ function isGlobalInstall() {
37
+ // Check if running from global node_modules
38
+ // Global installs typically have paths like:
39
+ // - /usr/local/lib/node_modules/
40
+ // - /Users/xxx/.npm-global/lib/node_modules/
41
+ // - C:\Users\xxx\AppData\Roaming\npm\node_modules\
42
+ const currentPath = (0, path_1.dirname)(__dirname);
43
+ return (currentPath.includes('/lib/node_modules/') ||
44
+ currentPath.includes('\\node_modules\\') ||
45
+ currentPath.includes('/node_modules/commandmate'));
46
+ }
47
+ /**
48
+ * Get the config directory path
49
+ * Issue #119: Returns ~/.commandmate for global, cwd for local
50
+ * Issue #125: Added symlink resolution for security (path traversal protection)
51
+ * Issue #136: Extracted to avoid circular imports
52
+ *
53
+ * @returns Path to config directory (absolute, with symlinks resolved)
54
+ * @throws Error if config directory resolves outside home directory (for global install)
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const configDir = getConfigDir();
59
+ * // Global: /Users/username/.commandmate
60
+ * // Local: /path/to/project (resolved from symlinks)
61
+ * ```
62
+ */
63
+ function getConfigDir() {
64
+ if (isGlobalInstall()) {
65
+ const configDir = (0, path_1.join)((0, os_1.homedir)(), '.commandmate');
66
+ // Verify config directory is within home directory (security check)
67
+ // Only validate if the directory exists (it may not exist yet during init)
68
+ if ((0, fs_1.existsSync)(configDir)) {
69
+ const realPath = (0, fs_1.realpathSync)(configDir);
70
+ const realHome = (0, fs_1.realpathSync)((0, os_1.homedir)());
71
+ if (!realPath.startsWith(realHome)) {
72
+ throw new Error(`Security error: Config directory ${configDir} is outside home directory`);
73
+ }
74
+ return realPath;
75
+ }
76
+ return configDir;
77
+ }
78
+ // Local install - resolve symlinks in cwd
79
+ const cwd = process.cwd();
80
+ return (0, fs_1.realpathSync)(cwd);
81
+ }
82
+ /**
83
+ * Ensure the config directory exists with proper permissions
84
+ * Issue #136: Utility for creating config directory if needed
85
+ *
86
+ * @returns Path to config directory (created if needed)
87
+ */
88
+ function ensureConfigDir() {
89
+ const configDir = isGlobalInstall()
90
+ ? (0, path_1.join)((0, os_1.homedir)(), '.commandmate')
91
+ : process.cwd();
92
+ if (!(0, fs_1.existsSync)(configDir)) {
93
+ (0, fs_1.mkdirSync)(configDir, { recursive: true, mode: 0o700 });
94
+ }
95
+ return getConfigDir();
96
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /**
3
+ * System Directories Configuration
4
+ * Issue #135: DB path resolution logic fix
5
+ *
6
+ * Centralized list of system directories that are not allowed for DB storage.
7
+ * This supports security measures SEC-001, SEC-002, SEC-005.
8
+ *
9
+ * @module system-directories
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SYSTEM_DIRECTORIES = void 0;
13
+ exports.isSystemDirectory = isSystemDirectory;
14
+ /**
15
+ * System directories that are not allowed for DB storage
16
+ *
17
+ * SEC-001: System directory protection
18
+ * These directories are protected to prevent writing database files
19
+ * to critical system paths which could cause security issues.
20
+ */
21
+ exports.SYSTEM_DIRECTORIES = [
22
+ '/etc',
23
+ '/usr',
24
+ '/bin',
25
+ '/sbin',
26
+ '/var',
27
+ '/tmp',
28
+ '/dev',
29
+ '/sys',
30
+ '/proc',
31
+ ];
32
+ /**
33
+ * Check if a path is within a system directory
34
+ *
35
+ * @param resolvedPath - The resolved absolute path to check
36
+ * @returns true if the path is within a system directory
37
+ */
38
+ function isSystemDirectory(resolvedPath) {
39
+ return exports.SYSTEM_DIRECTORIES.some((dir) => resolvedPath.startsWith(dir));
40
+ }