ai-devkit 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +15 -165
  3. package/dist/__tests__/lib/Config.test.d.ts +2 -0
  4. package/dist/__tests__/lib/Config.test.d.ts.map +1 -0
  5. package/dist/__tests__/lib/Config.test.js +281 -0
  6. package/dist/__tests__/lib/Config.test.js.map +1 -0
  7. package/dist/__tests__/lib/EnvironmentSelector.test.d.ts +2 -0
  8. package/dist/__tests__/lib/EnvironmentSelector.test.d.ts.map +1 -0
  9. package/dist/__tests__/lib/EnvironmentSelector.test.js +117 -0
  10. package/dist/__tests__/lib/EnvironmentSelector.test.js.map +1 -0
  11. package/dist/__tests__/lib/PhaseSelector.test.d.ts +2 -0
  12. package/dist/__tests__/lib/PhaseSelector.test.d.ts.map +1 -0
  13. package/dist/__tests__/lib/PhaseSelector.test.js +77 -0
  14. package/dist/__tests__/lib/PhaseSelector.test.js.map +1 -0
  15. package/dist/__tests__/util/env.test.d.ts +2 -0
  16. package/dist/__tests__/util/env.test.d.ts.map +1 -0
  17. package/dist/__tests__/util/env.test.js +166 -0
  18. package/dist/__tests__/util/env.test.js.map +1 -0
  19. package/dist/cli.js +0 -0
  20. package/dist/commands/init.d.ts +2 -2
  21. package/dist/commands/init.d.ts.map +1 -1
  22. package/dist/commands/init.js +54 -67
  23. package/dist/commands/init.js.map +1 -1
  24. package/dist/commands/phase.js +1 -1
  25. package/dist/commands/phase.js.map +1 -1
  26. package/dist/lib/Config.d.ts +5 -2
  27. package/dist/lib/Config.d.ts.map +1 -1
  28. package/dist/lib/Config.js +18 -3
  29. package/dist/lib/Config.js.map +1 -1
  30. package/dist/lib/EnvironmentSelector.d.ts +7 -0
  31. package/dist/lib/EnvironmentSelector.d.ts.map +1 -0
  32. package/dist/lib/EnvironmentSelector.js +62 -0
  33. package/dist/lib/EnvironmentSelector.js.map +1 -0
  34. package/dist/lib/PhaseSelector.d.ts +8 -0
  35. package/dist/lib/PhaseSelector.d.ts.map +1 -0
  36. package/dist/lib/PhaseSelector.js +58 -0
  37. package/dist/lib/PhaseSelector.js.map +1 -0
  38. package/dist/lib/TemplateManager.d.ts +5 -5
  39. package/dist/lib/TemplateManager.d.ts.map +1 -1
  40. package/dist/lib/TemplateManager.js +73 -60
  41. package/dist/lib/TemplateManager.js.map +1 -1
  42. package/dist/types.d.ts +9 -2
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/types.js.map +1 -1
  45. package/dist/util/env.d.ts +11 -0
  46. package/dist/util/env.d.ts.map +1 -0
  47. package/dist/util/env.js +109 -0
  48. package/dist/util/env.js.map +1 -0
  49. package/jest.config.js +22 -0
  50. package/package.json +16 -10
  51. package/web/.nojekyll +2 -0
  52. package/web/CNAME +1 -0
  53. package/web/README.md +100 -0
  54. package/web/app/favicon.ico +0 -0
  55. package/web/app/globals.css +122 -0
  56. package/web/app/layout.tsx +106 -0
  57. package/web/app/page.tsx +119 -0
  58. package/web/app/roadmap/page.tsx +150 -0
  59. package/web/app/robots.ts +16 -0
  60. package/web/app/sitemap.ts +46 -0
  61. package/web/app/vision/page.tsx +49 -0
  62. package/web/components/Footer.tsx +81 -0
  63. package/web/components/GitHubButton.tsx +49 -0
  64. package/web/components/GitHubStars.tsx +27 -0
  65. package/web/components/Header.tsx +108 -0
  66. package/web/components/MarkdownContent.tsx +35 -0
  67. package/web/components/SkipToContent.tsx +11 -0
  68. package/web/content/pages/vision.md +42 -0
  69. package/web/content/roadmap/1-web.md +9 -0
  70. package/web/content/roadmap/2-integrations.md +7 -0
  71. package/web/content/roadmap/3-cli-enhancements.md +8 -0
  72. package/web/content/roadmap/4-local-memory.md +8 -0
  73. package/web/eslint.config.mjs +18 -0
  74. package/web/lib/GitHubContext.tsx +57 -0
  75. package/web/lib/content/loader.ts +152 -0
  76. package/web/lib/content/types.ts +37 -0
  77. package/web/lib/utils.ts +12 -0
  78. package/web/next.config.ts +11 -0
  79. package/web/package-lock.json +8837 -0
  80. package/web/package.json +34 -0
  81. package/web/postcss.config.mjs +7 -0
  82. package/web/public/file.svg +1 -0
  83. package/web/public/globe.svg +1 -0
  84. package/web/public/next.svg +1 -0
  85. package/web/public/vercel.svg +1 -0
  86. package/web/public/window.svg +1 -0
  87. package/templates/env/claude/CLAUDE.md +0 -52
  88. package/templates/env/cursor/rules/base.md +0 -1
  89. /package/templates/env/{cursor/AGENTS.md → base.md} +0 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2025-10-31
