@procedures/git 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 (120) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1114 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/index.d.ts +20 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +20 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/module.d.ts +16 -0
  10. package/dist/module.d.ts.map +1 -0
  11. package/dist/module.js +437 -0
  12. package/dist/module.js.map +1 -0
  13. package/dist/procedures/git/add.d.ts +12 -0
  14. package/dist/procedures/git/add.d.ts.map +1 -0
  15. package/dist/procedures/git/add.js +26 -0
  16. package/dist/procedures/git/add.js.map +1 -0
  17. package/dist/procedures/git/args.d.ts +15 -0
  18. package/dist/procedures/git/args.d.ts.map +1 -0
  19. package/dist/procedures/git/args.js +20 -0
  20. package/dist/procedures/git/args.js.map +1 -0
  21. package/dist/procedures/git/branch.d.ts +12 -0
  22. package/dist/procedures/git/branch.d.ts.map +1 -0
  23. package/dist/procedures/git/branch.js +54 -0
  24. package/dist/procedures/git/branch.js.map +1 -0
  25. package/dist/procedures/git/checkout.d.ts +12 -0
  26. package/dist/procedures/git/checkout.d.ts.map +1 -0
  27. package/dist/procedures/git/checkout.js +23 -0
  28. package/dist/procedures/git/checkout.js.map +1 -0
  29. package/dist/procedures/git/clone.d.ts +12 -0
  30. package/dist/procedures/git/clone.d.ts.map +1 -0
  31. package/dist/procedures/git/clone.js +28 -0
  32. package/dist/procedures/git/clone.js.map +1 -0
  33. package/dist/procedures/git/commit.d.ts +12 -0
  34. package/dist/procedures/git/commit.d.ts.map +1 -0
  35. package/dist/procedures/git/commit.js +41 -0
  36. package/dist/procedures/git/commit.js.map +1 -0
  37. package/dist/procedures/git/diff.d.ts +18 -0
  38. package/dist/procedures/git/diff.d.ts.map +1 -0
  39. package/dist/procedures/git/diff.js +58 -0
  40. package/dist/procedures/git/diff.js.map +1 -0
  41. package/dist/procedures/git/fetch.d.ts +12 -0
  42. package/dist/procedures/git/fetch.d.ts.map +1 -0
  43. package/dist/procedures/git/fetch.js +29 -0
  44. package/dist/procedures/git/fetch.js.map +1 -0
  45. package/dist/procedures/git/index.d.ts +15 -0
  46. package/dist/procedures/git/index.d.ts.map +1 -0
  47. package/dist/procedures/git/index.js +15 -0
  48. package/dist/procedures/git/index.js.map +1 -0
  49. package/dist/procedures/git/init.d.ts +12 -0
  50. package/dist/procedures/git/init.d.ts.map +1 -0
  51. package/dist/procedures/git/init.js +32 -0
  52. package/dist/procedures/git/init.js.map +1 -0
  53. package/dist/procedures/git/log.d.ts +12 -0
  54. package/dist/procedures/git/log.d.ts.map +1 -0
  55. package/dist/procedures/git/log.js +35 -0
  56. package/dist/procedures/git/log.js.map +1 -0
  57. package/dist/procedures/git/predicates.d.ts +40 -0
  58. package/dist/procedures/git/predicates.d.ts.map +1 -0
  59. package/dist/procedures/git/predicates.js +76 -0
  60. package/dist/procedures/git/predicates.js.map +1 -0
  61. package/dist/procedures/git/pull.d.ts +12 -0
  62. package/dist/procedures/git/pull.d.ts.map +1 -0
  63. package/dist/procedures/git/pull.js +36 -0
  64. package/dist/procedures/git/pull.js.map +1 -0
  65. package/dist/procedures/git/push.d.ts +12 -0
  66. package/dist/procedures/git/push.d.ts.map +1 -0
  67. package/dist/procedures/git/push.js +39 -0
  68. package/dist/procedures/git/push.js.map +1 -0
  69. package/dist/procedures/git/remote.d.ts +12 -0
  70. package/dist/procedures/git/remote.d.ts.map +1 -0
  71. package/dist/procedures/git/remote.js +31 -0
  72. package/dist/procedures/git/remote.js.map +1 -0
  73. package/dist/procedures/git/run.d.ts +32 -0
  74. package/dist/procedures/git/run.d.ts.map +1 -0
  75. package/dist/procedures/git/run.js +60 -0
  76. package/dist/procedures/git/run.js.map +1 -0
  77. package/dist/procedures/git/stash.d.ts +38 -0
  78. package/dist/procedures/git/stash.d.ts.map +1 -0
  79. package/dist/procedures/git/stash.js +211 -0
  80. package/dist/procedures/git/stash.js.map +1 -0
  81. package/dist/procedures/git/status.d.ts +17 -0
  82. package/dist/procedures/git/status.d.ts.map +1 -0
  83. package/dist/procedures/git/status.js +107 -0
  84. package/dist/procedures/git/status.js.map +1 -0
  85. package/dist/procedures/git.d.ts +5 -0
  86. package/dist/procedures/git.d.ts.map +1 -0
  87. package/dist/procedures/git.js +5 -0
  88. package/dist/procedures/git.js.map +1 -0
  89. package/dist/register.d.ts +8 -0
  90. package/dist/register.d.ts.map +1 -0
  91. package/dist/register.js +14 -0
  92. package/dist/register.js.map +1 -0
  93. package/dist/types.d.ts +341 -0
  94. package/dist/types.d.ts.map +1 -0
  95. package/dist/types.js +387 -0
  96. package/dist/types.js.map +1 -0
  97. package/package.json +74 -3
  98. package/src/index.ts +20 -0
  99. package/src/module.ts +570 -0
  100. package/src/procedures/git/add.ts +29 -0
  101. package/src/procedures/git/args.ts +21 -0
  102. package/src/procedures/git/branch.ts +61 -0
  103. package/src/procedures/git/checkout.ts +27 -0
  104. package/src/procedures/git/clone.ts +33 -0
  105. package/src/procedures/git/commit.ts +46 -0
  106. package/src/procedures/git/diff.ts +62 -0
  107. package/src/procedures/git/fetch.ts +32 -0
  108. package/src/procedures/git/index.ts +22 -0
  109. package/src/procedures/git/init.ts +43 -0
  110. package/src/procedures/git/log.ts +40 -0
  111. package/src/procedures/git/predicates.ts +90 -0
  112. package/src/procedures/git/pull.ts +45 -0
  113. package/src/procedures/git/push.ts +42 -0
  114. package/src/procedures/git/remote.ts +33 -0
  115. package/src/procedures/git/run.ts +70 -0
  116. package/src/procedures/git/stash.ts +255 -0
  117. package/src/procedures/git/status.ts +98 -0
  118. package/src/procedures/git.ts +5 -0
  119. package/src/register.ts +17 -0
  120. package/src/types.ts +799 -0
