@emaenriquez/projectmanager-cli 0.1.0

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 (174) hide show
  1. package/README.md +98 -0
  2. package/dist/application/ConfigManager.d.ts +73 -0
  3. package/dist/application/ConfigManager.d.ts.map +1 -0
  4. package/dist/application/ConfigManager.js +253 -0
  5. package/dist/application/ConfigManager.js.map +1 -0
  6. package/dist/application/errors/ErrorHandler.d.ts +27 -0
  7. package/dist/application/errors/ErrorHandler.d.ts.map +1 -0
  8. package/dist/application/errors/ErrorHandler.js +88 -0
  9. package/dist/application/errors/ErrorHandler.js.map +1 -0
  10. package/dist/application/state/StateManager.d.ts +26 -0
  11. package/dist/application/state/StateManager.d.ts.map +1 -0
  12. package/dist/application/state/StateManager.js +114 -0
  13. package/dist/application/state/StateManager.js.map +1 -0
  14. package/dist/application/use_cases/AddProjectUseCase.d.ts +9 -0
  15. package/dist/application/use_cases/AddProjectUseCase.d.ts.map +1 -0
  16. package/dist/application/use_cases/AddProjectUseCase.js +85 -0
  17. package/dist/application/use_cases/AddProjectUseCase.js.map +1 -0
  18. package/dist/application/use_cases/ListProjectsUseCase.d.ts +7 -0
  19. package/dist/application/use_cases/ListProjectsUseCase.d.ts.map +1 -0
  20. package/dist/application/use_cases/ListProjectsUseCase.js +14 -0
  21. package/dist/application/use_cases/ListProjectsUseCase.js.map +1 -0
  22. package/dist/application/use_cases/OpenProjectUseCase.d.ts +10 -0
  23. package/dist/application/use_cases/OpenProjectUseCase.d.ts.map +1 -0
  24. package/dist/application/use_cases/OpenProjectUseCase.js +168 -0
  25. package/dist/application/use_cases/OpenProjectUseCase.js.map +1 -0
  26. package/dist/application/use_cases/RemoveProjectUseCase.d.ts +7 -0
  27. package/dist/application/use_cases/RemoveProjectUseCase.d.ts.map +1 -0
  28. package/dist/application/use_cases/RemoveProjectUseCase.js +24 -0
  29. package/dist/application/use_cases/RemoveProjectUseCase.js.map +1 -0
  30. package/dist/application/use_cases/ScanProjectsUseCase.d.ts +13 -0
  31. package/dist/application/use_cases/ScanProjectsUseCase.d.ts.map +1 -0
  32. package/dist/application/use_cases/ScanProjectsUseCase.js +57 -0
  33. package/dist/application/use_cases/ScanProjectsUseCase.js.map +1 -0
  34. package/dist/application/use_cases/SyncProjectsUseCase.d.ts +12 -0
  35. package/dist/application/use_cases/SyncProjectsUseCase.d.ts.map +1 -0
  36. package/dist/application/use_cases/SyncProjectsUseCase.js +74 -0
  37. package/dist/application/use_cases/SyncProjectsUseCase.js.map +1 -0
  38. package/dist/application/use_cases/SystemDiagnostics.d.ts +16 -0
  39. package/dist/application/use_cases/SystemDiagnostics.d.ts.map +1 -0
  40. package/dist/application/use_cases/SystemDiagnostics.js +35 -0
  41. package/dist/application/use_cases/SystemDiagnostics.js.map +1 -0
  42. package/dist/application/use_cases/TagProjectUseCase.d.ts +9 -0
  43. package/dist/application/use_cases/TagProjectUseCase.d.ts.map +1 -0
  44. package/dist/application/use_cases/TagProjectUseCase.js +41 -0
  45. package/dist/application/use_cases/TagProjectUseCase.js.map +1 -0
  46. package/dist/domain/patterns/ProjectPatterns.d.ts +47 -0
  47. package/dist/domain/patterns/ProjectPatterns.d.ts.map +1 -0
  48. package/dist/domain/patterns/ProjectPatterns.js +200 -0
  49. package/dist/domain/patterns/ProjectPatterns.js.map +1 -0
  50. package/dist/domain/services/GitService.d.ts +23 -0
  51. package/dist/domain/services/GitService.d.ts.map +1 -0
  52. package/dist/domain/services/GitService.js +155 -0
  53. package/dist/domain/services/GitService.js.map +1 -0
  54. package/dist/domain/services/IgnoreFileParser.d.ts +55 -0
  55. package/dist/domain/services/IgnoreFileParser.d.ts.map +1 -0
  56. package/dist/domain/services/IgnoreFileParser.js +200 -0
  57. package/dist/domain/services/IgnoreFileParser.js.map +1 -0
  58. package/dist/domain/services/ProjectScanner.d.ts +65 -0
  59. package/dist/domain/services/ProjectScanner.d.ts.map +1 -0
  60. package/dist/domain/services/ProjectScanner.js +207 -0
  61. package/dist/domain/services/ProjectScanner.js.map +1 -0
  62. package/dist/domain/services/TechnologyDetector.d.ts +78 -0
  63. package/dist/domain/services/TechnologyDetector.d.ts.map +1 -0
  64. package/dist/domain/services/TechnologyDetector.js +633 -0
  65. package/dist/domain/services/TechnologyDetector.js.map +1 -0
  66. package/dist/domain/services/ToolDetector.d.ts +74 -0
  67. package/dist/domain/services/ToolDetector.d.ts.map +1 -0
  68. package/dist/domain/services/ToolDetector.js +344 -0
  69. package/dist/domain/services/ToolDetector.js.map +1 -0
  70. package/dist/domain/services/index.d.ts +5 -0
  71. package/dist/domain/services/index.d.ts.map +1 -0
  72. package/dist/domain/services/index.js +12 -0
  73. package/dist/domain/services/index.js.map +1 -0
  74. package/dist/domain/types/Config.d.ts +37 -0
  75. package/dist/domain/types/Config.d.ts.map +1 -0
  76. package/dist/domain/types/Config.js +6 -0
  77. package/dist/domain/types/Config.js.map +1 -0
  78. package/dist/index.d.ts +6 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +17 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/infrastructure/database/DatabaseConnection.d.ts +88 -0
  83. package/dist/infrastructure/database/DatabaseConnection.d.ts.map +1 -0
  84. package/dist/infrastructure/database/DatabaseConnection.js +318 -0
  85. package/dist/infrastructure/database/DatabaseConnection.js.map +1 -0
  86. package/dist/infrastructure/database/DatabaseValidator.d.ts +154 -0
  87. package/dist/infrastructure/database/DatabaseValidator.d.ts.map +1 -0
  88. package/dist/infrastructure/database/DatabaseValidator.js +583 -0
  89. package/dist/infrastructure/database/DatabaseValidator.js.map +1 -0
  90. package/dist/infrastructure/database/index.d.ts +8 -0
  91. package/dist/infrastructure/database/index.d.ts.map +1 -0
  92. package/dist/infrastructure/database/index.js +14 -0
  93. package/dist/infrastructure/database/index.js.map +1 -0
  94. package/dist/infrastructure/database/migrations/001_initial.d.ts +24 -0
  95. package/dist/infrastructure/database/migrations/001_initial.d.ts.map +1 -0
  96. package/dist/infrastructure/database/migrations/001_initial.js +109 -0
  97. package/dist/infrastructure/database/migrations/001_initial.js.map +1 -0
  98. package/dist/infrastructure/database/migrations/Migration.d.ts +27 -0
  99. package/dist/infrastructure/database/migrations/Migration.d.ts.map +1 -0
  100. package/dist/infrastructure/database/migrations/Migration.js +3 -0
  101. package/dist/infrastructure/database/migrations/Migration.js.map +1 -0
  102. package/dist/infrastructure/database/migrations/MigrationRunner.d.ts +52 -0
  103. package/dist/infrastructure/database/migrations/MigrationRunner.d.ts.map +1 -0
  104. package/dist/infrastructure/database/migrations/MigrationRunner.js +167 -0
  105. package/dist/infrastructure/database/migrations/MigrationRunner.js.map +1 -0
  106. package/dist/infrastructure/database/migrations/index.d.ts +10 -0
  107. package/dist/infrastructure/database/migrations/index.d.ts.map +1 -0
  108. package/dist/infrastructure/database/migrations/index.js +22 -0
  109. package/dist/infrastructure/database/migrations/index.js.map +1 -0
  110. package/dist/infrastructure/filesystem/DirectoryManager.d.ts +50 -0
  111. package/dist/infrastructure/filesystem/DirectoryManager.d.ts.map +1 -0
  112. package/dist/infrastructure/filesystem/DirectoryManager.js +143 -0
  113. package/dist/infrastructure/filesystem/DirectoryManager.js.map +1 -0
  114. package/dist/infrastructure/filesystem/index.d.ts +2 -0
  115. package/dist/infrastructure/filesystem/index.d.ts.map +1 -0
  116. package/dist/infrastructure/filesystem/index.js +6 -0
  117. package/dist/infrastructure/filesystem/index.js.map +1 -0
  118. package/dist/infrastructure/logging/LogRotator.d.ts +43 -0
  119. package/dist/infrastructure/logging/LogRotator.d.ts.map +1 -0
  120. package/dist/infrastructure/logging/LogRotator.js +172 -0
  121. package/dist/infrastructure/logging/LogRotator.js.map +1 -0
  122. package/dist/infrastructure/logging/Logger.d.ts +59 -0
  123. package/dist/infrastructure/logging/Logger.d.ts.map +1 -0
  124. package/dist/infrastructure/logging/Logger.js +182 -0
  125. package/dist/infrastructure/logging/Logger.js.map +1 -0
  126. package/dist/infrastructure/logging/index.d.ts +3 -0
  127. package/dist/infrastructure/logging/index.d.ts.map +1 -0
  128. package/dist/infrastructure/logging/index.js +8 -0
  129. package/dist/infrastructure/logging/index.js.map +1 -0
  130. package/dist/infrastructure/repositories/ProjectRepository.d.ts +20 -0
  131. package/dist/infrastructure/repositories/ProjectRepository.d.ts.map +1 -0
  132. package/dist/infrastructure/repositories/ProjectRepository.js +61 -0
  133. package/dist/infrastructure/repositories/ProjectRepository.js.map +1 -0
  134. package/dist/infrastructure/repositories/QueryBuilder.d.ts +24 -0
  135. package/dist/infrastructure/repositories/QueryBuilder.d.ts.map +1 -0
  136. package/dist/infrastructure/repositories/QueryBuilder.js +92 -0
  137. package/dist/infrastructure/repositories/QueryBuilder.js.map +1 -0
  138. package/dist/infrastructure/repositories/TagRepository.d.ts +18 -0
  139. package/dist/infrastructure/repositories/TagRepository.d.ts.map +1 -0
  140. package/dist/infrastructure/repositories/TagRepository.js +66 -0
  141. package/dist/infrastructure/repositories/TagRepository.js.map +1 -0
  142. package/dist/infrastructure/repositories/TechnologyRepository.d.ts +16 -0
  143. package/dist/infrastructure/repositories/TechnologyRepository.d.ts.map +1 -0
  144. package/dist/infrastructure/repositories/TechnologyRepository.js +46 -0
  145. package/dist/infrastructure/repositories/TechnologyRepository.js.map +1 -0
  146. package/dist/infrastructure/repositories/TransactionManager.d.ts +11 -0
  147. package/dist/infrastructure/repositories/TransactionManager.d.ts.map +1 -0
  148. package/dist/infrastructure/repositories/TransactionManager.js +21 -0
  149. package/dist/infrastructure/repositories/TransactionManager.js.map +1 -0
  150. package/dist/infrastructure/ui/ScreenManager.d.ts +11 -0
  151. package/dist/infrastructure/ui/ScreenManager.d.ts.map +1 -0
  152. package/dist/infrastructure/ui/ScreenManager.js +69 -0
  153. package/dist/infrastructure/ui/ScreenManager.js.map +1 -0
  154. package/dist/ui/cli/CLI.d.ts +9 -0
  155. package/dist/ui/cli/CLI.d.ts.map +1 -0
  156. package/dist/ui/cli/CLI.js +211 -0
  157. package/dist/ui/cli/CLI.js.map +1 -0
  158. package/dist/ui/components/DetailsPanel.d.ts +11 -0
  159. package/dist/ui/components/DetailsPanel.d.ts.map +1 -0
  160. package/dist/ui/components/DetailsPanel.js +181 -0
  161. package/dist/ui/components/DetailsPanel.js.map +1 -0
  162. package/dist/ui/components/ProjectListView.d.ts +15 -0
  163. package/dist/ui/components/ProjectListView.d.ts.map +1 -0
  164. package/dist/ui/components/ProjectListView.js +122 -0
  165. package/dist/ui/components/ProjectListView.js.map +1 -0
  166. package/dist/ui/tui/TUIApplication.d.ts +17 -0
  167. package/dist/ui/tui/TUIApplication.d.ts.map +1 -0
  168. package/dist/ui/tui/TUIApplication.js +532 -0
  169. package/dist/ui/tui/TUIApplication.js.map +1 -0
  170. package/dist/ui/tui/Theme.d.ts +25 -0
  171. package/dist/ui/tui/Theme.d.ts.map +1 -0
  172. package/dist/ui/tui/Theme.js +62 -0
  173. package/dist/ui/tui/Theme.js.map +1 -0
  174. package/package.json +56 -0
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # Project Hub
2
+
3
+ A CLI/TUI application for managing development projects across multiple technologies.
4
+
5
+ ## Features
6
+
7
+ - **Project Scanning**: Automatically detect projects by scanning directories for config files (package.json, Cargo.toml, go.mod, etc.)
8
+ - **Technology Detection**: Identifies languages, frameworks, and tools used in each project
9
+ - **TUI Interface**: Interactive terminal UI with project list, details panel, and keyboard shortcuts
10
+ - **CLI Commands**: Full command-line interface for scripting and automation
11
+ - **Git Integration**: Shows branch, uncommitted changes, and unpushed commits
12
+ - **Tags & Favorites**: Organize projects with custom tags and mark favorites
13
+ - **Search & Filter**: Find projects quickly by name, technology, or tag
14
+ - **System Diagnostics**: `doctor` command to verify installed tools and database health
15
+
16
+ ## Quick Start
17
+
18
+ ```bash
19
+ # Install dependencies
20
+ npm install
21
+
22
+ # Run in TUI mode (interactive)
23
+ npm run dev
24
+
25
+ # Run CLI commands
26
+ npm run dev -- scan C:\path\to\projects
27
+ npm run dev -- add C:\path\to\project
28
+ npm run dev -- doctor
29
+ npm run dev -- --help
30
+ ```
31
+
32
+ ## CLI Commands
33
+
34
+ | Command | Description |
35
+ |---------|-------------|
36
+ | `scan [path]` | Scan a directory for projects |
37
+ | `add <path>` | Add a single project by path |
38
+ | `remove <id>` | Remove a project |
39
+ | `open <id> [--with <tool>]` | Open a project in an editor |
40
+ | `doctor` | Run system diagnostics |
41
+ | `sync` | Sync all projects (remove missing, update metadata) |
42
+ | `config list` | Show all configurations |
43
+ | `config get <key>` | Get a config value |
44
+ | `config set <key> <value>` | Set a config value |
45
+ | `config reset` | Reset config to defaults |
46
+
47
+ ## TUI Keyboard Shortcuts
48
+
49
+ | Key | Action |
50
+ |-----|--------|
51
+ | `↑/↓` | Navigate project list |
52
+ | `Enter` / `D` | View project details |
53
+ | `A` | Add a project by path |
54
+ | `S` | Scan a directory |
55
+ | `F` | Search/filter projects |
56
+ | `*` | Toggle favorite |
57
+ | `T` | Add tag to project |
58
+ | `O` | Open project in editor |
59
+ | `X` | Delete project |
60
+ | `R` | Refresh list |
61
+ | `Q` / `Esc` | Quit |
62
+
63
+ ## Architecture
64
+
65
+ The project follows **Clean Architecture** principles:
66
+
67
+ ```
68
+ src/
69
+ ├── domain/ # Business entities, services (ProjectScanner, TechnologyDetector, GitService)
70
+ ├── application/ # Use cases (ScanProjects, AddProject, etc.), state management, error handling
71
+ ├── infrastructure/ # Database (SQLite), repositories, UI framework (Blessed)
72
+ └── ui/ # CLI (Commander.js), TUI components, views
73
+ ```
74
+
75
+ ## Tech Stack
76
+
77
+ - **Runtime**: Node.js + TypeScript
78
+ - **Database**: SQLite via better-sqlite3
79
+ - **CLI Framework**: Commander.js
80
+ - **TUI Framework**: Blessed
81
+ - **Testing**: Jest
82
+
83
+ ## Development
84
+
85
+ ```bash
86
+ # Run tests
87
+ npm test
88
+
89
+ # Build
90
+ npm run build
91
+
92
+ # Lint
93
+ npm run lint
94
+ ```
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,73 @@
1
+ import { Config, DeepConfigKey } from '../domain/types/Config';
2
+ /**
3
+ * ConfigManager - Manages application configuration
4
+ *
5
+ * Responsibilities:
6
+ * - Load configuration from default and user-saved files
7
+ * - Provide get/set/reset operations
8
+ * - Validate configuration values
9
+ * - Persist configuration to disk
10
+ * - Merge defaults with user-saved configuration
11
+ */
12
+ export declare class ConfigManager {
13
+ private config;
14
+ private defaultConfig;
15
+ private configPath;
16
+ private defaultConfigPath;
17
+ constructor(defaultConfigPath?: string, userConfigDir?: string);
18
+ /**
19
+ * Expand ~ to home directory path
20
+ */
21
+ private expandHomePath;
22
+ /**
23
+ * Load default configuration from default.json
24
+ */
25
+ private loadDefaultConfig;
26
+ /**
27
+ * Load user configuration or initialize with defaults
28
+ */
29
+ private loadOrInitializeConfig;
30
+ /**
31
+ * Deep merge default and user configurations
32
+ */
33
+ private deepMerge;
34
+ /**
35
+ * Clone configuration object deeply
36
+ */
37
+ private cloneConfig;
38
+ /**
39
+ * Get configuration value by key
40
+ * Supports nested keys like "app.name"
41
+ */
42
+ get(key: DeepConfigKey): any;
43
+ /**
44
+ * Get entire configuration
45
+ */
46
+ getAll(): Config;
47
+ /**
48
+ * Set configuration value
49
+ * Supports nested keys like "app.name"
50
+ */
51
+ set(key: DeepConfigKey, value: any): void;
52
+ /**
53
+ * Reset configuration to defaults
54
+ */
55
+ reset(): void;
56
+ /**
57
+ * Validate configuration value before setting
58
+ */
59
+ private validateConfigValue;
60
+ /**
61
+ * Persist configuration to disk
62
+ */
63
+ private persist;
64
+ /**
65
+ * Get configuration file path
66
+ */
67
+ getConfigPath(): string;
68
+ /**
69
+ * Get default configuration file path
70
+ */
71
+ getDefaultConfigPath(): string;
72
+ }
73
+ //# sourceMappingURL=ConfigManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigManager.d.ts","sourceRoot":"","sources":["../../src/application/ConfigManager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;;;;;;;GASG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,iBAAiB,CAAS;gBAGhC,iBAAiB,GAAE,MAA0D,EAC7E,aAAa,GAAE,MAA8C;IAY/D;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IASzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAuB9B;;OAEG;IACH,OAAO,CAAC,SAAS;IAmBjB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,aAAa,GAAG,GAAG;IAe5B;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAsBzC;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA4C3B;;OAEG;IACH,OAAO,CAAC,OAAO;IAYf;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,oBAAoB,IAAI,MAAM;CAG/B"}
@@ -0,0 +1,253 @@
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.ConfigManager = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ /**
40
+ * ConfigManager - Manages application configuration
41
+ *
42
+ * Responsibilities:
43
+ * - Load configuration from default and user-saved files
44
+ * - Provide get/set/reset operations
45
+ * - Validate configuration values
46
+ * - Persist configuration to disk
47
+ * - Merge defaults with user-saved configuration
48
+ */
49
+ class ConfigManager {
50
+ constructor(defaultConfigPath = path.join(__dirname, '../../config/default.json'), userConfigDir = this.expandHomePath('~/.project-hub')) {
51
+ this.defaultConfigPath = defaultConfigPath;
52
+ this.configPath = path.join(userConfigDir, 'config.json');
53
+ // Load default configuration
54
+ this.defaultConfig = this.loadDefaultConfig();
55
+ // Load user configuration or use defaults
56
+ this.config = this.loadOrInitializeConfig();
57
+ }
58
+ /**
59
+ * Expand ~ to home directory path
60
+ */
61
+ expandHomePath(filepath) {
62
+ if (filepath === '~') {
63
+ return process.env.HOME || process.env.USERPROFILE || '';
64
+ }
65
+ if (filepath.startsWith('~/')) {
66
+ return path.join(process.env.HOME || process.env.USERPROFILE || '', filepath.slice(2));
67
+ }
68
+ return filepath;
69
+ }
70
+ /**
71
+ * Load default configuration from default.json
72
+ */
73
+ loadDefaultConfig() {
74
+ try {
75
+ const configContent = fs.readFileSync(this.defaultConfigPath, 'utf-8');
76
+ return JSON.parse(configContent);
77
+ }
78
+ catch (error) {
79
+ throw new Error(`Failed to load default configuration: ${error}`);
80
+ }
81
+ }
82
+ /**
83
+ * Load user configuration or initialize with defaults
84
+ */
85
+ loadOrInitializeConfig() {
86
+ // Ensure config directory exists
87
+ const configDir = path.dirname(this.configPath);
88
+ if (!fs.existsSync(configDir)) {
89
+ fs.mkdirSync(configDir, { recursive: true });
90
+ }
91
+ // If user config exists, load and merge with defaults
92
+ if (fs.existsSync(this.configPath)) {
93
+ try {
94
+ const userConfig = JSON.parse(fs.readFileSync(this.configPath, 'utf-8'));
95
+ return this.deepMerge(this.defaultConfig, userConfig);
96
+ }
97
+ catch (error) {
98
+ // If loading fails, return defaults and log warning
99
+ console.warn(`Failed to load user config, using defaults: ${error}`);
100
+ return this.cloneConfig(this.defaultConfig);
101
+ }
102
+ }
103
+ // Return defaults if no user config exists
104
+ return this.cloneConfig(this.defaultConfig);
105
+ }
106
+ /**
107
+ * Deep merge default and user configurations
108
+ */
109
+ deepMerge(defaultConfig, userConfig) {
110
+ const result = this.cloneConfig(defaultConfig);
111
+ for (const key in userConfig) {
112
+ if (key in result) {
113
+ if (typeof userConfig[key] === 'object' && userConfig[key] !== null) {
114
+ result[key] = {
115
+ ...result[key],
116
+ ...userConfig[key],
117
+ };
118
+ }
119
+ else {
120
+ result[key] = userConfig[key];
121
+ }
122
+ }
123
+ }
124
+ return result;
125
+ }
126
+ /**
127
+ * Clone configuration object deeply
128
+ */
129
+ cloneConfig(config) {
130
+ return JSON.parse(JSON.stringify(config));
131
+ }
132
+ /**
133
+ * Get configuration value by key
134
+ * Supports nested keys like "app.name"
135
+ */
136
+ get(key) {
137
+ const keys = key.split('.');
138
+ let value = this.config;
139
+ for (const k of keys) {
140
+ if (value && typeof value === 'object' && k in value) {
141
+ value = value[k];
142
+ }
143
+ else {
144
+ return undefined;
145
+ }
146
+ }
147
+ return value;
148
+ }
149
+ /**
150
+ * Get entire configuration
151
+ */
152
+ getAll() {
153
+ return this.cloneConfig(this.config);
154
+ }
155
+ /**
156
+ * Set configuration value
157
+ * Supports nested keys like "app.name"
158
+ */
159
+ set(key, value) {
160
+ this.validateConfigValue(key, value);
161
+ const keys = key.split('.');
162
+ const lastKey = keys.pop();
163
+ if (!lastKey) {
164
+ throw new Error('Invalid configuration key');
165
+ }
166
+ let target = this.config;
167
+ for (const k of keys) {
168
+ if (!(k in target)) {
169
+ target[k] = {};
170
+ }
171
+ target = target[k];
172
+ }
173
+ target[lastKey] = value;
174
+ this.persist();
175
+ }
176
+ /**
177
+ * Reset configuration to defaults
178
+ */
179
+ reset() {
180
+ this.config = this.cloneConfig(this.defaultConfig);
181
+ this.persist();
182
+ }
183
+ /**
184
+ * Validate configuration value before setting
185
+ */
186
+ validateConfigValue(key, value) {
187
+ // Theme validation
188
+ if (key === 'ui.theme') {
189
+ if (!['dark', 'light'].includes(value)) {
190
+ throw new Error(`Invalid theme: ${value}. Must be 'dark' or 'light'.`);
191
+ }
192
+ }
193
+ // Logging level validation
194
+ if (key === 'logging.level') {
195
+ if (!['debug', 'info', 'warn', 'error'].includes(value)) {
196
+ throw new Error(`Invalid logging level: ${value}. Must be 'debug', 'info', 'warn', or 'error'.`);
197
+ }
198
+ }
199
+ // Scanner depth validation
200
+ if (key === 'scanner.depth') {
201
+ if (typeof value !== 'number' || value < 1) {
202
+ throw new Error(`Invalid scanner depth: ${value}. Must be a positive number.`);
203
+ }
204
+ }
205
+ // Items per page validation
206
+ if (key === 'ui.itemsPerPage') {
207
+ if (typeof value !== 'number' || value < 1) {
208
+ throw new Error(`Invalid itemsPerPage: ${value}. Must be a positive number.`);
209
+ }
210
+ }
211
+ // Patterns validation
212
+ if (key === 'scanner.patterns') {
213
+ if (!Array.isArray(value) || value.some(v => typeof v !== 'string')) {
214
+ throw new Error('Invalid scanner patterns. Must be an array of strings.');
215
+ }
216
+ }
217
+ // Editors validation
218
+ if (key === 'tools.editors') {
219
+ if (!Array.isArray(value) || value.some(v => typeof v !== 'string')) {
220
+ throw new Error('Invalid editors. Must be an array of strings.');
221
+ }
222
+ }
223
+ }
224
+ /**
225
+ * Persist configuration to disk
226
+ */
227
+ persist() {
228
+ try {
229
+ const configDir = path.dirname(this.configPath);
230
+ if (!fs.existsSync(configDir)) {
231
+ fs.mkdirSync(configDir, { recursive: true });
232
+ }
233
+ fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2), 'utf-8');
234
+ }
235
+ catch (error) {
236
+ throw new Error(`Failed to persist configuration: ${error}`);
237
+ }
238
+ }
239
+ /**
240
+ * Get configuration file path
241
+ */
242
+ getConfigPath() {
243
+ return this.configPath;
244
+ }
245
+ /**
246
+ * Get default configuration file path
247
+ */
248
+ getDefaultConfigPath() {
249
+ return this.defaultConfigPath;
250
+ }
251
+ }
252
+ exports.ConfigManager = ConfigManager;
253
+ //# sourceMappingURL=ConfigManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigManager.js","sourceRoot":"","sources":["../../src/application/ConfigManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export declare class AppError extends Error {
2
+ readonly code: string;
3
+ readonly isTransient: boolean;
4
+ readonly recoverySteps: string[];
5
+ constructor(message: string, code: string, isTransient: boolean, recoverySteps?: string[]);
6
+ }
7
+ export declare class DatabaseError extends AppError {
8
+ constructor(message: string, isTransient?: boolean);
9
+ }
10
+ export declare class FileSystemError extends AppError {
11
+ constructor(message: string, isTransient?: boolean);
12
+ }
13
+ export declare class ErrorHandler {
14
+ /**
15
+ * Retry a function if it throws a transient error
16
+ */
17
+ static withRetry<T>(operation: () => Promise<T> | T, maxRetries?: number, delayMs?: number): Promise<T>;
18
+ /**
19
+ * Format error for UI display
20
+ */
21
+ static formatError(error: unknown): string;
22
+ }
23
+ /**
24
+ * Decorator/Wrapper for use cases to handle uncaught exceptions gracefully
25
+ */
26
+ export declare function withErrorGuard<TArgs extends any[], TResult>(fn: (...args: TArgs) => TResult): (...args: TArgs) => TResult;
27
+ //# sourceMappingURL=ErrorHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorHandler.d.ts","sourceRoot":"","sources":["../../../src/application/errors/ErrorHandler.ts"],"names":[],"mappings":"AAAA,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,WAAW,EAAE,OAAO,CAAC;IACrC,SAAgB,aAAa,EAAE,MAAM,EAAE,CAAC;gBAE5B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,GAAE,MAAM,EAAO;CAQ9F;AAED,qBAAa,aAAc,SAAQ,QAAQ;gBAC7B,OAAO,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe;CAM1D;AAED,qBAAa,eAAgB,SAAQ,QAAQ;gBAC/B,OAAO,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe;CAM1D;AAED,qBAAa,YAAY;IACvB;;OAEG;WACiB,SAAS,CAAC,CAAC,EAC7B,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAC/B,UAAU,GAAE,MAAU,EACtB,OAAO,GAAE,MAAa,GACrB,OAAO,CAAC,CAAC,CAAC;IAuBb;;OAEG;WACW,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;CAelD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,SAAS,GAAG,EAAE,EAAE,OAAO,EACzD,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,GAC9B,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAS7B"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ErrorHandler = exports.FileSystemError = exports.DatabaseError = exports.AppError = void 0;
4
+ exports.withErrorGuard = withErrorGuard;
5
+ class AppError extends Error {
6
+ constructor(message, code, isTransient, recoverySteps = []) {
7
+ super(message);
8
+ this.name = this.constructor.name;
9
+ this.code = code;
10
+ this.isTransient = isTransient;
11
+ this.recoverySteps = recoverySteps;
12
+ Error.captureStackTrace(this, this.constructor);
13
+ }
14
+ }
15
+ exports.AppError = AppError;
16
+ class DatabaseError extends AppError {
17
+ constructor(message, isTransient = false) {
18
+ super(message, 'DB_ERROR', isTransient, [
19
+ 'Ensure the database file is not locked by another process.',
20
+ 'Check if you have sufficient disk space and permissions.'
21
+ ]);
22
+ }
23
+ }
24
+ exports.DatabaseError = DatabaseError;
25
+ class FileSystemError extends AppError {
26
+ constructor(message, isTransient = false) {
27
+ super(message, 'FS_ERROR', isTransient, [
28
+ 'Verify the file or directory exists and you have read/write permissions.',
29
+ 'Check if the path is correct.'
30
+ ]);
31
+ }
32
+ }
33
+ exports.FileSystemError = FileSystemError;
34
+ class ErrorHandler {
35
+ /**
36
+ * Retry a function if it throws a transient error
37
+ */
38
+ static async withRetry(operation, maxRetries = 3, delayMs = 1000) {
39
+ let lastError;
40
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
41
+ try {
42
+ return await operation();
43
+ }
44
+ catch (error) {
45
+ lastError = error;
46
+ const isTransient = error instanceof AppError ? error.isTransient : false;
47
+ if (!isTransient || attempt === maxRetries) {
48
+ throw error;
49
+ }
50
+ // Exponential backoff
51
+ await new Promise(resolve => setTimeout(resolve, delayMs * Math.pow(2, attempt - 1)));
52
+ }
53
+ }
54
+ throw lastError;
55
+ }
56
+ /**
57
+ * Format error for UI display
58
+ */
59
+ static formatError(error) {
60
+ if (error instanceof AppError) {
61
+ let msg = `[${error.code}] ${error.message}`;
62
+ if (error.recoverySteps.length > 0) {
63
+ msg += '\n\nRecovery Options:\n' + error.recoverySteps.map(s => `- ${s}`).join('\n');
64
+ }
65
+ return msg;
66
+ }
67
+ if (error instanceof Error) {
68
+ return error.message;
69
+ }
70
+ return String(error);
71
+ }
72
+ }
73
+ exports.ErrorHandler = ErrorHandler;
74
+ /**
75
+ * Decorator/Wrapper for use cases to handle uncaught exceptions gracefully
76
+ */
77
+ function withErrorGuard(fn) {
78
+ return (...args) => {
79
+ try {
80
+ return fn(...args);
81
+ }
82
+ catch (error) {
83
+ console.error(ErrorHandler.formatError(error)); // For CLI/Logs
84
+ throw error; // Re-throw to be handled by UI layer
85
+ }
86
+ };
87
+ }
88
+ //# sourceMappingURL=ErrorHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorHandler.js","sourceRoot":"","sources":["../../../src/application/errors/ErrorHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export interface UIState {
2
+ selectedProjectId: string | null;
3
+ sortOrder: 'name' | 'created_at' | 'last_opened';
4
+ sortDirection: 'ASC' | 'DESC';
5
+ filters: {
6
+ name?: string;
7
+ is_favorite?: boolean;
8
+ tags?: string[];
9
+ technologies?: string[];
10
+ };
11
+ }
12
+ export type StateListener = (state: UIState) => void;
13
+ export declare class StateManager {
14
+ private state;
15
+ private listeners;
16
+ private stateFilePath;
17
+ constructor();
18
+ getState(): UIState;
19
+ updateState(partialState: Partial<UIState>): void;
20
+ resetState(): void;
21
+ subscribe(listener: StateListener): () => void;
22
+ private notifyListeners;
23
+ private loadState;
24
+ private saveState;
25
+ }
26
+ //# sourceMappingURL=StateManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StateManager.d.ts","sourceRoot":"","sources":["../../../src/application/state/StateManager.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,OAAO;IACtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,aAAa,CAAC;IACjD,aAAa,EAAE,KAAK,GAAG,MAAM,CAAC;IAC9B,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;KACzB,CAAC;CACH;AASD,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;AAErD,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,aAAa,CAAS;;IASvB,QAAQ,IAAI,OAAO;IAInB,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI;IAcjD,UAAU,IAAI,IAAI;IAMlB,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,IAAI;IAQrD,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,SAAS;CAWlB"}