ai-devkit 0.2.0 → 0.4.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 (106) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/README.md +20 -87
  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/commands/init.d.ts +2 -2
  20. package/dist/commands/init.d.ts.map +1 -1
  21. package/dist/commands/init.js +54 -73
  22. package/dist/commands/init.js.map +1 -1
  23. package/dist/commands/phase.d.ts.map +1 -1
  24. package/dist/commands/phase.js +1 -5
  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 -66
  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 -11
  51. package/templates/commands/capture-knowledge.md +46 -0
  52. package/templates/commands/debug.md +45 -0
  53. package/templates/{env/cursor/commands → commands}/new-requirement.md +1 -1
  54. package/web/.nojekyll +2 -0
  55. package/web/CNAME +1 -0
  56. package/web/README.md +100 -0
  57. package/web/app/favicon.ico +0 -0
  58. package/web/app/globals.css +122 -0
  59. package/web/app/layout.tsx +106 -0
  60. package/web/app/page.tsx +119 -0
  61. package/web/app/roadmap/page.tsx +150 -0
  62. package/web/app/robots.ts +16 -0
  63. package/web/app/sitemap.ts +46 -0
  64. package/web/app/vision/page.tsx +49 -0
  65. package/web/components/Footer.tsx +81 -0
  66. package/web/components/GitHubButton.tsx +49 -0
  67. package/web/components/GitHubStars.tsx +27 -0
  68. package/web/components/Header.tsx +108 -0
  69. package/web/components/MarkdownContent.tsx +35 -0
  70. package/web/components/SkipToContent.tsx +11 -0
  71. package/web/content/pages/vision.md +42 -0
  72. package/web/content/roadmap/1-web.md +9 -0
  73. package/web/content/roadmap/2-integrations.md +7 -0
  74. package/web/content/roadmap/3-cli-enhancements.md +8 -0
  75. package/web/content/roadmap/4-local-memory.md +8 -0
  76. package/web/eslint.config.mjs +18 -0
  77. package/web/lib/GitHubContext.tsx +57 -0
  78. package/web/lib/content/loader.ts +152 -0
  79. package/web/lib/content/types.ts +37 -0
  80. package/web/lib/utils.ts +12 -0
  81. package/web/next.config.ts +11 -0
  82. package/web/package-lock.json +8837 -0
  83. package/web/package.json +34 -0
  84. package/web/postcss.config.mjs +7 -0
  85. package/web/public/file.svg +1 -0
  86. package/web/public/globe.svg +1 -0
  87. package/web/public/next.svg +1 -0
  88. package/web/public/vercel.svg +1 -0
  89. package/web/public/window.svg +1 -0
  90. package/templates/env/claude/CLAUDE.md +0 -52
  91. package/templates/env/claude/commands/code-review.md +0 -81
  92. package/templates/env/claude/commands/execute-plan.md +0 -71
  93. package/templates/env/claude/commands/new-requirement.md +0 -127
  94. package/templates/env/claude/commands/update-planning.md +0 -61
  95. package/templates/env/cursor/commands/check-implementation.md +0 -21
  96. package/templates/env/cursor/commands/review-design.md +0 -11
  97. package/templates/env/cursor/commands/review-requirements.md +0 -9
  98. package/templates/env/cursor/commands/writing-test.md +0 -44
  99. package/templates/env/cursor/rules/ai-devkit.md +0 -51
  100. /package/templates/{env/claude/commands → commands}/check-implementation.md +0 -0
  101. /package/templates/{env/cursor/commands → commands}/code-review.md +0 -0
  102. /package/templates/{env/cursor/commands → commands}/execute-plan.md +0 -0
  103. /package/templates/{env/claude/commands → commands}/review-design.md +0 -0
  104. /package/templates/{env/claude/commands → commands}/review-requirements.md +0 -0
  105. /package/templates/{env/cursor/commands → commands}/update-planning.md +0 -0
  106. /package/templates/{env/claude/commands → commands}/writing-test.md +0 -0
package/CHANGELOG.md CHANGED
@@ -5,7 +5,17 @@ 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
- ## [Unreleased]
8
+ ## [0.3.0] - 2025-10-15
9
+
10
+ ### Added
11
+ - `/debug` - Structured assistant for clarifying issues, analyzing options, and agreeing on a fix plan before coding
12
+ - `/capture-knowledge` - Analyze and explain how code works from any entry point
13
+ - Supports file, folder, function, and API endpoint analysis
14
+ - Recursive dependency analysis with configurable depth (max: 3)
15
+ - Automatic generation of mermaid diagrams (flowcharts, sequence, architecture, class diagrams)
16
+ - Knowledge capture documentation saved to `docs/ai/implementation/knowledge-{feature-name}.md`
17
+ - Visual dependency tree and component relationship mapping
18
+ - Includes error handling, performance considerations, and improvement suggestions
9
19
 
10
20
  ## [0.2.0] - 2025-10-14
11
21
 
package/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  A CLI toolkit for AI-assisted software development with structured phase templates and environment setup for Cursor and Claude Code.
4
4
 
