@kleber.mottajr/juninho 1.0.1 → 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.
Files changed (49) hide show
  1. package/README.md +112 -13
  2. package/dist/cli.js +53 -29
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config.d.ts +5 -0
  5. package/dist/config.d.ts.map +1 -1
  6. package/dist/config.js +7 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/installer.d.ts +2 -0
  9. package/dist/installer.d.ts.map +1 -1
  10. package/dist/installer.js +178 -54
  11. package/dist/installer.js.map +1 -1
  12. package/dist/lint-detection.d.ts +26 -0
  13. package/dist/lint-detection.d.ts.map +1 -0
  14. package/dist/lint-detection.js +200 -0
  15. package/dist/lint-detection.js.map +1 -0
  16. package/dist/models.js +4 -4
  17. package/dist/models.js.map +1 -1
  18. package/dist/project-types.d.ts +47 -0
  19. package/dist/project-types.d.ts.map +1 -0
  20. package/dist/project-types.js +251 -0
  21. package/dist/project-types.js.map +1 -0
  22. package/dist/templates/agents.d.ts +2 -1
  23. package/dist/templates/agents.d.ts.map +1 -1
  24. package/dist/templates/agents.js +7 -5
  25. package/dist/templates/agents.js.map +1 -1
  26. package/dist/templates/commands.d.ts.map +1 -1
  27. package/dist/templates/commands.js +225 -150
  28. package/dist/templates/commands.js.map +1 -1
  29. package/dist/templates/docs.d.ts +2 -1
  30. package/dist/templates/docs.d.ts.map +1 -1
  31. package/dist/templates/docs.js +61 -14
  32. package/dist/templates/docs.js.map +1 -1
  33. package/dist/templates/plugins.d.ts +2 -1
  34. package/dist/templates/plugins.d.ts.map +1 -1
  35. package/dist/templates/plugins.js +167 -102
  36. package/dist/templates/plugins.js.map +1 -1
  37. package/dist/templates/skills.d.ts +2 -1
  38. package/dist/templates/skills.d.ts.map +1 -1
  39. package/dist/templates/skills.js +708 -195
  40. package/dist/templates/skills.js.map +1 -1
  41. package/dist/templates/support-scripts.d.ts +2 -1
  42. package/dist/templates/support-scripts.d.ts.map +1 -1
  43. package/dist/templates/support-scripts.js +468 -21
  44. package/dist/templates/support-scripts.js.map +1 -1
  45. package/dist/templates/tools.d.ts +2 -1
  46. package/dist/templates/tools.d.ts.map +1 -1
  47. package/dist/templates/tools.js +315 -74
  48. package/dist/templates/tools.js.map +1 -1
  49. package/package.json +1 -1
@@ -6,15 +6,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.writeTools = writeTools;
7
7
  const fs_1 = require("fs");
8
8
  const path_1 = __importDefault(require("path"));
