@procedures/git 0.0.0-stage → 0.2.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 +35 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1130 -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 +22 -0
  10. package/dist/module.d.ts.map +1 -0
  11. package/dist/module.js +514 -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 +13 -0
  70. package/dist/procedures/git/remote.d.ts.map +1 -0
  71. package/dist/procedures/git/remote.js +30 -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 +7 -0
  90. package/dist/register.d.ts.map +1 -0
  91. package/dist/register.js +9 -0
  92. package/dist/register.js.map +1 -0
  93. package/dist/types.d.ts +350 -0
  94. package/dist/types.d.ts.map +1 -0
  95. package/dist/types.js +397 -0
  96. package/dist/types.js.map +1 -0
  97. package/package.json +77 -3
  98. package/src/index.ts +20 -0
  99. package/src/module.ts +656 -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 +31 -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 +11 -0
  120. package/src/types.ts +815 -0
package/src/module.ts ADDED
@@ -0,0 +1,656 @@
1
+ /**
2
+ * Module (no side effects): the procedures of this package as data, for `createRegistry()`.
3
+ *
4
+ * Procedure Registration for git operations
5
+ *
6
+ * Registers git.* procedures with the client system.
7
+ * This file is referenced by package.json's client.procedures field.
8
+ */
9
+
10
+ import { createProcedure, zodAdapter, defineModule, type Effects, type ProcedureModule, PROCEDURE_REGISTRY, registerModules, type ProcedureRegistry } from "@procedures/client";
11
+ import { gitStatus } from "./procedures/git/status.js";
12
+ import { gitAdd } from "./procedures/git/add.js";
13
+ import { gitCommit } from "./procedures/git/commit.js";
14
+ import { gitPush } from "./procedures/git/push.js";
15
+ import { gitPull } from "./procedures/git/pull.js";
16
+ import { gitClone } from "./procedures/git/clone.js";
17
+ import { gitCheckout } from "./procedures/git/checkout.js";
18
+ import { gitBranch } from "./procedures/git/branch.js";
19
+ import { gitLog } from "./procedures/git/log.js";
20
+ import { gitDiff } from "./procedures/git/diff.js";
21
+ import { gitInit } from "./procedures/git/init.js";
22
+ import { gitRemote, gitRemoteSet } from "./procedures/git/remote.js";
23
+ import { gitFetch } from "./procedures/git/fetch.js";
24
+ import {
25
+ gitHasChanges,
26
+ gitHasStagedChanges,
27
+ gitHasUnstagedChanges,
28
+ gitHasUntrackedFiles,
29
+ gitHasLocalCommits,
30
+ gitIsClean,
31
+ } from "./procedures/git/predicates.js";
32
+ import {
33
+ gitStashList,
34
+ gitStashPush,
35
+ gitStashPop,
36
+ gitStashApply,
37
+ gitStashDrop,
38
+ gitStashExport,
39
+ gitStashImport,
40
+ } from "./procedures/git/stash.js";
41
+ import {
42
+ GitStatusInputSchema,
43
+ GitAddInputSchema,
44
+ GitCommitInputSchema,
45
+ GitPushInputSchema,
46
+ GitPullInputSchema,
47
+ GitCloneInputSchema,
48
+ GitCheckoutInputSchema,
49
+ GitBranchInputSchema,
50
+ GitLogInputSchema,
51
+ GitDiffInputSchema,
52
+ type GitStatusInput,
53
+ type GitStatusOutput,
54
+ type GitAddInput,
55
+ type GitAddOutput,
56
+ type GitCommitInput,
57
+ type GitCommitOutput,
58
+ type GitPushInput,
59
+ type GitPushOutput,
60
+ type GitPullInput,
61
+ type GitPullOutput,
62
+ type GitCloneInput,
63
+ type GitCloneOutput,
64
+ type GitCheckoutInput,
65
+ type GitCheckoutOutput,
66
+ type GitBranchInput,
67
+ type GitBranchOutput,
68
+ type GitLogInput,
69
+ type GitLogOutput,
70
+ type GitDiffInput,
71
+ type GitDiffOutput,
72
+ GitInitInputSchema,
73
+ type GitInitInput,
74
+ type GitInitOutput,
75
+ GitRemoteInputSchema,
76
+ type GitRemoteInput,
77
+ GitRemoteSetInputSchema,
78
+ type GitRemoteSetInput,
79
+ type GitRemoteOutput,
80
+ GitFetchInputSchema,
81
+ type GitFetchInput,
82
+ type GitFetchOutput,
83
+ GitPredicateInputSchema,
84
+ type GitPredicateInput,
85
+ type GitPredicateOutput,
86
+ GitStashListInputSchema,
87
+ GitStashPushInputSchema,
88
+ GitStashPopInputSchema,
89
+ GitStashApplyInputSchema,
90
+ GitStashDropInputSchema,
91
+ GitStashExportInputSchema,
92
+ GitStashImportInputSchema,
93
+ type GitStashListInput,
94
+ type GitStashListOutput,
95
+ type GitStashPushInput,
96
+ type GitStashPushOutput,
97
+ type GitStashPopInput,
98
+ type GitStashPopOutput,
99
+ type GitStashApplyInput,
100
+ type GitStashApplyOutput,
101
+ type GitStashDropInput,
102
+ type GitStashDropOutput,
103
+ type GitStashExportInput,
104
+ type GitStashExportOutput,
105
+ type GitStashImportInput,
106
+ type GitStashImportOutput,
107
+ GitStatusOutputSchema,
108
+ GitAddOutputSchema,
109
+ GitCommitOutputSchema,
110
+ GitPushOutputSchema,
111
+ GitPullOutputSchema,
112
+ GitCloneOutputSchema,
113
+ GitCheckoutOutputSchema,
114
+ GitBranchOutputSchema,
115
+ GitLogOutputSchema,
116
+ GitDiffOutputSchema,
117
+ GitInitOutputSchema,
118
+ GitRemoteOutputSchema,
119
+ GitFetchOutputSchema,
120
+ GitPredicateOutputSchema,
121
+ GitStashListOutputSchema,
122
+ GitStashPushOutputSchema,
123
+ GitStashPopOutputSchema,
124
+ GitStashApplyOutputSchema,
125
+ GitStashDropOutputSchema,
126
+ GitStashExportOutputSchema,
127
+ GitStashImportOutputSchema,
128
+ } from "./types.js";
129
+
130
+ // =============================================================================
131
+ // Effects
132
+ // =============================================================================
133
+
134
+ // Git is a fixed program, so the declarations name what git reads and changes, not "process".
135
+ // They do not include the hooks of a repository.
136
+
137
+ /** The effects of a git command that reads the repository and changes nothing. */
138
+ const READS_REPOSITORY: Effects = { reads: ["fs"], writes: [] };
139
+
140
+ /** The effects of a git command that can replace or remove the data of the repository. */
141
+ const CHANGES_REPOSITORY: Effects = { reads: ["fs"], writes: ["fs"] };
142
+
143
+ /**
144
+ * A change that starts the hooks of the repository (commit, push, pull, checkout). A hook is code
145
+ * of the project (husky, for example), so these write process (CAPABILITIES-2026-10). A hook
146
+ * that only the reference-transaction event starts (branch, fetch, stash) is not counted.
147
+ */
148
+ const RUNS_HOOKS: Effects = { reads: ["fs"], writes: ["fs", "process"] };
149
+
150
+ // =============================================================================
151
+ // Procedure Definitions
152
+ // =============================================================================
153
+
154
+ const gitStatusProcedure = createProcedure()
155
+ .path(["git", "status"])
156
+ .input(zodAdapter<GitStatusInput>(GitStatusInputSchema))
157
+ .output(zodAdapter<GitStatusOutput>(GitStatusOutputSchema))
158
+ .meta({
159
+ description: "Get git status",
160
+ args: [],
161
+ shorts: { cwd: "C", short: "s" },
162
+ output: "json",
163
+ effects: READS_REPOSITORY,
164
+ })
165
+ .handler(async (input: GitStatusInput, ctx): Promise<GitStatusOutput> => {
166
+ return gitStatus(input, ctx);
167
+ })
168
+ .build();
169
+
170
+ const gitAddProcedure = createProcedure()
171
+ .path(["git", "add"])
172
+ .input(zodAdapter<GitAddInput>(GitAddInputSchema))
173
+ .output(zodAdapter<GitAddOutput>(GitAddOutputSchema))
174
+ .meta({
175
+ description: "Stage files",
176
+ args: ["paths"],
177
+ shorts: { all: "A", cwd: "C" },
178
+ output: "json",
179
+ effects: { reads: ["fs"], writes: ["fs"], destructive: false, idempotent: true },
180
+ })
181
+ .handler(async (input: GitAddInput, ctx): Promise<GitAddOutput> => {
182
+ return gitAdd(input, ctx);
183
+ })
184
+ .build();
185
+
186
+ const gitCommitProcedure = createProcedure()
187
+ .path(["git", "commit"])
188
+ .input(zodAdapter<GitCommitInput>(GitCommitInputSchema))
189
+ .output(zodAdapter<GitCommitOutput>(GitCommitOutputSchema))
190
+ .meta({
191
+ description: "Create commit",
192
+ args: ["message"],
193
+ shorts: { message: "m", all: "a", amend: "A", cwd: "C" },
194
+ output: "json",
195
+ // With `amend`, it replaces the last commit, so it is destructive
196
+ effects: RUNS_HOOKS,
197
+ })
198
+ .handler(async (input: GitCommitInput, ctx): Promise<GitCommitOutput> => {
199
+ return gitCommit(input, ctx);
200
+ })
201
+ .build();
202
+
203
+ const gitPushProcedure = createProcedure()
204
+ .path(["git", "push"])
205
+ .input(zodAdapter<GitPushInput>(GitPushInputSchema))
206
+ .output(zodAdapter<GitPushOutput>(GitPushOutputSchema))
207
+ .meta({
208
+ description: "Push to remote",
209
+ args: [],
210
+ shorts: { remote: "r", branch: "b", force: "f", setUpstream: "u", cwd: "C" },
211
+ output: "json",
212
+ // With `force`, it replaces the commits of the remote. It also writes the remote-tracking ref.
213
+ effects: { reads: ["fs", "network"], writes: ["fs", "network", "process"], idempotent: true },
214
+ })
215
+ .handler(async (input: GitPushInput, ctx): Promise<GitPushOutput> => {
216
+ return gitPush(input, ctx);
217
+ })
218
+ .build();
219
+
220
+ const gitPullProcedure = createProcedure()
221
+ .path(["git", "pull"])
222
+ .input(zodAdapter<GitPullInput>(GitPullInputSchema))
223
+ .output(zodAdapter<GitPullOutput>(GitPullOutputSchema))
224
+ .meta({
225
+ description: "Pull from remote",
226
+ args: [],
227
+ shorts: { remote: "r", branch: "b", rebase: "R", cwd: "C" },
228
+ output: "json",
229
+ // It merges or rebases into the working tree
230
+ effects: { reads: ["fs", "network"], writes: ["fs", "process"], idempotent: true },
231
+ })
232
+ .handler(async (input: GitPullInput, ctx): Promise<GitPullOutput> => {
233
+ return gitPull(input, ctx);
234
+ })
235
+ .build();
236
+
237
+ const gitCloneProcedure = createProcedure()
238
+ .path(["git", "clone"])
239
+ .input(zodAdapter<GitCloneInput>(GitCloneInputSchema))
240
+ .output(zodAdapter<GitCloneOutput>(GitCloneOutputSchema))
241
+ .meta({
242
+ description: "Clone repository",
243
+ args: ["url"],
244
+ shorts: { dest: "d", branch: "b", depth: "D", cwd: "C" },
245
+ output: "json",
246
+ // The source can be a local path. Git clones only into a new or empty folder.
247
+ effects: { reads: ["fs", "network"], writes: ["fs"], destructive: false, idempotent: true },
248
+ })
249
+ .handler(async (input: GitCloneInput, ctx): Promise<GitCloneOutput> => {
250
+ return gitClone(input, ctx);
251
+ })
252
+ .build();
253
+
254
+ const gitCheckoutProcedure = createProcedure()
255
+ .path(["git", "checkout"])
256
+ .input(zodAdapter<GitCheckoutInput>(GitCheckoutInputSchema))
257
+ .output(zodAdapter<GitCheckoutOutput>(GitCheckoutOutputSchema))
258
+ .meta({
259
+ description: "Checkout branch or files",
260
+ args: ["ref"],
261
+ shorts: { create: "b", cwd: "C" },
262
+ output: "json",
263
+ // It replaces files of the working tree
264
+ effects: { reads: ["fs"], writes: ["fs", "process"], idempotent: true },
265
+ })
266
+ .handler(async (input: GitCheckoutInput, ctx): Promise<GitCheckoutOutput> => {
267
+ return gitCheckout(input, ctx);
268
+ })
269
+ .build();
270
+
271
+ const gitBranchProcedure = createProcedure()
272
+ .path(["git", "branch"])
273
+ .input(zodAdapter<GitBranchInput>(GitBranchInputSchema))
274
+ .output(zodAdapter<GitBranchOutput>(GitBranchOutputSchema))
275
+ .meta({
276
+ description: "Branch operations",
277
+ args: ["name"],
278
+ shorts: { delete: "d", list: "l", remote: "r", cwd: "C" },
279
+ output: "json",
280
+ // It lists, creates or deletes a branch
281
+ effects: { reads: ["fs"], writes: ["fs"], idempotent: true },
282
+ })
283
+ .handler(async (input: GitBranchInput, ctx): Promise<GitBranchOutput> => {
284
+ return gitBranch(input, ctx);
285
+ })
286
+ .build();
287
+
288
+ const gitLogProcedure = createProcedure()
289
+ .path(["git", "log"])
290
+ .input(zodAdapter<GitLogInput>(GitLogInputSchema))
291
+ .output(zodAdapter<GitLogOutput>(GitLogOutputSchema))
292
+ .meta({
293
+ description: "Show commit log",
294
+ args: [],
295
+ shorts: { count: "n", oneline: "o", ref: "r", cwd: "C" },
296
+ output: "json",
297
+ effects: READS_REPOSITORY,
298
+ })
299
+ .handler(async (input: GitLogInput, ctx): Promise<GitLogOutput> => {
300
+ return gitLog(input, ctx);
301
+ })
302
+ .build();
303
+
304
+ const gitDiffProcedure = createProcedure()
305
+ .path(["git", "diff"])
306
+ .input(zodAdapter<GitDiffInput>(GitDiffInputSchema))
307
+ .output(zodAdapter<GitDiffOutput>(GitDiffOutputSchema))
308
+ .meta({
309
+ description: "Show changes",
310
+ args: [],
311
+ shorts: { staged: "s", ref: "r", stat: "S", cwd: "C" },
312
+ output: "json",
313
+ effects: READS_REPOSITORY,
314
+ })
315
+ .handler(async (input: GitDiffInput, ctx): Promise<GitDiffOutput> => {
316
+ return gitDiff(input, ctx);
317
+ })
318
+ .build();
319
+
320
+
321
+ const gitInitProcedure = createProcedure()
322
+ .path(["git", "init"])
323
+ .input(zodAdapter<GitInitInput>(GitInitInputSchema))
324
+ .output(zodAdapter<GitInitOutput>(GitInitOutputSchema))
325
+ .meta({
326
+ description: "Initialize a git repository",
327
+ args: [],
328
+ shorts: { cwd: "C", bare: "b", initialBranch: "B" },
329
+ output: "json",
330
+ // It runs `git init` only when the folder has no .git (it calls fs.exists)
331
+ effects: { reads: ["fs"], writes: ["fs"], destructive: false, idempotent: true },
332
+ })
333
+ .handler(async (input: GitInitInput, ctx): Promise<GitInitOutput> => {
334
+ return gitInit(input, ctx);
335
+ })
336
+ .build();
337
+
338
+ const gitRemoteProcedure = createProcedure()
339
+ .path(["git", "remote"])
340
+ .input(zodAdapter<GitRemoteInput>(GitRemoteInputSchema))
341
+ .output(zodAdapter<GitRemoteOutput>(GitRemoteOutputSchema))
342
+ .meta({
343
+ description: "Get the URL of a remote",
344
+ args: [],
345
+ shorts: { name: "n", cwd: "C" },
346
+ output: "json",
347
+ effects: { reads: ["fs"], writes: [] },
348
+ })
349
+ .handler(async (input: GitRemoteInput, ctx): Promise<GitRemoteOutput> => {
350
+ return gitRemote(input, ctx);
351
+ })
352
+ .build();
353
+
354
+ const gitRemoteSetProcedure = createProcedure()
355
+ .path(["git", "remote", "set"])
356
+ .input(zodAdapter<GitRemoteSetInput>(GitRemoteSetInputSchema))
357
+ .output(zodAdapter<GitRemoteOutput>(GitRemoteOutputSchema))
358
+ .meta({
359
+ description: "Set the URL of a remote, or add the remote",
360
+ args: ["url"],
361
+ shorts: { name: "n", cwd: "C" },
362
+ output: "json",
363
+ // It replaces the URL of the remote in the git configuration
364
+ effects: { reads: ["fs"], writes: ["fs"], idempotent: true },
365
+ })
366
+ .handler(async (input: GitRemoteSetInput, ctx): Promise<GitRemoteOutput> => {
367
+ return gitRemoteSet(input, ctx);
368
+ })
369
+ .build();
370
+
371
+ const gitFetchProcedure = createProcedure()
372
+ .path(["git", "fetch"])
373
+ .input(zodAdapter<GitFetchInput>(GitFetchInputSchema))
374
+ .output(zodAdapter<GitFetchOutput>(GitFetchOutputSchema))
375
+ .meta({
376
+ description: "Fetch from remote",
377
+ args: [],
378
+ shorts: { remote: "r", branch: "b", all: "a", prune: "p", cwd: "C" },
379
+ output: "json",
380
+ // With `prune`, it deletes remote-tracking refs
381
+ effects: { reads: ["fs", "network"], writes: ["fs"], idempotent: true },
382
+ })
383
+ .handler(async (input: GitFetchInput, ctx): Promise<GitFetchOutput> => {
384
+ return gitFetch(input, ctx);
385
+ })
386
+ .build();
387
+
388
+ // =============================================================================
389
+ // Predicate Procedures (boolean checks for conditionals)
390
+ // =============================================================================
391
+
392
+ const gitHasChangesProcedure = createProcedure()
393
+ .path(["git", "hasChanges"])
394
+ .input(zodAdapter<GitPredicateInput>(GitPredicateInputSchema))
395
+ .output(zodAdapter<GitPredicateOutput>(GitPredicateOutputSchema))
396
+ .meta({
397
+ description: "Check if there are any changes (unstaged, staged, or untracked)",
398
+ args: [],
399
+ shorts: { cwd: "C" },
400
+ output: "json",
401
+ effects: READS_REPOSITORY,
402
+ })
403
+ .handler(async (input: GitPredicateInput, ctx): Promise<GitPredicateOutput> => {
404
+ return gitHasChanges(input, ctx);
405
+ })
406
+ .build();
407
+
408
+ const gitHasStagedChangesProcedure = createProcedure()
409
+ .path(["git", "hasStagedChanges"])
410
+ .input(zodAdapter<GitPredicateInput>(GitPredicateInputSchema))
411
+ .output(zodAdapter<GitPredicateOutput>(GitPredicateOutputSchema))
412
+ .meta({
413
+ description: "Check if there are any staged changes ready to commit",
414
+ args: [],
415
+ shorts: { cwd: "C" },
416
+ output: "json",
417
+ effects: READS_REPOSITORY,
418
+ })
419
+ .handler(async (input: GitPredicateInput, ctx): Promise<GitPredicateOutput> => {
420
+ return gitHasStagedChanges(input, ctx);
421
+ })
422
+ .build();
423
+
424
+ const gitHasUnstagedChangesProcedure = createProcedure()
425
+ .path(["git", "hasUnstagedChanges"])
426
+ .input(zodAdapter<GitPredicateInput>(GitPredicateInputSchema))
427
+ .output(zodAdapter<GitPredicateOutput>(GitPredicateOutputSchema))
428
+ .meta({
429
+ description: "Check if there are any unstaged changes",
430
+ args: [],
431
+ shorts: { cwd: "C" },
432
+ output: "json",
433
+ effects: READS_REPOSITORY,
434
+ })
435
+ .handler(async (input: GitPredicateInput, ctx): Promise<GitPredicateOutput> => {
436
+ return gitHasUnstagedChanges(input, ctx);
437
+ })
438
+ .build();
439
+
440
+ const gitHasUntrackedFilesProcedure = createProcedure()
441
+ .path(["git", "hasUntrackedFiles"])
442
+ .input(zodAdapter<GitPredicateInput>(GitPredicateInputSchema))
443
+ .output(zodAdapter<GitPredicateOutput>(GitPredicateOutputSchema))
444
+ .meta({
445
+ description: "Check if there are any untracked files",
446
+ args: [],
447
+ shorts: { cwd: "C" },
448
+ output: "json",
449
+ effects: READS_REPOSITORY,
450
+ })
451
+ .handler(async (input: GitPredicateInput, ctx): Promise<GitPredicateOutput> => {
452
+ return gitHasUntrackedFiles(input, ctx);
453
+ })
454
+ .build();
455
+
456
+ const gitHasLocalCommitsProcedure = createProcedure()
457
+ .path(["git", "hasLocalCommits"])
458
+ .input(zodAdapter<GitPredicateInput>(GitPredicateInputSchema))
459
+ .output(zodAdapter<GitPredicateOutput>(GitPredicateOutputSchema))
460
+ .meta({
461
+ description: "Check if there are local commits that haven't been pushed",
462
+ args: [],
463
+ shorts: { cwd: "C" },
464
+ output: "json",
465
+ // It compares with the local remote-tracking ref and does not fetch
466
+ effects: READS_REPOSITORY,
467
+ })
468
+ .handler(async (input: GitPredicateInput, ctx): Promise<GitPredicateOutput> => {
469
+ return gitHasLocalCommits(input, ctx);
470
+ })
471
+ .build();
472
+
473
+ const gitIsCleanProcedure = createProcedure()
474
+ .path(["git", "isClean"])
475
+ .input(zodAdapter<GitPredicateInput>(GitPredicateInputSchema))
476
+ .output(zodAdapter<GitPredicateOutput>(GitPredicateOutputSchema))
477
+ .meta({
478
+ description: "Check if the working directory is clean",
479
+ args: [],
480
+ shorts: { cwd: "C" },
481
+ output: "json",
482
+ effects: READS_REPOSITORY,
483
+ })
484
+ .handler(async (input: GitPredicateInput, ctx): Promise<GitPredicateOutput> => {
485
+ return gitIsClean(input, ctx);
486
+ })
487
+ .build();
488
+
489
+ // =============================================================================
490
+ // Stash Procedures
491
+ // =============================================================================
492
+
493
+ const gitStashListProcedure = createProcedure()
494
+ .path(["git", "stash", "list"])
495
+ .input(zodAdapter<GitStashListInput>(GitStashListInputSchema))
496
+ .output(zodAdapter<GitStashListOutput>(GitStashListOutputSchema))
497
+ .meta({
498
+ description: "List all stashes",
499
+ args: [],
500
+ shorts: { cwd: "C" },
501
+ output: "json",
502
+ effects: READS_REPOSITORY,
503
+ })
504
+ .handler(async (input: GitStashListInput, ctx): Promise<GitStashListOutput> => {
505
+ return gitStashList(input, ctx);
506
+ })
507
+ .build();
508
+
509
+ const gitStashPushProcedure = createProcedure()
510
+ .path(["git", "stash", "push"])
511
+ .input(zodAdapter<GitStashPushInput>(GitStashPushInputSchema))
512
+ .output(zodAdapter<GitStashPushOutput>(GitStashPushOutputSchema))
513
+ .meta({
514
+ description: "Push changes to stash",
515
+ args: ["message"],
516
+ shorts: { message: "m", includeUntracked: "u", keepIndex: "k", cwd: "C" },
517
+ output: "json",
518
+ // It removes the changes from the working tree. With `keepIndex`, a second call stashes again.
519
+ effects: CHANGES_REPOSITORY,
520
+ })
521
+ .handler(async (input: GitStashPushInput, ctx): Promise<GitStashPushOutput> => {
522
+ return gitStashPush(input, ctx);
523
+ })
524
+ .build();
525
+
526
+ const gitStashPopProcedure = createProcedure()
527
+ .path(["git", "stash", "pop"])
528
+ .input(zodAdapter<GitStashPopInput>(GitStashPopInputSchema))
529
+ .output(zodAdapter<GitStashPopOutput>(GitStashPopOutputSchema))
530
+ .meta({
531
+ description: "Pop stash (apply and remove)",
532
+ args: [],
533
+ shorts: { index: "n", cwd: "C" },
534
+ output: "json",
535
+ effects: CHANGES_REPOSITORY,
536
+ })
537
+ .handler(async (input: GitStashPopInput, ctx): Promise<GitStashPopOutput> => {
538
+ return gitStashPop(input, ctx);
539
+ })
540
+ .build();
541
+
542
+ const gitStashApplyProcedure = createProcedure()
543
+ .path(["git", "stash", "apply"])
544
+ .input(zodAdapter<GitStashApplyInput>(GitStashApplyInputSchema))
545
+ .output(zodAdapter<GitStashApplyOutput>(GitStashApplyOutputSchema))
546
+ .meta({
547
+ description: "Apply stash without removing",
548
+ args: [],
549
+ shorts: { index: "n", cwd: "C" },
550
+ output: "json",
551
+ effects: CHANGES_REPOSITORY,
552
+ })
553
+ .handler(async (input: GitStashApplyInput, ctx): Promise<GitStashApplyOutput> => {
554
+ return gitStashApply(input, ctx);
555
+ })
556
+ .build();
557
+
558
+ const gitStashDropProcedure = createProcedure()
559
+ .path(["git", "stash", "drop"])
560
+ .input(zodAdapter<GitStashDropInput>(GitStashDropInputSchema))
561
+ .output(zodAdapter<GitStashDropOutput>(GitStashDropOutputSchema))
562
+ .meta({
563
+ description: "Drop a stash",
564
+ args: [],
565
+ shorts: { index: "n", cwd: "C" },
566
+ output: "json",
567
+ effects: CHANGES_REPOSITORY,
568
+ })
569
+ .handler(async (input: GitStashDropInput, ctx): Promise<GitStashDropOutput> => {
570
+ return gitStashDrop(input, ctx);
571
+ })
572
+ .build();
573
+
574
+ const gitStashExportProcedure = createProcedure()
575
+ .path(["git", "stash", "export"])
576
+ .input(zodAdapter<GitStashExportInput>(GitStashExportInputSchema))
577
+ .output(zodAdapter<GitStashExportOutput>(GitStashExportOutputSchema))
578
+ .meta({
579
+ description: "Export stash as patch for snapshot storage",
580
+ args: [],
581
+ shorts: { index: "n", cwd: "C" },
582
+ output: "json",
583
+ effects: READS_REPOSITORY,
584
+ })
585
+ .handler(async (input: GitStashExportInput, ctx): Promise<GitStashExportOutput> => {
586
+ return gitStashExport(input, ctx);
587
+ })
588
+ .build();
589
+
590
+ const gitStashImportProcedure = createProcedure()
591
+ .path(["git", "stash", "import"])
592
+ .input(zodAdapter<GitStashImportInput>(GitStashImportInputSchema))
593
+ .output(zodAdapter<GitStashImportOutput>(GitStashImportOutputSchema))
594
+ .meta({
595
+ description: "Import stash from patch",
596
+ args: ["patch"],
597
+ shorts: { message: "m", includeUntracked: "u", cwd: "C" },
598
+ output: "json",
599
+ // It applies the patch to the working tree, then stashes all the changes of the working tree
600
+ effects: CHANGES_REPOSITORY,
601
+ })
602
+ .handler(async (input: GitStashImportInput, ctx): Promise<GitStashImportOutput> => {
603
+ return gitStashImport(input, ctx);
604
+ })
605
+ .build();
606
+
607
+ // =============================================================================
608
+ // Registration
609
+ // =============================================================================
610
+
611
+ /**
612
+ * The module of this package, for `createRegistry()`: its procedures as data. Importing this
613
+ * file registers nothing. (`register.ts` registers the same procedures in the global registry.)
614
+ */
615
+ export const gitModule: ProcedureModule = defineModule("@procedures/git", [
616
+ gitStatusProcedure,
617
+ gitAddProcedure,
618
+ gitCommitProcedure,
619
+ gitPushProcedure,
620
+ gitPullProcedure,
621
+ gitCloneProcedure,
622
+ gitCheckoutProcedure,
623
+ gitBranchProcedure,
624
+ gitLogProcedure,
625
+ gitDiffProcedure,
626
+ gitInitProcedure,
627
+ gitRemoteProcedure,
628
+ gitRemoteSetProcedure,
629
+ gitFetchProcedure,
630
+ // Predicate procedures
631
+ gitHasChangesProcedure,
632
+ gitHasStagedChangesProcedure,
633
+ gitHasUnstagedChangesProcedure,
634
+ gitHasUntrackedFilesProcedure,
635
+ gitHasLocalCommitsProcedure,
636
+ gitIsCleanProcedure,
637
+ // Stash procedures
638
+ gitStashListProcedure,
639
+ gitStashPushProcedure,
640
+ gitStashPopProcedure,
641
+ gitStashApplyProcedure,
642
+ gitStashDropProcedure,
643
+ gitStashExportProcedure,
644
+ gitStashImportProcedure,
645
+ ]);
646
+
647
+ export default gitModule;
648
+
649
+ /**
650
+ * This function registers the procedures of @procedures/git, and of the modules that it requires, in
651
+ * a registry. The default is the global registry. Importing this file registers nothing: the
652
+ * `./register` entry starts this function (ARCHITECTURE-PROPOSALS-2026-10, P9).
653
+ */
654
+ export function registerGitProcedures(registry: ProcedureRegistry = PROCEDURE_REGISTRY): void {
655
+ registerModules(registry, [gitModule]);
656
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * git.add procedure
3
+ *
4
+ * Stage files
5
+ */
6
+
7
+ import { git, type GitContext } from "./run.js";
8
+ import type { GitAddInput, GitAddOutput } from "../../types.js";
9
+
10
+ /**
11
+ * Stage files
12
+ */
13
+ export async function gitAdd(input: GitAddInput, ctx: GitContext = {}): Promise<GitAddOutput> {
14
+ const { paths, all, cwd } = input;
15
+ const run = { cwd, signal: ctx.signal };
16
+
17
+ if (all) {
18
+ await git(["add", "-A"], run);
19
+ } else if (paths.length > 0) {
20
+ await git(["add", "--", ...paths], run);
21
+ } else {
22
+ await git(["add", "."], run);
23
+ }
24
+
25
+ // Get list of staged files (with -z, a path with a space or a quote stays exact)
26
+ const staged = (await git(["diff", "--cached", "--name-only", "-z"], run)).split("\0").filter(Boolean);
27
+
28
+ return { staged };
29
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Guards for values that the git procedures pass to git as positional arguments.
3
+ *
4
+ * execFileSync passes arguments without a shell (BUGS-2026-07 C4), but git itself still reads
5
+ * a positional value that starts with "-" as an option. Some options run commands:
6
+ * `git clone/fetch/pull --upload-pack=<command>` and `git push --receive-pack=<command>`.
7
+ * `git diff --output=<file>` writes a file. Git does not allow a ref, branch or remote name
8
+ * to start with "-", so rejecting such values does not block a legitimate call.
9
+ */
10
+
11
+ export function gitArg(name: string, value: string): string {
12
+ if (value.startsWith("-")) {
13
+ throw new Error(`Invalid ${name} (must not start with "-"): ${value}`);
14
+ }
15
+ return value;
16
+ }
17
+
18
+ /**
19
+ * Diffs and stash patches can exceed Node's default maxBuffer of 1 MiB (BUGS-2026-07 L6)
20
+ */
21
+ export const GIT_MAX_BUFFER: number = 64 * 1024 * 1024;