package/README.md CHANGED
@@ -1,3 +1,1115 @@
1
- # Temporary Holding Version
1
+ # @procedures/git
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
+ [![npm version](https://img.shields.io/npm/v/@procedures/git.svg)](https://www.npmjs.com/package/@procedures/git)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ > Git operations as RPC procedures - `git.add`, `git.commit`, `git.push`, `git.status`, and 22 more git commands exposed as type-safe procedures.
7
+
8
+ ## Overview
9
+
10
+ `@procedures/git` exposes comprehensive Git functionality as RPC procedures through the client system. It provides 26 procedures covering all major Git operations including repository state management, staging, commits, branching, remote operations, stashing, and repository predicates.
11
+
12
+ This package wraps Git commands directly (not through `client-shell`), providing structured input/output with full type safety via Zod schemas.
13
+
14
+ ## Architecture
15
+
16
+ ```mermaid
17
+ graph TB
18
+ A[client-git] -->|depends on| B[@procedures/client]
19
+ A -->|validates with| C[zod]
20
+
21
+ D[Application] -->|calls| E[git.* procedures]
22
+ E -->|executes| F[Git CLI]
23
+ E -->|parses| G[Structured Output]
24
+
25
+ H[Repository State<br/>status, log, diff, branch] -.-> E
26
+ I[Staging & Commits<br/>add, commit, checkout] -.-> E
27
+ J[Remote Sync<br/>push, pull, fetch, clone] -.-> E
28
+ K[Stash Operations<br/>stash.*, export, import] -.-> E
29
+ L[Predicates<br/>hasChanges, isClean, etc] -.-> E
30
+
31
+ style A fill:#e1f5ff
32
+ style E fill:#e1ffe1
33
+ ```
34
+
35
+ ### Procedure Categories
36
+
37
+ ```mermaid
38
+ graph LR
39
+ A[client-git<br/>26 Procedures] --> B[Repository State<br/>5 procedures]
40
+ A --> C[Staging & Commits<br/>3 procedures]
41
+ A --> D[Remote Operations<br/>5 procedures]
42
+ A --> E[Predicates<br/>6 procedures]
43
+ A --> F[Stash Operations<br/>7 procedures]
44
+
45
+ B -.-> B1[status, log, diff,<br/>branch, init]
46
+ C -.-> C1[add, commit,<br/>checkout]
47
+ D -.-> D1[push, pull, fetch,<br/>clone, remote]
48
+ E -.-> E1[hasChanges, isClean,<br/>hasStagedChanges,<br/>hasUnstagedChanges,<br/>hasUntrackedFiles,<br/>hasLocalCommits]
49
+ F -.-> F1[stash.list, stash.push,<br/>stash.pop, stash.apply,<br/>stash.drop, stash.export,<br/>stash.import]
50
+
51
+ style A fill:#e1f5ff
52
+ style B fill:#ffe1e1
53
+ style C fill:#ffe1e1
54
+ style D fill:#ffe1e1
55
+ style E fill:#fff4e1
56
+ style F fill:#e1ffe1
57
+ ```
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ # Using pnpm (recommended)
63
+ pnpm add @procedures/git
64
+
65
+ # Using npm
66
+ npm install @procedures/git
67
+
68
+ # Using yarn
69
+ yarn add @procedures/git
70
+ ```
71
+
72
+ ## Quick Start
73
+
74
+ ```typescript
75
+ import { Client, LocalTransport, PROCEDURE_REGISTRY } from "@procedures/client";
76
+ import "@procedures/git"; // registers its procedures
77
+
78
+ const client = new Client(new LocalTransport({ registry: PROCEDURE_REGISTRY }));
79
+
80
+ // Get repository status
81
+ const status = await client.exec(["git", "status"], {
82
+ cwd: "/path/to/repo",
83
+ });
84
+ console.log(status.branch, status.files);
85
+
86
+ // Stage and commit
87
+ await client.exec(["git", "add"], { all: true });
88
+ await client.exec(["git", "commit"], { message: "feat: add new feature" });
89
+
90
+ // Push to remote
91
+ await client.exec(["git", "push"], { remote: "origin" });
92
+ ```
93
+
94
+ ## API Reference
95
+
96
+ ### Repository State Procedures
97
+
98
+ #### `git.status`
99
+
100
+ Get current repository status including branch, tracking info, and file changes.
101
+
102
+ **Path:** `["git", "status"]`
103
+
104
+ ```typescript
105
+ interface GitStatusInput {
106
+ /** Working directory (default: process.cwd()) */
107
+ cwd?: string;
108
+ /** Also give `lines` in the format of `git status --short` (default: false) */
109
+ short?: boolean;
110
+ }
111
+
112
+ interface GitStatusFile {
113
+ /** The path (for a rename or a copy, the new path). Paths are exact: no quotes. */
114
+ path: string;
115
+ /** For a rename or a copy, the old path */
116
+ from?: string;
117
+ status: "modified" | "added" | "deleted" | "renamed" | "copied" | "untracked" | "ignored";
118
+ staged: boolean;
119
+ }
120
+
121
+ interface GitStatusOutput {
122
+ branch: string;
123
+ ahead: number;
124
+ behind: number;
125
+ files: GitStatusFile[];
126
+ clean: boolean;
127
+ /** With `short: true`: one line per file, for example "R old.ts -> new.ts" */
128
+ lines?: string[];
129
+ }
130
+ ```
131
+
132
+ **Example:**
133
+ ```typescript
134
+ const status = await client.exec(["git", "status"], { cwd: "./my-repo" });
135
+ console.log(`On branch ${status.branch}`);
136
+ console.log(`${status.ahead} commits ahead, ${status.behind} behind`);
137
+ console.log(`${status.files.length} changed files`);
138
+ ```
139
+
140
+ #### `git.log`
141
+
142
+ Show commit history.
143
+
144
+ **Path:** `["git", "log"]`
145
+
146
+ ```typescript
147
+ interface GitLogInput {
148
+ /** Number of commits to show (default: 10) */
149
+ count?: number;
150
+ /** Also give `lines`: one line per commit, the short hash and the subject (default: false) */
151
+ oneline?: boolean;
152
+ /** Branch/tag/commit to start from (default: HEAD) */
153
+ ref?: string;
154
+ /** Working directory */
155
+ cwd?: string;
156
+ }
157
+
158
+ interface GitLogCommit {
159
+ hash: string;
160
+ shortHash: string;
161
+ author: string;
162
+ email: string;
163
+ date: string;
164
+ message: string;
165
+ }
166
+
167
+ interface GitLogOutput {
168
+ commits: GitLogCommit[];
169
+ /** With `oneline: true` */
170
+ lines?: string[];
171
+ }
172
+ ```
173
+
174
+ **Example:**
175
+ ```typescript
176
+ const log = await client.exec(["git", "log"], { count: 5 });
177
+ for (const commit of log.commits) {
178
+ console.log(`${commit.shortHash} - ${commit.message}`);
179
+ }
180
+ ```
181
+
182
+ #### `git.diff`
183
+
184
+ Show changes between commits, branches, or working directory.
185
+
186
+ **Path:** `["git", "diff"]`
187
+
188
+ ```typescript
189
+ interface GitDiffInput {
190
+ /** Show staged changes (default: false, shows unstaged) */
191
+ staged?: boolean;
192
+ /** Compare against ref (branch/commit) */
193
+ ref?: string;
194
+ /** Specific paths to diff */
195
+ paths?: string[];
196
+ /** Show stat summary only (default: false) */
197
+ stat?: boolean;
198
+ /** Working directory */
199
+ cwd?: string;
200
+ }
201
+
202
+ interface GitDiffFile {
203
+ /** The path (for a rename, the new path) */
204
+ path: string;
205
+ /** For a rename, the old path */
206
+ from?: string;
207
+ additions: number;
208
+ deletions: number;
209
+ }
210
+
211
+ interface GitDiffOutput {
212
+ files: GitDiffFile[];
213
+ additions: number;
214
+ deletions: number;
215
+ diff?: string;
216
+ }
217
+ ```
218
+
219
+ **Example:**
220
+ ```typescript
221
+ // Show unstaged changes
222
+ const diff = await client.exec(["git", "diff"], { stat: true });
223
+ console.log(`${diff.additions} additions, ${diff.deletions} deletions`);
224
+
225
+ // Show staged changes
226
+ const staged = await client.exec(["git", "diff"], { staged: true });
227
+ ```
228
+
229
+ #### `git.branch`
230
+
231
+ Branch operations - list, create, delete branches.
232
+
233
+ **Path:** `["git", "branch"]`
234
+
235
+ ```typescript
236
+ interface GitBranchInput {
237
+ /** Branch name (for create/delete) */
238
+ name?: string;
239
+ /** Delete the branch (default: false) */
240
+ delete?: boolean;
241
+ /** List branches (default: true if no name provided) */
242
+ list?: boolean;
243
+ /** Include remote branches in list (default: false) */
244
+ remote?: boolean;
245
+ /** Working directory */
246
+ cwd?: string;
247
+ }
248
+
249
+ interface GitBranchInfo {
250
+ name: string;
251
+ current: boolean;
252
+ remote: boolean;
253
+ tracking?: string;
254
+ }
255
+
256
+ interface GitBranchOutput {
257
+ branches?: GitBranchInfo[];
258
+ created?: string;
259
+ deleted?: string;
260
+ current: string;
261
+ }
262
+ ```
263
+
264
+ **Example:**
265
+ ```typescript
266
+ // List all branches
267
+ const branches = await client.exec(["git", "branch"], { list: true });
268
+ console.log("Branches:", branches.branches.map(b => b.name));
269
+
270
+ // Create new branch
271
+ await client.exec(["git", "branch"], { name: "feature/new-feature" });
272
+
273
+ // Delete branch
274
+ await client.exec(["git", "branch"], { name: "old-feature", delete: true });
275
+ ```
276
+
277
+ #### `git.init`
278
+
279
+ Initialize a new git repository.
280
+
281
+ **Path:** `["git", "init"]`
282
+
283
+ ```typescript
284
+ interface GitInitInput {
285
+ /** Directory to initialize (default: process.cwd()) */
286
+ cwd?: string;
287
+ /** Create a bare repository (default: false) */
288
+ bare?: boolean;
289
+ /** Name for the initial branch */
290
+ initialBranch?: string;
291
+ }
292
+
293
+ interface GitInitOutput {
294
+ /** The initialized repository path */
295
+ path: string;
296
+ /** Whether the repo was newly created */
297
+ created: boolean;
298
+ }
299
+ ```
300
+
301
+ ### Staging & Commit Procedures
302
+
303
+ #### `git.add`
304
+
305
+ Stage files for commit.
306
+
307
+ **Path:** `["git", "add"]`
308
+
309
+ ```typescript
310
+ interface GitAddInput {
311
+ /** Paths to stage (default: []) */
312
+ paths?: string[];
313
+ /** Stage all changes (default: false) */
314
+ all?: boolean;
315
+ /** Working directory */
316
+ cwd?: string;
317
+ }
318
+
319
+ interface GitAddOutput {
320
+ staged: string[];
321
+ }
322
+ ```
323
+
324
+ **Example:**
325
+ ```typescript
326
+ // Stage specific files
327
+ await client.exec(["git", "add"], { paths: ["file1.ts", "file2.ts"] });
328
+
329
+ // Stage all changes
330
+ await client.exec(["git", "add"], { all: true });
331
+ ```
332
+
333
+ #### `git.commit`
334
+
335
+ Create a commit with staged changes.
336
+
337
+ **Path:** `["git", "commit"]`
338
+
339
+ ```typescript
340
+ interface GitCommitInput {
341
+ /** Commit message */
342
+ message: string;
343
+ /** Automatically stage modified files (default: false) */
344
+ all?: boolean;
345
+ /** Amend the last commit (default: false) */
346
+ amend?: boolean;
347
+ /** Working directory */
348
+ cwd?: string;
349
+ }
350
+
351
+ interface GitCommitOutput {
352
+ hash: string;
353
+ message: string;
354
+ author: string;
355
+ date: string;
356
+ /** True if commit was skipped (nothing to commit) */
357
+ skipped?: boolean;
358
+ }
359
+ ```
360
+
361
+ **Example:**
362
+ ```typescript
363
+ const commit = await client.exec(["git", "commit"], {
364
+ message: "feat: add new feature\n\nDetailed description here",
365
+ all: true, // Auto-stage modified files
366
+ });
367
+ console.log(`Created commit ${commit.hash}`);
368
+ ```
369
+
370
+ #### `git.checkout`
371
+
372
+ Checkout branch, tag, or restore files.
373
+
374
+ **Path:** `["git", "checkout"]`
375
+
376
+ ```typescript
377
+ interface GitCheckoutInput {
378
+ /** Branch, tag, or commit to checkout */
379
+ ref: string;
380
+ /** Create branch if it doesn't exist (default: false) */
381
+ create?: boolean;
382
+ /** Specific paths to checkout (default: all) */
383
+ paths?: string[];
384
+ /** Working directory */
385
+ cwd?: string;
386
+ }
387
+
388
+ interface GitCheckoutOutput {
389
+ ref: string;
390
+ created: boolean;
391
+ }
392
+ ```
393
+
394
+ **Example:**
395
+ ```typescript
396
+ // Checkout existing branch
397
+ await client.exec(["git", "checkout"], { ref: "main" });
398
+
399
+ // Create and checkout new branch
400
+ await client.exec(["git", "checkout"], { ref: "feature/new", create: true });
401
+
402
+ // Restore specific file
403
+ await client.exec(["git", "checkout"], { ref: "HEAD", paths: ["file.ts"] });
404
+ ```
405
+
406
+ ### Remote Operations
407
+
408
+ #### `git.push`
409
+
410
+ Push commits to remote repository.
411
+
412
+ **Path:** `["git", "push"]`
413
+
414
+ ```typescript
415
+ interface GitPushInput {
416
+ /** Remote name (default: origin) */
417
+ remote?: string;
418
+ /** Branch to push (default: current branch) */
419
+ branch?: string;
420
+ /** Force push (default: false) */
421
+ force?: boolean;
422
+ /** Set upstream tracking (default: false) */
423
+ setUpstream?: boolean;
424
+ /** Working directory */
425
+ cwd?: string;
426
+ }
427
+
428
+ interface GitPushOutput {
429
+ remote: string;
430
+ branch: string;
431
+ commits: number;
432
+ }
433
+ ```
434
+
435
+ **Example:**
436
+ ```typescript
437
+ // Push to origin
438
+ await client.exec(["git", "push"], {});
439
+
440
+ // Push and set upstream
441
+ await client.exec(["git", "push"], {
442
+ remote: "origin",
443
+ branch: "feature/new",
444
+ setUpstream: true,
445
+ });
446
+
447
+ // Force push (use with caution!)
448
+ await client.exec(["git", "push"], { force: true });
449
+ ```
450
+
451
+ #### `git.pull`
452
+
453
+ Pull changes from remote repository.
454
+
455
+ **Path:** `["git", "pull"]`
456
+
457
+ ```typescript
458
+ interface GitPullInput {
459
+ /** Remote name (default: origin) */
460
+ remote?: string;
461
+ /** Branch to pull (default: current branch) */
462
+ branch?: string;
463
+ /** Rebase instead of merge (default: false) */
464
+ rebase?: boolean;
465
+ /** Working directory */
466
+ cwd?: string;
467
+ }
468
+
469
+ interface GitPullOutput {
470
+ remote: string;
471
+ branch: string;
472
+ commits: number;
473
+ fastForward: boolean;
474
+ }
475
+ ```
476
+
477
+ #### `git.fetch`
478
+
479
+ Fetch from remote without merging.
480
+
481
+ **Path:** `["git", "fetch"]`
482
+
483
+ ```typescript
484
+ interface GitFetchInput {
485
+ /** Remote name (default: origin) */
486
+ remote?: string;
487
+ /** Specific branch to fetch (default: all) */
488
+ branch?: string;
489
+ /** Fetch all remotes (default: false) */
490
+ all?: boolean;
491
+ /** Prune deleted branches (default: false) */
492
+ prune?: boolean;
493
+ /** Working directory */
494
+ cwd?: string;
495
+ }
496
+
497
+ interface GitFetchOutput {
498
+ remote: string;
499
+ fetched: boolean;
500
+ }
501
+ ```
502
+
503
+ #### `git.clone`
504
+
505
+ Clone a repository.
506
+
507
+ **Path:** `["git", "clone"]`
508
+
509
+ ```typescript
510
+ interface GitCloneInput {
511
+ /** Repository URL */
512
+ url: string;
513
+ /** Destination directory (default: derived from URL) */
514
+ dest?: string;
515
+ /** Branch to clone (default: default branch) */
516
+ branch?: string;
517
+ /** Create shallow clone with depth (default: full clone) */
518
+ depth?: number;
519
+ /** Working directory */
520
+ cwd?: string;
521
+ }
522
+
523
+ interface GitCloneOutput {
524
+ path: string;
525
+ branch: string;
526
+ }
527
+ ```
528
+
529
+ **Example:**
530
+ ```typescript
531
+ const clone = await client.exec(["git", "clone"], {
532
+ url: "https://github.com/user/repo.git",
533
+ dest: "./my-repo",
534
+ depth: 1, // Shallow clone
535
+ });
536
+ console.log(`Cloned to ${clone.path}`);
537
+ ```
538
+
539
+ #### `git.remote`
540
+
541
+ Get or set remote URLs.
542
+
543
+ **Path:** `["git", "remote"]`
544
+
545
+ ```typescript
546
+ interface GitRemoteInput {
547
+ /** Remote name (default: origin) */
548
+ name?: string;
549
+ /** New URL to set (if provided, sets the URL; otherwise gets it) */
550
+ url?: string;
551
+ /** Working directory */
552
+ cwd?: string;
553
+ }
554
+
555
+ interface GitRemoteOutput {
556
+ /** Remote name */
557
+ name: string;
558
+ /** Remote URL */
559
+ url: string;
560
+ }
561
+ ```
562
+
563
+ ### Predicate Procedures
564
+
565
+ These procedures return boolean predicates useful for conditional logic.
566
+
567
+ #### `git.hasChanges`
568
+
569
+ Check if there are any changes (unstaged, staged, or untracked).
570
+
571
+ **Path:** `["git", "hasChanges"]`
572
+
573
+ ```typescript
574
+ interface GitPredicateInput {
575
+ cwd?: string;
576
+ }
577
+
578
+ interface GitPredicateOutput {
579
+ value: boolean;
580
+ }
581
+ ```
582
+
583
+ #### `git.hasStagedChanges`
584
+
585
+ Check if there are staged changes ready to commit.
586
+
587
+ **Path:** `["git", "hasStagedChanges"]`
588
+
589
+ #### `git.hasUnstagedChanges`
590
+
591
+ Check if there are unstaged changes.
592
+
593
+ **Path:** `["git", "hasUnstagedChanges"]`
594
+
595
+ #### `git.hasUntrackedFiles`
596
+
597
+ Check if there are untracked files.
598
+
599
+ **Path:** `["git", "hasUntrackedFiles"]`
600
+
601
+ #### `git.hasLocalCommits`
602
+
603
+ Check if there are local commits that haven't been pushed.
604
+
605
+ **Path:** `["git", "hasLocalCommits"]`
606
+
607
+ #### `git.isClean`
608
+
609
+ Check if the working directory is clean (no changes).
610
+
611
+ **Path:** `["git", "isClean"]`
612
+
613
+ **Example:**
614
+ ```typescript
615
+ const { value: hasChanges } = await client.exec(["git", "hasChanges"], {});
616
+ if (hasChanges) {
617
+ console.log("Repository has uncommitted changes");
618
+ }
619
+
620
+ const { value: isClean } = await client.exec(["git", "isClean"], {});
621
+ if (isClean) {
622
+ console.log("Working directory is clean");
623
+ }
624
+ ```
625
+
626
+ ### Stash Procedures
627
+
628
+ #### `git.stash.list`
629
+
630
+ List all stashes.
631
+
632
+ **Path:** `["git", "stash", "list"]`
633
+
634
+ ```typescript
635
+ interface GitStashEntry {
636
+ /** Stash index (0 = most recent) */
637
+ index: number;
638
+ /** Stash reference (e.g., stash@{0}) */
639
+ ref: string;
640
+ /** Commit hash of the stash */
641
+ hash: string;
642
+ /** Stash message */
643
+ message: string;
644
+ }
645
+
646
+ interface GitStashListOutput {
647
+ /** List of stashes */
648
+ stashes: GitStashEntry[];
649
+ /** Total count */
650
+ count: number;
651
+ }
652
+ ```
653
+
654
+ #### `git.stash.push`
655
+
656
+ Save changes to stash.
657
+
658
+ **Path:** `["git", "stash", "push"]`
659
+
660
+ ```typescript
661
+ interface GitStashPushInput {
662
+ /** Stash message */
663
+ message?: string;
664
+ /** Include untracked files (default: false) */
665
+ includeUntracked?: boolean;
666
+ /** Keep staged changes in index (default: false) */
667
+ keepIndex?: boolean;
668
+ /** Specific paths to stash */
669
+ paths?: string[];
670
+ /** Working directory */
671
+ cwd?: string;
672
+ }
673
+
674
+ interface GitStashPushOutput {
675
+ /** Whether changes were stashed */
676
+ stashed: boolean;
677
+ /** Stash reference if created */
678
+ ref?: string;
679
+ /** Stash message */
680
+ message?: string;
681
+ }
682
+ ```
683
+
684
+ #### `git.stash.pop`
685
+
686
+ Apply and remove most recent stash.
687
+
688
+ **Path:** `["git", "stash", "pop"]`
689
+
690
+ ```typescript
691
+ interface GitStashPopInput {
692
+ /** Stash index to pop (default: 0 = most recent) */
693
+ index?: number;
694
+ cwd?: string;
695
+ }
696
+
697
+ interface GitStashPopOutput {
698
+ /** Whether stash was applied */
699
+ applied: boolean;
700
+ /** Stash reference that was popped */
701
+ ref: string;
702
+ /** Whether stash was dropped (false if conflict) */
703
+ dropped: boolean;
704
+ /** Whether there was a merge conflict */
705
+ conflict?: boolean;
706
+ }
707
+ ```
708
+
709
+ #### `git.stash.apply`
710
+
711
+ Apply stash without removing it.
712
+
713
+ **Path:** `["git", "stash", "apply"]`
714
+
715
+ ```typescript
716
+ interface GitStashApplyInput {
717
+ /** Stash index to apply (default: 0 = most recent) */
718
+ index?: number;
719
+ cwd?: string;
720
+ }
721
+
722
+ interface GitStashApplyOutput {
723
+ /** Whether stash was applied */
724
+ applied: boolean;
725
+ /** Stash reference that was applied */
726
+ ref: string;
727
+ /** Whether there was a merge conflict */
728
+ conflict?: boolean;
729
+ }
730
+ ```
731
+
732
+ #### `git.stash.drop`
733
+
734
+ Delete a stash.
735
+
736
+ **Path:** `["git", "stash", "drop"]`
737
+
738
+ ```typescript
739
+ interface GitStashDropInput {
740
+ /** Stash index to drop (default: 0 = most recent) */
741
+ index?: number;
742
+ cwd?: string;
743
+ }
744
+
745
+ interface GitStashDropOutput {
746
+ /** Whether stash was dropped */
747
+ dropped: boolean;
748
+ /** Stash reference that was dropped */
749
+ ref: string;
750
+ }
751
+ ```
752
+
753
+ #### `git.stash.export`
754
+
755
+ Export stash as patch for snapshot storage.
756
+
757
+ **Path:** `["git", "stash", "export"]`
758
+
759
+ ```typescript
760
+ interface GitStashExportInput {
761
+ /** Stash index to export (default: 0 = most recent) */
762
+ index?: number;
763
+ cwd?: string;
764
+ }
765
+
766
+ interface GitStashExportOutput {
767
+ /** Stash reference */
768
+ ref: string;
769
+ /** Commit hash */
770
+ hash: string;
771
+ /** Stash message */
772
+ message: string;
773
+ /** Patch content (diff) */
774
+ patch: string;
775
+ /** Whether stash includes untracked files */
776
+ hasUntracked: boolean;
777
+ /** Untracked files patch if present */
778
+ untrackedPatch?: string;
779
+ }
780
+ ```
781
+
782
+ #### `git.stash.import`
783
+
784
+ Import stash from patch.
785
+
786
+ **Path:** `["git", "stash", "import"]`
787
+
788
+ ```typescript
789
+ interface GitStashImportInput {
790
+ /** Patch content to apply and stash */
791
+ patch: string;
792
+ /** Message for the new stash */
793
+ message?: string;
794
+ /** Include untracked files (default: false) */
795
+ includeUntracked?: boolean;
796
+ cwd?: string;
797
+ }
798
+
799
+ interface GitStashImportOutput {
800
+ /** Whether import succeeded */
801
+ imported: boolean;
802
+ /** New stash reference if created */
803
+ ref?: string;
804
+ /** Error message if failed */
805
+ error?: string;
806
+ }
807
+ ```
808
+
809
+ **Stash Example:**
810
+ ```typescript
811
+ // Save current work
812
+ await client.exec(["git", "stash", "push"], {
813
+ message: "WIP: feature in progress",
814
+ includeUntracked: true,
815
+ });
816
+
817
+ // List stashes
818
+ const stashes = await client.exec(["git", "stash", "list"], {});
819
+ console.log(`${stashes.count} stashes`);
820
+
821
+ // Apply most recent stash
822
+ await client.exec(["git", "stash", "apply"], { index: 0 });
823
+
824
+ // Export stash for backup
825
+ const exported = await client.exec(["git", "stash", "export"], { index: 0 });
826
+ fs.writeFileSync("backup.patch", exported.patch);
827
+ ```
828
+
829
+ ## Complete Procedure List
830
+
831
+ | Category | Procedure | Description |
832
+ |----------|-----------|-------------|
833
+ | **Repository State** | `git.status` | Get repository status |
834
+ | | `git.log` | Show commit history |
835
+ | | `git.diff` | Show changes |
836
+ | | `git.branch` | Branch operations |
837
+ | | `git.init` | Initialize repository |
838
+ | **Staging & Commits** | `git.add` | Stage files |
839
+ | | `git.commit` | Create commit |
840
+ | | `git.checkout` | Checkout branch/files |
841
+ | **Remote Operations** | `git.push` | Push to remote |
842
+ | | `git.pull` | Pull from remote |
843
+ | | `git.fetch` | Fetch from remote |
844
+ | | `git.clone` | Clone repository |
845
+ | | `git.remote` | Manage remotes |
846
+ | **Predicates** | `git.hasChanges` | Check for any changes |
847
+ | | `git.hasStagedChanges` | Check for staged changes |
848
+ | | `git.hasUnstagedChanges` | Check for unstaged changes |
849
+ | | `git.hasUntrackedFiles` | Check for untracked files |
850
+ | | `git.hasLocalCommits` | Check for unpushed commits |
851
+ | | `git.isClean` | Check if working dir is clean |
852
+ | **Stash** | `git.stash.list` | List stashes |
853
+ | | `git.stash.push` | Save to stash |
854
+ | | `git.stash.pop` | Apply and remove stash |
855
+ | | `git.stash.apply` | Apply stash |
856
+ | | `git.stash.drop` | Delete stash |
857
+ | | `git.stash.export` | Export stash as patch |
858
+ | | `git.stash.import` | Import stash from patch |
859
+
860
+ ## Usage Examples
861
+
862
+ ### Complete Git Workflow
863
+
864
+ ```typescript
865
+ // 1. Check status
866
+ const status = await client.exec(["git", "status"], {});
867
+ console.log(`On branch ${status.branch}`);
868
+
869
+ // 2. Stage changes
870
+ await client.exec(["git", "add"], { all: true });
871
+
872
+ // 3. Create commit
873
+ const commit = await client.exec(["git", "commit"], {
874
+ message: "feat: implement new feature",
875
+ });
876
+
877
+ // 4. Push to remote
878
+ await client.exec(["git", "push"], {
879
+ remote: "origin",
880
+ setUpstream: true,
881
+ });
882
+ ```
883
+
884
+ ### Conditional Operations with Predicates
885
+
886
+ ```typescript
887
+ const { value: hasChanges } = await client.exec(["git", "hasChanges"], {});
888
+ if (hasChanges) {
889
+ await client.exec(["git", "stash", "push"], {
890
+ message: "Auto-stash before pull",
891
+ });
892
+ }
893
+
894
+ await client.exec(["git", "pull"], { rebase: true });
895
+
896
+ const { value: hasStashes } = await client.exec(["git", "stash", "list"], {});
897
+ if (hasStashes.count > 0) {
898
+ await client.exec(["git", "stash", "pop"], {});
899
+ }
900
+ ```
901
+
902
+ ### Branch Management
903
+
904
+ ```typescript
905
+ // Create feature branch
906
+ await client.exec(["git", "checkout"], {
907
+ ref: "feature/new-feature",
908
+ create: true,
909
+ });
910
+
911
+ // Work on feature...
912
+ await client.exec(["git", "add"], { all: true });
913
+ await client.exec(["git", "commit"], { message: "feat: complete feature" });
914
+
915
+ // Merge to main
916
+ await client.exec(["git", "checkout"], { ref: "main" });
917
+ await client.exec(["git", "pull"], {});
918
+
919
+ // Delete feature branch
920
+ await client.exec(["git", "branch"], {
921
+ name: "feature/new-feature",
922
+ delete: true,
923
+ });
924
+ ```
925
+
926
+ ### Working with Remotes
927
+
928
+ ```typescript
929
+ // Clone repository
930
+ const clone = await client.exec(["git", "clone"], {
931
+ url: "https://github.com/user/repo.git",
932
+ dest: "./my-repo",
933
+ });
934
+
935
+ // Set remote
936
+ await client.exec(["git", "remote"], {
937
+ name: "upstream",
938
+ url: "https://github.com/original/repo.git",
939
+ });
940
+
941
+ // Fetch from upstream
942
+ await client.exec(["git", "fetch"], {
943
+ remote: "upstream",
944
+ prune: true,
945
+ });
946
+ ```
947
+
948
+ ## Dependencies
949
+
950
+ ```mermaid
951
+ graph LR
952
+ A[@procedures/git] --> B[@procedures/client]
953
+ A --> C[zod]
954
+
955
+ B -.->|provides| D[Client & Procedure System]
956
+ C -.->|provides| E[Schema Validation]
957
+
958
+ style A fill:#e1f5ff
959
+ style B fill:#ffe1e1
960
+ style C fill:#fff4e1
961
+ ```
962
+
963
+ - **`@procedures/client`** - Core client library providing the procedure system
964
+ - **`zod`** - Schema validation library
965
+
966
+ ## Integration with Ecosystem
967
+
968
+ ### Used By
969
+
970
+ ```typescript
971
+ // bundle-dev/src/register.ts
972
+ import "@procedures/git"; // registers its procedures
973
+
974
+ // client-lib uses git procedures internally
975
+ import { gitStatus, gitAdd, gitCommit } from "@procedures/git";
976
+ ```
977
+
978
+ ### Usage in client-lib
979
+
980
+ The `client-lib` package uses git procedures extensively for package management:
981
+
982
+ ```typescript
983
+ // From client-lib/src/git/operations.ts
984
+ export async function stageAll(cwd: string, ctx: ProcedureContext): Promise<void> {
985
+ await ctx.client.call(["git", "add"], { all: true, cwd });
986
+ }
987
+
988
+ export async function commit(
989
+ message: string,
990
+ cwd: string,
991
+ ctx: ProcedureContext
992
+ ): Promise<string> {
993
+ const result = await ctx.client.call(["git", "commit"], { message, cwd });
994
+ return result.hash;
995
+ }
996
+ ```
997
+
998
+ ## Configuration
999
+
1000
+ ### Auto-Registration
1001
+
1002
+ Procedures are automatically registered when the package is loaded:
1003
+
1004
+ ```json
1005
+ {
1006
+ "client": {
1007
+ "procedures": "./dist/register.js"
1008
+ },
1009
+ "scripts": {
1010
+ "postinstall": "client-registry announce"
1011
+ }
1012
+ }
1013
+ ```
1014
+
1015
+ ### Build Scripts
1016
+
1017
+ ```bash
1018
+ # Build the package
1019
+ pnpm build
1020
+
1021
+ # Type check
1022
+ pnpm typecheck
1023
+
1024
+ # Clean build artifacts
1025
+ pnpm clean
1026
+ ```
1027
+
1028
+ ## Advanced Usage
1029
+
1030
+ ### Direct Function Imports
1031
+
1032
+ The handlers start git themselves. Their context is optional: it gives only the abort `signal`.
1033
+
1034
+ ```typescript
1035
+ import { gitStatus, gitCommit, gitPush } from "@procedures/git";
1036
+
1037
+ const status = await gitStatus({ cwd: "./repo" });
1038
+ await gitCommit({ message: "feat: new feature", cwd: "./repo" });
1039
+ await gitPush({ remote: "origin", cwd: "./repo" }, { signal: AbortSignal.timeout(60_000) });
1040
+ ```
1041
+
1042
+ ### Custom Registration
1043
+
1044
+ ```typescript
1045
+ import { createProcedure, registerProcedures } from "@procedures/client";
1046
+ import { gitStatus, GitStatusInputSchema } from "@procedures/git";
1047
+
1048
+ // Register under custom namespace
1049
+ const customStatusProcedure = createProcedure()
1050
+ .path(["custom", "git", "status"])
1051
+ .input(GitStatusInputSchema)
1052
+ .handler(gitStatus)
1053
+ .build();
1054
+
1055
+ registerProcedures([customStatusProcedure]);
1056
+ ```
1057
+
1058
+ ## Error Handling
1059
+
1060
+ All git procedures may throw errors for invalid operations:
1061
+
1062
+ ```typescript
1063
+ try {
1064
+ await client.exec(["git", "commit"], {
1065
+ message: "feat: new feature",
1066
+ });
1067
+ } catch (error) {
1068
+ if (error.message.includes("nothing to commit")) {
1069
+ console.log("No changes to commit");
1070
+ } else {
1071
+ throw error;
1072
+ }
1073
+ }
1074
+ ```
1075
+
1076
+ ## Performance Considerations
1077
+
1078
+ - **Command Execution**: Each procedure spawns a git process
1079
+ - **Large Repositories**: Operations may be slower on repos with extensive history
1080
+ - **Batch Operations**: Use predicate checks to avoid unnecessary operations
1081
+ - **Stash Export/Import**: Useful for snapshotting state without git history overhead
1082
+
1083
+ ## Troubleshooting
1084
+
1085
+ ### Git Not Found
1086
+
1087
+ Ensure git is installed and in PATH:
1088
+ ```bash
1089
+ git --version
1090
+ ```
1091
+
1092
+ ### Permission Errors
1093
+
1094
+ Check repository permissions:
1095
+ ```bash
1096
+ ls -la .git
1097
+ ```
1098
+
1099
+ ### Detached HEAD State
1100
+
1101
+ Use `git.checkout` to return to a branch:
1102
+ ```typescript
1103
+ await client.exec(["git", "checkout"], { ref: "main" });
1104
+ ```
1105
+
1106
+ ## License
1107
+
1108
+ MIT
1109
+
1110
+ ## Related Packages
1111
+
1112
+ - [@procedures/client](https://github.com/mark1russell7/client) - Core client library
1113
+ - [@procedures/lib](https://github.com/mark1russell7/client-lib) - Library management (uses git procedures)
1114
+ - [@procedures/pnpm](https://github.com/mark1russell7/client-pnpm) - PNPM wrapper
1115
+ - [@procedures/bundle-dev](https://github.com/mark1russell7/bundle-dev) - Development bundle