@samaksh/git-worker 0.0.0-stage → 0.1.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 (115) hide show
  1. package/GIT_WORKER_PROJECT_PLAN.md +499 -0
  2. package/README.md +114 -2
  3. package/demo/README.md +58 -0
  4. package/demo/index.ts +526 -0
  5. package/dist/app.d.ts +2 -0
  6. package/dist/app.js +48 -0
  7. package/dist/cli/commands.d.ts +6 -0
  8. package/dist/cli/commands.js +111 -0
  9. package/dist/cli/index.d.ts +2 -0
  10. package/dist/cli/index.js +115 -0
  11. package/dist/config/defaults.d.ts +2 -0
  12. package/dist/config/defaults.js +44 -0
  13. package/dist/config/load.d.ts +3 -0
  14. package/dist/config/load.js +65 -0
  15. package/dist/config/schema.d.ts +200 -0
  16. package/dist/config/schema.js +45 -0
  17. package/dist/core/errors.d.ts +12 -0
  18. package/dist/core/errors.js +34 -0
  19. package/dist/core/pipeline.d.ts +3 -0
  20. package/dist/core/pipeline.js +14 -0
  21. package/dist/core/types.d.ts +153 -0
  22. package/dist/core/types.js +16 -0
  23. package/dist/services/editor/index.d.ts +9 -0
  24. package/dist/services/editor/index.js +59 -0
  25. package/dist/services/git/ops.d.ts +30 -0
  26. package/dist/services/git/ops.js +139 -0
  27. package/dist/services/git/reader.d.ts +14 -0
  28. package/dist/services/git/reader.js +124 -0
  29. package/dist/services/report/index.d.ts +18 -0
  30. package/dist/services/report/index.js +68 -0
  31. package/dist/services/review/index.d.ts +30 -0
  32. package/dist/services/review/index.js +59 -0
  33. package/dist/services/state/index.d.ts +20 -0
  34. package/dist/services/state/index.js +49 -0
  35. package/dist/services/template/env.d.ts +23 -0
  36. package/dist/services/template/env.js +146 -0
  37. package/dist/services/template/interpolator.d.ts +30 -0
  38. package/dist/services/template/interpolator.js +75 -0
  39. package/dist/services/template/transform.d.ts +22 -0
  40. package/dist/services/template/transform.js +75 -0
  41. package/dist/services/template/validator.d.ts +5 -0
  42. package/dist/services/template/validator.js +68 -0
  43. package/dist/services/workspace/cache.d.ts +32 -0
  44. package/dist/services/workspace/cache.js +107 -0
  45. package/dist/services/workspace/manager.d.ts +44 -0
  46. package/dist/services/workspace/manager.js +301 -0
  47. package/dist/stages/apply.d.ts +3 -0
  48. package/dist/stages/apply.js +161 -0
  49. package/dist/stages/edit-template.d.ts +3 -0
  50. package/dist/stages/edit-template.js +198 -0
  51. package/dist/stages/eligibility.d.ts +3 -0
  52. package/dist/stages/eligibility.js +44 -0
  53. package/dist/stages/load-template.d.ts +3 -0
  54. package/dist/stages/load-template.js +168 -0
  55. package/dist/stages/plan.d.ts +3 -0
  56. package/dist/stages/plan.js +270 -0
  57. package/dist/stages/push.d.ts +3 -0
  58. package/dist/stages/push.js +99 -0
  59. package/dist/stages/report.d.ts +3 -0
  60. package/dist/stages/report.js +103 -0
  61. package/dist/stages/resolve-project.d.ts +3 -0
  62. package/dist/stages/resolve-project.js +294 -0
  63. package/dist/stages/review.d.ts +3 -0
  64. package/dist/stages/review.js +197 -0
  65. package/dist/stages/select.d.ts +13 -0
  66. package/dist/stages/select.js +250 -0
  67. package/dist/stages/sync.d.ts +3 -0
  68. package/dist/stages/sync.js +47 -0
  69. package/dist/utils/hash.d.ts +2 -0
  70. package/dist/utils/hash.js +7 -0
  71. package/dist/utils/ui.d.ts +21 -0
  72. package/dist/utils/ui.js +113 -0
  73. package/dist/utils/which.d.ts +5 -0
  74. package/dist/utils/which.js +16 -0
  75. package/docs/FEATURES.md +84 -0
  76. package/docs/SERVICES.md +57 -0
  77. package/docs/architecture.md +38 -0
  78. package/docs/usage.md +337 -0
  79. package/package.json +48 -4
  80. package/src/app.ts +69 -0
  81. package/src/cli/commands.ts +142 -0
  82. package/src/cli/index.ts +123 -0
  83. package/src/config/defaults.ts +46 -0
  84. package/src/config/load.ts +70 -0
  85. package/src/config/schema.ts +48 -0
  86. package/src/core/errors.ts +45 -0
  87. package/src/core/pipeline.ts +17 -0
  88. package/src/core/types.ts +188 -0
  89. package/src/services/editor/index.ts +85 -0
  90. package/src/services/git/ops.ts +170 -0
  91. package/src/services/git/reader.ts +148 -0
  92. package/src/services/report/index.ts +104 -0
  93. package/src/services/review/index.ts +80 -0
  94. package/src/services/state/index.ts +77 -0
  95. package/src/services/template/env.ts +169 -0
  96. package/src/services/template/interpolator.ts +109 -0
  97. package/src/services/template/transform.ts +97 -0
  98. package/src/services/template/validator.ts +75 -0
  99. package/src/services/workspace/cache.ts +144 -0
  100. package/src/services/workspace/manager.ts +370 -0
  101. package/src/stages/apply.ts +194 -0
  102. package/src/stages/edit-template.ts +227 -0
  103. package/src/stages/eligibility.ts +67 -0
  104. package/src/stages/load-template.ts +188 -0
  105. package/src/stages/plan.ts +362 -0
  106. package/src/stages/push.ts +120 -0
  107. package/src/stages/report.ts +126 -0
  108. package/src/stages/resolve-project.ts +341 -0
  109. package/src/stages/review.ts +228 -0
  110. package/src/stages/select.ts +302 -0
  111. package/src/stages/sync.ts +63 -0
  112. package/src/utils/hash.ts +15 -0
  113. package/src/utils/ui.ts +137 -0
  114. package/src/utils/which.ts +18 -0
  115. package/tsconfig.json +18 -0
