moicle 2.0.0 → 2.2.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.
- package/README.md +20 -9
- package/assets/architecture/_shared/severity-levels.md +34 -0
- package/assets/architecture/_shared/stack-detection.md +34 -0
- package/assets/commands/marketing.md +7 -7
- package/assets/skills/docs/sync/SKILL.md +159 -519
- package/assets/skills/docs/write/SKILL.md +89 -186
- package/assets/skills/feature/new/SKILL.md +152 -192
- package/assets/skills/feature/refactor/SKILL.md +152 -233
- package/assets/skills/fix/hotfix/SKILL.md +139 -305
- package/assets/skills/fix/incident/SKILL.md +107 -19
- package/assets/skills/fix/pr-comment/SKILL.md +98 -224
- package/assets/skills/fix/root-cause/SKILL.md +161 -104
- package/assets/skills/{docs → marketing}/content/SKILL.md +4 -4
- package/assets/skills/marketing/logo/SKILL.md +252 -0
- package/assets/skills/marketing/seo-blog/SKILL.md +367 -0
- package/assets/skills/marketing/video/SKILL.md +258 -0
- package/assets/skills/research/onboarding/SKILL.md +127 -510
- package/assets/skills/research/spike/SKILL.md +128 -436
- package/assets/skills/research/web/SKILL.md +124 -83
- package/assets/skills/review/architect/SKILL.md +157 -306
- package/assets/skills/review/branch/SKILL.md +153 -208
- package/assets/skills/review/pr/SKILL.md +129 -519
- package/assets/skills/review/tdd/SKILL.md +108 -69
- package/bin/cli.js +2 -2
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +176 -8
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +31 -1
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +30 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +64 -8
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/utils/symlink.d.ts +1 -0
- package/dist/utils/symlink.d.ts.map +1 -1
- package/dist/utils/symlink.js +8 -6
- package/dist/utils/symlink.js.map +1 -1
- package/package.json +1 -1
- package/assets/skills/docs/logo/SKILL.md +0 -492
- package/assets/skills/docs/video/SKILL.md +0 -666
|
@@ -5,17 +5,25 @@ description: Test-Driven Development workflow. Use when doing TDD, writing tests
|
|
|
5
5
|
|
|
6
6
|
# Test-Driven Development (TDD) Workflow
|
|
7
7
|
|
|
8
|
-
Red-Green-Refactor cycle: write failing test → write minimal code to pass → refactor while
|
|
8
|
+
Red-Green-Refactor cycle: write failing test → write minimal code to pass → refactor while green.
|
|
9
9
|
|
|
10
10
|
## When to use this skill
|
|
11
11
|
|
|
12
|
-
- ✅
|
|
13
|
-
- ✅ Fixing a bug —
|
|
12
|
+
- ✅ Logic with clear input → output behavior (parsers, validators, business rules, usecases)
|
|
13
|
+
- ✅ Fixing a bug — failing test first, then fix (regression guard)
|
|
14
14
|
- ✅ Refactoring critical code where you need a safety net
|
|
15
15
|
- ❌ UI prototyping / visual tweaks → manual is faster
|
|
16
|
-
- ❌ Exploratory spike → use `/research:spike` (throwaway
|
|
16
|
+
- ❌ Exploratory spike → use `/research:spike` (throwaway, no tests)
|
|
17
17
|
- ❌ One-line config change → just change it
|
|
18
18
|
|
|
19
|
+
## Read Architecture First
|
|
20
|
+
|
|
21
|
+
Detect stack via `~/.claude/architecture/_shared/stack-detection.md`. Architecture doc tells you:
|
|
22
|
+
- Test file location convention (`_test.go` next to source, `__tests__/`, `tests/Feature/`, etc.)
|
|
23
|
+
- Test framework (Go `testing`, Jest, Pest, `flutter_test`)
|
|
24
|
+
- Mocking pattern for ports
|
|
25
|
+
- AAA layout convention
|
|
26
|
+
|
|
19
27
|
---
|
|
20
28
|
|
|
21
29
|
## The TDD Cycle
|
|
@@ -29,62 +37,54 @@ Red-Green-Refactor cycle: write failing test → write minimal code to pass →
|
|
|
29
37
|
next requirement
|
|
30
38
|
```
|
|
31
39
|
|
|
32
|
-
## Read Architecture First
|
|
33
|
-
|
|
34
|
-
Read the stack architecture doc for:
|
|
35
|
-
- Test file location convention (e.g., `_test.go` next to source, `__tests__/` for Jest)
|
|
36
|
-
- Test framework (Go testing, Jest, Pest, Flutter test)
|
|
37
|
-
- Mocking patterns for ports (interfaces)
|
|
38
|
-
- AAA (Arrange-Act-Assert) layout
|
|
39
|
-
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
-
## Phase 1: RED —
|
|
42
|
+
## Phase 1: RED — failing test
|
|
43
43
|
|
|
44
|
-
**Goal:** describe
|
|
44
|
+
**Goal:** describe wanted behavior with a test that fails because the code doesn't exist yet.
|
|
45
45
|
|
|
46
46
|
### Steps
|
|
47
|
-
1. Pick ONE small requirement (
|
|
48
|
-
2.
|
|
49
|
-
3. Arrange:
|
|
47
|
+
1. Pick ONE small requirement (smallest verifiable behavior)
|
|
48
|
+
2. Name the test as a sentence describing behavior
|
|
49
|
+
3. Arrange: inputs + mocks
|
|
50
50
|
4. Act: call the method
|
|
51
|
-
5. Assert: verify
|
|
52
|
-
6. Run
|
|
51
|
+
5. Assert: verify output / state / interaction
|
|
52
|
+
6. Run — it MUST fail (compile error or assertion fail). If it passes, the test is wrong.
|
|
53
53
|
|
|
54
|
-
### Test
|
|
55
|
-
- `should_<
|
|
54
|
+
### Test naming
|
|
55
|
+
- `should_<behavior>_when_<condition>` (snake_case for Go / Python)
|
|
56
56
|
- `it("returns X when Y", ...)` (Jest / Mocha)
|
|
57
|
-
- `test_<behavior>_<condition>` (PHPUnit)
|
|
57
|
+
- `test_<behavior>_<condition>` (PHPUnit / Pest)
|
|
58
58
|
|
|
59
59
|
### AAA pattern
|
|
60
60
|
```
|
|
61
61
|
// Arrange — set up inputs and mocks
|
|
62
|
-
// Act — call
|
|
62
|
+
// Act — call function under test
|
|
63
63
|
// Assert — verify result / state / interactions
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
### Gate
|
|
67
|
-
- [ ] Test fails for the RIGHT reason (
|
|
68
|
-
- [ ]
|
|
69
|
-
- [ ]
|
|
67
|
+
- [ ] Test fails for the RIGHT reason (not a typo / missing import)
|
|
68
|
+
- [ ] Name describes behavior, not implementation
|
|
69
|
+
- [ ] One behavior per test
|
|
70
70
|
|
|
71
71
|
---
|
|
72
72
|
|
|
73
|
-
## Phase 2: GREEN —
|
|
73
|
+
## Phase 2: GREEN — minimal code to pass
|
|
74
74
|
|
|
75
|
-
**Goal:**
|
|
75
|
+
**Goal:** smallest amount of code to pass. **Resist** "doing it properly" — refactor is next.
|
|
76
76
|
|
|
77
77
|
### Rules
|
|
78
|
-
- **Minimal** means: hardcode return values if the test allows it;
|
|
78
|
+
- **Minimal** means: hardcode return values if the test allows it; triangulate with more tests
|
|
79
79
|
- Don't add code not required by a test
|
|
80
80
|
- Don't add error handling unless a test requires it
|
|
81
|
-
- Don't generalize until 3+ tests force
|
|
81
|
+
- Don't generalize until 3+ tests force it
|
|
82
82
|
|
|
83
83
|
### Steps
|
|
84
|
-
1.
|
|
85
|
-
2. Write just enough code to
|
|
86
|
-
3. Run all tests — all must
|
|
87
|
-
4. If
|
|
84
|
+
1. Confirm RED
|
|
85
|
+
2. Write just enough code to pass
|
|
86
|
+
3. Run all tests — all must stay green
|
|
87
|
+
4. If others broke, you're not minimal — revert + try smaller
|
|
88
88
|
|
|
89
89
|
### Gate
|
|
90
90
|
- [ ] New test passes
|
|
@@ -93,31 +93,31 @@ Read the stack architecture doc for:
|
|
|
93
93
|
|
|
94
94
|
---
|
|
95
95
|
|
|
96
|
-
## Phase 3: REFACTOR —
|
|
96
|
+
## Phase 3: REFACTOR — clean up while green
|
|
97
97
|
|
|
98
|
-
**Goal:** improve structure without changing behavior. Tests
|
|
98
|
+
**Goal:** improve structure without changing behavior. Tests stay green throughout.
|
|
99
99
|
|
|
100
100
|
### What to refactor
|
|
101
101
|
- **Duplication** — extract function / method / class
|
|
102
|
-
- **Long methods** — split when one
|
|
103
|
-
- **Bad names** — rename to reveal intent (most underrated refactor)
|
|
102
|
+
- **Long methods** — split when one does multiple things
|
|
103
|
+
- **Bad names** — rename to reveal intent (the most underrated refactor)
|
|
104
104
|
- **Dead branches** — remove code no test exercises
|
|
105
105
|
- **Coupling** — break dependencies that make tests painful
|
|
106
106
|
|
|
107
107
|
### Rules
|
|
108
|
-
- **Run tests after every change** — green is
|
|
108
|
+
- **Run tests after every change** — green is your safety net
|
|
109
109
|
- **One refactor at a time** — extract method, run tests, rename, run tests, …
|
|
110
|
-
- **No new behavior** — if a refactor needs a new test,
|
|
110
|
+
- **No new behavior** — if a refactor needs a new test, go back to RED
|
|
111
111
|
|
|
112
112
|
### When NOT to refactor
|
|
113
|
-
- Tests are flaky
|
|
114
|
-
-
|
|
115
|
-
-
|
|
113
|
+
- Tests are flaky → fix flakiness first
|
|
114
|
+
- Under time pressure → skip, leave a `// TODO: refactor` linked to a follow-up
|
|
115
|
+
- Code about to be deleted → don't polish trash
|
|
116
116
|
|
|
117
117
|
### Gate
|
|
118
118
|
- [ ] All tests still green
|
|
119
119
|
- [ ] No new public API added
|
|
120
|
-
- [ ] Code
|
|
120
|
+
- [ ] Code easier to read than before
|
|
121
121
|
|
|
122
122
|
---
|
|
123
123
|
|
|
@@ -128,14 +128,51 @@ Read the stack architecture doc for:
|
|
|
128
128
|
| Value Object | Unit (pure) | None | `Money.add()` |
|
|
129
129
|
| Entity | Unit (pure) | None | `Order.cancel()` raises event |
|
|
130
130
|
| UseCase | Unit | Mock ports | `CreateOrder` calls `OrderStore.save()` |
|
|
131
|
+
| Service | Unit | Mock usecase | Delegates correctly |
|
|
131
132
|
| Handler / Controller | Integration | Real router, mock service | `POST /orders` returns 201 |
|
|
132
133
|
| Infrastructure | Integration | Real DB / testcontainers | `OrderRepository.save()` persists |
|
|
133
134
|
| Listener | Unit | Mock infra | `on_order_created` sends email |
|
|
135
|
+
| API contract (cross-service) | Contract test | Real / sandboxed external | OpenAPI / Pact / Wiremock |
|
|
136
|
+
|
|
137
|
+
### Mock vs real — decision table
|
|
138
|
+
|
|
139
|
+
| Dependency type | Use real | Use stub / fake | Use mock |
|
|
140
|
+
|-----------------|----------|-----------------|----------|
|
|
141
|
+
| Value objects, entities (pure) | ✅ always | — | never (brittle) |
|
|
142
|
+
| Stdlib (time, fs, env) | mostly — use clock / fs abstraction in domain | for determinism | rarely |
|
|
143
|
+
| Repository / port | testcontainers in infra tests | in-memory fake for usecase tests | only when verifying call args |
|
|
144
|
+
| External HTTP API | sandbox URL in integration | wiremock / msw for predictable cases | for failure-mode tests |
|
|
145
|
+
| Auth / session | real in integration | fake user in unit | rarely |
|
|
146
|
+
| Time | clock abstraction | fixed clock in tests | rarely |
|
|
147
|
+
|
|
148
|
+
**Stub vs fake vs mock:**
|
|
149
|
+
- **Stub** — returns canned values (`returnsBalance(100)`)
|
|
150
|
+
- **Fake** — working in-memory implementation (`InMemoryOrderStore`)
|
|
151
|
+
- **Mock** — records calls + verifies them (`expect(store.save).toHaveBeenCalledWith(...)`)
|
|
134
152
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
-
|
|
138
|
-
|
|
153
|
+
Prefer **fake** for repository tests (closest to real behavior). Use **mock** only when call args are the assertion.
|
|
154
|
+
|
|
155
|
+
### Property-based testing
|
|
156
|
+
|
|
157
|
+
For pure logic with many edge cases (parsers, math, encoders), use property-based tests:
|
|
158
|
+
|
|
159
|
+
- Go: `testing/quick`, `gopter`
|
|
160
|
+
- TS / JS: `fast-check`
|
|
161
|
+
- Python: `hypothesis`
|
|
162
|
+
- Dart: `glados`
|
|
163
|
+
|
|
164
|
+
Pattern: "for all valid input X, property P holds." Generates 100s of cases including edge cases you wouldn't think of.
|
|
165
|
+
|
|
166
|
+
Example (TS, fast-check):
|
|
167
|
+
```ts
|
|
168
|
+
test("reverse twice = identity", () => {
|
|
169
|
+
fc.assert(fc.property(fc.array(fc.string()), (arr) => {
|
|
170
|
+
expect(reverse(reverse(arr))).toEqual(arr);
|
|
171
|
+
}));
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Use it for: serializers, parsers, math, sorting, encoding/decoding. Not for: business workflows.
|
|
139
176
|
|
|
140
177
|
---
|
|
141
178
|
|
|
@@ -143,12 +180,14 @@ Read the stack architecture doc for:
|
|
|
143
180
|
|
|
144
181
|
| Mistake | Why it's bad | Fix |
|
|
145
182
|
|---------|--------------|-----|
|
|
146
|
-
| Writing tests AFTER the code |
|
|
147
|
-
| Testing implementation, not behavior | Refactor breaks tests | Assert
|
|
148
|
-
| One test
|
|
149
|
-
| Mocking value objects | Brittle, no
|
|
150
|
-
| Skipping
|
|
183
|
+
| Writing tests AFTER the code | Tests what's written, not what's needed | Always RED first |
|
|
184
|
+
| Testing implementation, not behavior | Refactor breaks tests | Assert outputs / observable state |
|
|
185
|
+
| One test, many behaviors | Failure message unclear | One behavior per test |
|
|
186
|
+
| Mocking value objects | Brittle, no benefit | Use real — they're pure |
|
|
187
|
+
| Skipping REFACTOR | Tech debt accumulates | It's a phase, not optional |
|
|
151
188
|
| Test depends on order | Flaky | Each test sets up its own state |
|
|
189
|
+
| Slow tests (>1s each) | People stop running them | Move to integration tier; keep unit <100ms |
|
|
190
|
+
| 100% coverage chase | Forces tests for trivial code | Aim for high-value coverage on domain |
|
|
152
191
|
|
|
153
192
|
---
|
|
154
193
|
|
|
@@ -157,30 +196,32 @@ Read the stack architecture doc for:
|
|
|
157
196
|
```markdown
|
|
158
197
|
## TDD Cycle Complete: {feature}
|
|
159
198
|
|
|
160
|
-
### Cycles
|
|
161
|
-
| # | RED behavior | GREEN | REFACTOR |
|
|
162
|
-
|
|
163
|
-
| 1 |
|
|
164
|
-
| 2 |
|
|
199
|
+
### Cycles
|
|
200
|
+
| # | RED (behavior) | GREEN (code added) | REFACTOR |
|
|
201
|
+
|---|----------------|--------------------|----------|
|
|
202
|
+
| 1 | rejects negative amount | guard in constructor | extracted validator |
|
|
203
|
+
| 2 | transitions PENDING → ACTIVE | added transition method | — |
|
|
165
204
|
|
|
166
205
|
### Test Coverage
|
|
167
206
|
- {N} tests, all passing
|
|
168
|
-
-
|
|
207
|
+
- Domain coverage: {%} (target ≥80%)
|
|
169
208
|
|
|
170
209
|
### Files
|
|
171
210
|
- Tests: {paths}
|
|
172
|
-
- Code:
|
|
211
|
+
- Code: {paths}
|
|
173
212
|
```
|
|
174
213
|
|
|
175
214
|
---
|
|
176
215
|
|
|
177
216
|
## Hard Rules
|
|
178
217
|
|
|
179
|
-
- **RED first, always** —
|
|
180
|
-
- **Minimal GREEN** — hardcode if
|
|
218
|
+
- **RED first, always** — no production code without a failing test driving it
|
|
219
|
+
- **Minimal GREEN** — hardcode if test allows; triangulate later
|
|
181
220
|
- **REFACTOR is a phase, not optional**
|
|
182
221
|
- **All tests green at all times** (except briefly during RED)
|
|
183
222
|
- **One behavior per test**, named after the behavior
|
|
223
|
+
- **Unit tests <100ms each** — slow tests don't get run
|
|
224
|
+
- **Test behavior, not implementation** — refactor must not break tests
|
|
184
225
|
|
|
185
226
|
---
|
|
186
227
|
|
|
@@ -188,19 +229,17 @@ Read the stack architecture doc for:
|
|
|
188
229
|
|
|
189
230
|
| When | Use |
|
|
190
231
|
|------|-----|
|
|
191
|
-
| Building
|
|
232
|
+
| Building feature from scratch (with TDD inside) | `/feature:new` + this skill |
|
|
192
233
|
| Adding regression test for a bug | `/fix:hotfix` or `/fix:root-cause` → then this skill |
|
|
193
|
-
| Refactoring untested legacy code (add tests first) |
|
|
234
|
+
| Refactoring untested legacy code | `/feature:refactor` (add tests first) |
|
|
194
235
|
| Reviewing test quality on a PR | `/review:pr` |
|
|
195
236
|
|
|
196
|
-
---
|
|
197
|
-
|
|
198
237
|
## Recommended Agents
|
|
199
238
|
|
|
200
239
|
| Phase | Agent | Purpose |
|
|
201
240
|
|-------|-------|---------|
|
|
202
|
-
| RED | `@test-writer` |
|
|
241
|
+
| RED | `@test-writer` | Failing tests first |
|
|
203
242
|
| GREEN | Stack-specific dev agent | Minimal implementation |
|
|
204
|
-
| REFACTOR | `@refactor` |
|
|
243
|
+
| REFACTOR | `@refactor` | Patterns + cleanup |
|
|
205
244
|
| REFACTOR | `@code-reviewer` | Review refactored code |
|
|
206
|
-
| REFACTOR | `@perf-optimizer` |
|
|
245
|
+
| REFACTOR | `@perf-optimizer` | Perf tweaks (only with benchmarks) |
|
package/bin/cli.js
CHANGED
|
@@ -46,7 +46,7 @@ program
|
|
|
46
46
|
.description('List installed agents, commands, and skills')
|
|
47
47
|
.option('-g, --global', 'List global installations')
|
|
48
48
|
.option('-p, --project', 'List project installations')
|
|
49
|
-
.option('-t, --target <editor>', 'Target editor (claude, codex)')
|
|
49
|
+
.option('-t, --target <editor>', 'Target editor (claude, codex, antigravity)')
|
|
50
50
|
.action(listCommand);
|
|
51
51
|
|
|
52
52
|
program
|
|
@@ -75,7 +75,7 @@ program
|
|
|
75
75
|
.description('Show enabled/disabled status of all items')
|
|
76
76
|
.option('-g, --global', 'Show global status')
|
|
77
77
|
.option('-p, --project', 'Show project status')
|
|
78
|
-
.option('-t, --target <editor>', 'Target editor (claude, codex)')
|
|
78
|
+
.option('-t, --target <editor>', 'Target editor (claude, codex, antigravity)')
|
|
79
79
|
.action(statusCommand);
|
|
80
80
|
|
|
81
81
|
program
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAmC,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAmC,MAAM,aAAa,CAAC;AAkrBnF,eAAO,MAAM,cAAc,GAAU,SAAS,cAAc,KAAG,OAAO,CAAC,IAAI,CAwI1E,CAAC"}
|
package/dist/commands/install.js
CHANGED
|
@@ -2,7 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import fs from 'fs';
|
|
5
|
-
import { ASSETS_DIR, EDITOR_CONFIGS, isSymlinkSupported, ensureDir, createSymlink, copyFile, copyDir, getAgentsDir, getCommandsDir, getSkillsDir, getArchitectureDir, getClaudeDir, getCodexDir, getEditorDir, getEditorConfig, getFiles, getDirs, mergeAgentsToFile, } from '../utils/symlink.js';
|
|
5
|
+
import { ASSETS_DIR, EDITOR_CONFIGS, isSymlinkSupported, ensureDir, createSymlink, copyFile, copyDir, getAgentsDir, getCommandsDir, getSkillsDir, getArchitectureDir, getClaudeDir, getCodexDir, getAntigravityDir, getEditorDir, getEditorConfig, getFiles, getDirs, mergeAgentsToFile, } from '../utils/symlink.js';
|
|
6
6
|
import { addTarget } from '../utils/config.js';
|
|
7
7
|
const printHeader = () => {
|
|
8
8
|
console.log('');
|
|
@@ -133,6 +133,13 @@ const rewriteClaudePaths = (content, target) => {
|
|
|
133
133
|
if (target === 'claude') {
|
|
134
134
|
return content;
|
|
135
135
|
}
|
|
136
|
+
if (target === 'antigravity') {
|
|
137
|
+
return content
|
|
138
|
+
.replace(/~\/\.claude\//g, '~/.gemini/')
|
|
139
|
+
.replace(/\.claude\//g, '.gemini/')
|
|
140
|
+
.replace(/Claude Code/g, 'Antigravity')
|
|
141
|
+
.replace(/CLAUDE\.md/g, 'GEMINI.md');
|
|
142
|
+
}
|
|
136
143
|
return content
|
|
137
144
|
.replace(/~\/\.claude\//g, '~/.codex/')
|
|
138
145
|
.replace(/\.claude\//g, '.codex/')
|
|
@@ -297,6 +304,150 @@ const installCodexScope = async (scope) => {
|
|
|
297
304
|
console.log('');
|
|
298
305
|
console.log(chalk.green(`✓ ${label} Codex installation complete!`));
|
|
299
306
|
};
|
|
307
|
+
const ensureAntigravitySkillDir = (baseDir, name) => {
|
|
308
|
+
const skillDir = path.join(baseDir, name);
|
|
309
|
+
ensureDir(skillDir);
|
|
310
|
+
return skillDir;
|
|
311
|
+
};
|
|
312
|
+
const installAntigravitySkillFolder = (sourceDir, targetSkillsDir) => {
|
|
313
|
+
const skillName = path.basename(sourceDir);
|
|
314
|
+
const targetDir = ensureAntigravitySkillDir(targetSkillsDir, skillName);
|
|
315
|
+
const sourceFiles = getFiles(sourceDir, 8);
|
|
316
|
+
let status = 'created';
|
|
317
|
+
for (const file of sourceFiles) {
|
|
318
|
+
const relativePath = path.relative(sourceDir, file);
|
|
319
|
+
const targetFile = path.join(targetDir, relativePath);
|
|
320
|
+
ensureDir(path.dirname(targetFile));
|
|
321
|
+
const content = rewriteClaudePaths(fs.readFileSync(file, 'utf-8'), 'antigravity');
|
|
322
|
+
const existed = fs.existsSync(targetFile);
|
|
323
|
+
if (existed && fs.readFileSync(targetFile, 'utf-8') === content) {
|
|
324
|
+
status = status === 'created' ? 'exists' : status;
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
fs.writeFileSync(targetFile, content);
|
|
328
|
+
if (existed) {
|
|
329
|
+
status = 'updated';
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return { status, name: skillName };
|
|
333
|
+
};
|
|
334
|
+
const buildGeneratedAntigravitySkill = (name, description, body) => `---
|
|
335
|
+
name: ${name}
|
|
336
|
+
description: ${description}
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
${body}
|
|
340
|
+
`;
|
|
341
|
+
const installGeneratedAntigravitySkill = (targetSkillsDir, name, description, body) => {
|
|
342
|
+
const skillDir = ensureAntigravitySkillDir(targetSkillsDir, name);
|
|
343
|
+
const targetFile = path.join(skillDir, 'SKILL.md');
|
|
344
|
+
const content = buildGeneratedAntigravitySkill(name, description, rewriteClaudePaths(body, 'antigravity'));
|
|
345
|
+
if (fs.existsSync(targetFile)) {
|
|
346
|
+
if (fs.readFileSync(targetFile, 'utf-8') === content) {
|
|
347
|
+
return { status: 'exists', name };
|
|
348
|
+
}
|
|
349
|
+
fs.writeFileSync(targetFile, content);
|
|
350
|
+
return { status: 'updated', name };
|
|
351
|
+
}
|
|
352
|
+
fs.writeFileSync(targetFile, content);
|
|
353
|
+
return { status: 'created', name };
|
|
354
|
+
};
|
|
355
|
+
const installDirectAntigravitySkill = (targetSkillsDir, name, content) => {
|
|
356
|
+
const skillDir = ensureAntigravitySkillDir(targetSkillsDir, name);
|
|
357
|
+
const targetFile = path.join(skillDir, 'SKILL.md');
|
|
358
|
+
const rewritten = rewriteClaudePaths(content, 'antigravity');
|
|
359
|
+
if (fs.existsSync(targetFile)) {
|
|
360
|
+
if (fs.readFileSync(targetFile, 'utf-8') === rewritten) {
|
|
361
|
+
return { status: 'exists', name };
|
|
362
|
+
}
|
|
363
|
+
fs.writeFileSync(targetFile, rewritten);
|
|
364
|
+
return { status: 'updated', name };
|
|
365
|
+
}
|
|
366
|
+
fs.writeFileSync(targetFile, rewritten);
|
|
367
|
+
return { status: 'created', name };
|
|
368
|
+
};
|
|
369
|
+
const installAntigravityArchitecture = (targetDir) => {
|
|
370
|
+
const results = [];
|
|
371
|
+
const archDir = path.join(ASSETS_DIR, 'architecture');
|
|
372
|
+
const targetArchDir = path.join(targetDir, 'architecture');
|
|
373
|
+
ensureDir(targetArchDir);
|
|
374
|
+
if (!fs.existsSync(archDir)) {
|
|
375
|
+
return results;
|
|
376
|
+
}
|
|
377
|
+
for (const file of getFiles(archDir)) {
|
|
378
|
+
const targetFile = path.join(targetArchDir, path.basename(file));
|
|
379
|
+
const content = rewriteClaudePaths(fs.readFileSync(file, 'utf-8'), 'antigravity');
|
|
380
|
+
if (fs.existsSync(targetFile)) {
|
|
381
|
+
if (fs.readFileSync(targetFile, 'utf-8') === content) {
|
|
382
|
+
results.push({ status: 'exists', name: path.basename(file) });
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
fs.writeFileSync(targetFile, content);
|
|
386
|
+
results.push({ status: 'updated', name: path.basename(file) });
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
fs.writeFileSync(targetFile, content);
|
|
390
|
+
results.push({ status: 'created', name: path.basename(file) });
|
|
391
|
+
}
|
|
392
|
+
return results;
|
|
393
|
+
};
|
|
394
|
+
const installAntigravitySkills = (targetDir) => {
|
|
395
|
+
const results = [];
|
|
396
|
+
const targetSkillsDir = path.join(targetDir, 'skills');
|
|
397
|
+
ensureDir(targetSkillsDir);
|
|
398
|
+
const skillsDir = path.join(ASSETS_DIR, 'skills');
|
|
399
|
+
if (fs.existsSync(skillsDir)) {
|
|
400
|
+
for (const dir of getDirs(skillsDir)) {
|
|
401
|
+
results.push(installAntigravitySkillFolder(dir, targetSkillsDir));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const commandsDir = path.join(ASSETS_DIR, 'commands');
|
|
405
|
+
if (fs.existsSync(commandsDir)) {
|
|
406
|
+
for (const file of getFiles(commandsDir)) {
|
|
407
|
+
const name = path.basename(file, '.md');
|
|
408
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
409
|
+
results.push(installDirectAntigravitySkill(targetSkillsDir, name, content));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const agentDirs = ['developers', 'utilities'];
|
|
413
|
+
for (const dirName of agentDirs) {
|
|
414
|
+
const sourceDir = path.join(ASSETS_DIR, 'agents', dirName);
|
|
415
|
+
if (!fs.existsSync(sourceDir)) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
for (const file of getFiles(sourceDir)) {
|
|
419
|
+
const name = path.basename(file, '.md');
|
|
420
|
+
const rawContent = fs.readFileSync(file, 'utf-8');
|
|
421
|
+
const parsed = extractFrontmatter(rawContent);
|
|
422
|
+
const description = parsed.description
|
|
423
|
+
? parsed.description
|
|
424
|
+
: dirName === 'developers'
|
|
425
|
+
? `Imported MoiCle developer persona for ${name}. Use when the task matches this stack specialist.`
|
|
426
|
+
: `Imported MoiCle utility persona for ${name}. Use when the task matches this specialist.`;
|
|
427
|
+
results.push(installGeneratedAntigravitySkill(targetSkillsDir, name, description, parsed.body.trimStart()));
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return results;
|
|
431
|
+
};
|
|
432
|
+
const installAntigravityScope = async (scope) => {
|
|
433
|
+
const isGlobal = scope === 'global';
|
|
434
|
+
const label = isGlobal ? 'Global' : 'Project';
|
|
435
|
+
const targetPath = isGlobal ? '~/.gemini/' : `${process.cwd()}/.gemini/`;
|
|
436
|
+
console.log('');
|
|
437
|
+
console.log(chalk.cyan(`>>> ${label} Antigravity Installation`));
|
|
438
|
+
console.log(chalk.gray(` Target: ${targetPath}`));
|
|
439
|
+
console.log('');
|
|
440
|
+
const antigravityDir = getAntigravityDir(scope);
|
|
441
|
+
ensureDir(antigravityDir);
|
|
442
|
+
const archResults = installAntigravityArchitecture(antigravityDir);
|
|
443
|
+
console.log(chalk.green(` ✓ Architecture installed to ${chalk.cyan(path.join(antigravityDir, 'architecture'))}`));
|
|
444
|
+
printSummary(archResults);
|
|
445
|
+
const skillResults = installAntigravitySkills(antigravityDir);
|
|
446
|
+
console.log(chalk.green(` ✓ Antigravity skills installed to ${chalk.cyan(path.join(antigravityDir, 'skills'))}`));
|
|
447
|
+
printSummary(skillResults);
|
|
448
|
+
console.log('');
|
|
449
|
+
console.log(chalk.green(`✓ ${label} Antigravity installation complete!`));
|
|
450
|
+
};
|
|
300
451
|
const showTargetMenu = async () => {
|
|
301
452
|
const { target } = await inquirer.prompt([
|
|
302
453
|
{
|
|
@@ -306,14 +457,15 @@ const showTargetMenu = async () => {
|
|
|
306
457
|
choices: [
|
|
307
458
|
{ name: 'Claude Code', value: 'claude' },
|
|
308
459
|
{ name: 'Codex CLI', value: 'codex' },
|
|
460
|
+
{ name: 'Antigravity', value: 'antigravity' },
|
|
309
461
|
],
|
|
310
462
|
},
|
|
311
463
|
]);
|
|
312
464
|
return target;
|
|
313
465
|
};
|
|
314
466
|
const showInteractiveMenu = async (target) => {
|
|
315
|
-
const globalPath = target === 'claude' ? '~/.claude/' : '~/.codex/';
|
|
316
|
-
const projectPath = target === 'claude' ? './.claude/' : './.codex/';
|
|
467
|
+
const globalPath = target === 'claude' ? '~/.claude/' : target === 'codex' ? '~/.codex/' : '~/.gemini/';
|
|
468
|
+
const projectPath = target === 'claude' ? './.claude/' : target === 'codex' ? './.codex/' : './.gemini/';
|
|
317
469
|
const { installType } = await inquirer.prompt([
|
|
318
470
|
{
|
|
319
471
|
type: 'list',
|
|
@@ -401,7 +553,7 @@ export const installCommand = async (options) => {
|
|
|
401
553
|
}
|
|
402
554
|
for (const target of targets) {
|
|
403
555
|
addTarget(target);
|
|
404
|
-
if (target === 'claude' || target === 'codex') {
|
|
556
|
+
if (target === 'claude' || target === 'codex' || target === 'antigravity') {
|
|
405
557
|
let installType;
|
|
406
558
|
if (options.global) {
|
|
407
559
|
installType = 'global';
|
|
@@ -420,27 +572,37 @@ export const installCommand = async (options) => {
|
|
|
420
572
|
if (target === 'claude') {
|
|
421
573
|
await installScope('global', useSymlink);
|
|
422
574
|
}
|
|
423
|
-
else {
|
|
575
|
+
else if (target === 'codex') {
|
|
424
576
|
await installCodexScope('global');
|
|
425
577
|
}
|
|
578
|
+
else {
|
|
579
|
+
await installAntigravityScope('global');
|
|
580
|
+
}
|
|
426
581
|
break;
|
|
427
582
|
case 'project':
|
|
428
583
|
if (target === 'claude') {
|
|
429
584
|
await installScope('project', false);
|
|
430
585
|
}
|
|
431
|
-
else {
|
|
586
|
+
else if (target === 'codex') {
|
|
432
587
|
await installCodexScope('project');
|
|
433
588
|
}
|
|
589
|
+
else {
|
|
590
|
+
await installAntigravityScope('project');
|
|
591
|
+
}
|
|
434
592
|
break;
|
|
435
593
|
case 'all':
|
|
436
594
|
if (target === 'claude') {
|
|
437
595
|
await installScope('global', useSymlink);
|
|
438
596
|
await installScope('project', false);
|
|
439
597
|
}
|
|
440
|
-
else {
|
|
598
|
+
else if (target === 'codex') {
|
|
441
599
|
await installCodexScope('global');
|
|
442
600
|
await installCodexScope('project');
|
|
443
601
|
}
|
|
602
|
+
else {
|
|
603
|
+
await installAntigravityScope('global');
|
|
604
|
+
await installAntigravityScope('project');
|
|
605
|
+
}
|
|
444
606
|
break;
|
|
445
607
|
}
|
|
446
608
|
}
|
|
@@ -477,7 +639,13 @@ export const installCommand = async (options) => {
|
|
|
477
639
|
console.log(chalk.gray(' Restart Codex after global skill installation to pick up new skills'));
|
|
478
640
|
console.log('');
|
|
479
641
|
}
|
|
480
|
-
|
|
642
|
+
if (targets.includes('antigravity')) {
|
|
643
|
+
console.log(chalk.bold(' Antigravity:'));
|
|
644
|
+
console.log(chalk.gray(' Skills installed under ~/.gemini/skills or ./.gemini/skills'));
|
|
645
|
+
console.log(chalk.gray(' Architecture docs installed under ~/.gemini/architecture or ./.gemini/architecture'));
|
|
646
|
+
console.log('');
|
|
647
|
+
}
|
|
648
|
+
const otherTargets = targets.filter((t) => t !== 'claude' && t !== 'codex' && t !== 'antigravity');
|
|
481
649
|
if (otherTargets.length > 0) {
|
|
482
650
|
console.log(chalk.bold(' Other Editors:'));
|
|
483
651
|
for (const target of otherTargets) {
|