9
+
10
+ ### Added
11
+ - **Multi-Environment Setup** - Support for 10 AI development environments
12
+ - Interactive environment selection with multi-choice prompts
13
+ - Support for Cursor, Claude Code, GitHub Copilot, Google Gemini, OpenAI Codex, Windsurf, KiloCode, AMP, OpenCode, and Roo Code
14
+ - Unified template structure with AGENTS.md files for all environments
15
+ - Environment-specific command directories and configuration files
16
+ - Override protection with confirmation prompts for existing environments
17
+ - Config persistence storing selected environments array
18
+
19
+ ### Changed
20
+ - **Breaking Changes** - Removed legacy single-environment support for cleaner API
21
+ - Renamed `EnvironmentId` to `EnvironmentCode` throughout codebase
22
+ - Removed legacy `Environment` type union (cursor | claude | both)
23
+ - Updated config schema to use `environments: EnvironmentCode[]`
24
+ - All environments now use standardized AGENTS.md context files
25
+
26
+ ### Technical Improvements
27
+ - **Testing Infrastructure** - Complete test suite implementation
28
+ - **Architecture** - Modular design improvements
29
+
8
30
  ## [0.3.0] - 2025-10-15
9
31
 
10
32
  ### Added
package/README.md CHANGED
@@ -43,6 +43,8 @@ This will:
43
43
  2. Set up your AI development environment (Cursor/Claude Code)
44
44
  3. Generate phase templates in `docs/ai/`
45
45
 
