@almadar/integrations 2.8.0 → 2.10.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.
@@ -51,6 +51,80 @@ interface GitHubPushParams extends IntegrationParams {
51
51
  /** Working directory (defaults to workspace root) */
52
52
  workDir?: string;
53
53
  }
54
+ /**
55
+ * GitHub `git init` parameters. Used to initialize an empty workspace
56
+ * directory as a git repo before the first commit/push to a freshly
57
+ * created remote.
58
+ */
59
+ interface GitHubInitParams extends IntegrationParams {
60
+ /** Working directory to initialize. */
61
+ workDir: string;
62
+ /** Initial branch name (default: 'main'). */
63
+ initialBranch?: string;
64
+ }
65
+ /**
66
+ * GitHub `git pull` parameters. Used for cross-device workspace freshness
67
+ * — when a workspace is found locally but the linked repo may have moved
68
+ * ahead via commits from another device.
69
+ */
70
+ interface GitHubPullParams extends IntegrationParams {
71
+ /** Branch name to pull. */
72
+ branchName: string;
73
+ /** Working directory (defaults to workspace root). */
74
+ workDir?: string;
75
+ /** Fast-forward only (default: true). When false, allows merge. */
76
+ ffOnly?: boolean;
77
+ }
78
+ /**
79
+ * GitHub `git remote add` parameters.
80
+ */
81
+ interface GitHubAddRemoteParams extends IntegrationParams {
82
+ /** Remote name — typically 'origin'. */
83
+ name: string;
84
+ /** Remote URL (e.g., https://github.com/owner/repo). */
85
+ url: string;
86
+ /** Working directory (defaults to workspace root). */
87
+ workDir?: string;
88
+ }
89
+ /**
90
+ * GitHub `git config user.{name,email}` parameters. Set per-repo so
91
+ * individual commits don't need env-var juggling.
92
+ */
93
+ interface GitHubSetUserConfigParams extends IntegrationParams {
94
+ /** `user.name` value. */
95
+ userName: string;
96
+ /** `user.email` value. */
97
+ userEmail: string;
98
+ /** Scope: 'local' (per-repo, default) or 'global'. */
99
+ scope?: 'local' | 'global';
100
+ /** Working directory (defaults to workspace root). Required for scope='local'. */
101
+ workDir?: string;
102
+ }
103
+ /**
104
+ * GitHub `git branch -M` parameters. Renames the current branch (or
105
+ * forces it to the new name when one already exists with that name).
106
+ * Used to align a workspace's local branch with the linked repo's
107
+ * default branch — `git init` defaults to `master` on older git
108
+ * versions, while every new GitHub repo defaults to `main`, so the
109
+ * first push needs the names to match.
110
+ */
111
+ interface GitHubRenameBranchParams extends IntegrationParams {
112
+ /** New branch name (the current branch is renamed to this). */
113
+ newName: string;
114
+ /** Working directory (defaults to workspace root). */
115
+ workDir: string;
116
+ }
117
+ /**
118
+ * Pull outcome. Discriminates "the pull happened and updated state"
119
+ * from "no-op due to <reason>" so callers can route on it without
120
+ * stringy stderr parsing.
121
+ */
122
+ interface GitHubPullResult {
123
+ /** True when the working tree advanced (one or more commits pulled). */
124
+ updated: boolean;
125
+ /** Reason a non-update pull returned. */
126
+ reason?: 'no-upstream' | 'conflict' | 'network' | 'up-to-date';
127
+ }
54
128
  /**
55
129
  * GAP-104/105: GitHub create repository parameters.
56
130
  * Provider-agnostic: the same interface shape works for GitLab, Bitbucket, etc.
@@ -192,35 +266,6 @@ interface GitHubRateLimit {
192
266
  reset: number;
193
267
  }
194
268
 
195
- /**
196
- * Git CLI operations for GitHub integration
197
- */
198
-
199
- /**
200
- * Clone a repository
201
- */
202
- declare function cloneRepo(params: GitHubCloneParams, token: string): Promise<void>;
203
- /**
204
- * Create a new branch
205
- */
206
- declare function createBranch(params: GitHubCreateBranchParams, workDir: string): Promise<void>;
207
- /**
208
- * Commit changes
209
- */
210
- declare function commit(params: GitHubCommitParams, workDir: string): Promise<void>;
211
- /**
212
- * Push branch to remote
213
- */
214
- declare function push(params: GitHubPushParams, workDir: string, token: string): Promise<void>;
215
- /**
216
- * Get current branch name
217
- */
218
- declare function getCurrentBranch(workDir: string): Promise<string>;
219
- /**
220
- * Check if working directory has uncommitted changes
221
- */
222
- declare function hasUncommittedChanges(workDir: string): Promise<boolean>;
223
-
224
269
  /**
225
270
  * GitHub REST API client
226
271
  */
