@aaronshaf/ger 1.2.11 → 2.0.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 (180) hide show
  1. package/.ast-grep/rules/no-as-casting.yml +13 -0
  2. package/.claude-plugin/plugin.json +22 -0
  3. package/.github/workflows/ci-simple.yml +53 -0
  4. package/.github/workflows/ci.yml +171 -0
  5. package/.github/workflows/claude-code-review.yml +83 -0
  6. package/.github/workflows/claude.yml +50 -0
  7. package/.github/workflows/dependency-update.yml +84 -0
  8. package/.github/workflows/release.yml +166 -0
  9. package/.github/workflows/security-scan.yml +113 -0
  10. package/.github/workflows/security.yml +96 -0
  11. package/.husky/pre-commit +16 -0
  12. package/.husky/pre-push +25 -0
  13. package/.lintstagedrc.json +6 -0
  14. package/.tool-versions +1 -0
  15. package/CLAUDE.md +105 -0
  16. package/DEVELOPMENT.md +361 -0
  17. package/EXAMPLES.md +457 -0
  18. package/README.md +831 -16
  19. package/bin/ger +3 -18
  20. package/biome.json +36 -0
  21. package/bun.lock +678 -0
  22. package/bunfig.toml +8 -0
  23. package/docs/adr/0001-use-effect-for-side-effects.md +65 -0
  24. package/docs/adr/0002-use-bun-runtime.md +64 -0
  25. package/docs/adr/0003-store-credentials-in-home-directory.md +75 -0
  26. package/docs/adr/0004-use-commander-for-cli.md +76 -0
  27. package/docs/adr/0005-use-effect-schema-for-validation.md +93 -0
  28. package/docs/adr/0006-use-msw-for-api-mocking.md +89 -0
  29. package/docs/adr/0007-git-hooks-for-quality.md +94 -0
  30. package/docs/adr/0008-no-as-typecasting.md +83 -0
  31. package/docs/adr/0009-file-size-limits.md +82 -0
  32. package/docs/adr/0010-llm-friendly-xml-output.md +93 -0
  33. package/docs/adr/0011-ai-tool-strategy-pattern.md +102 -0
  34. package/docs/adr/0012-build-status-message-parsing.md +94 -0
  35. package/docs/adr/0013-git-subprocess-integration.md +98 -0
  36. package/docs/adr/0014-group-management-support.md +95 -0
  37. package/docs/adr/0015-batch-comment-processing.md +111 -0
  38. package/docs/adr/0016-flexible-change-identifiers.md +94 -0
  39. package/docs/adr/0017-git-worktree-support.md +102 -0
  40. package/docs/adr/0018-auto-install-commit-hook.md +103 -0
  41. package/docs/adr/0019-sdk-package-exports.md +95 -0
  42. package/docs/adr/0020-code-coverage-enforcement.md +105 -0
  43. package/docs/adr/0021-typescript-isolated-declarations.md +83 -0
  44. package/docs/adr/0022-biome-oxlint-tooling.md +124 -0
  45. package/docs/adr/README.md +30 -0
  46. package/docs/prd/README.md +12 -0
  47. package/docs/prd/architecture.md +325 -0
  48. package/docs/prd/commands.md +425 -0
  49. package/docs/prd/data-model.md +349 -0
  50. package/docs/prd/overview.md +124 -0
  51. package/index.ts +219 -0
  52. package/oxlint.json +24 -0
  53. package/package.json +82 -15
  54. package/scripts/check-coverage.ts +69 -0
  55. package/scripts/check-file-size.ts +38 -0
  56. package/scripts/fix-test-mocks.ts +55 -0
  57. package/skills/gerrit-workflow/SKILL.md +247 -0
  58. package/skills/gerrit-workflow/examples.md +572 -0
  59. package/skills/gerrit-workflow/reference.md +728 -0
  60. package/src/api/gerrit.ts +696 -0
  61. package/src/cli/commands/abandon.ts +65 -0
  62. package/src/cli/commands/add-reviewer.ts +156 -0
  63. package/src/cli/commands/build-status.ts +282 -0
  64. package/src/cli/commands/checkout.ts +422 -0
  65. package/src/cli/commands/comment.ts +460 -0
  66. package/src/cli/commands/comments.ts +85 -0
  67. package/src/cli/commands/diff.ts +71 -0
  68. package/src/cli/commands/extract-url.ts +266 -0
  69. package/src/cli/commands/groups-members.ts +104 -0
  70. package/src/cli/commands/groups-show.ts +169 -0
  71. package/src/cli/commands/groups.ts +137 -0
  72. package/src/cli/commands/incoming.ts +226 -0
  73. package/src/cli/commands/init.ts +164 -0
  74. package/src/cli/commands/mine.ts +115 -0
  75. package/src/cli/commands/open.ts +57 -0
  76. package/src/cli/commands/projects.ts +68 -0
  77. package/src/cli/commands/push.ts +430 -0
  78. package/src/cli/commands/rebase.ts +52 -0
  79. package/src/cli/commands/remove-reviewer.ts +123 -0
  80. package/src/cli/commands/restore.ts +50 -0
  81. package/src/cli/commands/review.ts +486 -0
  82. package/src/cli/commands/search.ts +162 -0
  83. package/src/cli/commands/setup.ts +286 -0
  84. package/src/cli/commands/show.ts +491 -0
  85. package/src/cli/commands/status.ts +35 -0
  86. package/src/cli/commands/submit.ts +108 -0
  87. package/src/cli/commands/vote.ts +119 -0
  88. package/src/cli/commands/workspace.ts +200 -0
  89. package/src/cli/index.ts +53 -0
  90. package/src/cli/register-commands.ts +659 -0
  91. package/src/cli/register-group-commands.ts +88 -0
  92. package/src/cli/register-reviewer-commands.ts +97 -0
  93. package/src/prompts/default-review.md +86 -0
  94. package/src/prompts/system-inline-review.md +135 -0
  95. package/src/prompts/system-overall-review.md +206 -0
  96. package/src/schemas/config.test.ts +245 -0
  97. package/src/schemas/config.ts +84 -0
  98. package/src/schemas/gerrit.ts +681 -0
  99. package/src/services/commit-hook.ts +314 -0
  100. package/src/services/config.test.ts +150 -0
  101. package/src/services/config.ts +250 -0
  102. package/src/services/git-worktree.ts +342 -0
  103. package/src/services/review-strategy.ts +292 -0
  104. package/src/test-utils/mock-generator.ts +138 -0
  105. package/src/utils/change-id.test.ts +98 -0
  106. package/src/utils/change-id.ts +63 -0
  107. package/src/utils/comment-formatters.ts +153 -0
  108. package/src/utils/diff-context.ts +103 -0
  109. package/src/utils/diff-formatters.ts +141 -0
  110. package/src/utils/formatters.ts +85 -0
  111. package/src/utils/git-commit.test.ts +277 -0
  112. package/src/utils/git-commit.ts +122 -0
  113. package/src/utils/index.ts +55 -0
  114. package/src/utils/message-filters.ts +26 -0
  115. package/src/utils/review-formatters.ts +89 -0
  116. package/src/utils/review-prompt-builder.ts +110 -0
  117. package/src/utils/shell-safety.ts +117 -0
  118. package/src/utils/status-indicators.ts +100 -0
  119. package/src/utils/url-parser.test.ts +271 -0
  120. package/src/utils/url-parser.ts +118 -0
  121. package/tests/abandon.test.ts +230 -0
  122. package/tests/add-reviewer.test.ts +579 -0
  123. package/tests/build-status-watch.test.ts +344 -0
  124. package/tests/build-status.test.ts +789 -0
  125. package/tests/change-id-formats.test.ts +268 -0
  126. package/tests/checkout/integration.test.ts +653 -0
  127. package/tests/checkout/parse-input.test.ts +55 -0
  128. package/tests/checkout/validation.test.ts +178 -0
  129. package/tests/comment-batch-advanced.test.ts +431 -0
  130. package/tests/comment-gerrit-api-compliance.test.ts +414 -0
  131. package/tests/comment.test.ts +708 -0
  132. package/tests/comments.test.ts +323 -0
  133. package/tests/config-service-simple.test.ts +100 -0
  134. package/tests/diff.test.ts +419 -0
  135. package/tests/extract-url.test.ts +517 -0
  136. package/tests/groups-members.test.ts +256 -0
  137. package/tests/groups-show.test.ts +323 -0
  138. package/tests/groups.test.ts +334 -0
  139. package/tests/helpers/build-status-test-setup.ts +83 -0
  140. package/tests/helpers/config-mock.ts +27 -0
  141. package/tests/incoming.test.ts +357 -0
  142. package/tests/init.test.ts +70 -0
  143. package/tests/integration/commit-hook.test.ts +246 -0
  144. package/tests/interactive-incoming.test.ts +173 -0
  145. package/tests/mine.test.ts +285 -0
  146. package/tests/mocks/msw-handlers.ts +80 -0
  147. package/tests/open.test.ts +233 -0
  148. package/tests/projects.test.ts +259 -0
  149. package/tests/rebase.test.ts +271 -0
  150. package/tests/remove-reviewer.test.ts +357 -0
  151. package/tests/restore.test.ts +237 -0
  152. package/tests/review.test.ts +135 -0
  153. package/tests/search.test.ts +712 -0
  154. package/tests/setup.test.ts +63 -0
  155. package/tests/show-auto-detect.test.ts +324 -0
  156. package/tests/show.test.ts +813 -0
  157. package/tests/status.test.ts +145 -0
  158. package/tests/submit.test.ts +316 -0
  159. package/tests/unit/commands/push.test.ts +194 -0
  160. package/tests/unit/git-branch-detection.test.ts +82 -0
  161. package/tests/unit/git-worktree.test.ts +55 -0
  162. package/tests/unit/patterns/push-patterns.test.ts +148 -0
  163. package/tests/unit/schemas/gerrit.test.ts +85 -0
  164. package/tests/unit/services/commit-hook.test.ts +132 -0
  165. package/tests/unit/services/review-strategy.test.ts +349 -0
  166. package/tests/unit/test-utils/mock-generator.test.ts +154 -0
  167. package/tests/unit/utils/comment-formatters.test.ts +415 -0
  168. package/tests/unit/utils/diff-context.test.ts +171 -0
  169. package/tests/unit/utils/diff-formatters.test.ts +165 -0
  170. package/tests/unit/utils/formatters.test.ts +411 -0
  171. package/tests/unit/utils/message-filters.test.ts +227 -0
  172. package/tests/unit/utils/shell-safety.test.ts +230 -0
  173. package/tests/unit/utils/status-indicators.test.ts +137 -0
  174. package/tests/vote.test.ts +317 -0
  175. package/tests/workspace.test.ts +295 -0
  176. package/tsconfig.json +36 -5
  177. package/src/commands/branch.ts +0 -196
  178. package/src/ger.ts +0 -22
  179. package/src/types.d.ts +0 -35
  180. package/src/utils.ts +0 -130
