@samaksh/git-worker 0.1.0 → 0.2.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 (47) hide show
  1. package/dist/cli/index.js +1 -1
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +3 -0
  4. package/dist/services/git/reader.js +105 -34
  5. package/dist/services/workspace/manager.js +1 -1
  6. package/dist/stages/eligibility.js +38 -14
  7. package/dist/stages/select.js +46 -37
  8. package/package.json +11 -1
  9. package/GIT_WORKER_PROJECT_PLAN.md +0 -499
  10. package/demo/README.md +0 -58
  11. package/demo/index.ts +0 -526
  12. package/src/app.ts +0 -69
  13. package/src/cli/commands.ts +0 -142
  14. package/src/cli/index.ts +0 -123
  15. package/src/config/defaults.ts +0 -46
  16. package/src/config/load.ts +0 -70
  17. package/src/config/schema.ts +0 -48
  18. package/src/core/errors.ts +0 -45
  19. package/src/core/pipeline.ts +0 -17
  20. package/src/core/types.ts +0 -188
  21. package/src/services/editor/index.ts +0 -85
  22. package/src/services/git/ops.ts +0 -170
  23. package/src/services/git/reader.ts +0 -148
  24. package/src/services/report/index.ts +0 -104
  25. package/src/services/review/index.ts +0 -80
  26. package/src/services/state/index.ts +0 -77
  27. package/src/services/template/env.ts +0 -169
  28. package/src/services/template/interpolator.ts +0 -109
  29. package/src/services/template/transform.ts +0 -97
  30. package/src/services/template/validator.ts +0 -75
  31. package/src/services/workspace/cache.ts +0 -144
  32. package/src/services/workspace/manager.ts +0 -370
  33. package/src/stages/apply.ts +0 -194
  34. package/src/stages/edit-template.ts +0 -227
  35. package/src/stages/eligibility.ts +0 -67
  36. package/src/stages/load-template.ts +0 -188
  37. package/src/stages/plan.ts +0 -362
  38. package/src/stages/push.ts +0 -120
  39. package/src/stages/report.ts +0 -126
  40. package/src/stages/resolve-project.ts +0 -341
  41. package/src/stages/review.ts +0 -228
  42. package/src/stages/select.ts +0 -302
  43. package/src/stages/sync.ts +0 -63
  44. package/src/utils/hash.ts +0 -15
  45. package/src/utils/ui.ts +0 -137
  46. package/src/utils/which.ts +0 -18
  47. package/tsconfig.json +0 -18
