Skip to content

Commit 2f2e79a

Browse files
committed
Implement global setup to support Codex
1 parent e26a833 commit 2f2e79a

12 files changed

Lines changed: 608 additions & 35 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
### Added
11+
- **Global Setup Command** - New `ai-devkit setup --global` command for installing commands globally
12+
- Copy AI DevKit commands to global environment folders
13+
- Support for Antigravity (`~/.gemini/antigravity/global_workflows/`) and Codex (`~/.codex/prompts/`)
14+
- Interactive environment selection with only global-capable environments shown
15+
- Overwrite prompts for existing global commands
16+
- Cross-platform support using `os.homedir()` and `path.join()`
17+
818
## [0.5.0] - 2025-01-15
919

1020
### Added

docs/ai/planning/feature-global-setup.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,31 @@ feature: global-setup
1010
## Milestones
1111
**What are the major checkpoints?**
1212

13-
- [ ] Milestone 1: Environment definitions updated with global support
14-
- [ ] Milestone 2: Core global setup functionality implemented
15-
- [ ] Milestone 3: CLI command integrated and tested
13+
- [x] Milestone 1: Environment definitions updated with global support
14+
- [x] Milestone 2: Core global setup functionality implemented
15+
- [x] Milestone 3: CLI command integrated and tested
1616

1717
## Task Breakdown
1818
**What specific work needs to be done?**
1919

2020
### Phase 1: Foundation - Update Types and Environment Definitions
21-
- [ ] Task 1.1: Add `globalCommandPath` property to `EnvironmentDefinition` interface in `src/types.ts`
22-
- [ ] Task 1.2: Add `globalCommandPath` to Antigravity definition in `src/util/env.ts`
23-
- [ ] Task 1.3: Add `globalCommandPath` to Codex definition in `src/util/env.ts`
24-
- [ ] Task 1.4: Add `getGlobalCapableEnvironments()` function in `src/util/env.ts`
25-
- [ ] Task 1.5: Add `hasGlobalSupport()` function in `src/util/env.ts`
21+
- [x] Task 1.1: Add `globalCommandPath` property to `EnvironmentDefinition` interface in `src/types.ts`
22+
- [x] Task 1.2: Add `globalCommandPath` to Antigravity definition in `src/util/env.ts`
23+
- [x] Task 1.3: Add `globalCommandPath` to Codex definition in `src/util/env.ts`
24+
- [x] Task 1.4: Add `getGlobalCapableEnvironments()` function in `src/util/env.ts`
25+
- [x] Task 1.5: Add `hasGlobalSupport()` function in `src/util/env.ts`
2626

2727
### Phase 2: Core Features - Implement Global Setup Logic
28-
- [ ] Task 2.1: Add `selectGlobalEnvironments()` method to `EnvironmentSelector` class
29-
- [ ] Task 2.2: Add `copyCommandsToGlobal()` method to `TemplateManager` class
30-
- [ ] Task 2.3: Add `checkGlobalCommandsExist()` method to `TemplateManager` class
31-
- [ ] Task 2.4: Create new `src/commands/setup.ts` file with global setup logic
28+
- [x] Task 2.1: Add `selectGlobalEnvironments()` method to `EnvironmentSelector` class
29+
- [x] Task 2.2: Add `copyCommandsToGlobal()` method to `TemplateManager` class
30+
- [x] Task 2.3: Add `checkGlobalCommandsExist()` method to `TemplateManager` class
31+
- [x] Task 2.4: Create new `src/commands/setup.ts` file with global setup logic
3232

3333
### Phase 3: Integration & Polish
34-
- [ ] Task 3.1: Add `setup` command to CLI in `src/cli.ts`
35-
- [ ] Task 3.2: Add user-friendly messages and error handling
36-
- [ ] Task 3.3: Update CHANGELOG.md with new feature
37-
- [ ] Task 3.4: Test end-to-end with Antigravity and Codex
34+
- [x] Task 3.1: Add `setup` command to CLI in `src/cli.ts`
35+
- [x] Task 3.2: Add user-friendly messages and error handling
36+
- [x] Task 3.3: Update CHANGELOG.md with new feature
37+
- [x] Task 3.4: Test end-to-end with Antigravity and Codex
3838

3939
## Dependencies
4040
**What needs to happen in what order?**

docs/ai/testing/feature-global-setup.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,27 @@ feature: global-setup
1818
**What individual components need testing?**
1919

2020
### `src/util/env.ts` - New Functions
21-
- [ ] `getGlobalCapableEnvironments()` returns only envs with `globalCommandPath`
22-
- [ ] `getGlobalCapableEnvironments()` returns empty array if no envs have global support
23-
- [ ] `hasGlobalSupport()` returns true for Antigravity
24-
- [ ] `hasGlobalSupport()` returns true for Codex
25-
- [ ] `hasGlobalSupport()` returns false for Cursor (no global support)
21+
- [x] `getGlobalCapableEnvironments()` returns only envs with `globalCommandPath`
22+
- [x] `getGlobalCapableEnvironments()` returns empty array if no envs have global support
23+
- [x] `hasGlobalSupport()` returns true for Antigravity
24+
- [x] `hasGlobalSupport()` returns true for Codex
25+
- [x] `hasGlobalSupport()` returns false for Cursor (no global support)
2626