9
- function writeTools(projectDir) {
9
+ const project_types_js_1 = require("../project-types.js");
10
+ function writeTools(projectDir, projectType = "node-nextjs", isKotlin = false) {
10
11
  const toolsDir = path_1.default.join(projectDir, ".opencode", "tools");
11
- (0, fs_1.writeFileSync)(path_1.default.join(toolsDir, "find-pattern.ts"), FIND_PATTERN);
12
- (0, fs_1.writeFileSync)(path_1.default.join(toolsDir, "next-version.ts"), NEXT_VERSION);
13
- (0, fs_1.writeFileSync)(path_1.default.join(toolsDir, "lsp.ts"), LSP);
14
- (0, fs_1.writeFileSync)(path_1.default.join(toolsDir, "ast-grep.ts"), AST_GREP);
12
+ const config = (0, project_types_js_1.getEffectiveConfig)(projectType, isKotlin);
13
+ (0, fs_1.writeFileSync)(path_1.default.join(toolsDir, "find-pattern.ts"), findPattern(projectType, isKotlin));
14
+ (0, fs_1.writeFileSync)(path_1.default.join(toolsDir, "next-version.ts"), nextVersion(config.migrationDirs));
15
+ (0, fs_1.writeFileSync)(path_1.default.join(toolsDir, "lsp.ts"), lsp(projectType, isKotlin));
16
+ (0, fs_1.writeFileSync)(path_1.default.join(toolsDir, "ast-grep.ts"), astGrep(config.astGrepLang));
15
17
  }
16
18
  // ─── Find Pattern ─────────────────────────────────────────────────────────────
17
- const FIND_PATTERN = `import { tool } from "@opencode-ai/plugin"
19
+ function findPattern(projectType, isKotlin) {
20
+ const fallbackPatterns = getFallbackPatterns(projectType, isKotlin);
21
+ return `import { tool } from "@opencode-ai/plugin"
18
22
  import { z } from "zod"
19
23
  import { existsSync, readFileSync } from "fs"
20
24
  import path from "path"
@@ -23,17 +27,7 @@ export const find_pattern = tool({
23
27
  name: "find_pattern",
24
28
  description: "Find canonical code patterns in the codebase for consistent implementation",
25
29
  parameters: z.object({
26
- patternType: z.enum([
27
- "api-route",
28
- "server-action",
29
- "react-component",
30
- "prisma-query",
31
- "error-handler",
32
- "test-unit",
33
- "test-integration",
34
- "zod-schema",
35
- "middleware",
36
- ]).describe("The type of pattern to find"),
30
+ patternType: z.string().describe("The type of pattern to find (e.g. api-route, service, repository, test-unit, error-handler)"),
37
31
  cwd: z.string().optional().describe("Working directory (defaults to process.cwd())"),
38
32
  }),
39
33
  execute: async ({ patternType, cwd: cwdInput }) => {
@@ -51,8 +45,83 @@ export const find_pattern = tool({
51
45
  }
52
46
 
53
47
  // Fallback patterns
54
- const FALLBACK_PATTERNS: Record<string, string> = {
55
- "api-route": \`// app/api/example/route.ts
48
+ const FALLBACK_PATTERNS: Record<string, string> = ${JSON.stringify(fallbackPatterns, null, 6).replace(/\n/g, "\n ")}
49
+
50
+ return {
51
+ pattern: patternType,
52
+ example: FALLBACK_PATTERNS[patternType] ?? "No canonical pattern found. Check docs/principles/manifest.",
53
+ }
54
+ },
55
+ })
56
+ `;
57
+ }
58
+ function getFallbackPatterns(projectType, isKotlin) {
59
+ if (projectType === "java" && isKotlin) {
60
+ return {
61
+ "service": `// src/main/kotlin/com/example/FooService.kt
62
+ @Service
63
+ class FooService(
64
+ private val repository: FooRepository,
65
+ ) {
66
+ fun findById(id: Long): Foo {
67
+ return repository.findById(id)
68
+ .orElseThrow { NotFoundException("Foo not found: $id") }
69
+ }
70
+ }`,
71
+ "repository": `// src/main/kotlin/com/example/FooRepository.kt
72
+ @Repository
73
+ interface FooRepository : JpaRepository<Foo, Long> {
74
+ fun findByName(name: String): List<Foo>
75
+ }`,
76
+ "controller": `// src/main/kotlin/com/example/FooController.kt
77
+ @RestController
78
+ @RequestMapping("/api/foo")
79
+ class FooController(
80
+ private val service: FooService,
81
+ ) {
82
+ @GetMapping("/{id}")
83
+ fun getById(@PathVariable id: Long): ResponseEntity<Foo> {
84
+ return ResponseEntity.ok(service.findById(id))
85
+ }
86
+
87
+ @PostMapping
88
+ fun create(@Valid @RequestBody request: CreateFooRequest): ResponseEntity<Foo> {
89
+ return ResponseEntity.status(HttpStatus.CREATED).body(service.create(request))
90
+ }
91
+ }`,
92
+ "test-unit": `// src/test/kotlin/com/example/FooServiceTest.kt
93
+ @ExtendWith(MockitoExtension::class)
94
+ class FooServiceTest {
95
+
96
+ @Mock
97
+ lateinit var repository: FooRepository
98
+
99
+ @InjectMocks
100
+ lateinit var service: FooService
101
+
102
+ @Test
103
+ fun \`should find by id\`() {
104
+ whenever(repository.findById(1L)).thenReturn(Optional.of(foo))
105
+ val result = service.findById(1L)
106
+ assertThat(result).isEqualTo(foo)
107
+ }
108
+ }`,
109
+ "error-handler": `// src/main/kotlin/com/example/GlobalExceptionHandler.kt
110
+ @RestControllerAdvice
111
+ class GlobalExceptionHandler {
112
+
113
+ @ExceptionHandler(NotFoundException::class)
114
+ fun handleNotFound(ex: NotFoundException): ResponseEntity<ErrorResponse> {
115
+ return ResponseEntity.status(HttpStatus.NOT_FOUND)
116
+ .body(ErrorResponse(ex.message ?: "Not found"))
117
+ }
118
+ }`,
119
+ };
120
+ }
121
+ switch (projectType) {
122
+ case "node-nextjs":
123
+ return {
124
+ "api-route": `// app/api/example/route.ts
56
125
  import { NextRequest, NextResponse } from "next/server"
57
126
 
58
127
  export async function GET(req: NextRequest) {
@@ -62,8 +131,8 @@ export async function GET(req: NextRequest) {
62
131
  } catch (error) {
63
132
  return NextResponse.json({ error: "Internal server error" }, { status: 500 })
64
133
  }
65
- }\`,
66
- "server-action": \`// app/actions/example.ts
134
+ }`,
135
+ "server-action": `// app/actions/example.ts
67
136
  "use server"
68
137
  import { revalidatePath } from "next/cache"
69
138
  import { z } from "zod"
@@ -76,8 +145,8 @@ export async function createExample(formData: FormData) {
76
145
  // ... implementation
77
146
  revalidatePath("/")
78
147
  return { success: true }
79
- }\`,
80
- "zod-schema": \`import { z } from "zod"
148
+ }`,
149
+ "zod-schema": `import { z } from "zod"
81
150
 
82
151
  export const ExampleSchema = z.object({
83
152
  id: z.string().cuid(),
@@ -85,18 +154,153 @@ export const ExampleSchema = z.object({
85
154
  createdAt: z.date(),
86
155
  })
87
156
 
88
- export type Example = z.infer<typeof ExampleSchema>\`,
157
+ export type Example = z.infer<typeof ExampleSchema>`,
158
+ };
159
+ case "node-generic":
160
+ return {
161
+ "service": `// src/services/example.ts
162
+ export class ExampleService {
163
+ constructor(private readonly repository: ExampleRepository) {}
164
+
165
+ async findById(id: string): Promise<Example> {
166
+ const result = await this.repository.findById(id)
167
+ if (!result) throw new NotFoundError(\`Example \${id} not found\`)
168
+ return result
169
+ }
170
+ }`,
171
+ "error-handler": `// src/middleware/error-handler.ts
172
+ export function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) {
173
+ if (err instanceof NotFoundError) {
174
+ return res.status(404).json({ error: err.message })
175
+ }
176
+ console.error(err)
177
+ res.status(500).json({ error: "Internal server error" })
178
+ }`,
179
+ };
180
+ case "python":
181
+ return {
182
+ "service": `# src/services/foo_service.py
183
+ class FooService:
184
+ def __init__(self, repository: FooRepository):
185
+ self._repository = repository
186
+
187
+ def find_by_id(self, id: int) -> Foo:
188
+ result = self._repository.find_by_id(id)
189
+ if not result:
190
+ raise NotFoundError(f"Foo {id} not found")
191
+ return result`,
192
+ "test-unit": `# tests/test_foo_service.py
193
+ import pytest
194
+ from unittest.mock import MagicMock
195
+
196
+ class TestFooService:
197
+ def test_find_by_id_returns_foo(self):
198
+ repository = MagicMock()
199
+ repository.find_by_id.return_value = Foo(id=1, name="test")
200
+ service = FooService(repository)
201
+
202
+ result = service.find_by_id(1)
203
+
204
+ assert result.name == "test"
205
+ repository.find_by_id.assert_called_once_with(1)`,
206
+ };
207
+ case "go":
208
+ return {
209
+ "handler": `// internal/handler/foo.go
210
+ func (h *FooHandler) GetByID(w http.ResponseWriter, r *http.Request) {
211
+ id := chi.URLParam(r, "id")
212
+
213
+ foo, err := h.service.FindByID(r.Context(), id)
214
+ if err != nil {
215
+ if errors.Is(err, ErrNotFound) {
216
+ http.Error(w, "not found", http.StatusNotFound)
217
+ return
218
+ }
219
+ http.Error(w, "internal error", http.StatusInternalServerError)
220
+ return
89
221
  }
90
222
 
91
- return {
92
- pattern: patternType,
93
- example: FALLBACK_PATTERNS[patternType] ?? "No canonical pattern found. Check docs/principles/manifest.",
223
+ json.NewEncoder(w).Encode(foo)
224
+ }`,
225
+ "test-unit": `// internal/service/foo_test.go
226
+ func TestFooService_FindByID(t *testing.T) {
227
+ tests := []struct {
228
+ name string
229
+ id string
230
+ want *Foo
231
+ wantErr bool
232
+ }{
233
+ {name: "found", id: "1", want: &Foo{ID: "1"}, wantErr: false},
234
+ {name: "not found", id: "999", want: nil, wantErr: true},
94
235
  }
95
- },
96
- })
97
- `;
236
+
237
+ for _, tt := range tests {
238
+ t.Run(tt.name, func(t *testing.T) {
239
+ got, err := svc.FindByID(context.Background(), tt.id)
240
+ if (err != nil) != tt.wantErr {
241
+ t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
242
+ }
243
+ if !reflect.DeepEqual(got, tt.want) {
244
+ t.Errorf("got %v, want %v", got, tt.want)
245
+ }
246
+ })
247
+ }
248
+ }`,
249
+ };
250
+ case "java":
251
+ return {
252
+ "service": `// src/main/java/com/example/FooService.java
253
+ @Service
254
+ public class FooService {
255
+ private final FooRepository repository;
256
+
257
+ public FooService(FooRepository repository) {
258
+ this.repository = repository;
259
+ }
260
+
261
+ public Foo findById(Long id) {
262
+ return repository.findById(id)
263
+ .orElseThrow(() -> new NotFoundException("Foo not found: " + id));
264
+ }
265
+ }`,
266
+ "controller": `// src/main/java/com/example/FooController.java
267
+ @RestController
268
+ @RequestMapping("/api/foo")
269
+ public class FooController {
270
+ private final FooService service;
271
+
272
+ @GetMapping("/{id}")
273
+ public ResponseEntity<Foo> getById(@PathVariable Long id) {
274
+ return ResponseEntity.ok(service.findById(id));
275
+ }
276
+ }`,
277
+ "test-unit": `// src/test/java/com/example/FooServiceTest.java
278
+ @ExtendWith(MockitoExtension.class)
279
+ class FooServiceTest {
280
+
281
+ @Mock
282
+ private FooRepository repository;
283
+
284
+ @InjectMocks
285
+ private FooService service;
286
+
287
+ @Test
288
+ void shouldFindById() {
289
+ when(repository.findById(1L)).thenReturn(Optional.of(foo));
290
+ var result = service.findById(1L);
291
+ assertThat(result).isEqualTo(foo);
292
+ }
293
+ }`,
294
+ };
295
+ case "generic":
296
+ default:
297
+ return {};
298
+ }
299
+ }
98
300
  // ─── Next Version ─────────────────────────────────────────────────────────────
99
- const NEXT_VERSION = `import { tool } from "@opencode-ai/plugin"
301
+ function nextVersion(migrationDirs) {
302
+ const dirsJson = JSON.stringify(migrationDirs);
303
+ return `import { tool } from "@opencode-ai/plugin"
100
304
  import { z } from "zod"
101
305
  import { existsSync, readdirSync } from "fs"
102
306
  import path from "path"
@@ -111,21 +315,12 @@ export const next_version = tool({
111
315
  execute: async ({ type, cwd: cwdInput }) => {
112
316
  const cwd = cwdInput ?? process.cwd()
113
317
 
114
- const dirs: Record<string, string[]> = {
115
- migration: [
116
- "prisma/migrations",
117
- "db/migrations",
118
- "migrations",
119
- "drizzle",
120
- ],
121
- schema: [
122
- "prisma",
123
- "db",
124
- "src/db",
125
- ],
126
- }
318
+ const migrationDirs = ${dirsJson}
319
+ const schemaDirs = ["prisma", "db", "src/db", "src/main/resources"]
127
320
 
128
- for (const dir of dirs[type]) {
321
+ const dirs = type === "migration" ? migrationDirs : schemaDirs
322
+
323
+ for (const dir of dirs) {
129
324
  const fullDir = path.join(cwd, dir)
130
325
  if (!existsSync(fullDir)) continue
131
326
 
@@ -161,34 +356,37 @@ export const next_version = tool({
161
356
  },
162
357
  })
163
358
  `;
359
+ }
164
360
  // ─── LSP ──────────────────────────────────────────────────────────────────────
165
- const LSP = `import { tool } from "@opencode-ai/plugin"
361
+ function lsp(projectType, isKotlin) {
362
+ const { typeCheckCmd, grepExtensions } = getLspConfig(projectType, isKotlin);
363
+ return `import { tool } from "@opencode-ai/plugin"
166
364
  import { z } from "zod"
167
365
  import { execSync } from "child_process"
168
366
 
169
- // LSP tools via typescript-language-server CLI
170
- // Falls back to tsc for type checking if LSP not available
367
+ // LSP tools type checker and reference search
368
+ // Type checker: ${typeCheckCmd}
171
369
 
172
- function runTsc(cwd: string, args: string): string {
370
+ function runTypeCheck(cwd: string, args: string): string {
173
371
  try {
174
- return execSync(\`npx tsc \${args}\`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] })
372
+ return execSync(\`${typeCheckCmd} \${args}\`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] })
175
373
  } catch (e: any) {
176
- return e.stdout ?? e.message ?? "tsc failed"
374
+ return e.stdout ?? e.message ?? "type check failed"
177
375
  }
178
376
  }
179
377
 
180
378
  export const lsp_diagnostics = tool({
181
379
  name: "lsp_diagnostics",
182
- description: "Get TypeScript diagnostics (errors and warnings) for a file or directory",
380
+ description: "Get diagnostics (errors and warnings) for a file or directory",
183
381
  parameters: z.object({
184
382
  path: z.string().describe("File or directory to check"),
185
383
  severity: z.enum(["error", "warning", "info"]).optional().default("error"),
186
384
  }),
187
385
  execute: async ({ path: targetPath, severity }) => {
188
- const output = runTsc(process.cwd(), \`--noEmit --pretty false 2>&1 | grep "\${targetPath}"\`)
386
+ const output = runTypeCheck(process.cwd(), \`2>&1 | grep "\${targetPath}"\`)
189
387
  const lines = output.split("\\n").filter((l) => {
190
- if (severity === "error") return l.includes("error TS")
191
- if (severity === "warning") return l.includes("warning TS")
388
+ if (severity === "error") return l.includes("error") || l.includes("ERROR")
389
+ if (severity === "warning") return l.includes("warning") || l.includes("WARN")
192
390
  return l.trim().length > 0
193
391
  })
194
392
  return { diagnostics: lines, count: lines.length }
@@ -204,18 +402,16 @@ export const lsp_goto_definition = tool({
204
402
  character: z.number().describe("Character position (0-indexed)"),
205
403
  }),
206
404
  execute: async ({ file, line, character }) => {
207
- // Use grep as fallback for definition finding
208
405
  try {
209
406
  const content = require("fs").readFileSync(file, "utf-8")
210
407
  const lines = content.split("\\n")
211
408
  const targetLine = lines[line - 1] ?? ""
212
- // Extract symbol at position
213
409
  const before = targetLine.slice(0, character)
214
410
  const after = targetLine.slice(character)
215
411
  const symbolMatch = /[\\w$]+$/.exec(before)
216
412
  const symbolEnd = /^[\\w$]*/.exec(after)
217
413
  const symbol = (symbolMatch?.[0] ?? "") + (symbolEnd?.[0] ?? "")
218
- return { symbol, hint: \`Search for 'export.*\${symbol}|function \${symbol}|class \${symbol}|const \${symbol}'\` }
414
+ return { symbol, hint: \`Search for 'export.*\${symbol}|function \${symbol}|class \${symbol}|const \${symbol}|fun \${symbol}|def \${symbol}|func \${symbol}'\` }
219
415
  } catch {
220
416
  return { error: "Could not read file" }
221
417
  }
@@ -239,10 +435,10 @@ export const lsp_find_references = tool({
239
435
  const after = lineContent.slice(character)
240
436
  const symbol = (/[\\w$]+$/.exec(before)?.[0] ?? "") + (/^[\\w$]*/.exec(after)?.[0] ?? "")
241
437
  if (!symbol) return { error: "No symbol at position" }
242
- const result = execSync(\`grep -rn --include="*.ts" --include="*.tsx" "\${symbol}" .\`, {
243
- encoding: "utf-8",
244
- stdio: ["pipe", "pipe", "pipe"],
245
- })
438
+ const result = execSync(
439
+ \`grep -rn ${grepExtensions} "\${symbol}" .\`,
440
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
441
+ )
246
442
  const refs = result.split("\\n").filter(Boolean)
247
443
  return { symbol, references: refs.slice(0, 20), total: refs.length }
248
444
  } catch (e: any) {
@@ -268,6 +464,13 @@ export const lsp_document_symbols = tool({
268
464
  [/^export\\s+(const|let|var)\\s+(\\w+)/, "variable"],
269
465
  [/^export\\s+(default\\s+)?class\\s+(\\w+)/, "class"],
270
466
  [/^export\\s+(type|interface)\\s+(\\w+)/, "type"],
467
+ [/^\\s*fun\\s+(\\w+)/, "function"],
468
+ [/^\\s*class\\s+(\\w+)/, "class"],
469
+ [/^\\s*interface\\s+(\\w+)/, "interface"],
470
+ [/^\\s*data\\s+class\\s+(\\w+)/, "class"],
471
+ [/^\\s*object\\s+(\\w+)/, "object"],
472
+ [/^\\s*def\\s+(\\w+)/, "function"],
473
+ [/^\\s*func\\s+(\\w+)/, "function"],
271
474
  [/^\\s+(async\\s+)?(\\w+)\\s*\\(/, "method"],
272
475
  ]
273
476
  for (const [pattern, kind] of patterns) {
@@ -295,7 +498,7 @@ export const lsp_workspace_symbols = tool({
295
498
  execute: async ({ query }) => {
296
499
  try {
297
500
  const result = execSync(
298
- \`grep -rn --include="*.ts" --include="*.tsx" -E "export.*(function|class|const|interface|type).*\${query}" .\`,
501
+ \`grep -rn ${grepExtensions} -E "export.*(function|class|const|interface|type|fun|def|func).*\${query}" .\`,
299
502
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
300
503
  )
301
504
  return { query, matches: result.split("\\n").filter(Boolean).slice(0, 15) }
@@ -307,7 +510,7 @@ export const lsp_workspace_symbols = tool({
307
510
 
308
511
  export const lsp_prepare_rename = tool({
309
512
  name: "lsp_prepare_rename",
310
- description: "Check if the symbol at the given position can be safely renamed. Returns the symbol name and its range.",
513
+ description: "Check if the symbol at the given position can be safely renamed.",
311
514
  parameters: z.object({
312
515
  file: z.string().describe("Source file path"),
313
516
  line: z.number().describe("Line number (1-indexed)"),
@@ -328,8 +531,7 @@ export const lsp_prepare_rename = tool({
328
531
  return { canRename: false, reason: "No symbol at the given position" }
329
532
  }
330
533
 
331
- // Symbols that cannot be renamed: language keywords
332
- const KEYWORDS = new Set(["const", "let", "var", "function", "class", "import", "export", "return", "if", "else", "for", "while"])
534
+ const KEYWORDS = new Set(["const", "let", "var", "function", "class", "import", "export", "return", "if", "else", "for", "while", "fun", "val", "def", "func", "package", "object", "data"])
333
535
  if (KEYWORDS.has(symbol)) {
334
536
  return { canRename: false, reason: \`'\${symbol}' is a language keyword\` }
335
537
  }
@@ -367,10 +569,10 @@ export const lsp_rename = tool({
367
569
  const oldName = (/[\\w$]+$/.exec(before)?.[0] ?? "") + (/^[\\w$]*/.exec(after)?.[0] ?? "")
368
570
  if (!oldName) return { error: "No symbol at position" }
369
571
 
370
- const result = execSync(\`grep -rln --include="*.ts" --include="*.tsx" "\\\\b\${oldName}\\\\b" .\`, {
371
- encoding: "utf-8",
372
- stdio: ["pipe", "pipe", "pipe"],
373
- })
572
+ const result = execSync(
573
+ \`grep -rln ${grepExtensions} "\\\\b\${oldName}\\\\b" .\`,
574
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
575
+ )
374
576
  const files = result.split("\\n").filter(Boolean)
375
577
  return {
376
578
  oldName,
@@ -385,8 +587,47 @@ export const lsp_rename = tool({
385
587
  },
386
588
  })
387
589
  `;
590
+ }
591
+ function getLspConfig(projectType, isKotlin) {
592
+ if (projectType === "java" && isKotlin) {
593
+ return {
594
+ typeCheckCmd: "./gradlew compileKotlin --console=plain",
595
+ grepExtensions: '--include="*.kt" --include="*.kts" --include="*.java"',
596
+ };
597
+ }
598
+ switch (projectType) {
599
+ case "node-nextjs":
600
+ case "node-generic":
601
+ return {
602
+ typeCheckCmd: "npx tsc --noEmit --pretty false",
603
+ grepExtensions: '--include="*.ts" --include="*.tsx"',
604
+ };
605
+ case "python":
606
+ return {
607
+ typeCheckCmd: "mypy --no-error-summary",
608
+ grepExtensions: '--include="*.py"',
609
+ };
610
+ case "go":
611
+ return {
612
+ typeCheckCmd: "go vet ./...",
613
+ grepExtensions: '--include="*.go"',
614
+ };
615
+ case "java":
616
+ return {
617
+ typeCheckCmd: "./gradlew compileJava --console=plain",
618
+ grepExtensions: '--include="*.java"',
619
+ };
620
+ case "generic":
621
+ default:
622
+ return {
623
+ typeCheckCmd: "echo 'No type checker configured'",
624
+ grepExtensions: '--include="*.ts" --include="*.tsx" --include="*.py" --include="*.go" --include="*.java" --include="*.kt"',
625
+ };
626
+ }
627
+ }
388
628
  // ─── AST Grep ─────────────────────────────────────────────────────────────────
389
- const AST_GREP = `import { tool } from "@opencode-ai/plugin"
629
+ function astGrep(defaultLang) {
630
+ return `import { tool } from "@opencode-ai/plugin"
390
631
  import { z } from "zod"
391
632
  import { execSync } from "child_process"
392
633
 
@@ -398,7 +639,6 @@ function runAstGrep(args: string): { output: string; error?: string } {
398
639
  })
399
640
  return { output }
400
641
  } catch (e: any) {
401
- // ast-grep may not be installed
402
642
  if (e.code === "ENOENT" || e.message?.includes("not found")) {
403
643
  return { output: "", error: "ast-grep not installed. Run: npm install -g @ast-grep/cli" }
404
644
  }
@@ -411,7 +651,7 @@ export const ast_grep_search = tool({
411
651
  description: "Search for code patterns using AST matching. More precise than text search. Use meta-variables: $NAME (single node), $$$ARGS (multiple nodes).",
412
652
  parameters: z.object({
413
653
  pattern: z.string().describe("AST pattern with meta-variables. E.g.: 'console.log($MSG)', 'function $NAME($$$ARGS)'"),
414
- language: z.enum(["typescript", "javascript", "tsx", "python", "rust", "go"]).default("typescript"),
654
+ language: z.enum(["typescript", "javascript", "tsx", "python", "rust", "go", "java", "kotlin"]).default("${defaultLang}"),
415
655
  path: z.string().optional().describe("Directory or file to search (defaults to current directory)"),
416
656
  maxResults: z.number().optional().default(20),
417
657
  }),
@@ -441,7 +681,7 @@ export const ast_grep_replace = tool({
441
681
  parameters: z.object({
442
682
  pattern: z.string().describe("Pattern to match (use meta-variables like $NAME, $$$ARGS)"),
443
683
  replacement: z.string().describe("Replacement pattern (use same meta-variables)"),
444
- language: z.enum(["typescript", "javascript", "tsx", "python", "rust", "go"]).default("typescript"),
684
+ language: z.enum(["typescript", "javascript", "tsx", "python", "rust", "go", "java", "kotlin"]).default("${defaultLang}"),
445
685
  path: z.string().optional().describe("Directory or file to transform"),
446
686
  dryRun: z.boolean().optional().default(true).describe("Preview changes without applying (default: true)"),
447
687
  }),
@@ -465,4 +705,5 @@ export const ast_grep_replace = tool({
465
705
  },
466
706
  })
467
707
  `;
708
+ }
468
709
  //# sourceMappingURL=tools.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/templates/tools.ts"],"names":[],"mappings":";;;;;AAGA,gCAOC;AAVD,2BAAkC;AAClC,gDAAuB;AAEvB,SAAgB,UAAU,CAAC,UAAkB;IAC3C,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,CAAA;IAE5D,IAAA,kBAAa,EAAC,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,EAAE,YAAY,CAAC,CAAA;IACnE,IAAA,kBAAa,EAAC,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,iBAAiB,CAAC,EAAE,YAAY,CAAC,CAAA;IACnE,IAAA,kBAAa,EAAC,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAA;IACjD,IAAA,kBAAa,EAAC,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,QAAQ,CAAC,CAAA;AAC7D,CAAC;AAED,iFAAiF;AAEjF,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgFpB,CAAA;AAED,iFAAiF;AAEjF,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgEpB,CAAA;AAED,iFAAiF;AAEjF,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8NX,CAAA;AAED,iFAAiF;AAEjF,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8EhB,CAAA"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/templates/tools.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kleber.mottajr/juninho",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Bootstrap the Agentic Coding Framework into any OpenCode project",
5
5
  "bin": {
6
6
  "juninho": "dist/cli.js"