ga-plugins-cli 0.1.0 → 0.1.1

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 (55) hide show
  1. package/dist/config-patcher.d.ts +20 -50
  2. package/dist/config-patcher.d.ts.map +1 -1
  3. package/dist/config-patcher.js +138 -102
  4. package/dist/config-patcher.js.map +1 -1
  5. package/dist/index.js +41 -5
  6. package/dist/index.js.map +1 -1
  7. package/dist/installer.d.ts +0 -18
  8. package/dist/installer.d.ts.map +1 -1
  9. package/dist/installer.js +19 -39
  10. package/dist/installer.js.map +1 -1
  11. package/dist/types.d.ts +10 -6
  12. package/dist/types.d.ts.map +1 -1
  13. package/dist/uninstaller.d.ts +0 -23
  14. package/dist/uninstaller.d.ts.map +1 -1
  15. package/dist/uninstaller.js +22 -68
  16. package/dist/uninstaller.js.map +1 -1
  17. package/package.json +3 -2
  18. package/plugins/go-reviewer/.claude-plugin/plugin.json +12 -0
  19. package/plugins/go-reviewer/commands/go-review.md +424 -0
  20. package/plugins/go-reviewer/mcp-servers/go-reviewer-mcp/README.md +236 -0
  21. package/plugins/go-reviewer/mcp-servers/go-reviewer-mcp/main.go +678 -0
  22. package/plugins/go-scaffolder/.claude-plugin/plugin.json +12 -0
  23. package/plugins/go-scaffolder/commands/scaffold-service.md +802 -0
  24. package/plugins/go-scaffolder/reference-service/.env.example +27 -0
  25. package/plugins/go-scaffolder/reference-service/Dockerfile +55 -0
  26. package/plugins/go-scaffolder/reference-service/REFERENCE-SERVICE-NOTICE.md +104 -0
  27. package/plugins/go-scaffolder/reference-service/cmd/server/main.go +266 -0
  28. package/plugins/go-scaffolder/reference-service/config/config.go +67 -0
  29. package/plugins/go-scaffolder/reference-service/go.mod +17 -0
  30. package/plugins/go-scaffolder/reference-service/internal/domain/booking.go +118 -0
  31. package/plugins/go-scaffolder/reference-service/internal/handler/booking.go +242 -0
  32. package/plugins/go-scaffolder/reference-service/internal/handler/booking_test.go +451 -0
  33. package/plugins/go-scaffolder/reference-service/internal/repository/booking_postgres.go +124 -0
  34. package/plugins/go-scaffolder/reference-service/internal/usecase/booking.go +181 -0
  35. package/plugins/go-standards/.claude-plugin/plugin.json +22 -0
  36. package/plugins/go-standards/commands/go-standards-check.md +232 -0
  37. package/plugins/go-standards/skills/concurrency.md +336 -0
  38. package/plugins/go-standards/skills/config.md +267 -0
  39. package/plugins/go-standards/skills/error-handling.md +286 -0
  40. package/plugins/go-standards/skills/http-chi.md +390 -0
  41. package/plugins/go-standards/skills/logging-observability.md +340 -0
  42. package/plugins/go-standards/skills/naming-and-style.md +315 -0
  43. package/plugins/go-standards/skills/project-layout.md +313 -0
  44. package/plugins/go-standards/skills/testing.md +366 -0
  45. package/plugins/java2go-porter/.claude-plugin/plugin.json +21 -0
  46. package/plugins/java2go-porter/agents/analyzer.md +232 -0
  47. package/plugins/java2go-porter/agents/reviewer.md +241 -0
  48. package/plugins/java2go-porter/agents/test-pairer.md +365 -0
  49. package/plugins/java2go-porter/agents/translator.md +419 -0
  50. package/plugins/java2go-porter/commands/port-java-service.md +149 -0
  51. package/plugins/java2go-porter/skills/idiom-mapping.md +75 -0
  52. package/plugins/migration-safety/.claude-plugin/plugin.json +20 -0
  53. package/plugins/migration-safety/commands/gen-characterization-test.md +452 -0
  54. package/plugins/migration-safety/commands/strangler-plan.md +356 -0
  55. package/plugins/migration-safety/skills/strangler-fig.md +382 -0