@@ -0,0 +1,30 @@
1
+ # ADR - Architecture Decision Records
2
+
3
+ Records of significant architectural decisions with context and rationale. Each ADR captures the why behind technical choices.
4
+
5
+ ## Records
6
+
7
+ | ADR | Decision |
8
+ |-----|----------|
9
+ | [0001](0001-use-effect-for-side-effects.md) | Effect-TS for all side effects |
10
+ | [0002](0002-use-bun-runtime.md) | Bun as the JavaScript runtime |
11
+ | [0003](0003-store-credentials-in-home-directory.md) | `~/.ger/config.json` for credentials |
12
+ | [0004](0004-use-commander-for-cli.md) | Commander.js for CLI framework |
13
+ | [0005](0005-use-effect-schema-for-validation.md) | Effect Schema for data validation |
14
+ | [0006](0006-use-msw-for-api-mocking.md) | MSW for HTTP mocking in tests |
15
+ | [0007](0007-git-hooks-for-quality.md) | Pre-commit hooks for code quality |
16
+ | [0008](0008-no-as-typecasting.md) | Prohibit `as` type casting |
17
+ | [0009](0009-file-size-limits.md) | Enforce file size limits |
18
+ | [0010](0010-llm-friendly-xml-output.md) | `--xml` flag for LLM consumption |
19
+ | [0011](0011-ai-tool-strategy-pattern.md) | Pluggable AI tool strategies |
20
+ | [0012](0012-build-status-message-parsing.md) | Parse messages for build status |
21
+ | [0013](0013-git-subprocess-integration.md) | Shell out to git instead of library |
22
+ | [0014](0014-group-management-support.md) | Full Gerrit group management |
23
+ | [0015](0015-batch-comment-processing.md) | JSON array input for bulk comments |
24
+ | [0016](0016-flexible-change-identifiers.md) | Accept both numeric and Change-ID formats |
25
+ | [0017](0017-git-worktree-support.md) | Full git worktree compatibility |
26
+ | [0018](0018-auto-install-commit-hook.md) | Auto-install Gerrit commit-msg hook |
27
+ | [0019](0019-sdk-package-exports.md) | Export SDK for programmatic usage |
28
+ | [0020](0020-code-coverage-enforcement.md) | 80% coverage threshold in pre-commit |
29
+ | [0021](0021-typescript-isolated-declarations.md) | Explicit return types on exports |
30
+ | [0022](0022-biome-oxlint-tooling.md) | Biome formatter + oxlint linter |
@@ -0,0 +1,12 @@
1
+ # PRD - Product Requirements Documents
2
+
3
+ Product specifications defining what the software should do, its goals, and design decisions.
4
+
5
+ ## Documents
6
+
7
+ | File | Description |
8
+ |------|-------------|
9
+ | [overview.md](overview.md) | Project overview, goals, design decisions |
10
+ | [architecture.md](architecture.md) | Technical architecture and component design |
11
+ | [commands.md](commands.md) | CLI command specifications |
12
+ | [data-model.md](data-model.md) | Data structures, schemas, API models |
@@ -0,0 +1,325 @@
1
+ # Architecture
2
+
3
+ ## System Overview
4
+
5
+ ```
6
+ ┌─────────────────────────────────────────────────────────────┐
7
+ │ CLI Layer │
8
+ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
9
+ │ │ show │ │ comment │ │ push │ │ review │ ... │
10
+ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
11
+ └───────┼───────────┼───────────┼───────────┼─────────────────┘
12
+ │ │ │ │
13
+ ┌───────┴───────────┴───────────┴───────────┴─────────────────┐
14
+ │ Service Layer │
15
+ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
16
+ │ │ GerritApi │ │ ConfigService│ │ ReviewStrategy│ │
17
+ │ │ Service │ │ │ │ │ │
18
+ │ └──────────────┘ └──────────────┘ └──────────────┘ │
19
+ └───────┬───────────────────────────────────────────────────────┘
20
+
21
+ ┌───────┴─────────────────────────────────────────────────────┐
22
+ │ External Systems │
23
+ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
24
+ │ │ Gerrit API │ │ Git (spawn) │ │ AI Tools │ │
25
+ │ │ │ │ │ │ (claude/llm) │ │
26
+ │ └──────────────┘ └──────────────┘ └──────────────┘ │
27
+ └─────────────────────────────────────────────────────────────┘
28
+ ```
29
+
30
+ ## Directory Structure
31
+
32
+ ```
33
+ src/
34
+ ├── cli/ # CLI interface layer
35
+ │ ├── index.ts # Entry point, command registration
36
+ │ ├── register-commands.ts # Main command setup
37
+ │ ├── register-group-commands.ts # Group commands
38
+ │ └── commands/ # Individual commands
39
+ │ ├── show.ts
40
+ │ ├── comment.ts
41
+ │ ├── push.ts
42
+ │ └── ... (27 commands)
43
+
44
+ ├── api/ # External API clients
45
+ │ └── gerrit.ts # Gerrit REST API service
46
+
47
+ ├── services/ # Business logic services
48
+ │ ├── config.ts # Configuration management
49
+ │ ├── review-strategy.ts # AI tool strategies
50
+ │ ├── git-worktree.ts # Git worktree operations
51
+ │ └── commit-hook.ts # Gerrit hook installation
52
+
53
+ ├── schemas/ # Data validation schemas
54
+ │ ├── gerrit.ts # Gerrit API types
55
+ │ └── config.ts # Config file schema
56
+
57
+ ├── utils/ # Shared utilities
58
+ │ ├── change-id.ts # Change identifier parsing
59
+ │ ├── git-commit.ts # Git operations
60
+ │ ├── formatters.ts # Output formatting
61
+ │ ├── shell-safety.ts # XML/CDATA handling
62
+ │ └── ... (diff, comment, review utils)
63
+
64
+ ├── prompts/ # AI review prompts
65
+ │ ├── default-review.md
66
+ │ ├── system-inline-review.md
67
+ │ └── system-overall-review.md
68
+
69
+ └── i18n/ # Internationalization (planned)
70
+
71
+ tests/
72
+ ├── unit/ # Pure function tests
73
+ ├── integration/ # API + command tests
74
+ ├── mocks/ # MSW handlers
75
+ └── helpers/ # Test utilities
76
+ ```
77
+
78
+ ## Dependency Injection
79
+
80
+ Effect Layers provide dependency injection:
81
+
82
+ ```typescript
83
+ // Define service interface
84
+ interface GerritApiService {
85
+ readonly getChange: (id: string) => Effect.Effect<ChangeInfo, ApiError>
86
+ readonly listChanges: (query?: string) => Effect.Effect<ChangeInfo[], ApiError>
87
+ // ...
88
+ }
89
+
90
+ // Create service tag
91
+ const GerritApiService = Context.GenericTag<GerritApiService>('GerritApiService')
92
+
93
+ // Implement live service
94
+ const GerritApiServiceLive = Layer.succeed(GerritApiService, {
95
+ getChange: (id) => Effect.gen(function* () {
96
+ const config = yield* ConfigService
97
+ const response = yield* fetchJson(`${config.host}/a/changes/${id}`)
98
+ return yield* Schema.decodeUnknown(ChangeInfo)(response)
99
+ }),
100
+ // ...
101
+ })
102
+
103
+ // Use in commands
104
+ const showCommand = Effect.gen(function* () {
105
+ const api = yield* GerritApiService
106
+ const change = yield* api.getChange(changeId)
107
+ console.log(formatChange(change))
108
+ })
109
+
110
+ // Provide layers at runtime
111
+ Effect.runPromise(
112
+ showCommand.pipe(
113
+ Effect.provide(GerritApiServiceLive),
114
+ Effect.provide(ConfigServiceLive)
115
+ )
116
+ )
117
+ ```
118
+
119
+ ## Error Handling
120
+
121
+ Tagged errors with Effect Schema:
122
+
123
+ ```typescript
124
+ // Define error types
125
+ export class ApiError extends Schema.TaggedError<ApiError>()('ApiError', {
126
+ message: Schema.String,
127
+ statusCode: Schema.Number,
128
+ url: Schema.String,
129
+ }) {}
130
+
131
+ export class ConfigError extends Schema.TaggedError<ConfigError>()('ConfigError', {
132
+ message: Schema.String,
133
+ }) {}
134
+
135
+ // Handle by tag
136
+ Effect.catchTag('ApiError', (error) => {
137
+ console.error(`API Error: ${error.message} (${error.statusCode})`)
138
+ return Effect.succeed(null)
139
+ })
140
+
141
+ // Or let errors propagate
142
+ Effect.runPromise(effect).catch((error) => {
143
+ if (error._tag === 'ApiError') {
144
+ console.error(`API failed: ${error.message}`)
145
+ }
146
+ })
147
+ ```
148
+
149
+ ## Data Flow
150
+
151
+ ### Read Operation (show command)
152
+
153
+ ```
154
+ User: ger show 12345
155
+
156
+
157
+ ┌─────────────────────┐
158
+ │ Parse arguments │
159
+ │ Normalize change ID │
160
+ └─────────┬───────────┘
161
+
162
+
163
+ ┌─────────────────────┐
164
+ │ Load config │
165
+ │ (~/.ger/config.json)│
166
+ └─────────┬───────────┘
167
+
168
+
169
+ ┌─────────────────────┐
170
+ │ API: GET /changes │
171
+ │ Validate response │
172
+ └─────────┬───────────┘
173
+
174
+
175
+ ┌─────────────────────┐
176
+ │ Format output │
177
+ │ (text/xml/json) │
178
+ └─────────┬───────────┘
179
+
180
+
181
+ Console
182
+ ```
183
+
184
+ ### Write Operation (comment command)
185
+
186
+ ```
187
+ User: echo '...' | ger comment 12345
188
+
189
+
190
+ ┌─────────────────────┐
191
+ │ Parse stdin (JSON) │
192
+ │ Validate schema │
193
+ └─────────┬───────────┘
194
+
195
+
196
+ ┌─────────────────────┐
197
+ │ Load config │
198
+ └─────────┬───────────┘
199
+
200
+
201
+ ┌─────────────────────┐
202
+ │ API: POST /review │
203
+ │ (batch comments) │
204
+ └─────────┬───────────┘
205
+
206
+
207
+ Success/Error
208
+ ```
209
+
210
+ ## AI Integration Flow
211
+
212
+ ```
213
+ User: ger review 12345
214
+
215
+
216
+ ┌─────────────────────┐
217
+ │ Fetch change diff │
218
+ │ (API: GET /patch) │
219
+ └─────────┬───────────┘
220
+
221
+
222
+ ┌─────────────────────┐
223
+ │ Detect AI tool │
224
+ │ (claude/llm/etc) │
225
+ └─────────┬───────────┘
226
+
227
+
228
+ ┌─────────────────────┐ ┌─────────────────────┐
229
+ │ Stage 1: Inline │────►│ AI: Generate inline │
230
+ │ comments │ │ comments JSON │
231
+ └─────────┬───────────┘ └─────────────────────┘
232
+
233
+
234
+ ┌─────────────────────┐ ┌─────────────────────┐
235
+ │ Stage 2: Overall │────►│ AI: Generate │
236
+ │ review │ │ summary │
237
+ └─────────┬───────────┘ └─────────────────────┘
238
+
239
+
240
+ ┌─────────────────────┐
241
+ │ API: POST /review │
242
+ │ (all comments) │
243
+ └─────────────────────┘
244
+ ```
245
+
246
+ ## Configuration Architecture
247
+
248
+ ```
249
+ Priority: File > Environment > Error
250
+
251
+ ┌─────────────────────────────────────┐
252
+ │ ~/.ger/config.json │
253
+ │ { │
254
+ │ "host": "https://gerrit.com", │
255
+ │ "username": "user", │
256
+ │ "password": "token", │
257
+ │ "aiTool": "claude", │
258
+ │ "aiAutoDetect": true │
259
+ │ } │
260
+ └─────────────────────────────────────┘
261
+
262
+ ▼ (file not found)
263
+ ┌─────────────────────────────────────┐
264
+ │ Environment Variables │
265
+ │ GERRIT_HOST │
266
+ │ GERRIT_USERNAME │
267
+ │ GERRIT_PASSWORD │
268
+ └─────────────────────────────────────┘
269
+
270
+ ▼ (not set)
271
+ ┌─────────────────────────────────────┐
272
+ │ ConfigError: No credentials found │
273
+ └─────────────────────────────────────┘
274
+ ```
275
+
276
+ ## Testing Architecture
277
+
278
+ ```
279
+ ┌─────────────────────────────────────────────────────────────┐
280
+ │ Test Runner (Bun) │
281
+ └─────────────────────────────────────────────────────────────┘
282
+
283
+ ┌─────────────────────┼─────────────────────┐
284
+ ▼ ▼ ▼
285
+ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐
286
+ │ Unit Tests │ │ Integration │ │ E2E Tests │
287
+ │ │ │ Tests │ │ (manual) │
288
+ │ - Schemas │ │ - Commands │ │ │
289
+ │ - Utilities │ │ - API flows │ │ - Full CLI │
290
+ │ - Formatters │ │ - Services │ │ - Real Gerrit │
291
+ └───────────────┘ └───────┬───────┘ └───────────────┘
292
+
293
+
294
+ ┌───────────────────┐
295
+ │ MSW Handlers │
296
+ │ │
297
+ │ Mock HTTP at │
298
+ │ network level │
299
+ └───────────────────┘
300
+ ```
301
+
302
+ ## Security Model
303
+
304
+ ```
305
+ ┌─────────────────────────────────────────────────────────────┐
306
+ │ Credential Storage │
307
+ │ - File: ~/.ger/config.json (mode 0600) │
308
+ │ - Never logged or printed in errors │
309
+ │ - Basic auth over HTTPS only │
310
+ └─────────────────────────────────────────────────────────────┘
311
+
312
+ ┌─────────────────────────────────────────────────────────────┐
313
+ │ Input Validation │
314
+ │ - Schema validation on all external data │
315
+ │ - Git subprocess (no shell=true, no string interpolation) │
316
+ │ - CDATA wrapping for XML output │
317
+ └─────────────────────────────────────────────────────────────┘
318
+
319
+ ┌─────────────────────────────────────────────────────────────┐
320
+ │ Output Sanitization │
321
+ │ - Credentials never in output │
322
+ │ - Error messages sanitized │
323
+ │ - XML special chars handled via CDATA │
324
+ └─────────────────────────────────────────────────────────────┘
325
+ ```