5
+ [![npm version](https://img.shields.io/npm/v/ai-devkit.svg)](https://www.npmjs.com/package/ai-devkit)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
5
8
  ## Features
6
9
 
7
10
  - 🎯 **Phase-based Development**: Structured templates for each stage of the software development lifecycle
@@ -40,6 +43,8 @@ This will:
40
43
  2. Set up your AI development environment (Cursor/Claude Code)
41
44
  3. Generate phase templates in `docs/ai/`
42
45
 
46
+ Detailed user guide can be found [here](https://ai-devkit.com/docs/).
47
+
43
48
  ## Available Phases
44
49
 
45
50
  - **Requirements**: Problem understanding, requirements gathering, and success criteria
@@ -113,50 +118,19 @@ your-project/
113
118
  └── [Environment-specific files]
114
119
  ```
115
120
 
116
- ### For Cursor:
117
- ```
118
- └── .cursor/
119
- ├── rules/ # Project-specific rules (Markdown files)
120
- │ └── ai-devkit.md
121
- └── commands/ # Custom slash commands (Markdown files)
122
- ├── new-requirement.md
123
- ├── code-review.md
124
- ├── execute-plan.md
125
- ├── writing-test.md
126
- ├── update-planning.md
127
- ├── check-implementation.md
128
- ├── review-design.md
129
- └── review-requirements.md
130
- ```
131
-
132
- ### For Claude Code:
133
- ```
134
- └── .claude/
135
- ├── CLAUDE.md # Workspace configuration
136
- └── commands/ # Custom commands (Markdown files)
137
- ├── new-requirement.md
138
- ├── code-review.md
139
- ├── execute-plan.md
140
- ├── writing-test.md
141
- ├── update-planning.md
142
- ├── check-implementation.md
143
- ├── review-design.md
144
- └── review-requirements.md
145
- ```
146
-
147
- ## Customizing Templates
148
-
149
- All templates are plain Markdown files with YAML frontmatter. You can customize them to fit your project's needs:
150
-
151
- ```markdown
152
- ---
153
- phase: requirements
154
- title: Requirements & Problem Understanding
155
- description: Clarify the problem space, gather requirements, and define success criteria
156
- ---
157
-
158
- # Your custom content here
159
- ```
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/) | ✅ | |
160
134
 
161
135
  Templates are designed to provide structure while remaining concise and AI-friendly.
162
136
 
@@ -195,6 +169,7 @@ Available commands:
195
169
  - `check-implementation` - Compare implementation with design
196
170
  - `review-design` - Review system design and architecture
197
171
  - `review-requirements` - Review and summarize requirements
172
+ - `capture-knowledge` - Analyze and explain code with recursive dependency analysis and Mermaid diagrams
198
173
 
199
174
  Commands can be referenced in Claude Code chats to guide AI assistance through your development phases.
200
175
 
@@ -234,48 +209,6 @@ Commands can be referenced in Claude Code chats to guide AI assistance through y
234
209
  8. **Monitor and iterate:**
235
210
  - Set up monitoring per `docs/ai/monitoring/README.md`
236
211
 
237
- ### Adding a New Feature
238
-
239
- **Use the `/new-requirement` command for a guided workflow:**
240
-
241
- 1. **In Cursor or Claude Code**, type `/new-requirement`
242
- 2. The AI will guide you through:
243
- - 📋 Capturing requirement details
244
- - 🔍 Creating feature-specific documentation
245
- - 📐 Designing the solution
246
- - 📅 Planning tasks and breaking down work
247
- - 💻 Implementation (task by task)
248
- - ✅ Testing and verification
249
- - 🔀 Git commits and PR/MR creation
250
-
251
- **Review and refine your documentation:**
252
- - After drafting requirements, run `/review-requirements` to validate completeness
253
- - After drafting design, run `/review-design` to ensure architecture clarity and mermaid diagrams
254
-
255
- **Execute your plan:**
256
- - Run `/execute-plan` to step through tasks interactively:
257
- - Reads `docs/ai/planning/feature-{name}.md`
258
- - Presents tasks in order with context
259
- - Captures status/notes for each task
260
- - Prompts you to update documentation as you progress
261
-
262
- **Before pushing your code:**
263
- - Run `/code-review` to perform a structured local review:
264
- - Checks alignment with design docs
265
- - Spots logic/security/performance issues
266
- - Highlights redundant code and missing tests
267
- - Suggests documentation updates
268
-
269
- **Generate comprehensive tests:**
270
- - Run `/writing-test` to create unit and integration tests targeting 100% coverage
271
-
272
- This workflow creates feature-specific files:
273
- - `docs/ai/requirements/feature-{name}.md`
274
- - `docs/ai/design/feature-{name}.md`
275
- - `docs/ai/planning/feature-{name}.md`
276
- - `docs/ai/implementation/feature-{name}.md`
277
- - `docs/ai/testing/feature-{name}.md`
278
-
279
212
  ## Use Cases
280
213
 
281
214
  - **New Projects**: Scaffold complete development documentation
@@ -354,7 +287,7 @@ MIT
354
287
  | Guided feature workflow | `/new-requirement` (Cursor & Claude) |
355
288
  | Execute feature plan | `/execute-plan` (Cursor & Claude) |
356
289
  | Generate tests | `/writing-test` (Cursor & Claude) |
357
- | **Local code review** | `/code-review` (Cursor & Claude) |
290
+ | Local code review | `/code-review` (Cursor & Claude) |
358
291
  | Help | `npx ai-devkit --help` |
359
292
 
360
293
  | Quick links | Description |
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnvironmentSelector.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/EnvironmentSelector.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=PhaseSelector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PhaseSelector.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/PhaseSelector.test.ts"],"names":[],"mappings":""}