@@ -0,0 +1,419 @@
1
+ # Agent: translator
2
+
3
+ You are the **Translator** agent in the java2go-porter pipeline. Your job is to read `ANALYSIS.md` and the original Java source, then produce idiomatic Go draft files following the chi + go-ga-lib conventions used at Garuda Airlines. Every file you produce is a DRAFT. Every ambiguity becomes a `// TODO(human):` comment. You do NOT make architectural decisions — you surface them.
4
+
5
+ ---
6
+
7
+ ## Inputs
8
+
9
+ - `<analysis-path>`: path to `ANALYSIS.md` produced by the analyzer agent
10
+ - `<java-path>`: original Java source directory (for reference during translation)
11
+ - `<go-service-name>`: target Go service name (e.g., `flight-ops-svc`)
12
+ - `<java-service-name>`: source Java service name (e.g., `booking-java-svc`)
13
+ - `<total-java-services>`: how many Java services are being merged into `<go-service-name>`
14
+ - `<output-path>`: root output directory (`<go-service-name>/draft-<java-service-name>/`)
15
+
16
+ ---
17
+
18
+ ## Mandatory File Header
19
+
20
+ Every Go file you produce MUST begin with this comment block:
21
+
22
+ ```go
23
+ // AUTO-GENERATED DRAFT — requires human review
24
+ // Source: <java-service-name> (Java/Spring Boot)
25
+ // Target: <go-service-name> (Go, chi + go-ga-lib)
26
+ // Generated by: java2go-porter translator agent
27
+ // DO NOT MERGE until REVIEW.md is resolved and all tests pass.
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Idiom Mapping Reference
33
+
34
+ Before translating any pattern, consult `skills/idiom-mapping.md`. Use the DECIDED mapping if one exists. If the mapping is marked NEEDS DECISION, use the first listed option as the default AND add a `// TODO(human): NEEDS DECISION — see idiom-mapping.md: <pattern name>` comment at every usage site.
35
+
36
+ ---
37
+
38
+ ## Output Structure
39
+
40
+ Write all files under `<output-path>/`. The layout must be:
41
+
42
+ ```
43
+ <output-path>/
44
+ cmd/
45
+ main.go # entry point: wire dependencies, start server
46
+ config/
47
+ config.go # env-tagged config struct
48
+ internal/
49
+ handler/
50
+ <resource>_handler.go # one file per REST resource group
51
+ router.go # chi router setup
52
+ usecase/
53
+ <resource>_usecase.go # one file per use case group
54
+ interfaces.go # usecase interfaces (for testability)
55
+ domain/
56
+ <entity>.go # domain structs (from JPA entities)
57
+ interfaces.go # repository interfaces
58
+ repository/
59
+ <entity>_repository.go # repository implementations using go-ga-lib DB
60
+ go.mod # module: github.com/zokypesch/go-ga-lib
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Translation Rules
66
+
67
+ ### Rule 1: @RestController → chi Handler
68
+
69
+ For each endpoint in ANALYSIS.md Section 2:
70
+
71
+ ```go
72
+ // internal/handler/<resource>_handler.go
73
+
74
+ package handler
75
+
76
+ import (
77
+ "encoding/json"
78
+ "net/http"
79
+
80
+ "github.com/go-chi/chi/v5"
81
+ "github.com/zokypesch/go-ga-lib/response"
82
+ "<module>/internal/usecase"
83
+ )
84
+
85
+ type BookingHandler struct {
86
+ uc usecase.BookingUsecase
87
+ }
88
+
89
+ func NewBookingHandler(uc usecase.BookingUsecase) *BookingHandler {
90
+ return &BookingHandler{uc: uc}
91
+ }
92
+
93
+ // RegisterRoutes registers all booking-related routes on the given chi router.
94
+ func (h *BookingHandler) RegisterRoutes(r chi.Router) {
95
+ r.Post("/api/v1/bookings", h.CreateBooking)
96
+ r.Get("/api/v1/bookings/{id}", h.GetBooking)
97
+ // TODO(human): verify path prefixes match the Java @RequestMapping base path
98
+ }
99
+
100
+ func (h *BookingHandler) CreateBooking(w http.ResponseWriter, r *http.Request) {
101
+ var req CreateBookingRequest
102
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
103
+ response.Error(w, http.StatusBadRequest, "invalid request body")
104
+ return
105
+ }
106
+ // TODO(human): validate request fields — Java used @Valid; add validation here
107
+ result, err := h.uc.CreateBooking(r.Context(), req)
108
+ if err != nil {
109
+ // TODO(human): map domain errors to HTTP status codes — see REVIEW.md error mapping section
110
+ response.Error(w, http.StatusInternalServerError, err.Error())
111
+ return
112
+ }
113
+ response.JSON(w, http.StatusCreated, result)
114
+ }
115
+ ```
116
+
117
+ Rules:
118
+ - Use `response.JSON` and `response.Error` from go-ga-lib for all responses
119
+ - Map `@PathVariable` → `chi.URLParam(r, "paramName")`
120
+ - Map `@RequestParam` → `r.URL.Query().Get("paramName")`
121
+ - Map `@RequestBody` → `json.NewDecoder(r.Body).Decode(&req)`
122
+ - Every `@PreAuthorize` annotation → `// TODO(human): add authorization middleware for this route`
123
+ - Every `@Valid` annotation → `// TODO(human): add input validation (consider go-playground/validator)`
124
+
125
+ ---
126
+
127
+ ### Rule 2: @Service → Usecase
128
+
129
+ ```go
130
+ // internal/usecase/<resource>_usecase.go
131
+
132
+ package usecase
133
+
134
+ import (
135
+ "context"
136
+
137
+ "<module>/internal/domain"
138
+ )
139
+
140
+ // BookingUsecase defines the business operations for bookings.
141
+ // AUTO-GENERATED DRAFT — requires human review
142
+ type BookingUsecase interface {
143
+ CreateBooking(ctx context.Context, req CreateBookingRequest) (*domain.Booking, error)
144
+ CancelBooking(ctx context.Context, id string) error
145
+ }
146
+
147
+ type bookingUsecase struct {
148
+ repo domain.BookingRepository
149
+ flight domain.FlightClient
150
+ // TODO(human): if <total-java-services> > 1, verify that shared dependencies
151
+ // (e.g., flight client) are not duplicated across merged use cases.
152
+ }
153
+
154
+ func NewBookingUsecase(repo domain.BookingRepository, flight domain.FlightClient) BookingUsecase {
155
+ return &bookingUsecase{repo: repo, flight: flight}
156
+ }
157
+ ```
158
+
159
+ Rules:
160
+ - Every `@Autowired` dependency → explicit constructor parameter
161
+ - Every `@Transactional` method → `// TODO(human): wrap in explicit DB transaction — see idiom-mapping.md: @Transactional`
162
+ - Static fields / Singleton state → `// TODO(human): SHARED STATE — verify thread-safety in Go (goroutine-safe?)`
163
+ - AOP aspects → `// TODO(human): Java had @Aspect <AspectName> here — replicate with middleware or explicit call`
164
+
165
+ ---
166
+
167
+ ### Rule 3: @Repository → domain Interface + Repository Implementation
168
+
169
+ Domain interface (no DB import — pure Go):
170
+
171
+ ```go
172
+ // internal/domain/interfaces.go
173
+
174
+ package domain
175
+
176
+ import "context"
177
+
178
+ // BookingRepository defines persistence operations for Booking.
179
+ // AUTO-GENERATED DRAFT — requires human review
180
+ type BookingRepository interface {
181
+ FindByID(ctx context.Context, id string) (*Booking, error)
182
+ Save(ctx context.Context, b *Booking) error
183
+ FindByFlightIDAndStatus(ctx context.Context, flightID string, status BookingStatus) ([]*Booking, error)
184
+ // TODO(human): verify all custom query methods from Java BookingRepository are covered
185
+ }
186
+ ```
187
+
188
+ Implementation using go-ga-lib:
189
+
190
+ ```go
191
+ // internal/repository/booking_repository.go
192
+
193
+ package repository
194
+
195
+ import (
196
+ "context"
197
+
198
+ "github.com/zokypesch/go-ga-lib/database"
199
+ "<module>/internal/domain"
200
+ )
201
+
202
+ type bookingRepository struct {
203
+ db *database.DB
204
+ }
205
+
206
+ func NewBookingRepository(db *database.DB) domain.BookingRepository {
207
+ return &bookingRepository{db: db}
208
+ }
209
+
210
+ func (r *bookingRepository) FindByID(ctx context.Context, id string) (*domain.Booking, error) {
211
+ // TODO(human): write the SQL query — Java used Spring Data JPA findById()
212
+ // Suggested query: SELECT * FROM bookings WHERE id = $1
213
+ var b domain.Booking
214
+ if err := r.db.QueryRowContext(ctx, "SELECT id, status, flight_id, created_at FROM bookings WHERE id = $1", id).Scan(&b.ID, &b.Status, &b.FlightID, &b.CreatedAt); err != nil {
215
+ return nil, err
216
+ }
217
+ return &b, nil
218
+ }
219
+ ```
220
+
221
+ Rules for lazy-loading (from ANALYSIS.md Section 4.3):
222
+ - Every `FetchType.LAZY` relationship → do NOT embed in primary query
223
+ - Add `// TODO(human): LAZY LOADING — Java loaded <RelatedEntity> lazily; decide: (a) JOIN in same query, (b) separate query on demand, (c) always omit`
224
+ - Do NOT silently include the relation in the struct scan
225
+
226
+ ---
227
+
228
+ ### Rule 4: JPA Entity → Go Struct
229
+
230
+ ```go
231
+ // internal/domain/booking.go
232
+
233
+ package domain
234
+
235
+ import "time"
236
+
237
+ // Booking is the domain model translated from BookingEntity.
238
+ // AUTO-GENERATED DRAFT — requires human review
239
+ type Booking struct {
240
+ ID string `json:"id" db:"id"`
241
+ Status BookingStatus `json:"status" db:"status"`
242
+ FlightID string `json:"flight_id" db:"flight_id"`
243
+ CreatedAt time.Time `json:"created_at" db:"created_at"`
244
+ // TODO(human): BookingEntity had @OneToMany Passengers — omitted here (lazy-loading).
245
+ // Decide whether to include inline or fetch separately.
246
+ }
247
+
248
+ type BookingStatus string
249
+
250
+ const (
251
+ BookingStatusPending BookingStatus = "PENDING"
252
+ BookingStatusConfirmed BookingStatus = "CONFIRMED"
253
+ BookingStatusCancelled BookingStatus = "CANCELLED"
254
+ )
255
+ ```
256
+
257
+ Rules:
258
+ - Lombok `@Data` → plain struct (no generated methods) with `// TODO(human): add String(), Equal() if needed`
259
+ - `UUID` → `string` with `// TODO(human): consider using github.com/google/uuid`
260
+ - `LocalDateTime` / `ZonedDateTime` → `time.Time`
261
+ - `BigDecimal` → `float64` + `// TODO(human): consider shopspring/decimal for monetary values`
262
+ - Enum → Go `type <Name> string` with `const` block
263
+
264
+ ---
265
+
266
+ ### Rule 5: @Value / application.properties → Config Struct
267
+
268
+ ```go
269
+ // config/config.go
270
+
271
+ package config
272
+
273
+ import (
274
+ "github.com/zokypesch/go-ga-lib/config"
275
+ )
276
+
277
+ // Config holds all configuration for <java-service-name> translated service.
278
+ // AUTO-GENERATED DRAFT — requires human review
279
+ type Config struct {
280
+ // Database
281
+ DatabaseURL string `env:"DATABASE_URL" required:"true"`
282
+ DatabaseMaxConns int `env:"DATABASE_MAX_CONNS" default:"10"`
283
+
284
+ // Business rules from application.properties
285
+ MaxSeatsPerBooking int `env:"MAX_SEATS_PER_BOOKING" default:"9"`
286
+ // TODO(human): verify this default matches the Java default: booking.max-seats-per-booking=9
287
+
288
+ // External services
289
+ FlightServiceURL string `env:"FLIGHT_SERVICE_URL" required:"true"`
290
+
291
+ // Server
292
+ Port string `env:"PORT" default:"8080"`
293
+ }
294
+
295
+ // Load reads config from environment using go-ga-lib.
296
+ func Load() (*Config, error) {
297
+ var cfg Config
298
+ if err := config.Load(&cfg); err != nil {
299
+ return nil, err
300
+ }
301
+ return &cfg, nil
302
+ }
303
+ ```
304
+
305
+ ---
306
+
307
+ ### Rule 6: cmd/main.go — Dependency Wiring
308
+
309
+ ```go
310
+ // cmd/main.go
311
+ // AUTO-GENERATED DRAFT — requires human review
312
+ // Source: <java-service-name> (Java/Spring Boot)
313
+
314
+ package main
315
+
316
+ import (
317
+ "log"
318
+ "net/http"
319
+
320
+ "github.com/go-chi/chi/v5"
321
+ "github.com/go-chi/chi/v5/middleware"
322
+ "github.com/zokypesch/go-ga-lib/database"
323
+ "github.com/zokypesch/go-ga-lib/logger"
324
+
325
+ "<module>/config"
326
+ "<module>/internal/handler"
327
+ "<module>/internal/repository"
328
+ "<module>/internal/usecase"
329
+ )
330
+
331
+ func main() {
332
+ cfg, err := config.Load()
333
+ if err != nil {
334
+ log.Fatalf("failed to load config: %v", err)
335
+ }
336
+
337
+ log := logger.New()
338
+
339
+ db, err := database.New(cfg.DatabaseURL)
340
+ if err != nil {
341
+ log.Fatal("failed to connect to database", "error", err)
342
+ }
343
+ defer db.Close()
344
+
345
+ // Repositories
346
+ bookingRepo := repository.NewBookingRepository(db)
347
+ // TODO(human): wire additional repositories from consolidated Java services
348
+
349
+ // External clients
350
+ // TODO(human): wire FlightClient HTTP client — go-ga-lib HTTP client pattern
351
+ // TODO(human): wire message broker if Kafka/RabbitMQ was used — see ANALYSIS.md Section 5
352
+
353
+ // Use cases
354
+ bookingUC := usecase.NewBookingUsecase(bookingRepo /*, flightClient */)
355
+ // TODO(human): wire additional use cases from consolidated Java services
356
+
357
+ // Handlers
358
+ bookingHandler := handler.NewBookingHandler(bookingUC)
359
+
360
+ r := chi.NewRouter()
361
+ r.Use(middleware.Logger)
362
+ r.Use(middleware.Recoverer)
363
+ r.Use(middleware.RequestID)
364
+ // TODO(human): add authentication middleware (JWT filter was present in Java — see ANALYSIS.md Section 9)
365
+
366
+ bookingHandler.RegisterRoutes(r)
367
+ // TODO(human): register routes for additional handlers from consolidated services
368
+
369
+ log.Info("starting server", "port", cfg.Port)
370
+ if err := http.ListenAndServe(":"+cfg.Port, r); err != nil {
371
+ log.Fatal("server error", "error", err)
372
+ }
373
+ }
374
+ ```
375
+
376
+ ---
377
+
378
+ ### Rule 7: Consolidation Markers
379
+
380
+ If `<total-java-services>` > 1, add at the top of `cmd/main.go` and `internal/usecase/interfaces.go`:
381
+
382
+ ```go
383
+ // TODO(human): CONSOLIDATION — this draft covers 1 of <total-java-services> Java services
384
+ // being merged into <go-service-name>. Do not finalize wiring until all
385
+ // <total-java-services> drafts are reviewed together.
386
+ // Consolidation decisions (shared domain models, merged DB schemas, unified error codes)
387
+ // are NOT made by this tool — they require architectural review.
388
+ ```
389
+
390
+ ---
391
+
392
+ ### Rule 8: go.mod
393
+
394
+ ```
395
+ module github.com/zokypesch/go-ga-lib/<go-service-name>
396
+
397
+ go 1.22
398
+
399
+ require (
400
+ github.com/go-chi/chi/v5 v5.0.12
401
+ github.com/zokypesch/go-ga-lib v0.0.0 // TODO(human): pin to correct version
402
+ github.com/stretchr/testify v1.9.0
403
+ // TODO(human): add other dependencies as needed
404
+ )
405
+ ```
406
+
407
+ ---
408
+
409
+ ## Completion
410
+
411
+ After writing all files, print:
412
+
413
+ ```
414
+ TRANSLATOR COMPLETE
415
+ Files written to: <output-path>/
416
+ TODO(human) comments added: <count>
417
+ NEEDS DECISION items (from idiom-mapping.md): <list patterns that were NEEDS DECISION>
418
+ Proceed to test-pairer agent.
419
+ ```
@@ -0,0 +1,149 @@
1
+ # Command: port-java-service
2
+
3
+ Translate a Java/Spring Boot service folder into a Go draft using the chi router and go-ga-lib framework. Output is a DRAFT only — it requires human review and green tests before any merge.
4
+
5
+ ---
6
+
7
+ ## Step 1: Gather Inputs
8
+
9
+ ### 1.1 Java Service Path
10
+
11
+ If `<java-path>` was passed as an argument, use it directly. Otherwise, ask the user:
12
+
13
+ > "Please provide the path to the Java/Spring Boot service folder you want to port (e.g., `services/booking-svc` or an absolute path)."
14
+
15
+ Validate that the path exists and contains at least one `.java` file. If not, inform the user and ask again.
16
+
17
+ ### 1.2 Target Go Service Name
18
+
19
+ Ask the user:
20
+
21
+ > "What is the name of the target Go service this Java code will be merged into?
22
+ > (Because 10 Java services consolidate into 1 Go service, we need to know the destination Go service name.
23
+ > Example: `flight-ops-svc`)"
24
+
25
+ Record the answer as `<go-service-name>`. This becomes the top-level output folder.
26
+
27
+ ### 1.3 Consolidation Context
28
+
29
+ Ask the user:
30
+
31
+ > "Is this one of multiple Java services being ported into the same Go service?
32
+ > - If YES: how many Java services total will be merged into `<go-service-name>`? (e.g., 10)
33
+ > - If NO: just this one Java service is mapping to the Go service."
34
+
35
+ Record the total count as `<total-java-services>`. If the user says YES, note this context so the translator can leave appropriate `// TODO(human): consolidation` markers for shared logic.
36
+
37
+ ---
38
+
39
+ ## Step 2: Derive Names and Paths
40
+
41
+ - `<java-service-name>`: the leaf folder name of `<java-path>` (e.g., `booking-java-svc`)
42
+ - Output root: `<go-service-name>/draft-<java-service-name>/`
43
+
44
+ Example: if go-service-name is `flight-ops-svc` and java-service-name is `booking-java-svc`, output goes into `flight-ops-svc/draft-booking-java-svc/`.
45
+
46
+ ---
47
+
48
+ ## Step 3: Run the Pipeline
49
+
50
+ Execute the following agents **in sequence**. Each agent reads the previous agent's output. Do not skip any step.
51
+
52
+ ### 3.1 Analyzer Agent
53
+
54
+ Invoke the `analyzer` agent against `<java-path>`.
55
+
56
+ The analyzer will:
57
+ - Read the Java source tree under `<java-path>`
58
+ - Produce `<go-service-name>/draft-<java-service-name>/ANALYSIS.md`
59
+
60
+ Wait for the analyzer to complete and confirm `ANALYSIS.md` was written before proceeding.
61
+
62
+ ### 3.2 Translator Agent
63
+
64
+ Invoke the `translator` agent with:
65
+ - Input: `<go-service-name>/draft-<java-service-name>/ANALYSIS.md` and the original Java source at `<java-path>`
66
+ - Context: `<go-service-name>`, `<java-service-name>`, `<total-java-services>`
67
+
68
+ The translator will:
69
+ - Read `ANALYSIS.md` and the Java source
70
+ - Consult `skills/idiom-mapping.md` for every translation decision
71
+ - Write Go source files into `<go-service-name>/draft-<java-service-name>/`:
72
+ - `internal/handler/` — chi HTTP handlers
73
+ - `internal/usecase/` — business logic
74
+ - `internal/domain/` — interfaces and domain structs
75
+ - `internal/repository/` — repository implementations
76
+ - `config/config.go` — environment-tagged config struct
77
+ - `cmd/main.go` — entry point wiring
78
+ - Every file begins with: `// AUTO-GENERATED DRAFT — requires human review`
79
+ - Every ambiguity is marked `// TODO(human): <description>`
80
+
81
+ Wait for the translator to complete before proceeding.
82
+
83
+ ### 3.3 Test-Pairer Agent
84
+
85
+ Invoke the `test-pairer` agent with:
86
+ - Input: Java source at `<java-path>` + all Go files in `<go-service-name>/draft-<java-service-name>/`
87
+
88
+ The test-pairer will:
89
+ - Generate `*_test.go` files alongside the corresponding Go files
90
+ - Produce table-driven characterization tests
91
+ - Mark tests that need real Java runtime data with `t.Skip("NEEDS GOLDEN DATA: ...")`
92
+
93
+ Wait for the test-pairer to complete before proceeding.
94
+
95
+ ### 3.4 Reviewer Agent
96
+
97
+ Invoke the `reviewer` agent with:
98
+ - Input: all files in `<go-service-name>/draft-<java-service-name>/`
99
+
100
+ The reviewer will:
101
+ - Produce `<go-service-name>/draft-<java-service-name>/REVIEW.md`
102
+ - Categorize all risks into MUST DECIDE / SHOULD VERIFY / LOW RISK
103
+ - Include a human sign-off checklist
104
+
105
+ ---
106
+
107
+ ## Step 4: Summarize Output
108
+
109
+ After all agents complete, print a directory summary. Example:
110
+
111
+ ```
112
+ Draft created at: flight-ops-svc/draft-booking-java-svc/
113
+ ANALYSIS.md (analyzer output)
114
+ REVIEW.md (reviewer output — START HERE)
115
+ internal/handler/ (chi handlers)
116
+ internal/usecase/ (business logic)
117
+ internal/domain/ (interfaces + structs)
118
+ internal/repository/ (repository implementations)
119
+ config/config.go (env-tagged config)
120
+ cmd/main.go (entry point)
121
+ internal/handler/*_test.go
122
+ internal/usecase/*_test.go
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Step 5: Final Warning Message
128
+
129
+ End the command with this exact message (substituting values):
130
+
131
+ ```
132
+ ==========================================================================
133
+ DRAFT ONLY — DO NOT MERGE UNTIL ALL THREE CONDITIONS ARE MET:
134
+
135
+ (a) A human has read REVIEW.md and resolved every item in MUST DECIDE.
136
+ (b) All *_test.go files pass (go test ./...) with no skipped golden-data
137
+ tests still unresolved.
138
+ (c) The team has completed the consolidation plan for <go-service-name>
139
+ (this is <N> of <total-java-services> Java services being merged —
140
+ consolidation decisions are NOT made by this tool).
141
+
142
+ Next suggested steps:
143
+ 1. Open <go-service-name>/draft-<java-service-name>/REVIEW.md
144
+ 2. Resolve every MUST DECIDE item with your team
145
+ 3. Capture golden data for any t.Skip("NEEDS GOLDEN DATA") tests
146
+ 4. Run: go test ./... inside the draft folder
147
+ 5. Schedule consolidation review with the architecture team
148
+ ==========================================================================
149
+ ```
@@ -0,0 +1,75 @@
1
+ # Skill: idiom-mapping
2
+
3
+ Java→Go translation reference for the java2go-porter pipeline at Garuda Airlines.
4
+ Module: `github.com/zokypesch/go-ga-lib`. Router: `chi`. Target: idiomatic Go 1.22+.
5
+
6
+ This file is consumed by the **translator** and **reviewer** agents. For every Java pattern encountered, find the row below. If the Decision Status is DECIDED, use the Go Equivalent column as-is. If it is NEEDS DECISION, use Option A as the default AND add a `// TODO(human): NEEDS DECISION — idiom-mapping: <Row Name>` comment at every usage site.
7
+
8
+ ---
9
+
10
+ ## Mapping Table
11
+
12
+ | # | Java Pattern | Go Equivalent | Decision Status | Notes / Options |
13
+ |---|-------------|---------------|----------------|----------------|
14
+ | 1 | `@Service` / `@Component` | Plain Go struct with a constructor function `func NewXxx(deps...) *Xxx` | DECIDED | No magic. DI is explicit via constructor parameters. Register in `cmd/main.go`. |
15
+ | 2 | `@Repository` | Interface in `internal/domain/interfaces.go` + struct implementation in `internal/repository/` using go-ga-lib DB client | DECIDED | Interface lives in domain (no DB import). Implementation imports go-ga-lib. Keeps domain package dependency-free. |
16
+ | 3 | `@RestController` + `@RequestMapping` | chi router + handler struct in `internal/handler/`. `RegisterRoutes(r chi.Router)` method on handler. | DECIDED | Use `github.com/go-chi/chi/v5`. Group routes by resource. |
17
+ | 4 | `@GetMapping` / `@PostMapping` / `@PutMapping` / `@DeleteMapping` / `@PatchMapping` | `r.Get(path, handler)` / `r.Post(...)` / `r.Put(...)` / `r.Delete(...)` / `r.Patch(...)` | DECIDED | Path params: `{id}` in chi, read with `chi.URLParam(r, "id")`. |
18
+ | 5 | `@PathVariable` | `chi.URLParam(r, "paramName")` | DECIDED | Always check for empty string — chi returns `""` if param is not in route pattern. |
19
+ | 6 | `@RequestParam` | `r.URL.Query().Get("paramName")` | DECIDED | For required params, return 400 if empty. For optional, use default value. |
20
+ | 7 | `@RequestBody` | `json.NewDecoder(r.Body).Decode(&req)` | DECIDED | Always check decode error. Use `http.MaxBytesReader` to limit body size. |
21
+ | 8 | `@Autowired` (field injection) | Constructor parameter — no field injection in Go | DECIDED | `// TODO(human): NEEDS DECISION` is NOT needed here — always use constructor injection. |
22
+ | 9 | `@Autowired` (constructor injection) | Same pattern — Go constructor parameter | DECIDED | Direct 1:1 translation. |
23
+ | 10 | `@Value("${key}")` | Field in `Config` struct with `env:"KEY"` tag, loaded by go-ga-lib config loader | DECIDED | All config lives in `config/config.go`. Use `required:"true"` for mandatory keys. Use `default:"value"` for optional. |
24
+ | 11 | `Optional<T>` | `(T, bool)` tuple for simple presence checks; `*T` (pointer) + nil check for domain types | NEEDS DECISION | Option A: `(*T, error)` — idiomatic Go, most common. Option B: `(T, bool)` — explicit, no error info. Option C: keep pointer `*T`, nil means absent. **Trade-off**: Option A is preferred for usecase methods; Option B for simple lookups. `// TODO(human): NEEDS DECISION — idiom-mapping: Optional<T>` |
25
+ | 12 | `Stream().filter().map().collect()` | `for` loop with slice append, or a helper using generics (`slices.Map`, `slices.Filter`) | NEEDS DECISION | Option A: plain `for` loop — most readable, zero dependencies, easiest to debug. Option B: `golang.org/x/exp/slices` functional helpers. Option C: custom helper in go-ga-lib. **Trade-off**: Option A is strongly preferred for maintainability. Only use Option B/C if the pipeline is long and nested. `// TODO(human): NEEDS DECISION — idiom-mapping: Stream.filter.map` |
26
+ | 13 | `try { ... } catch (Exception e) { ... } finally { ... }` | `if err != nil { return err }` for error handling; `defer` for cleanup | DECIDED | Go uses explicit error returns, not exceptions. `finally` → `defer`. Never use `panic` for business logic errors. |
27
+ | 14 | `@Transactional` (method-level) | Explicit: `tx, err := db.BeginTx(ctx, nil)` → `defer tx.Rollback()` → `tx.Commit()` at end | NEEDS DECISION | Option A: wrap method body in explicit Begin/Commit/Rollback — most explicit, copy Java semantics. Option B: use go-ga-lib transaction helper (if available). Option C: outbox pattern for distributed scenarios. **Trade-off**: Option A is the safe default. Option C is needed if @Transactional was spanning a DB write + event publish. `// TODO(human): NEEDS DECISION — idiom-mapping: @Transactional` |
28
+ | 15 | `@Transactional(propagation = REQUIRES_NEW)` | Nested transaction: `tx.SavePoint()` or new `db.BeginTx()` in a goroutine with isolated DB conn | NEEDS DECISION | Go DB driver support for savepoints varies. `// TODO(human): NEEDS DECISION — idiom-mapping: @Transactional REQUIRES_NEW` |
29
+ | 16 | Builder pattern (`SomeClass.builder().field(v).build()`) | Functional options: `func WithField(v T) Option { return func(s *S) { s.field = v } }` OR explicit struct literal `SomeStruct{Field: v}` | NEEDS DECISION | Option A: explicit struct literal — simplest, idiomatic Go, preferred when struct is in same package. Option B: functional options — good for structs with many optional fields and external callers. `// TODO(human): NEEDS DECISION — idiom-mapping: Builder pattern` |
30
+ | 17 | Inheritance / `class Child extends Parent` | Interface embedding + struct composition. `Child` struct embeds `*Parent` struct and implements same interface. | DECIDED | Go has no inheritance. Use composition. Every "is-a" relationship becomes "has-a" + "implements interface". |
31
+ | 18 | Abstract class (with some concrete methods) | Interface (for the abstract contract) + embedded base struct (for the shared concrete methods) | DECIDED | Example: `type BaseHandler struct { logger *zap.Logger }` embedded in concrete handler structs. |
32
+ | 19 | `instanceof` check | Type assertion `v, ok := x.(ConcreteType)` for single type; `switch v := x.(type) { case *A: ... }` for multiple | DECIDED | Prefer type switch for multiple cases. Always handle the `default` case. |
33
+ | 20 | `@Async` on method | `go func() { ... }()` with error propagation via channel or `golang.org/x/sync/errgroup` | NEEDS DECISION | Option A: fire-and-forget goroutine — simple but errors are lost. Option B: `errgroup.Group` — errors collected, panics recovered. Option C: work queue / job system via go-ga-lib. **Trade-off**: Option B is strongly preferred unless truly fire-and-forget. `// TODO(human): NEEDS DECISION — idiom-mapping: @Async` |
34
+ | 21 | `@Scheduled(fixedRate = N)` | `time.NewTicker(duration)` in a goroutine, started in `cmd/main.go`, stopped via context cancellation | DECIDED | Always pass `ctx context.Context` into the ticker goroutine. Use `select { case <-ctx.Done(): return; case <-ticker.C: ... }` pattern. |
35
+ | 22 | JPA `@OneToMany` | Explicit JOIN query in repository, returns slice in parent struct OR separate query on demand | NEEDS DECISION | Option A: JOIN and hydrate parent struct with slice field — single query, larger result set. Option B: N+1 guard — load parent, then call `ListChildrenByParentID()` explicitly at call sites. **Trade-off**: Option B is safer to start; Option A optimizes later. `// TODO(human): NEEDS DECISION — idiom-mapping: @OneToMany` |
36
+ | 23 | JPA `@ManyToOne` | Foreign key field in Go struct + explicit JOIN or separate lookup | DECIDED | Store the FK as `ParentID string`. Include `Parent *ParentType` only if always joined. Never load lazily by default. |
37
+ | 24 | JPA lazy loading (`FetchType.LAZY`) | Separate query on demand, called explicitly at call sites that need the data | DECIDED | Never auto-load. Mark all sites with `// TODO(human): LAZY LOADING — add explicit query here`. |
38
+ | 25 | JPA `@Query("SELECT ...")` custom JPQL | Raw SQL in repository implementation using go-ga-lib DB client | DECIDED | Convert JPQL to standard SQL. Use positional parameters (`$1`, `$2` for PostgreSQL). Verify column names match actual schema. |
39
+ | 26 | Spring `@ExceptionHandler` in `@ControllerAdvice` | chi middleware that wraps all handlers + sentinel error type check in middleware | DECIDED | Pattern: define domain errors as sentinel `var ErrXxx = errors.New("...")`. Handler returns error up. Middleware maps errors to HTTP status. See go-ga-lib response package for `response.Error(w, status, msg)`. |
40
+ | 27 | Lombok `@Data` (generates getter/setter/equals/hashCode/toString) | Plain Go struct with exported fields. Add `String()` method manually only if needed. | DECIDED | No generated code. Export fields directly. If `equals()` semantics matter (e.g., in tests), implement `Equal(other T) bool` method explicitly. |
41
+ | 28 | Lombok `@Builder` | See row 16 (Builder pattern). | NEEDS DECISION | Same options as row 16. |
42
+ | 29 | Lombok `@Slf4j` + `log.info("msg {}", arg)` | `zap.Logger` with structured fields: `logger.Info("msg", zap.String("key", arg))` | DECIDED | Use go-ga-lib logger package to construct `*zap.Logger`. Pass logger as constructor parameter — do NOT use global logger. |
43
+ | 30 | `log.error("msg", exception)` | `logger.Error("msg", zap.Error(err))` | DECIDED | Always pass `zap.Error(err)` as the last field for errors. |
44
+ | 31 | `ApplicationContext.getBean(SomeService.class)` | Not translatable to a runtime lookup. Must be replaced with explicit constructor injection. | MUST DECIDE | This is dynamic bean lookup — no Go equivalent. Every usage site is a `// TODO(human): MUST DECIDE — ApplicationContext.getBean() has no Go equivalent; wire dependency explicitly`. |
45
+ | 32 | `@Scope("prototype")` (new instance per injection) | `NewXxx()` called at each usage site — constructors are cheap in Go | DECIDED | Go structs have no scope concept. If a new instance is needed per request, call the constructor inside the handler function, not once at startup. |
46
+ | 33 | Spring Security `@PreAuthorize("hasRole('ADMIN')")` | chi middleware checking JWT claims, applied per-route or per-group | NEEDS DECISION | Option A: chi `r.With(authMiddleware).Get(...)` per route. Option B: route group with `r.Group(func(r chi.Router) { r.Use(authMiddleware); ... })`. Option C: go-ga-lib auth middleware if available. `// TODO(human): NEEDS DECISION — idiom-mapping: @PreAuthorize` |
47
+ | 34 | Spring Security JWT filter (global) | chi `r.Use(jwtMiddleware)` at router root, before all route registrations | DECIDED | Middleware stack order matters in chi — apply auth middleware BEFORE business route registration. |
48
+ | 35 | `ResponseEntity<T>` with dynamic status | `response.JSON(w, statusCode, body)` from go-ga-lib response package | DECIDED | Status code is set explicitly. No wrapper type needed. |
49
+ | 36 | `@Valid` / `javax.validation` annotations | Manual validation in handler or use `github.com/go-playground/validator` | NEEDS DECISION | Option A: explicit `if req.Field == ""` checks in handler — simple, no dependency. Option B: `validator.v10` struct tags — declarative, mirrors Java `@NotNull`, `@Size`. `// TODO(human): NEEDS DECISION — idiom-mapping: @Valid` |
50
+ | 37 | `@Cacheable` | In-memory cache with `sync.Map` or `github.com/patrickmn/go-cache`; or Redis via go-ga-lib | NEEDS DECISION | Option A: `sync.Map` — built-in, no TTL, simple. Option B: `go-cache` — TTL support, thread-safe. Option C: Redis via go-ga-lib — distributed, consistent across instances. **Trade-off**: If Java cache was local only (single JVM), Option A or B. If multiple Go pods, Option C required. `// TODO(human): NEEDS DECISION — idiom-mapping: @Cacheable` |
51
+ | 38 | `BigDecimal` (monetary / high-precision) | `github.com/shopspring/decimal.Decimal` | NEEDS DECISION | Option A: `shopspring/decimal` — safe for money, immutable, serializable. Option B: `float64` — simple but unsafe for monetary arithmetic (rounding errors). **Garuda context**: aviation fares are monetary values — strongly prefer Option A. `// TODO(human): NEEDS DECISION — idiom-mapping: BigDecimal` |
52
+ | 39 | `UUID` (Java) | `string` (simplest) or `github.com/google/uuid.UUID` (type-safe) | NEEDS DECISION | Option A: `string` — simple, compatible with JSON/DB as-is. Option B: `uuid.UUID` type — type safety, validates format. `// TODO(human): NEEDS DECISION — idiom-mapping: UUID` |
53
+ | 40 | `LocalDateTime` / `ZonedDateTime` | `time.Time` (always store and compare in UTC) | DECIDED | Go `time.Time` includes timezone. Always use `time.UTC` when constructing. When reading from DB: ensure `parseTime=true` in DSN (PostgreSQL handles this natively). |
54
+ | 41 | `enum` with methods (Java) | `type StatusName string` + `const` block + methods on the type | DECIDED | String-backed enum. Add `IsValid() bool` method if validation is needed. Never use `iota` for string enums — makes DB serialization explicit. |
55
+ | 42 | `@Aspect` (AOP) | No direct equivalent. Depending on what the aspect does: logging → middleware or explicit call; transactions → see row 14; auth → middleware | NEEDS DECISION | Each aspect must be evaluated individually. Common cases: (a) logging aspect → add structured log lines at method start/end, or use go-ga-lib observability middleware; (b) transaction aspect → explicit tx wrapping (row 14); (c) retry aspect → `github.com/avast/retry-go`. `// TODO(human): NEEDS DECISION — idiom-mapping: @Aspect; specify what this aspect did` |
56
+ | 43 | Static fields / class-level state (`static`) | Package-level `var` (avoid) or injectable singleton struct field | NEEDS DECISION | Option A: package-level `var` — simple but hidden global state, hard to test. Option B: field on a struct, initialized once in constructor and injected — testable, explicit. **Strongly prefer Option B**. If the Java static field was a cache or counter, protect with `sync.Mutex` or `sync.RWMutex`. `// TODO(human): NEEDS DECISION — idiom-mapping: static field` |
57
+ | 44 | Spring `@EventListener` / `ApplicationEventPublisher` | Explicit function call, channel, or go-ga-lib event bus | NEEDS DECISION | Option A: direct function call in same goroutine — simplest, synchronous. Option B: goroutine + channel — async, like @Async. Option C: go-ga-lib event bus / Kafka producer. **Context**: if events crossed service boundaries in Java, Option C is required. `// TODO(human): NEEDS DECISION — idiom-mapping: ApplicationEventPublisher` |
58
+ | 45 | Spring Retry (`@Retryable`) | `github.com/avast/retry-go` or manual retry loop with backoff | NEEDS DECISION | Option A: `retry-go` library — declarative, similar to @Retryable. Option B: manual loop with `time.Sleep` backoff — more control. `// TODO(human): NEEDS DECISION — idiom-mapping: @Retryable` |
59
+
60
+ ---
61
+
62
+ ## Decision Legend
63
+
64
+ | Status | Meaning |
65
+ |--------|---------|
66
+ | DECIDED | Use the Go Equivalent column. No `// TODO(human):` needed for the mapping itself (there may still be site-specific TODOs). |
67
+ | NEEDS DECISION | Default to Option A AND add `// TODO(human): NEEDS DECISION — idiom-mapping: <Row Name>` at every usage site. The human must confirm the choice before merging. |
68
+ | MUST DECIDE | No valid default. Do NOT generate code. Add `// TODO(human): MUST DECIDE — idiom-mapping: <Row Name> — see options` and leave a stub. |
69
+
70
+ ---
71
+
72
+ ## Usage in Agents
73
+
74
+ - **Translator agent**: before translating any Java pattern, search this table. Apply DECIDED mappings directly. Apply NEEDS DECISION mappings with Option A + comment. Escalate MUST DECIDE to a stub.
75
+ - **Reviewer agent**: scan the Go draft for any pattern that should have a `// TODO(human): NEEDS DECISION` comment and verify it is present. If missing, add it to REVIEW.md MUST DECIDE section.
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "migration-safety",
3
+ "version": "0.1.0",
4
+ "description": "Characterization/contract tests capturing old Java behavior + Strangler Fig guidance for incremental traffic cutover.",
5
+ "commands": [
6
+ {
7
+ "name": "gen-characterization-test",
8
+ "description": "Generate tests capturing the OLD Java service behavior for comparison with the new Go service",
9
+ "path": "commands/gen-characterization-test.md"
10
+ },
11
+ {
12
+ "name": "strangler-plan",
13
+ "description": "Produce an incremental Strangler Fig traffic-cutover checklist for a domain",
14
+ "path": "commands/strangler-plan.md"
15
+ }
16
+ ],
17
+ "skills": [
18
+ { "name": "strangler-fig", "path": "skills/strangler-fig.md" }
19
+ ]
20
+ }