@kleber.mottajr/juninho 1.1.0 → 1.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 +112 -13
- package/dist/cli.js +40 -23
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -1
- package/dist/config.js.map +1 -1
- package/dist/installer.d.ts +2 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +178 -54
- package/dist/installer.js.map +1 -1
- package/dist/lint-detection.d.ts +26 -0
- package/dist/lint-detection.d.ts.map +1 -0
- package/dist/lint-detection.js +200 -0
- package/dist/lint-detection.js.map +1 -0
- package/dist/models.js +4 -4
- package/dist/models.js.map +1 -1
- package/dist/project-types.d.ts +47 -0
- package/dist/project-types.d.ts.map +1 -0
- package/dist/project-types.js +251 -0
- package/dist/project-types.js.map +1 -0
- package/dist/templates/agents.d.ts +2 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +7 -5
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/commands.d.ts.map +1 -1
- package/dist/templates/commands.js +225 -150
- package/dist/templates/commands.js.map +1 -1
- package/dist/templates/docs.d.ts +2 -1
- package/dist/templates/docs.d.ts.map +1 -1
- package/dist/templates/docs.js +61 -14
- package/dist/templates/docs.js.map +1 -1
- package/dist/templates/plugins.d.ts +2 -1
- package/dist/templates/plugins.d.ts.map +1 -1
- package/dist/templates/plugins.js +167 -102
- package/dist/templates/plugins.js.map +1 -1
- package/dist/templates/skills.d.ts +2 -1
- package/dist/templates/skills.d.ts.map +1 -1
- package/dist/templates/skills.js +708 -195
- package/dist/templates/skills.js.map +1 -1
- package/dist/templates/support-scripts.d.ts +2 -1
- package/dist/templates/support-scripts.d.ts.map +1 -1
- package/dist/templates/support-scripts.js +468 -21
- package/dist/templates/support-scripts.js.map +1 -1
- package/dist/templates/tools.d.ts +2 -1
- package/dist/templates/tools.d.ts.map +1 -1
- package/dist/templates/tools.js +315 -74
- package/dist/templates/tools.js.map +1 -1
- package/package.json +1 -1
package/dist/templates/skills.js
CHANGED
|
@@ -6,22 +6,52 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.writeSkills = writeSkills;
|
|
7
7
|
const fs_1 = require("fs");
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
-
|
|
9
|
+
const project_types_js_1 = require("../project-types.js");
|
|
10
|
+
function writeSkills(projectDir, projectType = "node-nextjs", isKotlin = false) {
|
|
10
11
|
const skillsDir = path_1.default.join(projectDir, ".opencode", "skills");
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
const config = project_types_js_1.PROJECT_TYPE_REGISTRY[projectType];
|
|
13
|
+
// Create skill directories and write only the skills for this project type
|
|
14
|
+
const skillWriters = {
|
|
15
|
+
"j.test-writing": () => testWriting(projectType, isKotlin),
|
|
16
|
+
"j.page-creation": () => PAGE_CREATION,
|
|
17
|
+
"j.api-route-creation": () => API_ROUTE_CREATION,
|
|
18
|
+
"j.server-action-creation": () => SERVER_ACTION_CREATION,
|
|
19
|
+
"j.schema-migration": () => SCHEMA_MIGRATION,
|
|
20
|
+
"j.agents-md-writing": () => AGENTS_MD_WRITING,
|
|
21
|
+
"j.domain-doc-writing": () => DOMAIN_DOC_WRITING,
|
|
22
|
+
"j.principle-doc-writing": () => PRINCIPLE_DOC_WRITING,
|
|
23
|
+
"j.shell-script-writing": () => SHELL_SCRIPT_WRITING,
|
|
24
|
+
};
|
|
25
|
+
for (const skill of config.skills) {
|
|
26
|
+
const writer = skillWriters[skill];
|
|
27
|
+
if (!writer)
|
|
28
|
+
continue;
|
|
29
|
+
const skillDir = path_1.default.join(skillsDir, skill);
|
|
30
|
+
if (!(0, fs_1.existsSync)(skillDir)) {
|
|
31
|
+
(0, fs_1.mkdirSync)(skillDir, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
(0, fs_1.writeFileSync)(path_1.default.join(skillDir, "SKILL.md"), writer());
|
|
34
|
+
}
|
|
20
35
|
}
|
|
21
|
-
// ─── Test Writing
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
36
|
+
// ─── Test Writing (parameterized by type) ──────────────────────────────────
|
|
37
|
+
function testWriting(projectType, isKotlin) {
|
|
38
|
+
switch (projectType) {
|
|
39
|
+
case "node-nextjs":
|
|
40
|
+
case "node-generic":
|
|
41
|
+
return TEST_WRITING_NODE;
|
|
42
|
+
case "python":
|
|
43
|
+
return TEST_WRITING_PYTHON;
|
|
44
|
+
case "go":
|
|
45
|
+
return TEST_WRITING_GO;
|
|
46
|
+
case "java":
|
|
47
|
+
return isKotlin ? TEST_WRITING_KOTLIN : TEST_WRITING_JAVA;
|
|
48
|
+
case "generic":
|
|
49
|
+
return TEST_WRITING_GENERIC;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const TEST_WRITING_NODE = `---
|
|
53
|
+
name: j.test-writing
|
|
54
|
+
description: Write focused unit and integration tests following project conventions
|
|
25
55
|
# Optional: uncomment to enable Playwright MCP for E2E tests
|
|
26
56
|
# mcp:
|
|
27
57
|
# playwright:
|
|
@@ -61,12 +91,12 @@ describe("ComponentName / functionName", () => {
|
|
|
61
91
|
})
|
|
62
92
|
\`\`\`
|
|
63
93
|
|
|
64
|
-
### 3. Coverage requirements
|
|
65
|
-
- Happy path: at least 1 test
|
|
66
|
-
- Error cases: test each distinct error path
|
|
67
|
-
- Edge cases: empty inputs, boundary values, null/undefined
|
|
68
|
-
- Prefer tests related to the changed files before running the full suite
|
|
69
|
-
- Do NOT test implementation details — test behavior
|
|
94
|
+
### 3. Coverage requirements
|
|
95
|
+
- Happy path: at least 1 test
|
|
96
|
+
- Error cases: test each distinct error path
|
|
97
|
+
- Edge cases: empty inputs, boundary values, null/undefined
|
|
98
|
+
- Prefer tests related to the changed files before running the full suite
|
|
99
|
+
- Do NOT test implementation details — test behavior
|
|
70
100
|
|
|
71
101
|
### 4. Mock strategy
|
|
72
102
|
- Mock external dependencies (APIs, DB, file system)
|
|
@@ -96,15 +126,501 @@ it("should handle async operation", async () => {
|
|
|
96
126
|
- \`expect.assertions(0)\` — always assert something
|
|
97
127
|
- Tests that depend on order of execution
|
|
98
128
|
`;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
129
|
+
const TEST_WRITING_PYTHON = `---
|
|
130
|
+
name: j.test-writing
|
|
131
|
+
description: Write focused unit and integration tests using pytest
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
# Skill: Test Writing (Python)
|
|
135
|
+
|
|
136
|
+
## When this skill activates
|
|
137
|
+
Writing or editing \`test_*.py\` or \`*_test.py\` files.
|
|
138
|
+
|
|
139
|
+
## Required Steps
|
|
140
|
+
|
|
141
|
+
### 1. Read the implementation first
|
|
142
|
+
Before writing any test, read the file being tested. Understand:
|
|
143
|
+
- What it does (not what you think it does)
|
|
144
|
+
- Its dependencies and side effects
|
|
145
|
+
- Error cases and edge conditions
|
|
146
|
+
|
|
147
|
+
### 2. Test structure
|
|
148
|
+
Follow the AAA pattern with pytest:
|
|
149
|
+
\`\`\`python
|
|
150
|
+
class TestFunctionName:
|
|
151
|
+
"""Tests for function_name."""
|
|
152
|
+
|
|
153
|
+
def test_happy_path(self):
|
|
154
|
+
# Arrange
|
|
155
|
+
input_data = ...
|
|
156
|
+
|
|
157
|
+
# Act
|
|
158
|
+
result = function_name(input_data)
|
|
159
|
+
|
|
160
|
+
# Assert
|
|
161
|
+
assert result == expected
|
|
162
|
+
|
|
163
|
+
def test_when_invalid_input(self):
|
|
164
|
+
with pytest.raises(ValueError, match="specific message"):
|
|
165
|
+
function_name(invalid_input)
|
|
166
|
+
\`\`\`
|
|
167
|
+
|
|
168
|
+
Or functional style:
|
|
169
|
+
\`\`\`python
|
|
170
|
+
def test_function_does_expected_thing():
|
|
171
|
+
# Arrange
|
|
172
|
+
input_data = ...
|
|
173
|
+
|
|
174
|
+
# Act
|
|
175
|
+
result = function_name(input_data)
|
|
176
|
+
|
|
177
|
+
# Assert
|
|
178
|
+
assert result == expected
|
|
179
|
+
\`\`\`
|
|
180
|
+
|
|
181
|
+
### 3. Coverage requirements
|
|
182
|
+
- Happy path: at least 1 test
|
|
183
|
+
- Error cases: test each distinct error path with \`pytest.raises\`
|
|
184
|
+
- Edge cases: empty inputs, boundary values, None
|
|
185
|
+
- Prefer tests related to the changed files before running the full suite
|
|
186
|
+
|
|
187
|
+
### 4. Mock strategy
|
|
188
|
+
- Use \`unittest.mock.patch\` or \`pytest-mock\` fixtures
|
|
189
|
+
- Mock external dependencies (APIs, DB, file system)
|
|
190
|
+
- Do NOT mock the module under test
|
|
191
|
+
\`\`\`python
|
|
192
|
+
from unittest.mock import patch, MagicMock
|
|
193
|
+
|
|
194
|
+
def test_with_mock(mocker):
|
|
195
|
+
mock_service = mocker.patch("module.ExternalService")
|
|
196
|
+
mock_service.return_value.fetch.return_value = {"data": "value"}
|
|
197
|
+
|
|
198
|
+
result = my_function()
|
|
199
|
+
assert result == expected
|
|
200
|
+
\`\`\`
|
|
201
|
+
|
|
202
|
+
### 5. Fixtures
|
|
203
|
+
Use pytest fixtures for reusable setup:
|
|
204
|
+
\`\`\`python
|
|
205
|
+
@pytest.fixture
|
|
206
|
+
def sample_user():
|
|
207
|
+
return User(name="Test", email="test@example.com")
|
|
208
|
+
|
|
209
|
+
def test_user_display(sample_user):
|
|
210
|
+
assert sample_user.display_name == "Test"
|
|
211
|
+
\`\`\`
|
|
212
|
+
|
|
213
|
+
### 6. Naming conventions
|
|
214
|
+
- File: \`test_{module}.py\` or \`{module}_test.py\`
|
|
215
|
+
- Class: \`TestClassName\`
|
|
216
|
+
- Function: \`test_{what_it_does}\` or \`test_when_{condition}_then_{outcome}\`
|
|
217
|
+
|
|
218
|
+
## Anti-patterns to avoid
|
|
219
|
+
- \`assert True\` — meaningless assertion
|
|
220
|
+
- Testing private methods (\`_method\`) directly
|
|
221
|
+
- Tests that depend on execution order
|
|
222
|
+
- Mocking the module under test
|
|
223
|
+
- Using \`time.sleep\` in tests
|
|
224
|
+
`;
|
|
225
|
+
const TEST_WRITING_GO = `---
|
|
226
|
+
name: j.test-writing
|
|
227
|
+
description: Write focused unit and integration tests using Go testing
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
# Skill: Test Writing (Go)
|
|
231
|
+
|
|
232
|
+
## When this skill activates
|
|
233
|
+
Writing or editing \`*_test.go\` files.
|
|
234
|
+
|
|
235
|
+
## Required Steps
|
|
236
|
+
|
|
237
|
+
### 1. Read the implementation first
|
|
238
|
+
Before writing any test, read the file being tested. Understand:
|
|
239
|
+
- What it does (not what you think it does)
|
|
240
|
+
- Its dependencies and side effects
|
|
241
|
+
- Error cases and edge conditions
|
|
242
|
+
|
|
243
|
+
### 2. Test structure
|
|
244
|
+
Use table-driven tests with \`t.Run\`:
|
|
245
|
+
\`\`\`go
|
|
246
|
+
func TestFunctionName(t *testing.T) {
|
|
247
|
+
tests := []struct {
|
|
248
|
+
name string
|
|
249
|
+
input InputType
|
|
250
|
+
expected OutputType
|
|
251
|
+
wantErr bool
|
|
252
|
+
}{
|
|
253
|
+
{
|
|
254
|
+
name: "happy path",
|
|
255
|
+
input: validInput,
|
|
256
|
+
expected: expectedOutput,
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: "invalid input returns error",
|
|
260
|
+
input: invalidInput,
|
|
261
|
+
wantErr: true,
|
|
262
|
+
},
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
for _, tt := range tests {
|
|
266
|
+
t.Run(tt.name, func(t *testing.T) {
|
|
267
|
+
result, err := FunctionName(tt.input)
|
|
268
|
+
|
|
269
|
+
if tt.wantErr {
|
|
270
|
+
if err == nil {
|
|
271
|
+
t.Fatal("expected error, got nil")
|
|
272
|
+
}
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
if err != nil {
|
|
276
|
+
t.Fatalf("unexpected error: %v", err)
|
|
277
|
+
}
|
|
278
|
+
if result != tt.expected {
|
|
279
|
+
t.Errorf("got %v, want %v", result, tt.expected)
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
\`\`\`
|
|
285
|
+
|
|
286
|
+
### 3. Coverage requirements
|
|
287
|
+
- Happy path: at least 1 test case
|
|
288
|
+
- Error cases: test each distinct error path
|
|
289
|
+
- Edge cases: nil inputs, zero values, empty slices
|
|
290
|
+
- Use \`t.Parallel()\` for independent subtests
|
|
291
|
+
|
|
292
|
+
### 4. Mock strategy
|
|
293
|
+
- Use interfaces for dependency injection
|
|
294
|
+
- Create mock implementations in test files
|
|
295
|
+
- For HTTP, use \`httptest.NewServer\`
|
|
296
|
+
- For DB, use test containers or in-memory implementations
|
|
297
|
+
|
|
298
|
+
### 5. Test helpers
|
|
299
|
+
\`\`\`go
|
|
300
|
+
func setupTestDB(t *testing.T) *DB {
|
|
301
|
+
t.Helper()
|
|
302
|
+
db := NewTestDB()
|
|
303
|
+
t.Cleanup(func() { db.Close() })
|
|
304
|
+
return db
|
|
305
|
+
}
|
|
306
|
+
\`\`\`
|
|
307
|
+
|
|
308
|
+
### 6. Naming conventions
|
|
309
|
+
- File: \`{package}_test.go\` in the same package
|
|
310
|
+
- Function: \`TestFunctionName\`, \`TestType_Method\`
|
|
311
|
+
- Subtests: descriptive names in \`t.Run("when condition", ...)\`
|
|
312
|
+
|
|
313
|
+
## Anti-patterns to avoid
|
|
314
|
+
- Using \`t.Log\` instead of assertions
|
|
315
|
+
- Testing unexported functions from external packages
|
|
316
|
+
- Using global state between tests
|
|
317
|
+
- Not using \`t.Helper()\` in helper functions
|
|
318
|
+
`;
|
|
319
|
+
const TEST_WRITING_JAVA = `---
|
|
320
|
+
name: j.test-writing
|
|
321
|
+
description: Write focused unit and integration tests using JUnit 5
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
# Skill: Test Writing (Java/JUnit 5)
|
|
325
|
+
|
|
326
|
+
## When this skill activates
|
|
327
|
+
Writing or editing \`*Test.java\`, \`*Tests.java\`, or \`*IT.java\` files.
|
|
328
|
+
|
|
329
|
+
## Required Steps
|
|
330
|
+
|
|
331
|
+
### 1. Read the implementation first
|
|
332
|
+
Before writing any test, read the file being tested. Understand:
|
|
333
|
+
- What it does (not what you think it does)
|
|
334
|
+
- Its dependencies and side effects
|
|
335
|
+
- Error cases and edge conditions
|
|
336
|
+
|
|
337
|
+
### 2. Test structure
|
|
338
|
+
Follow the AAA pattern with JUnit 5:
|
|
339
|
+
\`\`\`java
|
|
340
|
+
@DisplayName("FunctionName")
|
|
341
|
+
class FunctionNameTest {
|
|
342
|
+
|
|
343
|
+
@Nested
|
|
344
|
+
@DisplayName("when valid input")
|
|
345
|
+
class WhenValidInput {
|
|
346
|
+
|
|
347
|
+
@Test
|
|
348
|
+
@DisplayName("should return expected result")
|
|
349
|
+
void shouldReturnExpectedResult() {
|
|
350
|
+
// Arrange
|
|
351
|
+
var input = createValidInput();
|
|
352
|
+
|
|
353
|
+
// Act
|
|
354
|
+
var result = functionName(input);
|
|
355
|
+
|
|
356
|
+
// Assert
|
|
357
|
+
assertThat(result).isEqualTo(expected);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
@Nested
|
|
362
|
+
@DisplayName("when invalid input")
|
|
363
|
+
class WhenInvalidInput {
|
|
364
|
+
|
|
365
|
+
@Test
|
|
366
|
+
@DisplayName("should throw IllegalArgumentException")
|
|
367
|
+
void shouldThrowException() {
|
|
368
|
+
assertThatThrownBy(() -> functionName(invalidInput))
|
|
369
|
+
.isInstanceOf(IllegalArgumentException.class)
|
|
370
|
+
.hasMessageContaining("specific message");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
\`\`\`
|
|
375
|
+
|
|
376
|
+
### 3. Coverage requirements
|
|
377
|
+
- Happy path: at least 1 test
|
|
378
|
+
- Error cases: test each distinct error path
|
|
379
|
+
- Edge cases: null inputs, empty collections, boundary values
|
|
380
|
+
- Use \`@ParameterizedTest\` for multiple inputs
|
|
381
|
+
|
|
382
|
+
### 4. Mock strategy (Mockito)
|
|
383
|
+
- Mock external dependencies
|
|
384
|
+
- Do NOT mock the class under test
|
|
385
|
+
\`\`\`java
|
|
386
|
+
@ExtendWith(MockitoExtension.class)
|
|
387
|
+
class ServiceTest {
|
|
388
|
+
|
|
389
|
+
@Mock
|
|
390
|
+
private Repository repository;
|
|
391
|
+
|
|
392
|
+
@InjectMocks
|
|
393
|
+
private Service service;
|
|
394
|
+
|
|
395
|
+
@Test
|
|
396
|
+
void shouldReturnData() {
|
|
397
|
+
when(repository.findById(1L)).thenReturn(Optional.of(entity));
|
|
398
|
+
|
|
399
|
+
var result = service.getData(1L);
|
|
400
|
+
|
|
401
|
+
assertThat(result).isNotNull();
|
|
402
|
+
verify(repository).findById(1L);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
\`\`\`
|
|
406
|
+
|
|
407
|
+
### 5. Naming conventions
|
|
408
|
+
- File: \`{ClassName}Test.java\` in \`src/test/java/\`
|
|
409
|
+
- Class: \`{ClassName}Test\`
|
|
410
|
+
- Method: \`should{ExpectedBehavior}\` or \`should{Action}When{Condition}\`
|
|
411
|
+
- Use \`@DisplayName\` for readable test names
|
|
412
|
+
|
|
413
|
+
## Anti-patterns to avoid
|
|
414
|
+
- \`assertTrue(true)\` — meaningless assertion
|
|
415
|
+
- Testing private methods via reflection
|
|
416
|
+
- \`@SuppressWarnings\` in tests
|
|
417
|
+
- Tests that depend on execution order
|
|
418
|
+
- Not using \`@ExtendWith(MockitoExtension.class)\`
|
|
419
|
+
`;
|
|
420
|
+
const TEST_WRITING_KOTLIN = `---
|
|
421
|
+
name: j.test-writing
|
|
422
|
+
description: Write focused unit and integration tests using JUnit 5 with Kotlin idioms
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
# Skill: Test Writing (Kotlin/JUnit 5)
|
|
426
|
+
|
|
427
|
+
## When this skill activates
|
|
428
|
+
Writing or editing \`*Test.kt\`, \`*Tests.kt\`, or \`*IT.kt\` files.
|
|
429
|
+
|
|
430
|
+
## Required Steps
|
|
431
|
+
|
|
432
|
+
### 1. Read the implementation first
|
|
433
|
+
Before writing any test, read the file being tested. Understand:
|
|
434
|
+
- What it does (not what you think it does)
|
|
435
|
+
- Its dependencies and side effects
|
|
436
|
+
- Error cases and edge conditions
|
|
437
|
+
|
|
438
|
+
### 2. Test structure
|
|
439
|
+
Follow the AAA pattern with JUnit 5 and Kotlin idioms:
|
|
440
|
+
\`\`\`kotlin
|
|
441
|
+
@DisplayName("FunctionName")
|
|
442
|
+
class FunctionNameTest {
|
|
443
|
+
|
|
444
|
+
@Nested
|
|
445
|
+
@DisplayName("when valid input")
|
|
446
|
+
inner class WhenValidInput {
|
|
447
|
+
|
|
448
|
+
@Test
|
|
449
|
+
@DisplayName("should return expected result")
|
|
450
|
+
fun \`should return expected result\`() {
|
|
451
|
+
// Arrange
|
|
452
|
+
val input = createValidInput()
|
|
453
|
+
|
|
454
|
+
// Act
|
|
455
|
+
val result = functionName(input)
|
|
456
|
+
|
|
457
|
+
// Assert
|
|
458
|
+
assertThat(result).isEqualTo(expected)
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
@Nested
|
|
463
|
+
@DisplayName("when invalid input")
|
|
464
|
+
inner class WhenInvalidInput {
|
|
465
|
+
|
|
466
|
+
@Test
|
|
467
|
+
fun \`should throw IllegalArgumentException\`() {
|
|
468
|
+
assertThatThrownBy { functionName(invalidInput) }
|
|
469
|
+
.isInstanceOf(IllegalArgumentException::class.java)
|
|
470
|
+
.hasMessageContaining("specific message")
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
\`\`\`
|
|
475
|
+
|
|
476
|
+
### 3. Kotlin-specific patterns
|
|
477
|
+
- Use backtick method names for readable test names: \`fun \\\`should do something\\\`()\`
|
|
478
|
+
- Use \`assertThrows<ExceptionType>\` from JUnit 5 Kotlin extensions
|
|
479
|
+
- Prefer \`shouldBe\`, \`shouldThrow\` if using Kotest assertions
|
|
480
|
+
- Use data classes for test fixtures
|
|
481
|
+
- Use \`@ParameterizedTest\` with \`@MethodSource\` for multiple inputs
|
|
482
|
+
|
|
483
|
+
\`\`\`kotlin
|
|
484
|
+
@ParameterizedTest
|
|
485
|
+
@MethodSource("invalidInputs")
|
|
486
|
+
fun \`should reject invalid input\`(input: String) {
|
|
487
|
+
assertThrows<ValidationException> {
|
|
488
|
+
validate(input)
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
companion object {
|
|
493
|
+
@JvmStatic
|
|
494
|
+
fun invalidInputs() = listOf("", " ", "invalid@chars")
|
|
495
|
+
}
|
|
496
|
+
\`\`\`
|
|
497
|
+
|
|
498
|
+
### 4. Mock strategy (Mockito-Kotlin or MockK)
|
|
499
|
+
\`\`\`kotlin
|
|
500
|
+
// Using MockK (preferred for Kotlin)
|
|
501
|
+
class ServiceTest {
|
|
502
|
+
|
|
503
|
+
private val repository = mockk<Repository>()
|
|
504
|
+
private val service = Service(repository)
|
|
505
|
+
|
|
506
|
+
@Test
|
|
507
|
+
fun \`should return data\`() {
|
|
508
|
+
every { repository.findById(1L) } returns Optional.of(entity)
|
|
509
|
+
|
|
510
|
+
val result = service.getData(1L)
|
|
511
|
+
|
|
512
|
+
assertThat(result).isNotNull
|
|
513
|
+
verify { repository.findById(1L) }
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
\`\`\`
|
|
517
|
+
|
|
518
|
+
Or with Mockito-Kotlin:
|
|
519
|
+
\`\`\`kotlin
|
|
520
|
+
@ExtendWith(MockitoExtension::class)
|
|
521
|
+
class ServiceTest {
|
|
522
|
+
|
|
523
|
+
@Mock
|
|
524
|
+
lateinit var repository: Repository
|
|
525
|
+
|
|
526
|
+
@InjectMocks
|
|
527
|
+
lateinit var service: Service
|
|
528
|
+
|
|
529
|
+
@Test
|
|
530
|
+
fun \`should return data\`() {
|
|
531
|
+
whenever(repository.findById(1L)).thenReturn(Optional.of(entity))
|
|
532
|
+
|
|
533
|
+
val result = service.getData(1L)
|
|
534
|
+
|
|
535
|
+
assertThat(result).isNotNull
|
|
536
|
+
verify(repository).findById(1L)
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
\`\`\`
|
|
540
|
+
|
|
541
|
+
### 5. Spring Boot test patterns
|
|
542
|
+
\`\`\`kotlin
|
|
543
|
+
@SpringBootTest
|
|
544
|
+
@ActiveProfiles("test")
|
|
545
|
+
class IntegrationTest {
|
|
546
|
+
|
|
547
|
+
@Autowired
|
|
548
|
+
lateinit var service: MyService
|
|
549
|
+
|
|
550
|
+
@MockkBean // or @MockBean for Mockito
|
|
551
|
+
lateinit var externalClient: ExternalClient
|
|
552
|
+
|
|
553
|
+
@Test
|
|
554
|
+
fun \`should integrate correctly\`() {
|
|
555
|
+
every { externalClient.fetch() } returns listOf(data)
|
|
556
|
+
val result = service.process()
|
|
557
|
+
assertThat(result).hasSize(1)
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
\`\`\`
|
|
561
|
+
|
|
562
|
+
### 6. Naming conventions
|
|
563
|
+
- File: \`{ClassName}Test.kt\` in \`src/test/kotlin/\`
|
|
564
|
+
- Class: \`{ClassName}Test\`
|
|
565
|
+
- Method: backtick names for readability
|
|
566
|
+
- Use \`@DisplayName\` or \`@Nested\` for grouping
|
|
567
|
+
|
|
568
|
+
## Anti-patterns to avoid
|
|
569
|
+
- \`assertTrue(true)\` — meaningless assertion
|
|
570
|
+
- Testing private methods via reflection
|
|
571
|
+
- Not using \`inner class\` with \`@Nested\` (required in Kotlin)
|
|
572
|
+
- Using Java-style mock setup instead of Kotlin DSL
|
|
573
|
+
- Not cleaning up coroutine test scopes in coroutine tests
|
|
574
|
+
- Ignoring \`runTest {}\` for suspend function tests
|
|
575
|
+
`;
|
|
576
|
+
const TEST_WRITING_GENERIC = `---
|
|
577
|
+
name: j.test-writing
|
|
578
|
+
description: Write focused unit and integration tests following AAA pattern
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
# Skill: Test Writing (Generic)
|
|
582
|
+
|
|
583
|
+
## When this skill activates
|
|
584
|
+
Writing or editing test files in any language.
|
|
585
|
+
|
|
586
|
+
## Required Steps
|
|
587
|
+
|
|
588
|
+
### 1. Read the implementation first
|
|
589
|
+
Before writing any test, read the file being tested.
|
|
590
|
+
|
|
591
|
+
### 2. Test structure
|
|
592
|
+
Follow the AAA (Arrange-Act-Assert) pattern:
|
|
593
|
+
- **Arrange**: set up test data and dependencies
|
|
594
|
+
- **Act**: call the function/method under test
|
|
595
|
+
- **Assert**: verify the result matches expectations
|
|
596
|
+
|
|
597
|
+
### 3. Coverage requirements
|
|
598
|
+
- Happy path: at least 1 test
|
|
599
|
+
- Error cases: test each distinct error path
|
|
600
|
+
- Edge cases: empty inputs, boundary values, null/nil/None
|
|
601
|
+
|
|
602
|
+
### 4. Mock strategy
|
|
603
|
+
- Mock external dependencies (APIs, DB, file system)
|
|
604
|
+
- Do NOT mock the module under test
|
|
605
|
+
|
|
606
|
+
### 5. Naming conventions
|
|
607
|
+
- Test names should describe the expected behavior
|
|
608
|
+
- Group related tests logically
|
|
609
|
+
|
|
610
|
+
## Anti-patterns to avoid
|
|
611
|
+
- Meaningless assertions
|
|
612
|
+
- Testing implementation details instead of behavior
|
|
613
|
+
- Tests that depend on execution order
|
|
614
|
+
`;
|
|
615
|
+
// ─── Non-test skills (unchanged) ─────────────────────────────────────────────
|
|
616
|
+
const PAGE_CREATION = `---
|
|
617
|
+
name: j.page-creation
|
|
618
|
+
description: Create Next.js App Router pages with correct patterns
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
# Skill: Page Creation
|
|
622
|
+
|
|
623
|
+
This is a stack-specific skill. Only apply it when the project actually uses Next.js App Router patterns.
|
|
108
624
|
|
|
109
625
|
## When this skill activates
|
|
110
626
|
Creating or editing \`app/**/page.tsx\` or \`app/**/layout.tsx\` files.
|
|
@@ -176,15 +692,14 @@ Always create companion files:
|
|
|
176
692
|
- Missing loading states
|
|
177
693
|
- Not handling error boundaries
|
|
178
694
|
`;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
This is a stack-specific skill. Only apply it when the project actually uses Next.js App Router routes.
|
|
695
|
+
const API_ROUTE_CREATION = `---
|
|
696
|
+
name: j.api-route-creation
|
|
697
|
+
description: Create Next.js App Router API routes with correct patterns
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
# Skill: API Route Creation
|
|
701
|
+
|
|
702
|
+
This is a stack-specific skill. Only apply it when the project actually uses Next.js App Router routes.
|
|
188
703
|
|
|
189
704
|
## When this skill activates
|
|
190
705
|
Creating or editing \`app/api/**/*.ts\` route files.
|
|
@@ -272,15 +787,14 @@ const CreateSchema = z.object({
|
|
|
272
787
|
- Missing input validation
|
|
273
788
|
- Returning 200 for errors
|
|
274
789
|
`;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
This is a stack-specific skill. Only apply it when the project actually uses Next.js Server Actions.
|
|
790
|
+
const SERVER_ACTION_CREATION = `---
|
|
791
|
+
name: j.server-action-creation
|
|
792
|
+
description: Create Next.js Server Actions with correct patterns
|
|
793
|
+
---
|
|
794
|
+
|
|
795
|
+
# Skill: Server Action Creation
|
|
796
|
+
|
|
797
|
+
This is a stack-specific skill. Only apply it when the project actually uses Next.js Server Actions.
|
|
284
798
|
|
|
285
799
|
## When this skill activates
|
|
286
800
|
Creating or editing files with \`"use server"\` directive, typically \`actions.ts\` or \`**/actions/*.ts\`.
|
|
@@ -361,11 +875,10 @@ export function ExampleForm() {
|
|
|
361
875
|
- Catching errors silently without logging
|
|
362
876
|
- Forgetting to revalidate affected paths
|
|
363
877
|
`;
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
---
|
|
878
|
+
const SCHEMA_MIGRATION = `---
|
|
879
|
+
name: j.schema-migration
|
|
880
|
+
description: Modify Prisma schema and create migrations safely
|
|
881
|
+
---
|
|
369
882
|
|
|
370
883
|
# Skill: Schema Migration
|
|
371
884
|
|
|
@@ -432,152 +945,152 @@ npx tsc --noEmit # type check
|
|
|
432
945
|
npm test # run tests
|
|
433
946
|
\`\`\`
|
|
434
947
|
|
|
435
|
-
## Anti-patterns
|
|
436
|
-
- Renaming columns without a migration step (data loss)
|
|
437
|
-
- Adding required columns without defaults to non-empty tables
|
|
438
|
-
- Forgetting to run \`prisma generate\` after schema changes
|
|
439
|
-
- Not updating TypeScript types after schema changes
|
|
948
|
+
## Anti-patterns
|
|
949
|
+
- Renaming columns without a migration step (data loss)
|
|
950
|
+
- Adding required columns without defaults to non-empty tables
|
|
951
|
+
- Forgetting to run \`prisma generate\` after schema changes
|
|
952
|
+
- Not updating TypeScript types after schema changes
|
|
440
953
|
`;
|
|
441
|
-
const AGENTS_MD_WRITING = `---
|
|
442
|
-
name: j.agents-md-writing
|
|
443
|
-
description: Write strong AGENTS.md files with local rules, commands, and boundaries
|
|
444
|
-
---
|
|
445
|
-
|
|
446
|
-
# Skill: AGENTS.md Writing
|
|
447
|
-
|
|
448
|
-
## When this skill activates
|
|
449
|
-
Creating or editing any \`AGENTS.md\` file.
|
|
450
|
-
|
|
451
|
-
## Goal
|
|
452
|
-
Write an agent-facing operating manual for the current directory only.
|
|
453
|
-
|
|
454
|
-
## Required Sections
|
|
455
|
-
- Project or directory purpose
|
|
456
|
-
- Build, lint, and test commands that actually work here
|
|
457
|
-
- File layout and ownership boundaries
|
|
458
|
-
- Local coding conventions and pitfalls
|
|
459
|
-
- Review and verification expectations
|
|
460
|
-
|
|
461
|
-
## Rules
|
|
462
|
-
- Keep the root \`AGENTS.md\` concise and high-signal
|
|
463
|
-
- Make nested \`AGENTS.md\` files additive, not repetitive
|
|
464
|
-
- Prefer concrete commands over vague guidance
|
|
465
|
-
- Separate business rules from technical principles:
|
|
466
|
-
- \`AGENTS.md\` = how to work in this area
|
|
467
|
-
- \`docs/domain/*\` = business behavior
|
|
468
|
-
- \`docs/principles/*\` = cross-cutting technical patterns
|
|
469
|
-
|
|
470
|
-
## Good patterns
|
|
471
|
-
- Include exact commands such as \`npm test -- foo\` or \`./gradlew test --tests
|
|
472
|
-
- Call out invariants, ownership boundaries, and high-blast-radius files
|
|
473
|
-
- Mention generated files, migrations, or release steps when relevant
|
|
474
|
-
|
|
475
|
-
## Anti-patterns
|
|
476
|
-
- Dumping generic style advice with no repository specifics
|
|
477
|
-
- Repeating the same commands in every nested file
|
|
478
|
-
- Mixing business flows into technical instructions
|
|
479
|
-
- Writing aspirational rules that are not enforced anywhere
|
|
954
|
+
const AGENTS_MD_WRITING = `---
|
|
955
|
+
name: j.agents-md-writing
|
|
956
|
+
description: Write strong AGENTS.md files with local rules, commands, and boundaries
|
|
957
|
+
---
|
|
958
|
+
|
|
959
|
+
# Skill: AGENTS.md Writing
|
|
960
|
+
|
|
961
|
+
## When this skill activates
|
|
962
|
+
Creating or editing any \`AGENTS.md\` file.
|
|
963
|
+
|
|
964
|
+
## Goal
|
|
965
|
+
Write an agent-facing operating manual for the current directory only.
|
|
966
|
+
|
|
967
|
+
## Required Sections
|
|
968
|
+
- Project or directory purpose
|
|
969
|
+
- Build, lint, and test commands that actually work here
|
|
970
|
+
- File layout and ownership boundaries
|
|
971
|
+
- Local coding conventions and pitfalls
|
|
972
|
+
- Review and verification expectations
|
|
973
|
+
|
|
974
|
+
## Rules
|
|
975
|
+
- Keep the root \`AGENTS.md\` concise and high-signal
|
|
976
|
+
- Make nested \`AGENTS.md\` files additive, not repetitive
|
|
977
|
+
- Prefer concrete commands over vague guidance
|
|
978
|
+
- Separate business rules from technical principles:
|
|
979
|
+
- \`AGENTS.md\` = how to work in this area
|
|
980
|
+
- \`docs/domain/*\` = business behavior
|
|
981
|
+
- \`docs/principles/*\` = cross-cutting technical patterns
|
|
982
|
+
|
|
983
|
+
## Good patterns
|
|
984
|
+
- Include exact commands such as \`npm test -- foo\` or \`./gradlew test --tests "..."\`
|
|
985
|
+
- Call out invariants, ownership boundaries, and high-blast-radius files
|
|
986
|
+
- Mention generated files, migrations, or release steps when relevant
|
|
987
|
+
|
|
988
|
+
## Anti-patterns
|
|
989
|
+
- Dumping generic style advice with no repository specifics
|
|
990
|
+
- Repeating the same commands in every nested file
|
|
991
|
+
- Mixing business flows into technical instructions
|
|
992
|
+
- Writing aspirational rules that are not enforced anywhere
|
|
480
993
|
`;
|
|
481
|
-
const DOMAIN_DOC_WRITING = `---
|
|
482
|
-
name: j.domain-doc-writing
|
|
483
|
-
description: Write business-domain documentation that stays aligned with code
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
# Skill: Domain Doc Writing
|
|
487
|
-
|
|
488
|
-
## When this skill activates
|
|
489
|
-
Creating or editing files under \`docs/domain/\`.
|
|
490
|
-
|
|
491
|
-
## Goal
|
|
492
|
-
Document how the business domain works now, not how the code is implemented internally.
|
|
493
|
-
|
|
494
|
-
## Required Structure
|
|
495
|
-
- Domain summary
|
|
496
|
-
- Rules and invariants
|
|
497
|
-
- Inputs, outputs, and state transitions when relevant
|
|
498
|
-
- Edge cases and operational limits
|
|
499
|
-
- Source of truth references to the key code files
|
|
500
|
-
|
|
501
|
-
## Sync marker pattern
|
|
502
|
-
At the top of a generated or refreshed section, prefer a marker like:
|
|
503
|
-
|
|
504
|
-
\`<!-- juninho:sync source=src/payments/service.ts hash=abc123 -->\`
|
|
505
|
-
|
|
506
|
-
Use the marker to indicate which code file justified the current documentation.
|
|
507
|
-
|
|
508
|
-
## Rules
|
|
509
|
-
- Write in present tense
|
|
510
|
-
- Prefer user-visible behavior and business meaning
|
|
511
|
-
- Cite key files that justify each rule
|
|
512
|
-
- Update \`docs/domain/INDEX.md\` when adding or renaming a domain doc
|
|
513
|
-
|
|
514
|
-
## Anti-patterns
|
|
515
|
-
- Explaining framework internals instead of business behavior
|
|
516
|
-
- Copying raw code into the document
|
|
517
|
-
- Leaving undocumented edge cases discovered during implementation
|
|
994
|
+
const DOMAIN_DOC_WRITING = `---
|
|
995
|
+
name: j.domain-doc-writing
|
|
996
|
+
description: Write business-domain documentation that stays aligned with code
|
|
997
|
+
---
|
|
998
|
+
|
|
999
|
+
# Skill: Domain Doc Writing
|
|
1000
|
+
|
|
1001
|
+
## When this skill activates
|
|
1002
|
+
Creating or editing files under \`docs/domain/\`.
|
|
1003
|
+
|
|
1004
|
+
## Goal
|
|
1005
|
+
Document how the business domain works now, not how the code is implemented internally.
|
|
1006
|
+
|
|
1007
|
+
## Required Structure
|
|
1008
|
+
- Domain summary
|
|
1009
|
+
- Rules and invariants
|
|
1010
|
+
- Inputs, outputs, and state transitions when relevant
|
|
1011
|
+
- Edge cases and operational limits
|
|
1012
|
+
- Source of truth references to the key code files
|
|
1013
|
+
|
|
1014
|
+
## Sync marker pattern
|
|
1015
|
+
At the top of a generated or refreshed section, prefer a marker like:
|
|
1016
|
+
|
|
1017
|
+
\`<!-- juninho:sync source=src/payments/service.ts hash=abc123 -->\`
|
|
1018
|
+
|
|
1019
|
+
Use the marker to indicate which code file justified the current documentation.
|
|
1020
|
+
|
|
1021
|
+
## Rules
|
|
1022
|
+
- Write in present tense
|
|
1023
|
+
- Prefer user-visible behavior and business meaning
|
|
1024
|
+
- Cite key files that justify each rule
|
|
1025
|
+
- Update \`docs/domain/INDEX.md\` when adding or renaming a domain doc
|
|
1026
|
+
|
|
1027
|
+
## Anti-patterns
|
|
1028
|
+
- Explaining framework internals instead of business behavior
|
|
1029
|
+
- Copying raw code into the document
|
|
1030
|
+
- Leaving undocumented edge cases discovered during implementation
|
|
518
1031
|
`;
|
|
519
|
-
const PRINCIPLE_DOC_WRITING = `---
|
|
520
|
-
name: j.principle-doc-writing
|
|
521
|
-
description: Write technical principle docs with rationale, rules, and examples
|
|
522
|
-
---
|
|
523
|
-
|
|
524
|
-
# Skill: Principle Doc Writing
|
|
525
|
-
|
|
526
|
-
## When this skill activates
|
|
527
|
-
Creating or editing files under \`docs/principles/\`.
|
|
528
|
-
|
|
529
|
-
## Goal
|
|
530
|
-
Capture cross-cutting engineering guidance that multiple modules should follow.
|
|
531
|
-
|
|
532
|
-
## Required Structure
|
|
533
|
-
- Problem this principle solves
|
|
534
|
-
- Rule set (do / avoid)
|
|
535
|
-
- Rationale and trade-offs
|
|
536
|
-
- Canonical examples in this repository
|
|
537
|
-
- Related files or tooling that enforce the rule
|
|
538
|
-
|
|
539
|
-
## Sync marker pattern
|
|
540
|
-
For generated sections, prefer a marker like:
|
|
541
|
-
|
|
542
|
-
\`<!-- juninho:sync source=src/api/client.ts hash=def456 -->\`
|
|
543
|
-
|
|
544
|
-
## Rules
|
|
545
|
-
- Keep principles technical, reusable, and stack-aware
|
|
546
|
-
- Reference concrete files or commands when possible
|
|
547
|
-
- Register or update the keyword mapping in \`docs/principles/manifest\`
|
|
548
|
-
- Distinguish principle docs from domain docs and \`AGENTS.md\`
|
|
549
|
-
|
|
550
|
-
## Anti-patterns
|
|
551
|
-
- Repeating business requirements here
|
|
552
|
-
- Writing slogans with no enforcement or examples
|
|
553
|
-
- Documenting obsolete patterns without marking them deprecated
|
|
1032
|
+
const PRINCIPLE_DOC_WRITING = `---
|
|
1033
|
+
name: j.principle-doc-writing
|
|
1034
|
+
description: Write technical principle docs with rationale, rules, and examples
|
|
1035
|
+
---
|
|
1036
|
+
|
|
1037
|
+
# Skill: Principle Doc Writing
|
|
1038
|
+
|
|
1039
|
+
## When this skill activates
|
|
1040
|
+
Creating or editing files under \`docs/principles/\`.
|
|
1041
|
+
|
|
1042
|
+
## Goal
|
|
1043
|
+
Capture cross-cutting engineering guidance that multiple modules should follow.
|
|
1044
|
+
|
|
1045
|
+
## Required Structure
|
|
1046
|
+
- Problem this principle solves
|
|
1047
|
+
- Rule set (do / avoid)
|
|
1048
|
+
- Rationale and trade-offs
|
|
1049
|
+
- Canonical examples in this repository
|
|
1050
|
+
- Related files or tooling that enforce the rule
|
|
1051
|
+
|
|
1052
|
+
## Sync marker pattern
|
|
1053
|
+
For generated sections, prefer a marker like:
|
|
1054
|
+
|
|
1055
|
+
\`<!-- juninho:sync source=src/api/client.ts hash=def456 -->\`
|
|
1056
|
+
|
|
1057
|
+
## Rules
|
|
1058
|
+
- Keep principles technical, reusable, and stack-aware
|
|
1059
|
+
- Reference concrete files or commands when possible
|
|
1060
|
+
- Register or update the keyword mapping in \`docs/principles/manifest\`
|
|
1061
|
+
- Distinguish principle docs from domain docs and \`AGENTS.md\`
|
|
1062
|
+
|
|
1063
|
+
## Anti-patterns
|
|
1064
|
+
- Repeating business requirements here
|
|
1065
|
+
- Writing slogans with no enforcement or examples
|
|
1066
|
+
- Documenting obsolete patterns without marking them deprecated
|
|
554
1067
|
`;
|
|
555
|
-
const SHELL_SCRIPT_WRITING = `---
|
|
556
|
-
name: j.shell-script-writing
|
|
557
|
-
description: Write robust shell automation for project workflows and hooks
|
|
558
|
-
---
|
|
559
|
-
|
|
560
|
-
# Skill: Shell Script Writing
|
|
561
|
-
|
|
562
|
-
## When this skill activates
|
|
563
|
-
Creating or editing shell scripts, especially in \`.opencode/scripts/\`, \`scripts/\`, or git hooks.
|
|
564
|
-
|
|
565
|
-
## Required Steps
|
|
566
|
-
1. Start with \`#!/bin/sh\` unless bash-only features are required
|
|
567
|
-
2. Use \`set -e\` and quote every variable expansion that can contain spaces
|
|
568
|
-
3. Resolve and \`cd\` to the project root before running project commands
|
|
569
|
-
4. Prefer delegating to project scripts (\`npm run ...\`, \`make ...\`, \`./gradlew ...\`) over embedding large command logic
|
|
570
|
-
5. Print short, actionable failure messages
|
|
571
|
-
|
|
572
|
-
## Preferred patterns
|
|
573
|
-
- Detect staged files once and reuse them
|
|
574
|
-
- Support project-specific overrides before framework defaults
|
|
575
|
-
- Keep hook scripts fast; full-suite checks belong outside the pre-commit path
|
|
576
|
-
|
|
577
|
-
## Anti-patterns
|
|
578
|
-
- Hardcoding one stack when multiple fallback commands are possible
|
|
579
|
-
- Running the full test suite inside pre-commit by default
|
|
580
|
-
- Using unquoted file lists or unsafe globbing
|
|
581
|
-
- Mixing environment bootstrapping with small hook utilities
|
|
1068
|
+
const SHELL_SCRIPT_WRITING = `---
|
|
1069
|
+
name: j.shell-script-writing
|
|
1070
|
+
description: Write robust shell automation for project workflows and hooks
|
|
1071
|
+
---
|
|
1072
|
+
|
|
1073
|
+
# Skill: Shell Script Writing
|
|
1074
|
+
|
|
1075
|
+
## When this skill activates
|
|
1076
|
+
Creating or editing shell scripts, especially in \`.opencode/scripts/\`, \`scripts/\`, or git hooks.
|
|
1077
|
+
|
|
1078
|
+
## Required Steps
|
|
1079
|
+
1. Start with \`#!/bin/sh\` unless bash-only features are required
|
|
1080
|
+
2. Use \`set -e\` and quote every variable expansion that can contain spaces
|
|
1081
|
+
3. Resolve and \`cd\` to the project root before running project commands
|
|
1082
|
+
4. Prefer delegating to project scripts (\`npm run ...\`, \`make ...\`, \`./gradlew ...\`) over embedding large command logic
|
|
1083
|
+
5. Print short, actionable failure messages
|
|
1084
|
+
|
|
1085
|
+
## Preferred patterns
|
|
1086
|
+
- Detect staged files once and reuse them
|
|
1087
|
+
- Support project-specific overrides before framework defaults
|
|
1088
|
+
- Keep hook scripts fast; full-suite checks belong outside the pre-commit path
|
|
1089
|
+
|
|
1090
|
+
## Anti-patterns
|
|
1091
|
+
- Hardcoding one stack when multiple fallback commands are possible
|
|
1092
|
+
- Running the full test suite inside pre-commit by default
|
|
1093
|
+
- Using unquoted file lists or unsafe globbing
|
|
1094
|
+
- Mixing environment bootstrapping with small hook utilities
|
|
582
1095
|
`;
|
|
583
1096
|
//# sourceMappingURL=skills.js.map
|