@@ -316,6 +361,84 @@ declare function parseRepoUrl(repoUrl: string): {
316
361
  repo: string;
317
362
  };
318
363
 
364
+ /**
365
+ * Git CLI operations for GitHub integration
366
+ */
367
+
368
+ /**
369
+ * Clone a repository
370
+ */
371
+ declare function cloneRepo(params: GitHubCloneParams, token: string): Promise<void>;
372
+ /**
373
+ * Create a new branch
374
+ */
375
+ declare function createBranch(params: GitHubCreateBranchParams, workDir: string): Promise<void>;
376
+ /**
377
+ * Commit changes
378
+ */
379
+ declare function commit(params: GitHubCommitParams, workDir: string): Promise<void>;
380
+ /**
381
+ * Push branch to remote
382
+ */
383
+ declare function push(params: GitHubPushParams, workDir: string, token: string): Promise<void>;
384
+ /**
385
+ * Get current branch name
386
+ */
387
+ declare function getCurrentBranch(workDir: string): Promise<string>;
388
+ /**
389
+ * Check if working directory has uncommitted changes
390
+ */
391
+ declare function hasUncommittedChanges(workDir: string): Promise<boolean>;
392
+ /**
393
+ * Initialize a git repository in `workDir`. No-op when `.git/` already
394
+ * exists. The default initial branch is `main` to match what GitHub
395
+ * creates for new repos.
396
+ */
397
+ declare function init(params: GitHubInitParams): Promise<void>;
398
+ /**
399
+ * Add a remote to the repository. Used after `init` to attach the
400
+ * freshly-created GitHub remote as `origin`.
401
+ */
402
+ declare function addRemote(params: GitHubAddRemoteParams): Promise<void>;
403
+ /**
404
+ * Set `user.name` and `user.email` for git commits. Scope defaults to
405
+ * per-repo (`--local`) so each workspace can carry the GitHub identity
406
+ * of the user who owns it — global config would clobber the developer's
407
+ * own machine settings.
408
+ */
409
+ declare function setUserConfig(params: GitHubSetUserConfigParams): Promise<void>;
410
+ /**
411
+ * Pull from the remote. Returns a structured outcome so callers can
412
+ * tell "the workspace advanced" from "nothing changed because <X>"
413
+ * without parsing git's stderr. Best-effort posture: network / conflict
414
+ * / no-upstream all resolve cleanly rather than throwing — the caller
415
+ * decides whether to surface the no-op as user-facing.
416
+ */
417
+ declare function pull(params: GitHubPullParams, token: string): Promise<GitHubPullResult>;
418
+ /**
419
+ * Return the current HEAD commit sha (full 40-char). Throws when the
420
+ * workspace has no commits yet (caller decides whether that's an error
421
+ * or an "init, then commit, then read" sequencing issue).
422
+ */
423
+ declare function getHeadSha(workDir: string): Promise<string>;
424
+ /**
425
+ * Return the current HEAD commit as a `GitHubCommit` shape. Reuses the
426
+ * canonical type from the REST module (no parallel `LocalCommit`
427
+ * interface). `stats` is absent — that's GitHub-API-only and would
428
+ * require a separate fetch to populate.
429
+ *
430
+ * The format string uses NUL (`%x00`) as a field separator so commit
431
+ * messages containing newlines / pipes can't break parsing.
432
+ */
433
+ declare function getHeadCommit(workDir: string): Promise<GitHubCommit>;
434
+ /**
435
+ * Rename the current branch with `git branch -M <newName>`. The `-M`
436
+ * (capital) flag forces the rename even if a branch with that name
437
+ * already exists, which is fine for our use case (aligning a fresh
438
+ * local workspace's branch with the linked repo's default branch).
439
+ */
440
+ declare function renameCurrentBranch(params: GitHubRenameBranchParams): Promise<void>;
441
+
319
442
  /**
320
443
  * GitHub Integration for Almadar
321
444
  * Provides git operations and GitHub API access for the agent
@@ -372,4 +495,4 @@ declare class GitHubIntegration extends BaseIntegration {
372
495
  private getAPIConfig;
373
496
  }
374
497
 
375
- export { type GitHubAPIConfig, type GitHubCloneParams, type GitHubComment, type GitHubCommit, type GitHubCommitParams, type GitHubCreateBranchParams, type GitHubCreatePRParams, type GitHubCreateRepoParams, type GitHubGetIssueParams, type GitHubGetPRCommentsParams, GitHubIntegration, type GitHubIssue, type GitHubListIssuesParams, type GitHubPullRequest, type GitHubPushParams, type GitHubRateLimit, type GitHubRepoCreated, cloneRepo, commit, createBranch, createPR, createRepo, getCommitDiff, getCurrentBranch, getFileAtCommit, getIssue, getPRComments, getRateLimit, hasUncommittedChanges, listCommits, listIssues, listRepoTree, parseRepoUrl, push, readRepoFile };
498
+ export { type GitHubAPIConfig, type GitHubAddRemoteParams, type GitHubCloneParams, type GitHubComment, type GitHubCommit, type GitHubCommitParams, type GitHubCreateBranchParams, type GitHubCreatePRParams, type GitHubCreateRepoParams, type GitHubGetIssueParams, type GitHubGetPRCommentsParams, type GitHubInitParams, GitHubIntegration, type GitHubIssue, type GitHubListIssuesParams, type GitHubPullParams, type GitHubPullRequest, type GitHubPullResult, type GitHubPushParams, type GitHubRateLimit, type GitHubRenameBranchParams, type GitHubRepoCreated, type GitHubSetUserConfigParams, addRemote, cloneRepo, commit, createBranch, createPR, createRepo, getCommitDiff, getCurrentBranch, getFileAtCommit, getHeadCommit, getHeadSha, getIssue, getPRComments, getRateLimit, hasUncommittedChanges, init, listCommits, listIssues, listRepoTree, parseRepoUrl, pull, push, readRepoFile, renameCurrentBranch, setUserConfig };
@@ -374,6 +374,145 @@ async function hasUncommittedChanges(workDir) {
374
374
  );
375
375
  }
376
376
  }
377
+ async function init(params) {
378
+ const { workDir, initialBranch = "main" } = params;
379
+ try {
380
+ await execGit(["init", "-b", initialBranch], workDir);
381
+ } catch (error) {
382
+ throw new IntegrationError(
383
+ `Failed to git init: ${error instanceof Error ? error.message : String(error)}`,
384
+ "SERVICE_ERROR",
385
+ { workDir, error }
386
+ );
387
+ }
388
+ }
389
+ async function addRemote(params) {
390
+ const { name, url, workDir } = params;
391
+ const cwd = workDir || params.workDir;
392
+ if (!cwd) {
393
+ throw new IntegrationError(
394
+ `addRemote requires workDir`,
395
+ "VALIDATION_ERROR",
396
+ { params }
397
+ );
398
+ }
399
+ try {
400
+ await execGit(["remote", "add", name, url], cwd);
401
+ } catch (error) {
402
+ throw new IntegrationError(
403
+ `Failed to add remote ${name}: ${error instanceof Error ? error.message : String(error)}`,
404
+ "SERVICE_ERROR",
405
+ { name, error }
406
+ );
407
+ }
408
+ }
409
+ async function setUserConfig(params) {
410
+ const { userName, userEmail, scope = "local", workDir } = params;
411
+ const scopeFlag = scope === "global" ? "--global" : "--local";
412
+ if (scope === "local" && !workDir) {
413
+ throw new IntegrationError(
414
+ `setUserConfig with scope='local' requires workDir`,
415
+ "VALIDATION_ERROR",
416
+ { params }
417
+ );
418
+ }
419
+ const cwd = workDir ?? process.cwd();
420
+ try {
421
+ await execGit(["config", scopeFlag, "user.name", userName], cwd);
422
+ await execGit(["config", scopeFlag, "user.email", userEmail], cwd);
423
+ } catch (error) {
424
+ throw new IntegrationError(
425
+ `Failed to set git user config: ${error instanceof Error ? error.message : String(error)}`,
426
+ "SERVICE_ERROR",
427
+ { error }
428
+ );
429
+ }
430
+ }
431
+ async function pull(params, token) {
432
+ const { branchName, ffOnly = true, workDir } = params;
433
+ const cwd = workDir || params.workDir;
434
+ if (!cwd) {
435
+ return { updated: false, reason: "no-upstream" };
436
+ }
437
+ let credHelper = null;
438
+ try {
439
+ credHelper = await createTempCredentialHelper(token);
440
+ const args = ["pull"];
441
+ if (ffOnly) args.push("--ff-only");
442
+ args.push("origin", branchName);
443
+ const { stdout } = await execGit(args, cwd, {
444
+ GIT_ASKPASS: credHelper,
445
+ GIT_TERMINAL_PROMPT: "0"
446
+ });
447
+ const updated = !/^Already up to date\.?\s*$/m.test(stdout.trim());
448
+ return updated ? { updated: true } : { updated: false, reason: "up-to-date" };
449
+ } catch (error) {
450
+ const message = error instanceof Error ? error.message : String(error);
451
+ if (/no upstream|no such ref|couldn't find remote ref/i.test(message)) {
452
+ return { updated: false, reason: "no-upstream" };
453
+ }
454
+ if (/conflict|not possible to fast-forward|divergent/i.test(message)) {
455
+ return { updated: false, reason: "conflict" };
456
+ }
457
+ return { updated: false, reason: "network" };
458
+ } finally {
459
+ if (credHelper) {
460
+ try {
461
+ await promises.unlink(credHelper);
462
+ } catch {
463
+ }
464
+ }
465
+ }
466
+ }
467
+ async function getHeadSha(workDir) {
468
+ try {
469
+ const { stdout } = await execGit(["rev-parse", "HEAD"], workDir);
470
+ return stdout.trim();
471
+ } catch (error) {
472
+ throw new IntegrationError(
473
+ `Failed to get HEAD sha: ${error instanceof Error ? error.message : String(error)}`,
474
+ "SERVICE_ERROR",
475
+ { error }
476
+ );
477
+ }
478
+ }
479
+ async function getHeadCommit(workDir) {
480
+ try {
481
+ const FORMAT = "%H%x00%an%x00%ae%x00%aI%x00%B";
482
+ const { stdout } = await execGit(
483
+ ["show", "--no-patch", `--format=${FORMAT}`, "HEAD"],
484
+ workDir
485
+ );
486
+ const [sha, name, email, date, ...messageParts] = stdout.split("\0");
487
+ return {
488
+ sha: sha.trim(),
489
+ message: messageParts.join("\0").trimEnd(),
490
+ author: {
491
+ name: name?.trim() ?? "",
492
+ email: email?.trim() ?? "",
493
+ date: date?.trim() ?? ""
494
+ }
495
+ };
496
+ } catch (error) {
497
+ throw new IntegrationError(
498
+ `Failed to read HEAD commit: ${error instanceof Error ? error.message : String(error)}`,
499
+ "SERVICE_ERROR",
500
+ { error }
501
+ );
502
+ }
503
+ }
504
+ async function renameCurrentBranch(params) {
505
+ const { newName, workDir } = params;
506
+ try {
507
+ await execGit(["branch", "-M", newName], workDir);
508
+ } catch (error) {
509
+ throw new IntegrationError(
510
+ `Failed to rename branch to ${newName}: ${error instanceof Error ? error.message : String(error)}`,
511
+ "SERVICE_ERROR",
512
+ { newName, error }
513
+ );
514
+ }
515
+ }
377
516
 
378
517
  // src/integrations/github/github-api.ts
379
518
  async function githubFetch(endpoint, config, options = {}) {
@@ -806,6 +945,6 @@ var GitHubIntegration = class extends BaseIntegration {
806
945
  }
807
946
  };
808
947
 
809
- export { GitHubIntegration, cloneRepo, commit, createBranch, createPR, createRepo, getCommitDiff, getCurrentBranch, getFileAtCommit, getIssue, getPRComments, getRateLimit, hasUncommittedChanges, listCommits, listIssues, listRepoTree, parseRepoUrl, push, readRepoFile };
948
+ export { GitHubIntegration, addRemote, cloneRepo, commit, createBranch, createPR, createRepo, getCommitDiff, getCurrentBranch, getFileAtCommit, getHeadCommit, getHeadSha, getIssue, getPRComments, getRateLimit, hasUncommittedChanges, init, listCommits, listIssues, listRepoTree, parseRepoUrl, pull, push, readRepoFile, renameCurrentBranch, setUserConfig };
810
949
  //# sourceMappingURL=index.js.map
811
950
  //# sourceMappingURL=index.js.map