@@ -0,0 +1,499 @@
1
+ # Git Worker — Implementation Blueprint
2
+
3
+ **Status:** Proposed architecture, based on `REFERENCE.md` and the subsequent design decisions.
4
+ **Goal:** A safe, composable TypeScript CLI/TUI for applying reviewed file changes across many Git branches, with VS Code as the preferred editor/diff tool and Vim as a supported alternative.
5
+
6
+ ## 1. Product contract
7
+
8
+ Git Worker lets a developer target a local Git repository or remote URL, select branches and a file, edit a reusable template, preview the exact per-branch changes without checking out branches, approve/reject them, and then commit and optionally push approved changes. It should work with a handful or 100+ branches without requiring one persistent worktree per branch.
9
+
10
+ **Primary design principles**
11
+
12
+ 1. **Readable over clever:** small functions, explicit data types, straightforward control flow.
13
+ 2. **Functional core, imperative shell:** transformations, validation, planning, and diff decisions are pure; filesystem, Git, editor, and TUI interactions are injected services.
14
+ 3. **Pipeline composition:** `pipeline(stage1, stage2, ...)`, where each stage receives the previous stage's context and returns an updated context or a typed failure.
15
+ 4. **Inspect without checkout:** use Git object reads (`git show`, `git ls-tree`, `git cat-file`) to plan changes.
16
+ 5. **Review before mutation:** collect missing values and build exact diffs before checkout/commit.
17
+ 6. **Controlled destructive operations:** reset/clean only inside a verified, Git Worker-owned disposable checkout, never inside a user's original repository.
18
+ 7. **Explicit staging and pushes:** stage only intended paths; push only on confirmation or explicit policy.
19
+ 8. **Configuration in one place:** no scattered magic defaults, commands, timeouts, paths, or UI preferences.
20
+ 9. **Small first release:** `.env` is the first-class transformer; other file formats are extensions, not implied to be fully supported by the `.env` engine.
21
+
22
+ ## 2. Decisions settled in discussion
23
+
24
+ | Topic | Final decision |
25
+ |---|---|
26
+ | Static `.env` entries | `DB_PORT=54331` enforces that value on every selected branch. |
27
+ | Dynamic `.env` entries | `SERVICE_NAME={SERVICE_NAME}` takes the existing value from each branch. |
28
+ | Missing dynamic key | Ask for a value for **each** affected branch, individually. |
29
+ | Empty existing value | Empty is present and is preserved; do not prompt. |
30
+ | Prompt timing | During planning/staging, before review and before any commits. |
31
+ | Unmanaged keys | Preserve keys not mentioned in the template. |
32
+ | Template persistence | Save the last **successfully applied** template locally for next run. |
33
+ | Branch-specific binding persistence | Not necessary; Git branch contents are the source of truth. |
34
+ | Drift behavior | Static values are re-enforced on each run; dynamic values are read fresh from each branch. No separate drift subsystem for MVP. |
35
+ | Cleanup | Keep reset/clean before and after checkout, **only in a verified managed disposable checkout**. |
36
+ | Editing/diffing | VS Code (`code --wait`, `code --diff`) preferred; Vim supported. |
37
+ | Execution | Per-branch fault isolation; one branch failure does not silently corrupt another. |
38
+ | Push | Prompt by default; `always` and `never` configurable. |
39
+ | Deleting a key from template | **Proposed default:** do not delete it from branches; require an explicit delete operation. This remains a decision to confirm. |
40
+
41
+ **Important distinction:** The template is authoritative for **keys it declares**. It is not a whole-file replacement: comments, formatting, ordering, and keys not declared in the template should be preserved by the `.env` transformer. A dynamic placeholder expresses *per-branch ownership of the value*, not a common literal value.
42
+
43
+ ## 3. Example semantics
44
+
45
+ Template:
46
+
47
+ ```dotenv
48
+ # Uniform values
49
+ DB_NAME=postgres
50
+ DB_PORT=54331
51
+
52
+ # Values sourced from each branch
53
+ SERVICE_NAME={SERVICE_NAME}
54
+ ```
55
+
56
+ Existing `uat-payments/.env`:
57
+
58
+ ```dotenv
59
+ DB_NAME=legacy
60
+ DB_PORT=54330
61
+ SERVICE_NAME=payments
62
+ CUSTOM_FLAG=yes
63
+ ```
64
+
65
+ Proposed result:
66
+
67
+ ```dotenv
68
+ DB_NAME=postgres
69
+ DB_PORT=54331
70
+ SERVICE_NAME=payments
71
+ CUSTOM_FLAG=yes
72
+ ```
73
+
74
+ If `uat-orders` has no `SERVICE_NAME`, the staging/planning interaction asks:
75
+
76
+ ```text
77
+ Branch: uat-orders
78
+ Missing SERVICE_NAME
79
+ Enter value for uat-orders > orders
80
+ ```
81
+
82
+ It then stages and displays the proposed addition. If the user cancels, that branch is skipped with a recorded reason. Missing values for 50 branches result in 50 individual prompts (not a bulk editor).
83
+
84
+ ### `.env` parsing and preservation rules
85
+
86
+ - Recognize blank lines, full-line comments, assignments, `export KEY=...` where supported, and inline comments where syntactically unambiguous.
87
+ - Update the value of a declared existing key **in place**; preserve key ordering, unrelated lines, and comments.
88
+ - Append declared keys that do not exist; preserve the file's existing newline style and final-newline convention where practical.
89
+ - Treat `KEY=` as present with an empty value.
90
+ - Define duplicate-key behavior explicitly: **fail that branch with a diagnostic** in MVP instead of guessing which assignment is authoritative.
91
+ - Fail safely on unsupported or ambiguous syntax instead of rewriting it heuristically.
92
+ - Resolve placeholders from the branch's existing key value first, and prompt only when the key is absent.
93
+ - Use the exact `{NAME}` placeholder syntax from the discussion, with escaping and validation rules documented. Avoid silently mixing it with the earlier `${name}` syntax; support an alias only if intentionally configured.
94
+ - Treat values as opaque text, preserving quoting when possible. Never print secret values in normal logs or persisted reports.
95
+ - The template editor's instruction header is temporary and stripped before parsing/saving; real user comments remain.
96
+
97
+ ## 4. CLI/TUI user experience
98
+
99
+ ### Interactive default
100
+
101
+ ```text
102
+ git-worker [repo]
103
+ 1. Resolve/sync project workspace
104
+ 2. Select branches (glob/regex, live refilter)
105
+ 3. Select target file and operation (edit/add/rename)
106
+ 4. Check eligibility per branch
107
+ 5. Load saved template or synthesize first-run starter
108
+ 6. Open template in VS Code (or Vim)
109
+ 7. Validate template and inspect branch files
110
+ 8. Prompt individually for missing dynamic values
111
+ 9. Build out-of-tree before/after snapshots and diffs
112
+ 10. Review each branch; approve/reject/skip, batch actions optional
113
+ 11. Revalidate branch tips, apply approved changes, commit
114
+ 12. Confirm push policy; push eligible commits
115
+ 13. Display report; persist state only according to actual outcomes
116
+ ```
117
+
118
+ ### Example commands
119
+
120
+ ```bash
121
+ git-worker
122
+ git-worker --repo ../my-repo --branch 'uat*' --file .env
123
+ git-worker --repo git@github.com:org/repo.git --branch '/^release\//' --editor code
124
+ git-worker --ci --branch 'uat*' --file .env --push never
125
+ ```
126
+
127
+ Keep the original CLI flags from the reference where useful: `--repo/-r`, `--branch/-b`, `--file/-f`, `--op/-o`, `--editor/-e`, `--commit-msg/-m`, `--push/-p`, `--remote`, `--auto-approve`, `--ci`, `--help`, `--version`. Validate incompatible combinations early. In noninteractive mode, unresolved dynamic variables **fail or skip explicitly**; never hang waiting for prompts or silently invent values. Provide a configured values-input mechanism later if CI needs dynamic additions.
128
+
129
+ ### Editor adapter
130
+
131
+ - `code --wait <template-file>` for editing; `code --diff <before> <after>` for review. Ensure VS Code is actually installed; show a clear error or configurable fallback.
132
+ - Vim editing through terminal ownership (`vim <template-file>`); diff via `vim -d <before> <after>` (or `vimdiff`).
133
+ - Editor launch, cancellation, file-watch/save behavior, and error re-edit loops are centralized in the editor service.
134
+ - Editor-specific flags are configuration, not hard-coded in the pipeline.
135
+
136
+ ## 5. Architecture: services + pipeline
137
+
138
+ ### Suggested directory layout
139
+
140
+ ```text
141
+ src/
142
+ cli/
143
+ index.ts # parse args and launch
144
+ commands.ts
145
+ config/
146
+ defaults.ts # all default values
147
+ schema.ts # validate merged configuration
148
+ load.ts # defaults -> project config -> CLI
149
+ core/
150
+ types.ts # domain models and Result types
151
+ pipeline.ts # generic stage composer
152
+ errors.ts
153
+ services/
154
+ git/ # Git commands, refs, cleanup, commit, push
155
+ workspace/ # owned project checkout/cache
156
+ selection/ # branch/file selector and eligibility
157
+ editor/ # VS Code/Vim adapter
158
+ template/ # parse, render, transform .env
159
+ state/ # local saved templates
160
+ review/ # snapshots, diffs, approvals
161
+ report/ # terminal and optional JSON/Markdown
162
+ stages/
163
+ resolve-project.ts
164
+ sync.ts
165
+ select.ts
166
+ eligibility.ts
167
+ load-template.ts
168
+ edit-template.ts
169
+ plan.ts
170
+ review.ts
171
+ apply.ts
172
+ push.ts
173
+ report.ts
174
+ app.ts # composition root / dependency wiring
175
+ ```
176
+
177
+ These are *logical boundaries*, not a mandate for dozens of classes or barrel files. A service is a small set of related functions behind an injected interface. Keep domain types separate from terminal rendering and Git process execution.
178
+
179
+ ### Composition root
180
+
181
+ ```ts
182
+ const services = createServices(config);
183
+ const context = createInitialContext(input, config);
184
+
185
+ const run = pipeline(
186
+ resolveProject,
187
+ syncRepository,
188
+ selectTargets,
189
+ checkEligibility,
190
+ loadTemplate,
191
+ editTemplate,
192
+ planChanges,
193
+ reviewChanges,
194
+ applyChanges,
195
+ pushChanges,
196
+ reportResults,
197
+ );
198
+
199
+ const result = await run(context, services);
200
+ ```
201
+
202
+ ### Simple pipeline API
203
+
204
+ ```ts
205
+ type Result<T> =
206
+ | { ok: true; value: T }
207
+ | { ok: false; error: AppError };
208
+
209
+ type Stage<C, D> = (context: C, deps: D) => Promise<Result<C>>;
210
+
211
+ function pipeline<C, D>(...stages: Stage<C, D>[]): Stage<C, D> {
212
+ return async (initial, deps) => {
213
+ let current = initial;
214
+ for (const stage of stages) {
215
+ const result = await stage(current, deps);
216
+ if (!result.ok) return result;
217
+ current = result.value;
218
+ }
219
+ return { ok: true, value: current };
220
+ };
221
+ }
222
+ ```
223
+
224
+ This intentionally uses one evolving typed context to minimize generic type complexity. If stage-specific input/output types become important, introduce typed composition overloads later. **Do not let stages mutate the context in place**; return new objects with added fields. For expensive branch data, use maps or immutable records and replace only changed entries.
225
+
226
+ ### Core context
227
+
228
+ ```ts
229
+ type RunContext = {
230
+ runId: string;
231
+ input: CliInput;
232
+ project?: ManagedProject;
233
+ selectedBranches?: BranchRef[];
234
+ target?: TargetFile;
235
+ eligible?: BranchRef[];
236
+ template?: TemplateDocument;
237
+ plans?: BranchPlan[];
238
+ approvals?: Approval[];
239
+ results?: BranchResult[];
240
+ pushResults?: PushResult[];
241
+ };
242
+ ```
243
+
244
+ Stage preconditions are validated explicitly. For example, `applyChanges` refuses to run without plans and approvals. `BranchPlan` should capture `branch`, `baseCommitSha`, `path`, `beforeContentHash`, `afterContent`, `diff`, `status`, and an optional reason. Each stage has one job; the pipeline owns ordering, not business rules.
245
+
246
+ ### Pure vs impure responsibilities
247
+
248
+ | Pure functions | Effectful services |
249
+ |---|---|
250
+ | Parse/validate template | Read Git objects |
251
+ | Extract placeholders | Fetch remotes |
252
+ | Compare template assignments | Launch editor |
253
+ | Render branch-specific content | Prompt for missing values |
254
+ | Compute eligibility decisions from supplied facts | Create staging files |
255
+ | Build branch plan from content + answers | Reset/clean/checkout/commit/push |
256
+ | Format report model | Print TUI / write report |
257
+
258
+ Prefer dependency injection by passing service objects, not a service locator or class hierarchy.
259
+
260
+ ## 6. Workspace and Git safety
261
+
262
+ ### Workspace layout
263
+
264
+ ```text
265
+ .git-worker/
266
+ projects/
267
+ <stable-project-id>/
268
+ repo/ # disposable checkout owned by git-worker
269
+ staging/<run-id>/ # outside repo: before/after files
270
+ state/ # local templates
271
+ reports/ # optional run reports
272
+ ```
273
+
274
+ - Project ID derived from a canonicalized repository identity; avoid collisions and handle multiple remotes.
275
+ - Never delete or reset the user's supplied local repository. If a local path is supplied, discover its remote and create/use a **separate managed checkout**; define an explicit error when no suitable remote exists rather than silently mutating the original.
276
+ - Verify ownership with a marker file outside the checkout plus canonical real-path checks; reject paths that escape the workspace (including symlinks).
277
+ - Serialize operations per managed checkout (lock file) to prevent concurrent runs corrupting one another.
278
+ - Keep state/staging/reports outside `repo/` so cleanup cannot remove them.
279
+ - `git reset --hard` and `git clean -fd` may run **before and after** branch switches inside the verified managed checkout. Document that ignored files are not removed by `-fd`; if a truly pristine tree is required, make `-fdx` a carefully gated, managed-checkout-only option, not the default.
280
+ - Always stage only the target paths (`git add -- <path>`); check `git diff --cached --name-only` before committing.
281
+ - If an operation fails, clean/reset the disposable checkout and continue to the next branch, recording the failure.
282
+
283
+ ### Preventing stale-plan commits
284
+
285
+ Planning reads branch commit `A`. Before applying, fetch and verify the branch still points to `A`. If the tip changed, **skip that branch and require re-planning/re-review**. Do not apply an approved diff against a different version of the file. Use non-force pushes and report non-fast-forward failures.
286
+
287
+ ### Per-branch commit behavior
288
+
289
+ 1. Verify managed checkout and branch tip matches the reviewed plan.
290
+ 2. Clean before checkout, switch to the target branch, clean again.
291
+ 3. Verify target file still matches the reviewed before-content hash.
292
+ 4. Write only the approved after-content, using a temporary file/atomic replace where practical.
293
+ 5. Stage only the approved path(s), inspect staged names and staged diff.
294
+ 6. Skip no-op changes (no empty commits).
295
+ 7. Commit with configured message and identity; record SHA.
296
+ 8. On error, restore disposable checkout, mark branch failed, continue.
297
+ 9. Push only after commit phase and explicit push gate; record per-branch push outcome.
298
+
299
+ A Git commit is atomic for its tree, but rollback after a successful commit is not automatic. Do not claim the entire multi-branch run is atomic: commits and pushes across different branches can partially succeed.
300
+
301
+ ## 7. Stateful living template, minimal persistence
302
+
303
+ ### State location and format
304
+
305
+ One local state file per project + target path, for example:
306
+
307
+ ```json
308
+ {
309
+ "schemaVersion": 1,
310
+ "targetPath": ".env",
311
+ "template": "DB_NAME=postgres\nDB_PORT=54331\nSERVICE_NAME={SERVICE_NAME}\n",
312
+ "updatedAt": "2026-10-28T00:00:00Z"
313
+ }
314
+ ```
315
+
316
+ **No persistent branch binding map**: branch-specific values are read directly from each branch's `.env` on every run. Avoid storing secrets or sensitive values in state, reports, or terminal logs.
317
+
318
+ ### First run
319
+
320
+ - If saved state exists, load it into the editor.
321
+ - Otherwise, synthesize a helpful draft from common keys/values across selected branches. This is a **proposal**, never automatically applied; distinguish uniform values from varying values and suggest placeholders for varying keys.
322
+ - If synthesis is ambiguous, show a minimal valid blank template rather than inventing config values.
323
+
324
+ ### Subsequent runs
325
+
326
+ - Load the last saved template; the developer edits it in VS Code/Vim.
327
+ - Re-evaluate the **whole set of declared keys** against every selected branch: static values are enforced and dynamic values are read per branch.
328
+ - Unmanaged keys remain untouched. Removing a key from the template does not implicitly delete it (proposed policy; confirm).
329
+ - If no branches are approved/applied, don't overwrite the saved successful template. If only some branches succeed, retain a clear partial-run status in the report and make it obvious that a subsequent run will re-enforce the template on remaining branches.
330
+ - **State persistence policy needs explicit finalization:** simplest policy is to save the new template after at least one successful local commit, with an accompanying last-run summary; when zero commits occur because everything already matches, allow saving the reviewed template after explicit approval. Never claim a template was applied to all branches when only some succeeded.
331
+
332
+ ### Drift and manual edits
333
+
334
+ - No dedicated drift tracking needed in MVP. On each run, the current branch content is the truth for dynamic placeholders, and template literals are authoritative for static keys.
335
+ - A branch that has a manually modified static value will show the proposed correction in its diff.
336
+ - A manually modified dynamic value remains unchanged.
337
+
338
+ ## 8. Review UX and diff integrity
339
+
340
+ - Staging stores `before` and `after` snapshots for each eligible branch, outside the checkout.
341
+ - TUI displays statuses: `unchanged`, `changed`, `missing input`, `ineligible`, `approved`, `rejected`, `skipped`, `failed`.
342
+ - Preview changes with VS Code side-by-side (`code --diff before after`); Vim diff is supported.
343
+ - A branch may be approved/rejected independently. Batch approve/reject is a convenience, not a separate execution path.
344
+ - Approval is tied to a **specific immutable plan hash** (branch, base SHA, target path, before/after content hashes). Any change to plan content invalidates approval and requires review again.
345
+ - If the user edits the *after* snapshot during review, revalidate the resulting file and recompute the plan hash/diff before approval. For MVP, a simpler and safer alternative is **read-only diff review** and requiring a template re-edit to change results.
346
+ - Provide an all-branch summary before the first destructive checkout.
347
+
348
+ ## 9. Configuration design
349
+
350
+ ### Precedence
351
+
352
+ `defaults < project git-worker.config.json < CLI flags` (and environment variables only for explicitly documented options). Validate once at startup and pass a frozen `AppConfig` through dependency injection.
353
+
354
+ Example:
355
+
356
+ ```json
357
+ {
358
+ "git": {
359
+ "remote": "origin",
360
+ "fetchPrune": true,
361
+ "cleanMode": "tracked-and-untracked",
362
+ "pushPolicy": "prompt",
363
+ "commitMessage": "chore: sync configuration"
364
+ },
365
+ "workspace": {
366
+ "root": ".git-worker",
367
+ "lockTimeoutMs": 30000
368
+ },
369
+ "editor": {
370
+ "name": "code",
371
+ "editCommand": ["code", "--wait", "{file}"],
372
+ "diffCommand": ["code", "--diff", "{before}", "{after}"]
373
+ },
374
+ "template": {
375
+ "placeholderOpen": "{",
376
+ "placeholderClose": "}",
377
+ "missingValuePolicy": "prompt-each-branch",
378
+ "emptyValueIsPresent": true,
379
+ "unmanagedKeyPolicy": "preserve",
380
+ "removedTemplateKeyPolicy": "preserve"
381
+ },
382
+ "review": {
383
+ "autoApprove": false
384
+ },
385
+ "execution": {
386
+ "concurrency": 1,
387
+ "stopOnBranchError": false
388
+ },
389
+ "report": {
390
+ "writeJson": false,
391
+ "writeMarkdown": false,
392
+ "redactValues": true
393
+ }
394
+ }
395
+ ```
396
+
397
+ Keep security invariants **non-configurable**: never clean outside a verified owned workspace, never force-push by default, never stage unspecified paths, never bypass revalidation of a reviewed plan.
398
+
399
+ ## 10. Errors, cancellation, and reporting
400
+
401
+ - Use typed error categories: `CONFIG`, `GIT`, `EDITOR`, `TEMPLATE`, `ELIGIBILITY`, `INPUT_CANCELLED`, `STALE_PLAN`, `APPLY`, `PUSH`.
402
+ - Distinguish **run-fatal** errors (invalid config, workspace ownership failure) from **per-branch** errors (missing file, invalid syntax, rejected diff, failed commit).
403
+ - Cancellation before apply must leave target branches unchanged.
404
+ - End with an accurate summary per branch: eligible, skipped, rejected, unchanged, committed, pushed, failed; include commit SHA and reasons where relevant.
405
+ - Reports must avoid leaking `.env` secret values. If a full diff is displayed, warn that it may contain secrets; redact exported reports by default.
406
+ - Persistent Markdown/JSON reports are useful but can follow the terminal summary in implementation priority.
407
+
408
+ ## 11. Test strategy
409
+
410
+ ### Unit tests (pure logic)
411
+
412
+ - `.env` tokenizer: comments, whitespace, quotes, empty values, CRLF, duplicate keys, `export`, malformed assignments.
413
+ - Static enforcement; dynamic preservation; missing-key prompting request; absent vs empty distinction.
414
+ - Unmanaged-key preservation; append semantics; no-op detection.
415
+ - Template serialization and first-run synthesis.
416
+ - Config precedence and schema validation.
417
+ - Plan hash and stale-plan detection logic.
418
+ - Pipeline short-circuit and context passing.
419
+
420
+ ### Integration tests (temporary local Git repositories)
421
+
422
+ - 3–5 branches with different `.env` values; verify exact per-branch results.
423
+ - Managed checkout cleanup never affects the source repository.
424
+ - Dirty/untracked files inside managed checkout cannot enter commits.
425
+ - Stale branch tip causes skip, not accidental commit.
426
+ - One branch fails while later branches continue.
427
+ - Stage only intended paths; no-op branches do not commit.
428
+ - Push confirmation/never/always and non-fast-forward failures.
429
+ - Editor adapter with fake command runner; real VS Code/Vim manual smoke tests.
430
+ - Interrupted run leaves managed workspace recoverable.
431
+
432
+ ### Acceptance scenario
433
+
434
+ 1. Create three remote branches, each with unique `SERVICE_NAME` and an unmanaged key.
435
+ 2. First-run template enforces two static values and preserves `SERVICE_NAME`.
436
+ 3. Fourth branch lacks `SERVICE_NAME`; CLI asks once for that branch during staging.
437
+ 4. Review shows exact four branch diffs before any commit.
438
+ 5. Reject one branch; approve three; only three are committed.
439
+ 6. Push prompt defaults to explicit confirmation.
440
+ 7. Re-run: saved template opens; add `LOG_LEVEL=debug`; dynamic values are read from Git without re-prompting.
441
+ 8. Manually change a static value and a dynamic value on different branches; next run restores static but preserves dynamic.
442
+ 9. Force a branch to advance after review; it is skipped as stale.
443
+ 10. Source repository remains untouched throughout.
444
+
445
+ ## 12. Delivery roadmap
446
+
447
+ ### Phase 0 — Lock contracts and test fixtures
448
+ - Confirm placeholder syntax, template-key deletion policy, source-repo handling, state-save semantics, and `.env` grammar.
449
+ - Build fixture repositories and golden before/after files.
450
+
451
+ ### Phase 1 — Safe Git foundation
452
+ - Config schema, workspace ownership, project identity, Git command runner, fetch, branch enumeration, clean/checkout/commit/push, lock, path-scoped staging.
453
+ - Tests for isolation, cleanup, and branch-tip revalidation.
454
+
455
+ ### Phase 2 — Pure `.env` engine
456
+ - Parser/tokenizer, static/dynamic rules, missing-input request generation, preservation rules, diagnostics, deterministic rendering.
457
+ - Exhaustive unit tests.
458
+
459
+ ### Phase 3 — Interactive pipeline and staging
460
+ - Branch/file selection, editor adapter, eligibility, missing-value prompts, out-of-tree snapshots, diff review, approvals.
461
+ - End-to-end dry run that makes no commits.
462
+
463
+ ### Phase 4 — Apply and push
464
+ - Revalidate approved plans, clean switch, path-scoped stage, commit, failure isolation, push gate, terminal summary.
465
+ - Full integration tests.
466
+
467
+ ### Phase 5 — Living-template persistence
468
+ - Save/load local template; first-run synthesis; accurate partial-success handling; incremental subsequent runs.
469
+ - Tests for repeated execution and manual branch edits.
470
+
471
+ ### Phase 6 — Polish and extensions
472
+ - Better TUI filtering, batch actions, exportable reports, CI behavior, additional file-format transformer plugins (Dockerfile/YAML/JSON/Groovy), optional shared state.
473
+ - Add only when real use cases justify them.
474
+
475
+ ## 13. Explicit non-goals for first release
476
+
477
+ - No one-worktree-per-branch persistent checkout farm.
478
+ - No remote or team-shared template state.
479
+ - No complex drift database or three-way merge engine.
480
+ - No automatic guessing of missing dynamic values.
481
+ - No implicit deletion of unmanaged keys.
482
+ - No multi-repository distributed transaction or guarantee that all branch pushes succeed atomically.
483
+ - No assumption that arbitrary YAML/JSON/Dockerfile/Groovy can be safely transformed by `.env` rules.
484
+
485
+ ## 14. Chosen defaults (open to change)
486
+
487
+ These decisions were made as reasonable defaults; they can be revisited as needs evolve.
488
+
489
+ | Decision | Default | Notes |
490
+ |---|---|---|
491
+ | **Removed template key** | **Preserve** on branches | Never delete keys implicitly. If deletion is needed, add an explicit delete operation later. |
492
+ | **Source repository with no remote** | **Reject with error** | Fail fast rather than guessing. A remote (e.g., `origin`) is required to safely use a separate managed checkout without mutating a local-only repo. |
493
+ | **Saved template on partial success** | **Save after first successful commit** (or after explicit approval if no commits occur because everything already matches) | Preserves progress on partial runs; reports clearly show partial status and remaining branches. |
494
+ | **Editable review snapshots** | **Read-only diff for MVP** | Safer and simpler. To change results, re-edit the template and re-run the plan (with mandatory revalidation). |
495
+ | **Secrets in diffs/reports** | **Show in interactive diff with a warning**; **redact from exported reports by default** | Needed to review `.env` changes, but exports (JSON/Markdown) must redact values by default to prevent accidental leaks. |
496
+
497
+ ## 15. Definition of done
498
+
499
+ Git Worker is ready for its first stable release when a developer can run `git-worker` from anywhere, target a repository, select many branches, edit a remembered `.env` template in VS Code or Vim, provide missing per-branch values before review, inspect deterministic diffs, approve a subset, safely commit and optionally push only approved changes, and receive an accurate report—without modifying their original checkout or carrying accidental files into commits.
package/README.md CHANGED
@@ -1,3 +1,115 @@
1
- # Temporary Holding Version
1
+ # Git Worker
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A safe, composable TypeScript CLI/TUI for applying reviewed file changes across many Git branches.
4
+
5
+ ## Overview
6
+
7
+ Git Worker lets you target a Git repository, select branches and a file, edit a reusable template, preview exact per-branch changes without checking out branches, approve/reject them, and then commit and optionally push approved changes. It works with a handful or 100+ branches without requiring one persistent worktree per branch.
8
+
9
+ ## Design Principles
10
+
11
+ - **Readable over clever** - Small functions, explicit types, straightforward control flow
12
+ - **Functional core, imperative shell** - Pure transformations/validation; side effects injected via services
13
+ - **Inspect without checkout** - Uses `git show`, `git ls-tree`, `git cat-file` to plan changes
14
+ - **Review before mutation** - Collect missing values and build diffs before any checkout/commit
15
+ - **Safe by default** - Never modifies your original repository; only commits to managed disposable checkouts
16
+ - **Per-branch fault isolation** - One branch failure doesn't corrupt others
17
+
18
+ ## How It Works
19
+
20
+ 1. **Resolve & sync** - Detects current Git repository or prompts to clone a remote URL into `~/.git-worker`. If already cloned, reuses the cached clone and fetches latest branches.
21
+ 2. **Select** - Choose branches (glob e.g. `uat/*`, regex e.g. `/uat.*/`, select all, or invert selection) and target file
22
+ 3. **Template** - Edit a template in VS Code or Vim (or restore previously applied template):
23
+ - **For `.env` files (AST Reconciliation)**: `KEY=value` sets static values; `KEY={KEY}` reads existing branch values; unmanaged keys and comments are preserved.
24
+ - **For other files (`Dockerfile`, JSON, YAML, configs)**: The template is the declarative **Source of Truth**; variables like `${VAR}` or `${VAR:-default}` are resolved per-branch from that branch's `.env`, alongside built-ins (`${BRANCH_NAME}`, `${BRANCH_SLUG}`). The evaluated template overrides or creates the file.
25
+ - **Syntax Validation**: Checks JSON/YAML syntax immediately before planning.
26
+ 4. **Plan & review** - See exact diffs per branch, approve/reject individually
27
+ 5. **Apply & push** - Commits approved changes in a managed checkout, optionally pushes with confirmation
28
+
29
+ ## Configuration Defaults (Chosen)
30
+
31
+ We made reasonable defaults for the open decisions. These are easy to change later.
32
+
33
+ | Decision | Default | Rationale |
34
+ |---|---|---|
35
+ | **Remove key from template** | **Preserve** on branches | Safest - never deletes keys implicitly. If you need deletion, use an explicit delete operation in the future. |
36
+ | **Source repo with no remote** | **Prompt or reject with error** | When outside a repo, prompts to clone remote or target path. Requires a remote to avoid mutating local-only repos unexpectedly. |
37
+ | **Save template on partial success** | **Save after first successful commit** (or after explicit approval if no commits but reviewed) | Preserves progress if some branches succeed and others fail. Partial status is clearly reported. |
38
+ | **Editable review snapshots** | **Read-only diff for MVP** | Much safer. To change results, re-edit the template and re-run the plan. Prevents accidental manual edits creating inconsistent diffs. |
39
+ | **Secrets in diffs** | **Show in interactive diff with warning**; **redact from exported reports by default** | You need to see secrets to review `.env` changes, but exports (JSON/Markdown) redact by default to avoid accidental leaks. |
40
+
41
+ ## CLI Usage
42
+
43
+ ```bash
44
+ # Interactive mode (prompts to use current repo, select cached repo, or clone remote)
45
+ git-worker
46
+
47
+ # Select from previously cloned/cached repositories directly
48
+ git-worker --cached
49
+
50
+ # Target a remote repository URL directly
51
+ git-worker --repo https://github.com/my-org/my-project.git
52
+
53
+ # Target a local repo with specific branches and file
54
+ git-worker --repo ../my-repo --branch 'uat/*' --file .env
55
+
56
+ # Non-interactive CI mode
57
+ git-worker --repo git@github.com:org/repo.git --branch 'uat/*' --file .env --ci --push never
58
+
59
+ # Cache management
60
+ git-worker cache list # List all cached repositories in ~/.git-worker
61
+ git-worker cache clean [target] # Clean a specific project or select interactively
62
+ git-worker cache clean --all # Clean all cached checkouts (or: git-worker clean -a -y)
63
+ ```
64
+
65
+ Key flags: `--repo/-r`, `--cached/-c`, `--branch/-b`, `--invert-branch/-i`, `--file/-f`, `--editor/-e`, `--commit-msg/-m`, `--push/-p`, `--remote`, `--auto-approve`, `--ci`
66
+
67
+ ## Workspace Layout
68
+
69
+ Git Worker stores all checkouts, templates, and state under `~/.git-worker/` in the user's home directory:
70
+
71
+ ```
72
+ ~/.git-worker/
73
+ projects/
74
+ <stable-project-id>/
75
+ repo/ # Disposable checkout owned by git-worker
76
+ staging/<run-id>/ # Before/after snapshots outside repo
77
+ state/ # Saved templates (per project+target)
78
+ reports/ # Run reports (optional)
79
+ .git-worker-marker # Ownership verification marker
80
+ .git-worker-lock # Process lock
81
+ ```
82
+
83
+ **Safety guarantees:**
84
+ - Never touches your original repository
85
+ - Only stages target paths (`git add -- <path>`)
86
+ - Revalidates branch tips before applying (skips if changed)
87
+ - Resets/cleans only inside verified managed checkout
88
+ - Commits/pushes per-branch with fault isolation
89
+
90
+ ## Supported Editors
91
+
92
+ - **VS Code (preferred)**: `code --wait` for editing, `code --diff` for review
93
+ - **Vim**: `vim` for editing, `vim -d`/`vimdiff` for review
94
+
95
+ ## Development Status
96
+
97
+ This is the implementation blueprint. See [GIT_WORKER_PROJECT_PLAN.md](./GIT_WORKER_PROJECT_PLAN.md) for the full architecture, API contracts, and roadmap.
98
+
99
+ ## Non-Goals (MVP)
100
+
101
+ - One-worktree-per-branch persistent checkout farm
102
+ - Remote/team-shared template state
103
+ - Complex drift database or three-way merge
104
+ - Automatic guessing of missing values
105
+ - Implicit deletion of unmanaged keys
106
+ - Multi-repo distributed transactions
107
+ - Full support for arbitrary YAML/JSON/Dockerfile/Groovy (only `.env` in MVP)
108
+
109
+ ## Documentation
110
+
111
+ - [Architecture](docs/architecture.md) - Pipeline design, safety model, and structure
112
+ - [Features](docs/FEATURES.md) - Complete feature list and capabilities
113
+ - [Services](docs/SERVICES.md) - Service APIs and dependency injection
114
+ - [Usage Guide](docs/usage.md) - Detailed usage examples
115
+ - [Implementation Plan](GIT_WORKER_PROJECT_PLAN.md) - Full blueprint and roadmap
package/demo/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # git-worker Demo
2
+
3
+ End-to-end functional verification that exercises every pipeline stage using a **real local Git repository** with 4 branches.
4
+
5
+ ## What it does
6
+
7
+ 1. **Creates** a local bare Git repo (acts as the "remote") in `/tmp/git-worker-demo/`
8
+ 2. **Populates** 4 UAT branches with different `.env` files:
9
+
10
+ | Branch | Contents | Notes |
11
+ |---|---|---|
12
+ | `uat/payments` | All keys present | Baseline |
13
+ | `uat/orders` | `LOG_LEVEL` **missing** | Triggers per-branch prompt |
14
+ | `uat/inventory` | Has `export LEGACY_VAR=…` | Verifies `export` prefix preserved |
15
+ | `uat/notifications` | `DB_NAME=legacy_db` (wrong) | Verifies static enforcement |
16
+
17
+ 3. **Pre-loads** this template (skips editor opening):
18
+ ```dotenv
19
+ # Static — enforced on all branches
20
+ DB_NAME=uat_db
21
+
22
+ # Dynamic — read from each branch
23
+ DB_PORT={DB_PORT}
24
+ SERVICE_NAME={SERVICE_NAME}
25
+ LOG_LEVEL={LOG_LEVEL}
26
+ ```
27
+
28
+ 4. **Runs `git-worker`** against the repo matching pattern `/^uat\//`
29
+ 5. **Verifies** results: DB_NAME corrected, CUSTOM_FLAG preserved, SERVICE_NAME intact
30
+
31
+ ## Run
32
+
33
+ ```bash
34
+ # Interactive — full TUI with prompts and per-branch diff review
35
+ npx tsx demo/index.ts
36
+
37
+ # Non-interactive — CI mode, auto-approve, uat/orders skipped (LOG_LEVEL missing)
38
+ npx tsx demo/index.ts --ci
39
+
40
+ # Clean up demo files
41
+ npx tsx demo/index.ts --clean
42
+ ```
43
+
44
+ ## Expected outcomes
45
+
46
+ | Branch | Outcome |
47
+ |---|---|
48
+ | `uat/payments` | `DB_NAME` stays `uat_db`, all dynamic values preserved |
49
+ | `uat/orders` | **CI:** skipped (missing `LOG_LEVEL`) — **Interactive:** prompted, then committed |
50
+ | `uat/inventory` | `export LEGACY_VAR` prefix preserved, `DB_NAME` unchanged |
51
+ | `uat/notifications` | `DB_NAME` corrected `legacy_db → uat_db`, `CUSTOM_FLAG` preserved |
52
+
53
+ ## What each verification checks
54
+
55
+ After the run, the script reads `.env` from each branch via `git show origin/BRANCH:.env` and asserts:
56
+ - `DB_NAME=uat_db` (static enforcement worked)
57
+ - `CUSTOM_FLAG` still present (unmanaged key preservation)
58
+ - `SERVICE_NAME` still present (dynamic placeholder — value unchanged)