@adhdev/daemon-core 0.9.61 → 0.9.63

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/session-host-core",
3
- "version": "0.9.61",
3
+ "version": "0.9.63",
4
4
  "description": "ADHDev local session host core \u2014 session registry, protocol, buffers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.61",
3
+ "version": "0.9.63",
4
4
  "description": "ADHDev daemon core \u2014 CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -53,6 +53,14 @@ export interface GitStashPushResult extends GitRepoIdentity {
53
53
  lastCheckedAt: number;
54
54
  }
55
55
 
56
+ export interface GitPushResult extends GitRepoIdentity {
57
+ remote: string;
58
+ branch: string;
59
+ output: string;
60
+ newBranch: boolean;
61
+ lastCheckedAt: number;
62
+ }
63
+
56
64
  export interface GitCommandServices {
57
65
  getStatus?: (params: { workspace: string }) => Promise<GitRepoStatus> | GitRepoStatus;
58
66
  getDiffSummary?: (params: { workspace: string; staged?: boolean }) => Promise<GitDiffSummary> | GitDiffSummary;
@@ -88,6 +96,7 @@ export interface GitCommandServices {
88
96
  stashPop?: (params: { workspace: string; stashRef?: string }) => Promise<void>;
89
97
  checkoutFiles?: (params: { workspace: string; paths: string[] }) => Promise<{ checkedOut: string[] }>;
90
98
  getRemoteUrl?: (params: { workspace: string; remote?: string }) => Promise<{ remoteUrl: string; remote: string }>;
99
+ push?: (params: { workspace: string; remote?: string; branch?: string; setUpstream?: boolean }) => Promise<GitPushResult>;
91
100
  }
92
101
 
93
102
  type GitCommandFailure = {
@@ -107,7 +116,8 @@ type GitCommandSuccess =
107
116
  | { success: true; stash: GitStashPushResult }
108
117
  | { success: true; stashPopped: true }
109
118
  | { success: true; checkedOut: string[] }
110
- | { success: true; remoteUrl: string; remote: string };
119
+ | { success: true; remoteUrl: string; remote: string }
120
+ | { success: true; push: GitPushResult };
111
121
 
112
122
  export type GitCommandResult = GitCommandSuccess | GitCommandFailure;
113
123
 
@@ -123,6 +133,7 @@ const GIT_COMMAND_NAMES = new Set<GitCommandName>([
123
133
  'git_stash_pop',
124
134
  'git_checkout_files',
125
135
  'git_remote_url',
136
+ 'git_push',
126
137
  ]);
127
138
 
128
139
  const SNAPSHOT_REASONS = new Set<GitSnapshotReason>([
@@ -175,6 +186,8 @@ export function createDefaultGitCommandServices(): GitCommandServices {
175
186
  stashPop: async ({ workspace, stashRef }) => gitStashPop(workspace, stashRef),
176
187
  checkoutFiles: async ({ workspace, paths }) => gitCheckoutFiles(workspace, paths),
177
188
  getRemoteUrl: async ({ workspace, remote = 'origin' }) => gitGetRemoteUrl(workspace, remote),
189
+ push: async ({ workspace, remote = 'origin', branch, setUpstream = false }) =>
190
+ gitPush(workspace, remote, branch, setUpstream),
178
191
  };
179
192
  }
180
193
 
@@ -385,6 +398,21 @@ export async function handleGitCommand(
385
398
  return { success: true, remoteUrl: remoteResult.remoteUrl, remote: remoteResult.remote };
386
399
  }
387
400
 
401
+ case 'git_push': {
402
+ if (!services.push) return serviceNotImplemented(command);
403
+ const remote = typeof args?.remote === 'string' && args.remote.trim() ? args.remote.trim() : 'origin';
404
+ const branch = typeof args?.branch === 'string' && args.branch.trim() ? args.branch.trim() : undefined;
405
+ const setUpstream = Boolean(args?.setUpstream);
406
+ if (!/^[a-zA-Z0-9_.\-]+$/.test(remote)) {
407
+ return failure('invalid_args', 'remote must contain only alphanumeric characters, dots, hyphens, and underscores');
408
+ }
409
+ if (branch !== undefined && !/^[a-zA-Z0-9/_.\-]+$/.test(branch)) {
410
+ return failure('invalid_args', 'branch must contain only alphanumeric characters, slashes, dots, hyphens, and underscores');
411
+ }
412
+ const pushResult = await runService(() => services.push!({ workspace, remote, branch, setUpstream }));
413
+ return 'success' in pushResult ? pushResult : { success: true, push: pushResult };
414
+ }
415
+
388
416
  default:
389
417
  return failure('invalid_args', `Unknown Git command: ${command}`);
390
418
  }
@@ -512,6 +540,61 @@ async function gitGetRemoteUrl(workspace: string, remote: string): Promise<{ rem
512
540
  return { remoteUrl, remote };
513
541
  }
514
542
 
543
+ async function gitPush(
544
+ workspace: string,
545
+ remote: string,
546
+ branch: string | undefined,
547
+ setUpstream: boolean,
548
+ ): Promise<GitPushResult> {
549
+ const lastCheckedAt = Date.now();
550
+ const repo = await resolveGitRepository(workspace);
551
+ const repoRoot = repo.repoRoot!;
552
+
553
+ // Resolve branch name if not provided
554
+ let resolvedBranch = branch;
555
+ if (!resolvedBranch) {
556
+ const branchResult = await runGit(repo, ['symbolic-ref', '--short', 'HEAD'], { cwd: repoRoot });
557
+ resolvedBranch = branchResult.stdout.trim();
558
+ if (!resolvedBranch) {
559
+ throw new GitCommandError('git_command_failed', 'Cannot push: not on a branch (detached HEAD)');
560
+ }
561
+ }
562
+
563
+ const pushArgs = ['push'];
564
+ if (setUpstream) pushArgs.push('--set-upstream');
565
+ pushArgs.push(remote, resolvedBranch);
566
+
567
+ let output = '';
568
+ let newBranch = false;
569
+ try {
570
+ const result = await runGit(repo, pushArgs, { cwd: repoRoot });
571
+ output = (result.stdout + result.stderr).trim();
572
+ newBranch = /\[new branch\]/i.test(output);
573
+ } catch (err: any) {
574
+ const errOutput = (err?.stdout ?? '') + (err?.stderr ?? '');
575
+ // --set-upstream hint: retry with --set-upstream automatically
576
+ if (!setUpstream && /no upstream branch|set-upstream/i.test(errOutput)) {
577
+ const retryArgs = ['push', '--set-upstream', remote, resolvedBranch];
578
+ const retryResult = await runGit(repo, retryArgs, { cwd: repoRoot });
579
+ output = (retryResult.stdout + retryResult.stderr).trim();
580
+ newBranch = /\[new branch\]/i.test(output);
581
+ } else {
582
+ throw new GitCommandError('git_command_failed', errOutput || err?.message || 'git push failed');
583
+ }
584
+ }
585
+
586
+ return {
587
+ workspace: repo.workspace,
588
+ repoRoot,
589
+ isGitRepo: true,
590
+ remote,
591
+ branch: resolvedBranch,
592
+ output,
593
+ newBranch,
594
+ lastCheckedAt,
595
+ };
596
+ }
597
+
515
598
  function formatOptionalGitLogRangeArg(flag: '--since' | '--until', value: string | undefined): string[] {
516
599
  return value ? [`${flag}=${value}`] : [];
517
600
  }
@@ -151,4 +151,5 @@ export type GitCommandName =
151
151
  | 'git_stash_push'
152
152
  | 'git_stash_pop'
153
153
  | 'git_checkout_files'
154
- | 'git_remote_url';
154
+ | 'git_remote_url'
155
+ | 'git_push';