46
+ Detailed user guide can be found [here](https://ai-devkit.com/docs/).
47
+
46
48
  ## Available Phases
47
49
 
48
50
  - **Requirements**: Problem understanding, requirements gathering, and success criteria
@@ -116,52 +118,19 @@ your-project/
116
118
  └── [Environment-specific files]
117
119
  ```
118
120
 
119
- ### For Cursor:
120
- ```
121
- └── .cursor/
122
- ├── rules/ # Project-specific rules (Markdown files)
123
- │ └── ai-devkit.md
124
- └── commands/ # Custom slash commands (Markdown files)
125
- ├── new-requirement.md
126
- ├── code-review.md
127
- ├── execute-plan.md
128
- ├── writing-test.md
129
- ├── update-planning.md
130
- ├── check-implementation.md
131
- ├── review-design.md
132
- ├── review-requirements.md
133
- └── capture-knowledge.md
134
- ```
135
-
136
- ### For Claude Code:
137
- ```
138
- └── .claude/
139
- ├── CLAUDE.md # Workspace configuration
140
- └── commands/ # Custom commands (Markdown files)
141
- ├── new-requirement.md
142
- ├── code-review.md
143
- ├── execute-plan.md
144
- ├── writing-test.md
145
- ├── update-planning.md
146
- ├── check-implementation.md
147
- ├── review-design.md
148
- ├── review-requirements.md
149
- └── capture-knowledge.md
150
- ```
151
-
152
- ## Customizing Templates
153
-
154
- All templates are plain Markdown files with YAML frontmatter. You can customize them to fit your project's needs:
155
-
156
- ```markdown
157
- ---
158
- phase: requirements
159
- title: Requirements & Problem Understanding
160
- description: Clarify the problem space, gather requirements, and define success criteria
161
- ---
162
-
163
- # Your custom content here
164
- ```
121
+ Supported Tools & Agents:
122
+ | Agent | Support | Notes |
123
+ |-----------------------------------------------------------|---------|---------------------------------------------------|
124
+ | [Claude Code](https://www.anthropic.com/claude-code) | | |
125
+ | [GitHub Copilot](https://code.visualstudio.com/) | ✅ | |
126
+ | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | | |
127
+ | [Cursor](https://cursor.sh/) | ✅ | |
128
+ | [opencode](https://opencode.ai/) | ✅ | |
129
+ | [Windsurf](https://windsurf.com/) | ✅ | |
130
+ | [Kilo Code](https://github.com/Kilo-Org/kilocode) | ✅ | |
131
+ | [Roo Code](https://roocode.com/) | ✅ | |
132
+ | [Codex CLI](https://github.com/openai/codex) | ✅ | |
133
+ | [Amp](https://ampcode.com/) | ✅ | |
165
134
 
166
135
  Templates are designed to provide structure while remaining concise and AI-friendly.
167
136
 
@@ -240,125 +209,6 @@ Commands can be referenced in Claude Code chats to guide AI assistance through y
240
209
  8. **Monitor and iterate:**
241
210
  - Set up monitoring per `docs/ai/monitoring/README.md`
242
211
 
243
- ### Adding a New Feature
244
-
245
- **Use the `/new-requirement` command for a guided workflow:**
246
-
247
- 1. **In Cursor or Claude Code**, type `/new-requirement`
248
- 2. The AI will guide you through:
249
- - 📋 Capturing requirement details
250
- - 🔍 Creating feature-specific documentation
251
- - 📐 Designing the solution
252
- - 📅 Planning tasks and breaking down work
253
- - 💻 Implementation (task by task)
254
- - ✅ Testing and verification
255
- - 🔀 Git commits and PR/MR creation
256
-
257
- **Review and refine your documentation:**
258
- - After drafting requirements, run `/review-requirements` to validate completeness
259
- - After drafting design, run `/review-design` to ensure architecture clarity and mermaid diagrams
260
-
261
- **Execute your plan:**
262
- - Run `/execute-plan` to step through tasks interactively:
263
- - Reads `docs/ai/planning/feature-{name}.md`
264
- - Presents tasks in order with context
265
- - Captures status/notes for each task
266
- - Prompts you to update documentation as you progress
267
-
268
- **Before pushing your code:**
269
- - Run `/code-review` to perform a structured local review:
270
- - Checks alignment with design docs
271
- - Spots logic/security/performance issues
272
- - Highlights redundant code and missing tests
273
- - Suggests documentation updates
274
-
275
- **Generate comprehensive tests:**
276
- - Run `/writing-test` to create unit and integration tests targeting 100% coverage
277
-
278
- This workflow creates feature-specific files:
279
- - `docs/ai/requirements/feature-{name}.md`
280
- - `docs/ai/design/feature-{name}.md`
281
- - `docs/ai/planning/feature-{name}.md`
282
- - `docs/ai/implementation/feature-{name}.md`
283
- - `docs/ai/testing/feature-{name}.md`
284
-
285
- ### Understanding Existing Code
286
-
287
- **Use the `/capture-knowledge` command to analyze and document code:**
288
-
289
- The `capture-knowledge` command helps you understand how existing code works by analyzing it from any entry point and generating comprehensive documentation with visual diagrams.
290
-
291
- **In Cursor:**
292
- ```
293
- /capture-knowledge <entry-point> [options]
294
- ```
295
-
296
- **In Claude Code:**
297
- ```
298
- Use the capture-knowledge command to analyze <entry-point>
299
- ```
300
-
301
- **Entry Point Types:**
302
- - **File**: `/capture-knowledge src/api/users.ts` - Analyzes a specific file
303
- - **Folder**: `/capture-knowledge src/services/` - Analyzes an entire module
304
- - **Function**: `/capture-knowledge calculateTotalPrice` - Analyzes a specific function
305
- - **API Endpoint**: `/capture-knowledge POST:/api/users` - Analyzes an API endpoint flow
306
-
307
- **Options:**
308
- - `--depth <n>` - Control recursion depth (default: 3)
309
- - `--save` - Save output to `docs/ai/knowledge/`
310
- - `--diagram-only` - Generate only diagrams
311
-
312
- **What You Get:**
313
- - 📖 **Detailed Explanation**: Natural language description of how the code works
314
- - 🔍 **Implementation Details**: Key components, logic flow, and design patterns
315
- - 🔗 **Recursive Dependency Analysis**: Automatically traces and explains all dependencies
316
- - 📊 **Mermaid Diagrams**: Visual flowcharts, sequence diagrams, and architecture diagrams
317
- - 💡 **Insights**: Performance considerations, security notes, potential improvements
318
-
319
- **Example Outputs:**
320
-
321
- For functions, you get:
322
- - Flowchart showing execution path
323
- - Parameter and return value documentation
324
- - Called functions and their purposes
325
- - Error handling strategy
326
-
327
- For API endpoints, you get:
328
- - Sequence diagram showing request flow
329
- - Validation and authentication steps
330
- - Database operations
331
- - Response format
332
-
333
- For modules/folders, you get:
334
- - Architecture diagram showing component relationships
335
- - Overview of each file's purpose
336
- - Module boundaries and dependencies
337
-
338
- **Use Cases:**
339
- - 🎯 Onboarding new developers to understand the codebase
340
- - 📚 Generating documentation for complex systems
341
- - 🔍 Debugging by understanding complete execution flow
342
- - 🏗️ Refactoring with full context of dependencies
343
- - 📖 Creating knowledge base entries
344
-
345
- **Example Workflow:**
346
- ```bash
347
- # Understand a payment function
348
- /capture-knowledge processPayment --depth 4
349
-
350
- # Document an entire authentication module
351
- /capture-knowledge src/auth/ --save
352
-
353
- # Analyze an API endpoint
354
- /capture-knowledge POST:/api/checkout
355
-
356
- # Get just the diagrams for a complex function
357
- /capture-knowledge handleOrderProcessing --diagram-only
358
- ```
359
-
360
- The analysis is saved to `docs/ai/knowledge/` and can be versioned alongside your code.
361
-
362
212
  ## Use Cases
363
213
 
364
214
  - **New Projects**: Scaffold complete development documentation
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Config.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/Config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,281 @@
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
+ const fs = __importStar(require("fs-extra"));
37
+ const path = __importStar(require("path"));
38
+ const Config_1 = require("../../lib/Config");
39
+ jest.mock('fs-extra');
40
+ jest.mock('path');
41
+ jest.mock('../../../package.json', () => ({
42
+ version: '1.0.0'
43
+ }));
44
+ describe('ConfigManager', () => {
45
+ let configManager;
46
+ let mockFs;
47
+ let mockPath;
48
+ beforeEach(() => {
49
+ configManager = new Config_1.ConfigManager('/test/dir');
50
+ mockFs = fs;
51
+ mockPath = path;
52
+ mockPath.join.mockImplementation((...args) => args.join('/'));
53
+ });
54
+ afterEach(() => {
55
+ jest.clearAllMocks();
56
+ });
57
+ describe('constructor', () => {
58
+ it('should set correct config path', () => {
59
+ expect(mockPath.join).toHaveBeenCalledWith('/test/dir', '.ai-devkit.json');
60
+ });
61
+ it('should use current directory as default', () => {
62
+ const defaultConfig = new Config_1.ConfigManager();
63
+ expect(mockPath.join).toHaveBeenCalledWith(process.cwd(), '.ai-devkit.json');
64
+ });
65
+ });
66
+ describe('exists', () => {
67
+ it('should return true when config file exists', async () => {
68
+ mockFs.pathExists.mockResolvedValue(true);
69
+ const result = await configManager.exists();
70
+ expect(result).toBe(true);
71
+ expect(mockFs.pathExists).toHaveBeenCalledWith('/test/dir/.ai-devkit.json');
72
+ });
73
+ it('should return false when config file does not exist', async () => {
74
+ mockFs.pathExists.mockResolvedValue(false);
75
+ const result = await configManager.exists();
76
+ expect(result).toBe(false);
77
+ });
78
+ });
79
+ describe('read', () => {
80
+ it('should return parsed config when file exists', async () => {
81
+ const mockConfig = {
82
+ version: '1.0.0',
83
+ environments: ['cursor'],
84
+ initializedPhases: ['requirements'],
85
+ createdAt: '2024-01-01T00:00:00.000Z',
86
+ updatedAt: '2024-01-01T00:00:00.000Z'
87
+ };
88
+ mockFs.pathExists.mockResolvedValue(true);
89
+ mockFs.readJson.mockResolvedValue(mockConfig);
90
+ const result = await configManager.read();
91
+ expect(result).toEqual(mockConfig);
92
+ expect(mockFs.readJson).toHaveBeenCalledWith('/test/dir/.ai-devkit.json');
93
+ });
94
+ it('should return null when file does not exist', async () => {
95
+ mockFs.pathExists.mockResolvedValue(false);
96
+ const result = await configManager.read();
97
+ expect(result).toBeNull();
98
+ expect(mockFs.readJson).not.toHaveBeenCalled();
99
+ });
100
+ });
101
+ describe('create', () => {
102
+ it('should create config with default values', async () => {
103
+ const expectedConfig = {
104
+ version: '1.0.0',
105
+ environments: [],
106
+ initializedPhases: [],
107
+ createdAt: expect.any(String),
108
+ updatedAt: expect.any(String)
109
+ };
110
+ mockFs.writeJson.mockResolvedValue(undefined);
111
+ const result = await configManager.create();
112
+ expect(result).toEqual(expectedConfig);
113
+ expect(mockFs.writeJson).toHaveBeenCalledWith('/test/dir/.ai-devkit.json', expectedConfig, { spaces: 2 });
114
+ });
115
+ });
116
+ describe('update', () => {
117
+ it('should update existing config and set updatedAt', async () => {
118
+ const existingConfig = {
119
+ version: '1.0.0',
120
+ environments: ['cursor'],
121
+ initializedPhases: [],
122
+ createdAt: '2024-01-01T00:00:00.000Z',
123
+ updatedAt: '2024-01-01T00:00:00.000Z'
124
+ };
125
+ const updates = { environments: ['cursor', 'claude'] };
126
+ mockFs.pathExists.mockResolvedValue(true);
127
+ mockFs.readJson.mockResolvedValue(existingConfig);
128
+ mockFs.writeJson.mockResolvedValue(undefined);
129
+ const result = await configManager.update(updates);
130
+ expect(result.environments).toEqual(['cursor', 'claude']);
131
+ expect(result.updatedAt).not.toBe(existingConfig.updatedAt);
132
+ expect(result.createdAt).toBe(existingConfig.createdAt);
133
+ });
134
+ it('should throw error when config file not found', async () => {
135
+ mockFs.readJson.mockResolvedValue(null);
136
+ await expect(configManager.update({})).rejects.toThrow('Config file not found. Run ai-devkit init first.');
137
+ });
138
+ });
139
+ describe('addPhase', () => {
140
+ it('should add new phase to initializedPhases', async () => {
141
+ const config = {
142
+ version: '1.0.0',
143
+ environments: [],
144
+ initializedPhases: ['requirements'],
145
+ createdAt: '2024-01-01T00:00:00.000Z',
146
+ updatedAt: '2024-01-01T00:00:00.000Z'
147
+ };
148
+ mockFs.pathExists.mockResolvedValue(true);
149
+ mockFs.readJson.mockResolvedValue(config);
150
+ mockFs.writeJson.mockResolvedValue(undefined);
151
+ const result = await configManager.addPhase('design');
152
+ expect(result.initializedPhases).toEqual(['requirements', 'design']);
153
+ });
154
+ it('should not add duplicate phase', async () => {
155
+ const config = {
156
+ version: '1.0.0',
157
+ environments: [],
158
+ initializedPhases: ['requirements'],
159
+ createdAt: '2024-01-01T00:00:00.000Z',
160
+ updatedAt: '2024-01-01T00:00:00.000Z'
161
+ };
162
+ mockFs.pathExists.mockResolvedValue(true);
163
+ mockFs.readJson.mockResolvedValue(config);
164
+ const result = await configManager.addPhase('requirements');
165
+ expect(result.initializedPhases).toEqual(['requirements']);
166
+ expect(mockFs.writeJson).not.toHaveBeenCalled();
167
+ });
168
+ });
169
+ describe('hasPhase', () => {
170
+ it('should return true when phase exists', async () => {
171
+ const config = {
172
+ version: '1.0.0',
173
+ environments: [],
174
+ initializedPhases: ['requirements', 'design'],
175
+ createdAt: '2024-01-01T00:00:00.000Z',
176
+ updatedAt: '2024-01-01T00:00:00.000Z'
177
+ };
178
+ mockFs.pathExists.mockResolvedValue(true);
179
+ mockFs.readJson.mockResolvedValue(config);
180
+ const result = await configManager.hasPhase('design');
181
+ expect(result).toBe(true);
182
+ });
183
+ it('should return false when phase does not exist', async () => {
184
+ const config = {
185
+ version: '1.0.0',
186
+ environments: [],
187
+ initializedPhases: ['requirements'],
188
+ createdAt: '2024-01-01T00:00:00.000Z',
189
+ updatedAt: '2024-01-01T00:00:00.000Z'
190
+ };
191
+ mockFs.pathExists.mockResolvedValue(true);
192
+ mockFs.readJson.mockResolvedValue(config);
193
+ const result = await configManager.hasPhase('design');
194
+ expect(result).toBe(false);
195
+ });
196
+ it('should return false when config does not exist', async () => {
197
+ mockFs.pathExists.mockResolvedValue(false);
198
+ const result = await configManager.hasPhase('requirements');
199
+ expect(result).toBe(false);
200
+ });
201
+ });
202
+ describe('getEnvironments', () => {
203
+ it('should return environments array when config exists', async () => {
204
+ const config = {
205
+ version: '1.0.0',
206
+ environments: ['cursor', 'claude'],
207
+ initializedPhases: [],
208
+ createdAt: '2024-01-01T00:00:00.000Z',
209
+ updatedAt: '2024-01-01T00:00:00.000Z'
210
+ };
211
+ mockFs.pathExists.mockResolvedValue(true);
212
+ mockFs.readJson.mockResolvedValue(config);
213
+ const result = await configManager.getEnvironments();
214
+ expect(result).toEqual(['cursor', 'claude']);
215
+ });
216
+ it('should return empty array when config does not exist', async () => {
217
+ mockFs.pathExists.mockResolvedValue(false);
218
+ const result = await configManager.getEnvironments();
219
+ expect(result).toEqual([]);
220
+ });
221
+ it('should return empty array when environments field is missing', async () => {
222
+ const config = {
223
+ version: '1.0.0',
224
+ environments: [],
225
+ initializedPhases: [],
226
+ createdAt: '2024-01-01T00:00:00.000Z',
227
+ updatedAt: '2024-01-01T00:00:00.000Z'
228
+ };
229
+ mockFs.pathExists.mockResolvedValue(true);
230
+ mockFs.readJson.mockResolvedValue(config);
231
+ const result = await configManager.getEnvironments();
232
+ expect(result).toEqual([]);
233
+ });
234
+ });
235
+ describe('setEnvironments', () => {
236
+ it('should update environments and return config', async () => {
237
+ const config = {
238
+ version: '1.0.0',
239
+ environments: ['cursor'],
240
+ initializedPhases: [],
241
+ createdAt: '2024-01-01T00:00:00.000Z',
242
+ updatedAt: '2024-01-01T00:00:00.000Z'
243
+ };
244
+ mockFs.pathExists.mockResolvedValue(true);
245
+ mockFs.readJson.mockResolvedValue(config);
246
+ mockFs.writeJson.mockResolvedValue(undefined);
247
+ const result = await configManager.setEnvironments(['cursor', 'claude']);
248
+ expect(result.environments).toEqual(['cursor', 'claude']);
249
+ expect(mockFs.writeJson).toHaveBeenCalled();
250
+ });
251
+ });
252
+ describe('hasEnvironment', () => {
253
+ it('should return true when environment exists', async () => {
254
+ const config = {
255
+ version: '1.0.0',
256
+ environments: ['cursor', 'claude'],
257
+ initializedPhases: [],
258
+ createdAt: '2024-01-01T00:00:00.000Z',
259
+ updatedAt: '2024-01-01T00:00:00.000Z'
260
+ };
261
+ mockFs.pathExists.mockResolvedValue(true);
262
+ mockFs.readJson.mockResolvedValue(config);
263
+ const result = await configManager.hasEnvironment('claude');
264
+ expect(result).toBe(true);
265
+ });
266
+ it('should return false when environment does not exist', async () => {
267
+ const config = {
268
+ version: '1.0.0',
269
+ environments: ['cursor'],
270
+ initializedPhases: [],
271
+ createdAt: '2024-01-01T00:00:00.000Z',
272
+ updatedAt: '2024-01-01T00:00:00.000Z'
273
+ };
274
+ mockFs.pathExists.mockResolvedValue(true);
275
+ mockFs.readJson.mockResolvedValue(config);
276
+ const result = await configManager.hasEnvironment('claude');
277
+ expect(result).toBe(false);
278
+ });
279
+ });
280
+ });
281
+ //# sourceMappingURL=Config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Config.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/Config.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6CAA+B;AAC/B,2CAA6B;AAC7B,6CAAiD;AAGjD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACtB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAClB,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,GAAG,EAAE,CAAC,CAAC;IACxC,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC,CAAC;AAEJ,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,IAAI,aAA4B,CAAC;IACjC,IAAI,MAA8B,CAAC;IACnC,IAAI,QAAkC,CAAC;IAEvC,UAAU,CAAC,GAAG,EAAE;QACd,aAAa,GAAG,IAAI,sBAAa,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,GAAG,EAA4B,CAAC;QACtC,QAAQ,GAAG,IAAgC,CAAC;QAC5C,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;YACxC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;YACjD,MAAM,aAAa,GAAG,IAAI,sBAAa,EAAE,CAAC;YAC1C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,iBAAiB,CAAC,CAAC;QAC/E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;QACtB,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAEnD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,CAAC;YAE5C,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,2BAA2B,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YAClE,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAEpD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,CAAC;YAE5C,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE;QACpB,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;YAC5D,MAAM,UAAU,GAAiB;gBAC/B,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,CAAC,QAAe,CAAC;gBAC/B,iBAAiB,EAAE,CAAC,cAAqB,CAAC;gBAC1C,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAED,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAEvD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC;YAE1C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,2BAA2B,CAAC,CAAC;QAC5E,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAEpD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,CAAC;YAE1C,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC1B,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;QACtB,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,MAAM,cAAc,GAAiB;gBACnC,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,EAAE;gBAChB,iBAAiB,EAAE,EAAE;gBACrB,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAC7B,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;aAC9B,CAAC;YAED,MAAM,CAAC,SAAiB,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAEvD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,CAAC;YAE5C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;YACvC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,oBAAoB,CAC3C,2BAA2B,EAC3B,cAAc,EACd,EAAE,MAAM,EAAE,CAAC,EAAE,CACd,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;QACtB,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YAC/D,MAAM,cAAc,GAAiB;gBACnC,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,CAAC,QAAQ,CAAC;gBACxB,iBAAiB,EAAE,EAAE;gBACrB,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAEF,MAAM,OAAO,GAAG,EAAE,YAAY,EAAE,CAAC,QAAe,EAAE,QAAe,CAAC,EAAE,CAAC;YAEpE,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;YAC1D,MAAM,CAAC,SAAiB,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAEvD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAEnD,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC1D,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;YAC5D,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;YAC5D,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAEjD,MAAM,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACpD,kDAAkD,CACnD,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,EAAE;gBAChB,iBAAiB,EAAE,CAAC,cAAc,CAAC;gBACnC,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAED,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAClD,MAAM,CAAC,SAAiB,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAEvD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAEtD,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC;QACvE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,EAAE;gBAChB,iBAAiB,EAAE,CAAC,cAAc,CAAC;gBACnC,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAED,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAEnD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;YAE5D,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;YAC3D,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,EAAE;gBAChB,iBAAiB,EAAE,CAAC,cAAc,EAAE,QAAQ,CAAC;gBAC7C,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAED,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAEnD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAEtD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;YAC7D,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,EAAE;gBAChB,iBAAiB,EAAE,CAAC,cAAc,CAAC;gBACnC,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAED,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAEnD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAEtD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC7D,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAEpD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;YAE5D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBAClC,iBAAiB,EAAE,EAAE;gBACrB,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAED,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAEnD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,eAAe,EAAE,CAAC;YAErD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAEpD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,eAAe,EAAE,CAAC;YAErD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;YAC5E,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,EAAE;gBAChB,iBAAiB,EAAE,EAAE;gBACrB,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAED,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAEnD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,eAAe,EAAE,CAAC;YAErD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;YAC5D,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,CAAC,QAAQ,CAAC;gBACxB,iBAAiB,EAAE,EAAE;gBACrB,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAED,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAClD,MAAM,CAAC,SAAiB,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YAEvD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,eAAe,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YAEzE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC1D,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,gBAAgB,EAAE,CAAC;QAC9C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBAClC,iBAAiB,EAAE,EAAE;gBACrB,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAED,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAEnD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YAE5D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,MAAM,GAAiB;gBAC3B,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,CAAC,QAAQ,CAAC;gBACxB,iBAAiB,EAAE,EAAE;gBACrB,SAAS,EAAE,0BAA0B;gBACrC,SAAS,EAAE,0BAA0B;aACtC,CAAC;YAED,MAAM,CAAC,UAAkB,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,CAAC,QAAgB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAEnD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YAE5D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=EnvironmentSelector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnvironmentSelector.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/EnvironmentSelector.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const EnvironmentSelector_1 = require("../../lib/EnvironmentSelector");
4
+ const env_1 = require("../../util/env");
5
+ jest.mock('inquirer');
6
+ describe('EnvironmentSelector', () => {
7
+ let selector;
8
+ let mockPrompt;
9
+ beforeEach(() => {
10
+ selector = new EnvironmentSelector_1.EnvironmentSelector();
11
+ const inquirer = require('inquirer');
12
+ mockPrompt = jest.fn();
13
+ inquirer.prompt = mockPrompt;
14
+ });
15
+ afterEach(() => {
16
+ jest.clearAllMocks();
17
+ });
18
+ describe('selectEnvironments', () => {
19
+ it('should create choices from all environments', async () => {
20
+ const environments = (0, env_1.getAllEnvironments)();
21
+ mockPrompt.mockResolvedValue({ environments: ['cursor', 'claude'] });
22
+ await selector.selectEnvironments();
23
+ expect(mockPrompt).toHaveBeenCalledWith([
24
+ expect.objectContaining({
25
+ type: 'checkbox',
26
+ name: 'environments',
27
+ message: 'Select AI environments to set up (use space to select, enter to confirm):',
28
+ choices: environments.map(env => ({
29
+ name: env.name,
30
+ value: env.code,
31
+ short: env.name
32
+ })),
33
+ validate: expect.any(Function)
34
+ })
35
+ ]);
36
+ });
37
+ it('should return selected environments', async () => {
38
+ const selectedEnvs = ['cursor', 'claude'];
39
+ mockPrompt.mockResolvedValue({ environments: selectedEnvs });
40
+ const result = await selector.selectEnvironments();
41
+ expect(result).toEqual(selectedEnvs);
42
+ });
43
+ it('should validate that at least one environment is selected', async () => {
44
+ mockPrompt.mockResolvedValue({ environments: [] });
45
+ const result = await selector.selectEnvironments();
46
+ expect(result).toEqual([]);
47
+ const callArgs = mockPrompt.mock.calls[0][0];
48
+ const validateFn = callArgs[0].validate;
49
+ expect(validateFn([])).toBe('Please select at least one environment.');
50
+ expect(validateFn(['cursor'])).toBe(true);
51
+ });
52
+ it('should handle prompt rejection', async () => {
53
+ mockPrompt.mockRejectedValue(new Error('User cancelled'));
54
+ await expect(selector.selectEnvironments()).rejects.toThrow('User cancelled');
55
+ });
56
+ });
57
+ describe('confirmOverride', () => {
58
+ it('should return true immediately for empty conflicts', async () => {
59
+ const result = await selector.confirmOverride([]);
60
+ expect(result).toBe(true);
61
+ expect(mockPrompt).not.toHaveBeenCalled();
62
+ });
63
+ it('should prompt user for confirmation with conflict details', async () => {
64
+ mockPrompt.mockResolvedValue({ proceed: true });
65
+ const result = await selector.confirmOverride(['cursor', 'claude']);
66
+ expect(result).toBe(true);
67
+ expect(mockPrompt).toHaveBeenCalledWith([
68
+ {
69
+ type: 'confirm',
70
+ name: 'proceed',
71
+ message: 'The following environments are already set up and will be overwritten:\n Cursor, Claude Code\n\nDo you want to continue?',
72
+ default: false
73
+ }
74
+ ]);
75
+ });
76
+ it('should return false when user declines', async () => {
77
+ mockPrompt.mockResolvedValue({ proceed: false });
78
+ const result = await selector.confirmOverride(['cursor']);
79
+ expect(result).toBe(false);
80
+ });
81
+ it('should handle single environment conflict', async () => {
82
+ mockPrompt.mockResolvedValue({ proceed: true });
83
+ const result = await selector.confirmOverride(['cursor']);
84
+ expect(result).toBe(true);
85
+ const message = mockPrompt.mock.calls[0][0][0].message;
86
+ expect(message).toContain('Cursor');
87
+ });
88
+ });
89
+ describe('displaySelectionSummary', () => {
90
+ let consoleSpy;
91
+ beforeEach(() => {
92
+ consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
93
+ });
94
+ afterEach(() => {
95
+ consoleSpy.mockRestore();
96
+ });
97
+ it('should display nothing selected message for empty array', () => {
98
+ selector.displaySelectionSummary([]);
99
+ expect(consoleSpy).toHaveBeenCalledWith('No environments selected.');
100
+ expect(consoleSpy).toHaveBeenCalledTimes(1);
101
+ });
102
+ it('should display selected environments with checkmarks', () => {
103
+ selector.displaySelectionSummary(['cursor', 'claude']);
104
+ expect(consoleSpy).toHaveBeenCalledWith('\nSelected environments:');
105
+ expect(consoleSpy).toHaveBeenCalledWith(' [OK] Cursor');
106
+ expect(consoleSpy).toHaveBeenCalledWith(' [OK] Claude Code');
107
+ expect(consoleSpy).toHaveBeenCalledWith('');
108
+ });
109
+ it('should handle single environment selection', () => {
110
+ selector.displaySelectionSummary(['cursor']);
111
+ expect(consoleSpy).toHaveBeenCalledWith('\nSelected environments:');
112
+ expect(consoleSpy).toHaveBeenCalledWith(' [OK] Cursor');
113
+ expect(consoleSpy).toHaveBeenCalledWith('');
114
+ });
115
+ });
116
+ });
117
+ //# sourceMappingURL=EnvironmentSelector.test.js.map