2727
### `src/lib/EnvironmentSelector.ts` - selectGlobalEnvironments
28-
- [ ] Returns empty array if user selects nothing
29-
- [ ] Returns only global-capable environments in choices
30-
- [ ] Does not show environments without `globalCommandPath`
28+
- [x] Returns empty array if user selects nothing
29+
- [x] Returns only global-capable environments in choices
30+
- [x] Does not show environments without `globalCommandPath`
3131

3232
### `src/lib/TemplateManager.ts` - copyCommandsToGlobal
33-
- [ ] Creates global directory if it doesn't exist
34-
- [ ] Copies all command files to global folder
35-
- [ ] Uses correct file extension for each environment
36-
- [ ] Returns list of copied files
33+
- [x] Creates global directory if it doesn't exist
34+
- [x] Copies all command files to global folder
35+
- [x] Uses correct file extension for each environment
36+
- [x] Returns list of copied files
3737

3838
### `src/lib/TemplateManager.ts` - checkGlobalCommandsExist
39-
- [ ] Returns true if any command file exists in global folder
40-
- [ ] Returns false if global folder is empty
41-
- [ ] Returns false if global folder doesn't exist
39+
- [x] Returns true if any command file exists in global folder
40+
- [x] Returns false if global folder is empty
41+
- [x] Returns false if global folder doesn't exist
4242

4343
## Integration Tests
4444
**How do we test component interactions?**

src/__tests__/lib/EnvironmentSelector.test.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ describe('EnvironmentSelector', () => {
117117
let consoleSpy: jest.SpyInstance;
118118

119119
beforeEach(() => {
120-
consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
120+
consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => { });
121121
});
122122

123123
afterEach(() => {
@@ -148,4 +148,76 @@ describe('EnvironmentSelector', () => {
148148
expect(consoleSpy).toHaveBeenCalledWith('');
149149
});
150150
});
151+
152+
describe('selectGlobalEnvironments', () => {
153+
it('should create choices only from global-capable environments', async () => {
154+
mockPrompt.mockResolvedValue({ environments: ['antigravity'] });
155+
156+
await selector.selectGlobalEnvironments();
157+
158+
expect(mockPrompt).toHaveBeenCalledWith([
159+
expect.objectContaining({
160+
type: 'checkbox',
161+
name: 'environments',
162+
message: 'Select AI environments for global setup (use space to select, enter to confirm):',
163+
choices: expect.arrayContaining([
164+
expect.objectContaining({ value: 'antigravity' }),
165+
expect.objectContaining({ value: 'codex' })
166+
]),
167+
validate: expect.any(Function)
168+
})
169+
]);
170+
});
171+
172+
it('should return selected global environments', async () => {
173+
const selectedEnvs = ['antigravity', 'codex'];
174+
mockPrompt.mockResolvedValue({ environments: selectedEnvs });
175+
176+
const result = await selector.selectGlobalEnvironments();
177+
178+
expect(result).toEqual(selectedEnvs);
179+
});
180+
181+
it('should validate that at least one environment is selected', async () => {
182+
mockPrompt.mockResolvedValue({ environments: [] });
183+
184+
const result = await selector.selectGlobalEnvironments();
185+
186+
expect(result).toEqual([]);
187+
188+
const callArgs = mockPrompt.mock.calls[0][0];
189+
const validateFn = callArgs[0].validate;
190+
191+
expect(validateFn([])).toBe('Please select at least one environment.');
192+
expect(validateFn(['antigravity'])).toBe(true);
193+
});
194+
195+
it('should not include non-global environments in choices', async () => {
196+
mockPrompt.mockResolvedValue({ environments: ['antigravity'] });
197+
198+
await selector.selectGlobalEnvironments();
199+
200+
const callArgs = mockPrompt.mock.calls[0][0];
201+
const choices = callArgs[0].choices;
202+
const choiceValues = choices.map((c: any) => c.value);
203+
204+
// Should only have Antigravity and Codex
205+
expect(choiceValues).toContain('antigravity');
206+
expect(choiceValues).toContain('codex');
207+
expect(choiceValues).not.toContain('cursor');
208+
expect(choiceValues).not.toContain('claude');
209+
expect(choiceValues).not.toContain('github');
210+
});
211+
212+
it('should show exactly 2 choices (Antigravity and Codex)', async () => {
213+
mockPrompt.mockResolvedValue({ environments: [] });
214+
215+
await selector.selectGlobalEnvironments();
216+
217+
const callArgs = mockPrompt.mock.calls[0][0];
218+
const choices = callArgs[0].choices;
219+
220+
expect(choices).toHaveLength(2);
221+
});
222+
});
151223
});

0 commit comments

Comments
 (0)