package/demo/index.ts DELETED
@@ -1,526 +0,0 @@
1
- #!/usr/bin/env tsx
2
- /**
3
- * git-worker demo / functional verification script
4
- *
5
- * Sets up a realistic local Git repository with 4 branches, each containing
6
- * a .env file with different values, then runs the full git-worker pipeline
7
- * against it in non-interactive (--ci --auto-approve) mode so you can verify
8
- * every stage end-to-end without clicking through prompts.
9
- *
10
- * Usage:
11
- * npx tsx demo/index.ts # full interactive run
12
- * npx tsx demo/index.ts --ci # non-interactive (CI) run
13
- * npx tsx demo/index.ts --clean # remove demo repo only
14
- */
15
-
16
- import { execSync, spawnSync } from "child_process";
17
- import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync } from "fs";
18
- import { join, resolve } from "path";
19
- import { tmpdir, homedir } from "os";
20
- import { createHash } from "crypto";
21
- import chalk from "chalk";
22
-
23
- // ── Config ─────────────────────────────────────────────────────────────────
24
-
25
- const DEMO_DIR = join(tmpdir(), "git-worker-demo");
26
- const BARE_REPO = join(DEMO_DIR, "remote.git"); // acts as the "remote"
27
- const WORK_REPO = join(DEMO_DIR, "workspace"); // working clone for branch setup
28
-
29
- // ── Arg parsing ────────────────────────────────────────────────────────────
30
-
31
- const args = process.argv.slice(2);
32
- const isCI = args.includes("--ci");
33
- const cleanOnly = args.includes("--clean");
34
-
35
- // ── Helpers ────────────────────────────────────────────────────────────────
36
-
37
- function section(title: string) {
38
- console.log("");
39
- console.log(chalk.bold.cyan(" ══════════════════════════════════════════════"));
40
- console.log(chalk.bold.cyan(` ${title}`));
41
- console.log(chalk.bold.cyan(" ══════════════════════════════════════════════"));
42
- }
43
-
44
- function git(args: string[], cwd: string): string {
45
- const result = spawnSync("git", args, { cwd, encoding: "utf-8" });
46
- if (result.status !== 0) {
47
- throw new Error(`git ${args.join(" ")} failed:\n${result.stderr}`);
48
- }
49
- return result.stdout.trim();
50
- }
51
-
52
- function step(msg: string) {
53
- console.log(chalk.gray(` → ${msg}`));
54
- }
55
-
56
- function ok(msg: string) {
57
- console.log(chalk.green(` ✓ ${msg}`));
58
- }
59
-
60
- // ── Branches + .env content ────────────────────────────────────────────────
61
-
62
- /**
63
- * The demo repo models a microservices monorepo with four UAT branches.
64
- * Each branch has a .env file with:
65
- * - SERVICE_NAME (varies per branch — will become a dynamic placeholder)
66
- * - DB_PORT (varies — will become a dynamic placeholder)
67
- * - DB_NAME (same on all — will become a static value)
68
- * - LOG_LEVEL (missing on uat/orders — triggers per-branch prompt in interactive mode)
69
- * - CUSTOM_FLAG (unmanaged key — must be preserved)
70
- */
71
- const BRANCHES: Array<{
72
- name: string;
73
- env: string;
74
- note: string;
75
- }> = [
76
- {
77
- name: "uat/payments",
78
- env: `# UAT payments service
79
- DB_NAME=uat_db
80
- DB_PORT=54321
81
- SERVICE_NAME=payments
82
- LOG_LEVEL=info
83
- CUSTOM_FLAG=payments-only-feature
84
- `,
85
- note: "All keys present",
86
- },
87
- {
88
- name: "uat/orders",
89
- env: `# UAT orders service
90
- DB_NAME=uat_db
91
- DB_PORT=54322
92
- SERVICE_NAME=orders
93
- # LOG_LEVEL is intentionally missing here — will trigger a prompt in interactive mode
94
- CUSTOM_FLAG=orders-flag
95
- `,
96
- note: "LOG_LEVEL missing (will prompt or fail in CI)",
97
- },
98
- {
99
- name: "uat/inventory",
100
- env: `# UAT inventory service
101
- DB_NAME=uat_db
102
- DB_PORT=54323
103
- SERVICE_NAME=inventory
104
- LOG_LEVEL=warn
105
- CUSTOM_FLAG=inventory-flag
106
- export LEGACY_VAR=keep-export-prefix
107
- `,
108
- note: "Has 'export' prefix on one key — must be preserved",
109
- },
110
- {
111
- name: "uat/notifications",
112
- env: `# UAT notifications service
113
- DB_NAME=legacy_db
114
- DB_PORT=54324
115
- SERVICE_NAME=notifications
116
- LOG_LEVEL=debug
117
- CUSTOM_FLAG=notif-flag
118
- `,
119
- note: "DB_NAME is wrong ('legacy_db') — static template will correct it",
120
- },
121
- ];
122
-
123
- /**
124
- * The template we'll use.
125
- * - DB_NAME=uat_db → static: enforced on all branches
126
- * - DB_PORT={DB_PORT} → dynamic: read per branch
127
- * - SERVICE_NAME={SERVICE_NAME} → dynamic
128
- * - LOG_LEVEL={LOG_LEVEL} → dynamic (missing on uat/orders — prompts in interactive)
129
- * CUSTOM_FLAG is not declared → preserved untouched on each branch
130
- */
131
- const TEMPLATE = `# Git Worker Demo Template
132
- # Static values are enforced on every branch
133
- DB_NAME=uat_db
134
-
135
- # Dynamic values are read from each branch
136
- DB_PORT={DB_PORT}
137
- SERVICE_NAME={SERVICE_NAME}
138
- LOG_LEVEL={LOG_LEVEL}
139
- `;
140
-
141
- // ── Setup ──────────────────────────────────────────────────────────────────
142
-
143
- function setupDemoRepo() {
144
- section("Setting up demo repository");
145
-
146
- if (existsSync(DEMO_DIR)) {
147
- step("Removing existing demo directory…");
148
- rmSync(DEMO_DIR, { recursive: true, force: true });
149
- }
150
- mkdirSync(DEMO_DIR, { recursive: true });
151
-
152
- // 1. Create bare repo (the "remote")
153
- step("Creating bare repo (remote)…");
154
- git(["init", "--bare", "remote.git"], DEMO_DIR);
155
- ok(`Bare repo: ${BARE_REPO}`);
156
-
157
- // 2. Clone into workspace
158
- step("Cloning into workspace…");
159
- git(["clone", BARE_REPO, "workspace"], DEMO_DIR);
160
- ok(`Workspace: ${WORK_REPO}`);
161
-
162
- // Configure git identity for commits
163
- git(["config", "user.email", "demo@git-worker.local"], WORK_REPO);
164
- git(["config", "user.name", "Git Worker Demo"], WORK_REPO);
165
-
166
- // 3. Create initial commit on main
167
- step("Creating initial commit on main…");
168
- writeFileSync(join(WORK_REPO, "README.md"), "# Demo Repo\nManaged by git-worker demo.\n");
169
- git(["add", "."], WORK_REPO);
170
- git(["commit", "-m", "chore: initial commit"], WORK_REPO);
171
- git(["push", "-u", "origin", "main"], WORK_REPO);
172
-
173
- // 4. Create branches with .env files
174
- for (const branch of BRANCHES) {
175
- step(`Creating branch ${chalk.cyan(branch.name)} — ${branch.note}`);
176
- git(["checkout", "-b", branch.name], WORK_REPO);
177
- writeFileSync(join(WORK_REPO, ".env"), branch.env);
178
- git(["add", ".env"], WORK_REPO);
179
- git(["commit", "-m", `chore: add .env for ${branch.name}`], WORK_REPO);
180
- git(["push", "-u", "origin", branch.name], WORK_REPO);
181
- git(["checkout", "main"], WORK_REPO);
182
- ok(` ${branch.name}`);
183
- }
184
-
185
- // 5. Save the template as the saved state so git-worker loads it automatically
186
- // (skipping the editor step in CI mode)
187
- const stateDir = join(DEMO_DIR, "state");
188
- mkdirSync(stateDir, { recursive: true });
189
-
190
- console.log("");
191
- section("Demo Repository Contents");
192
-
193
- console.log("");
194
- console.log(chalk.bold(" Branches created:"));
195
- for (const branch of BRANCHES) {
196
- console.log(chalk.gray(` ${branch.name.padEnd(22)} ${branch.note}`));
197
- }
198
-
199
- console.log("");
200
- console.log(chalk.bold(" Template that will be applied:"));
201
- console.log(chalk.gray(" ┌" + "─".repeat(58) + "┐"));
202
- for (const line of TEMPLATE.split("\n")) {
203
- console.log(chalk.gray(` │ ${line.padEnd(57)}│`));
204
- }
205
- console.log(chalk.gray(" └" + "─".repeat(58) + "┘"));
206
-
207
- console.log("");
208
- console.log(chalk.bold(" Expected outcome per branch:"));
209
- console.log(chalk.gray(" uat/payments — DB_NAME corrected if wrong, LOG_LEVEL preserved"));
210
- console.log(chalk.gray(" uat/orders — LOG_LEVEL missing → prompt (or skip in CI)"));
211
- console.log(chalk.gray(" uat/inventory — 'export' prefix on LEGACY_VAR preserved"));
212
- console.log(chalk.gray(" uat/notifications— DB_NAME: legacy_db → uat_db (static enforcement)"));
213
-
214
- return WORK_REPO;
215
- }
216
-
217
- // ── Run git-worker ─────────────────────────────────────────────────────────
218
-
219
- function runGitWorker(workRepoPath: string) {
220
- section("Running git-worker");
221
-
222
- // Discover the remote URL that git-worker will use to identify the project
223
- // (the bare repo path is what workspace/manager.ts will resolve from WORK_REPO's origin)
224
- const remoteUrl = BARE_REPO;
225
-
226
- // Save the template into the workspace state dir so the tool loads it
227
- // (avoids the editor opening in the demo)
228
- const workerStateRoot = join(homedir(), ".git-worker");
229
-
230
- const projectId = createHash("sha256").update(remoteUrl).digest("hex").slice(0, 16);
231
- const stateDir = join(workerStateRoot, "projects", projectId, "state");
232
- mkdirSync(stateDir, { recursive: true });
233
-
234
- const stateFile = join(stateDir, ".env.state.json");
235
- writeFileSync(
236
- stateFile,
237
- JSON.stringify(
238
- {
239
- schemaVersion: 1,
240
- targetPath: ".env",
241
- template: TEMPLATE,
242
- updatedAt: new Date().toISOString(),
243
- },
244
- null,
245
- 2
246
- )
247
- );
248
- ok(`Pre-loaded template into state: ${stateFile}`);
249
-
250
- const cliPath = resolve("dist/cli/index.js");
251
- if (!existsSync(cliPath)) {
252
- console.log(chalk.yellow(" ⚠ dist/ not found — building first…"));
253
- execSync("node_modules/.bin/tsc", { stdio: "inherit" });
254
- }
255
-
256
- const pushPolicy = isCI ? "always" : "never";
257
-
258
- const flags = [
259
- "--repo", WORK_REPO,
260
- "--branch", "/^uat\\//",
261
- "--file", ".env",
262
- "--push", pushPolicy,
263
- ];
264
-
265
- if (isCI) {
266
- // In CI mode: non-interactive, auto-approve, LOG_LEVEL missing branch will be skipped
267
- flags.push("--ci", "--auto-approve");
268
- console.log("");
269
- console.log(chalk.bold(" Mode: CI (non-interactive, auto-approve, push: always)"));
270
- console.log(chalk.gray(" Note: uat/orders will fail because LOG_LEVEL is missing (expected in CI)"));
271
- } else {
272
- // Interactive mode: git-worker will prompt for LOG_LEVEL on uat/orders
273
- console.log("");
274
- console.log(chalk.bold(" Mode: Interactive"));
275
- console.log(chalk.yellow(" ℹ The editor will NOT open (template is pre-loaded)"));
276
- console.log(chalk.yellow(" ℹ You will be prompted to enter LOG_LEVEL for uat/orders"));
277
- console.log(chalk.yellow(" ℹ Review each branch diff and approve/reject individually"));
278
- }
279
-
280
- console.log("");
281
- console.log(chalk.bold(" Command:"));
282
- console.log(chalk.gray(` node ${cliPath} ${flags.join(" ")}`));
283
- console.log("");
284
- console.log(chalk.cyan("─".repeat(66)));
285
- console.log("");
286
-
287
- // Run git-worker
288
- const result = spawnSync("node", [cliPath, ...flags], {
289
- stdio: "inherit",
290
- env: { ...process.env },
291
- });
292
-
293
- console.log("");
294
- console.log(chalk.cyan("─".repeat(66)));
295
-
296
- if (result.status === 0) {
297
- section("Verification");
298
- verifyResults(BARE_REPO);
299
- } else {
300
- console.log(chalk.red(`\n git-worker exited with code ${result.status}`));
301
- // Still run verification to see partial results
302
- section("Partial Verification");
303
- verifyResults(BARE_REPO);
304
- }
305
- }
306
-
307
- // ── Verify results ─────────────────────────────────────────────────────────
308
-
309
- function verifyResults(remoteUrl: string) {
310
- console.log("");
311
- console.log(chalk.bold(" Checking .env on each branch after git-worker run:"));
312
- console.log("");
313
-
314
- const verifyRepo = join(DEMO_DIR, "verify-clone");
315
- if (!existsSync(verifyRepo)) {
316
- git(["clone", remoteUrl, "verify-clone"], DEMO_DIR);
317
- git(["config", "user.email", "verify@git-worker.local"], verifyRepo);
318
- git(["config", "user.name", "Verify"], verifyRepo);
319
- } else {
320
- git(["fetch", "--all"], verifyRepo);
321
- }
322
-
323
- for (const branch of BRANCHES) {
324
- try {
325
- const content = git(["show", `origin/${branch.name}:.env`], verifyRepo);
326
- const lines = content.split("\n").filter((l) => !l.startsWith("#") && l.trim());
327
- const map = new Map<string, string>();
328
- for (const line of lines) {
329
- const eq = line.indexOf("=");
330
- if (eq !== -1) {
331
- const key = line.slice(0, eq).replace(/^export\s+/, "").trim();
332
- const val = line.slice(eq + 1).trim();
333
- map.set(key, val);
334
- }
335
- }
336
-
337
- const checks: Array<{ key: string; expected: string; actual: string | undefined; ok: boolean }> = [];
338
-
339
- // DB_NAME must be uat_db (static enforcement)
340
- checks.push({
341
- key: "DB_NAME",
342
- expected: "uat_db",
343
- actual: map.get("DB_NAME"),
344
- ok: map.get("DB_NAME") === "uat_db",
345
- });
346
-
347
- // CUSTOM_FLAG must still exist (unmanaged key preservation)
348
- checks.push({
349
- key: "CUSTOM_FLAG",
350
- expected: "(present)",
351
- actual: map.has("CUSTOM_FLAG") ? "(present)" : "(missing)",
352
- ok: map.has("CUSTOM_FLAG"),
353
- });
354
-
355
- // SERVICE_NAME must still be set (dynamic)
356
- checks.push({
357
- key: "SERVICE_NAME",
358
- expected: "(present)",
359
- actual: map.has("SERVICE_NAME") ? "(present)" : "(missing)",
360
- ok: map.has("SERVICE_NAME"),
361
- });
362
-
363
- const branchOk = checks.every((c) => c.ok);
364
- const icon = branchOk ? chalk.green("✓") : chalk.red("✗");
365
- console.log(` ${icon} ${chalk.bold(branch.name.padEnd(24))}`);
366
- for (const c of checks) {
367
- const cIcon = c.ok ? chalk.green(" ✓") : chalk.red(" ✗");
368
- const val = c.ok
369
- ? chalk.gray(`${c.key}=${c.actual}`)
370
- : chalk.red(`${c.key}: expected ${c.expected}, got ${c.actual ?? "(missing)"}`);
371
- console.log(` ${cIcon} ${val}`);
372
- }
373
- } catch {
374
- console.log(chalk.yellow(` ⚠ ${branch.name} — could not read (may not have been committed)`));
375
- }
376
- }
377
-
378
- console.log("");
379
- console.log(chalk.bold(" Demo workspace:"));
380
- console.log(chalk.gray(` Remote: ${BARE_REPO}`));
381
- console.log(chalk.gray(` Clone: ${WORK_REPO}`));
382
- console.log(chalk.gray(` Verify: ${verifyRepo}`));
383
- console.log("");
384
- }
385
-
386
- const DOCKERFILE_TEMPLATE = `# Source-of-Truth Dockerfile Template
387
- FROM node:20-alpine AS base
388
- ENV SERVICE_NAME=\${SERVICE_NAME}
389
- ENV BRANCH_NAME=\${BRANCH_NAME}
390
- ENV BRANCH_SLUG=\${BRANCH_SLUG}
391
- EXPOSE \${PORT:-8080}
392
- `;
393
-
394
- function runDockerfileDemo(remoteUrl: string) {
395
- section("Phase 2: Dockerfile Synchronization (Source of Truth Engine)");
396
-
397
- const workerStateRoot = join(homedir(), ".git-worker");
398
- const projectId = createHash("sha256").update(remoteUrl).digest("hex").slice(0, 16);
399
- const stateDir = join(workerStateRoot, "projects", projectId, "state");
400
- mkdirSync(stateDir, { recursive: true });
401
-
402
- const stateFile = join(stateDir, "Dockerfile.state.json");
403
- writeFileSync(
404
- stateFile,
405
- JSON.stringify(
406
- {
407
- schemaVersion: 1,
408
- targetPath: "Dockerfile",
409
- template: DOCKERFILE_TEMPLATE,
410
- updatedAt: new Date().toISOString(),
411
- },
412
- null,
413
- 2
414
- )
415
- );
416
- ok(`Pre-loaded Dockerfile template into state: ${stateFile}`);
417
-
418
- const cliPath = resolve("dist/cli/index.js");
419
- const pushPolicy = isCI ? "always" : "never";
420
- const flags = [
421
- "--repo", WORK_REPO,
422
- "--branch", "/^uat\\//",
423
- "--file", "Dockerfile",
424
- "--push", pushPolicy,
425
- ];
426
-
427
- if (isCI) {
428
- flags.push("--ci", "--auto-approve");
429
- }
430
-
431
- console.log("");
432
- console.log(chalk.bold(" Command:"));
433
- console.log(chalk.gray(` node ${cliPath} ${flags.join(" ")}`));
434
- console.log("");
435
- console.log(chalk.cyan("─".repeat(66)));
436
- console.log("");
437
-
438
- const result = spawnSync("node", [cliPath, ...flags], {
439
- stdio: "inherit",
440
- env: { ...process.env },
441
- });
442
-
443
- console.log("");
444
- console.log(chalk.cyan("─".repeat(66)));
445
-
446
- if (result.status === 0) {
447
- section("Dockerfile Verification");
448
- verifyDockerfileResults(BARE_REPO);
449
- } else {
450
- console.log(chalk.red(`\n git-worker exited with code ${result.status}`));
451
- section("Partial Dockerfile Verification");
452
- verifyDockerfileResults(BARE_REPO);
453
- }
454
- }
455
-
456
- function verifyDockerfileResults(remoteUrl: string) {
457
- console.log("");
458
- console.log(chalk.bold(" Checking Dockerfile on each branch after git-worker run:"));
459
- console.log("");
460
-
461
- const verifyRepo = join(DEMO_DIR, "verify-clone");
462
- git(["fetch", "--all"], verifyRepo);
463
-
464
- for (const branch of BRANCHES) {
465
- try {
466
- const content = git(["show", `origin/${branch.name}:Dockerfile`], verifyRepo);
467
- const hasService = content.includes(`ENV SERVICE_NAME=`);
468
- const hasBranch = content.includes(`ENV BRANCH_NAME=${branch.name}`);
469
- const hasPort = content.includes(`EXPOSE 8080`);
470
-
471
- const checks = [
472
- { label: "SERVICE_NAME resolved from .env", ok: hasService },
473
- { label: `BRANCH_NAME built-in (${branch.name})`, ok: hasBranch },
474
- { label: "PORT fallback used (8080)", ok: hasPort },
475
- ];
476
-
477
- const allOk = checks.every((c) => c.ok);
478
- const icon = allOk ? chalk.green("✓") : chalk.red("✗");
479
- console.log(` ${icon} ${chalk.bold(branch.name.padEnd(24))}`);
480
- for (const c of checks) {
481
- const cIcon = c.ok ? chalk.green(" ✓") : chalk.red(" ✗");
482
- console.log(` ${cIcon} ${c.label}`);
483
- }
484
- } catch {
485
- console.log(chalk.yellow(` ⚠ ${branch.name} — could not read Dockerfile`));
486
- }
487
- }
488
-
489
- // Verify persistence: check that state file was saved
490
- const workerStateRoot = join(homedir(), ".git-worker");
491
- const projectId = createHash("sha256").update(remoteUrl).digest("hex").slice(0, 16);
492
- const stateFile = join(workerStateRoot, "projects", projectId, "state", "Dockerfile.state.json");
493
- if (existsSync(stateFile)) {
494
- console.log("");
495
- ok(`State persistence verified: ${stateFile} updated with latest applied template`);
496
- }
497
- }
498
-
499
- // ── Entry point ────────────────────────────────────────────────────────────
500
-
501
- async function main() {
502
- console.log("");
503
- console.log(chalk.bold.cyan(" ◆ git-worker — Demo & Functional Verification"));
504
- console.log(chalk.gray(" Exercises every stage of the pipeline with a real local Git repo"));
505
- console.log("");
506
-
507
- if (cleanOnly) {
508
- if (existsSync(DEMO_DIR)) {
509
- rmSync(DEMO_DIR, { recursive: true, force: true });
510
- console.log(chalk.green(` ✓ Removed ${DEMO_DIR}`));
511
- } else {
512
- console.log(chalk.gray(` Nothing to clean at ${DEMO_DIR}`));
513
- }
514
- return;
515
- }
516
-
517
- const _workRepo = setupDemoRepo();
518
- runGitWorker(BARE_REPO);
519
- runDockerfileDemo(BARE_REPO);
520
- }
521
-
522
- main().catch((e) => {
523
- console.error(chalk.red(`\nDemo error: ${e.message}`));
524
- if (process.env.DEBUG) console.error(e.stack);
525
- process.exit(1);
526
- });
package/src/app.ts DELETED
@@ -1,69 +0,0 @@
1
- import { CliInput, RunContext, AppDeps, AppServices, Result, ok } from "./core/types.js";
2
- import { loadConfig } from "./config/load.js";
3
- import { pipeline } from "./core/pipeline.js";
4
-
5
- // Services
6
- import { createGitReader } from "./services/git/reader.js";
7
- import { createGitOps } from "./services/git/ops.js";
8
- import { createWorkspaceManager } from "./services/workspace/manager.js";
9
- import { createTemplateService } from "./services/template/transform.js";
10
- import { createStateService } from "./services/state/index.js";
11
- import { createEditorService } from "./services/editor/index.js";
12
- import { createReviewService } from "./services/review/index.js";
13
-
14
- // Stages
15
- import { resolveProjectStage } from "./stages/resolve-project.js";
16
- import { syncRepositoryStage } from "./stages/sync.js";
17
- import { selectTargetsStage } from "./stages/select.js";
18
- import { checkEligibilityStage } from "./stages/eligibility.js";
19
- import { loadTemplateStage } from "./stages/load-template.js";
20
- import { editTemplateStage } from "./stages/edit-template.js";
21
- import { planChangesStage } from "./stages/plan.js";
22
- import { reviewChangesStage } from "./stages/review.js";
23
- import { applyChangesStage } from "./stages/apply.js";
24
- import { pushChangesStage } from "./stages/push.js";
25
- import { reportResultsStage } from "./stages/report.js";
26
-
27
- function createServices(): AppServices {
28
- return {
29
- git: createGitReader(),
30
- gitOps: createGitOps(),
31
- workspace: createWorkspaceManager(),
32
- template: createTemplateService(),
33
- state: createStateService(),
34
- editor: createEditorService(),
35
- review: createReviewService(),
36
- };
37
- }
38
-
39
- export async function run(input: CliInput): Promise<Result<RunContext>> {
40
- const config = loadConfig(input);
41
- const services = createServices();
42
-
43
- const deps: AppDeps = { config, services };
44
-
45
- const context: RunContext = {
46
- runId: `${Date.now()}`,
47
- input,
48
- };
49
-
50
- const runPipeline = pipeline<RunContext, AppDeps>(
51
- resolveProjectStage,
52
- syncRepositoryStage,
53
- selectTargetsStage,
54
- checkEligibilityStage,
55
- loadTemplateStage,
56
- editTemplateStage,
57
- planChangesStage,
58
- reviewChangesStage,
59
- applyChangesStage,
60
- pushChangesStage,
61
- reportResultsStage,
62
- );
63
-
64
- const result = await runPipeline(context, deps);
65
- if (result.ok && result.value.project) {
66
- services.workspace.releaseLock(result.value.project);
67
- }
68
- return result;
69
- }