adhdev 0.9.82-rc.8 → 0.9.82-rc.9

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adhdev",
3
- "version": "0.9.82-rc.8",
3
+ "version": "0.9.82-rc.9",
4
4
  "description": "ADHDev — Agent Dashboard Hub for Dev. Remote-control AI coding agents from anywhere.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -47,7 +47,7 @@
47
47
  "node": ">=18"
48
48
  },
49
49
  "dependencies": {
50
- "@adhdev/daemon-core": "0.9.82-rc.8",
50
+ "@adhdev/daemon-core": "0.9.82-rc.9",
51
51
  "@adhdev/ghostty-vt-node": "*",
52
52
  "@modelcontextprotocol/sdk": "^1.0.0",
53
53
  "@xterm/addon-serialize": "^0.14.0",
@@ -880,6 +880,10 @@ function summarizeRelatedRepoStatus(repo, status) {
880
880
  workspace: repo.workspace,
881
881
  isGitRepo: status?.isGitRepo === true,
882
882
  branch: status?.branch ?? null,
883
+ upstream: status?.upstream ?? null,
884
+ upstreamStatus: typeof status?.upstreamStatus === "string" ? status.upstreamStatus : status?.upstream ? "unchecked" : "no_upstream",
885
+ upstreamFetchedAt: Number.isFinite(Number(status?.upstreamFetchedAt)) ? Number(status.upstreamFetchedAt) : null,
886
+ upstreamFetchError: typeof status?.upstreamFetchError === "string" ? status.upstreamFetchError : null,
883
887
  ahead: Number.isFinite(Number(status?.ahead)) ? Number(status.ahead) : 0,
884
888
  behind: Number.isFinite(Number(status?.behind)) ? Number(status.behind) : 0,
885
889
  dirty,
@@ -896,7 +900,7 @@ async function collectRelatedRepoStatuses(ctx, node) {
896
900
  const results = [];
897
901
  for (const repo of relatedRepos) {
898
902
  try {
899
- const statusResult = !isLocalTransport(ctx.transport) && node.daemonId ? await ctx.transport.gitStatus(node.daemonId, repo.workspace, false) : await commandForNode(ctx, node, "git_status", { workspace: repo.workspace });
903
+ const statusResult = !isLocalTransport(ctx.transport) && node.daemonId ? await ctx.transport.gitStatus(node.daemonId, repo.workspace, false, true) : await commandForNode(ctx, node, "git_status", { workspace: repo.workspace, refreshUpstream: true });
900
904
  const status = extractGitStatus(statusResult);
901
905
  results.push(summarizeRelatedRepoStatus(repo, status));
902
906
  } catch (e) {
@@ -944,11 +948,13 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
944
948
  const ahead = readNumeric(status?.ahead);
945
949
  const behind = readNumeric(status?.behind);
946
950
  const upstream = readString(status?.upstream) ?? null;
951
+ const upstreamStatus = readString(status?.upstreamStatus) ?? (upstream ? "unchecked" : "no_upstream");
947
952
  const hasConflicts = status?.hasConflicts === true || Array.isArray(status?.conflictFiles) && status.conflictFiles.length > 0;
948
953
  const base = {
949
954
  defaultBranch,
950
955
  branch,
951
956
  upstream,
957
+ upstreamStatus,
952
958
  ahead,
953
959
  behind,
954
960
  isWorktree: node.isLocalWorktree === true,
@@ -982,6 +988,15 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
982
988
  };
983
989
  }
984
990
  if (branch === defaultBranch) {
991
+ if (upstream && upstreamStatus !== "fresh") {
992
+ return {
993
+ ...base,
994
+ status: "blocked_review",
995
+ needsConvergence: true,
996
+ reason: "default_branch_upstream_unverified",
997
+ nextStep: `Refresh ${defaultBranch}'s upstream refs or resolve the fetch failure before declaring convergence complete for node '${node.id}'.`
998
+ };
999
+ }
985
1000
  if (ahead > 0 || behind > 0) {
986
1001
  return {
987
1002
  ...base,
@@ -1008,6 +1023,15 @@ function buildBranchConvergence(mesh, node, status, dirty, uncommittedChanges) {
1008
1023
  nextStep: `Run mesh_refine_node(node_id: "${node.id}") or explicitly classify this worktree as blocked_review/not_mergeable before ending the task.`
1009
1024
  };
1010
1025
  }
1026
+ if (upstream && upstreamStatus !== "fresh") {
1027
+ return {
1028
+ ...base,
1029
+ status: "blocked_review",
1030
+ needsConvergence: true,
1031
+ reason: "feature_branch_upstream_unverified",
1032
+ nextStep: `Refresh branch '${branch}' upstream refs or resolve the fetch failure before deciding whether it is ready to merge into ${defaultBranch}.`
1033
+ };
1034
+ }
1011
1035
  if (!upstream || ahead > 0 || behind > 0) {
1012
1036
  return {
1013
1037
  ...base,
@@ -1352,7 +1376,7 @@ async function meshStatus(ctx) {
1352
1376
  };
1353
1377
  try {
1354
1378
  if (!isLocalTransport(transport) && node.daemonId) {
1355
- const result = await transport.gitStatus(node.daemonId, node.workspace, false);
1379
+ const result = await transport.gitStatus(node.daemonId, node.workspace, false, true);
1356
1380
  const status = extractGitStatus(result);
1357
1381
  const uncommittedChanges = countUncommittedChanges(status);
1358
1382
  const dirty = isGitStatusDirty(status);
@@ -1370,6 +1394,7 @@ async function meshStatus(ctx) {
1370
1394
  const autoDiscover = node.policy?.autoDiscoverSubmodules !== false;
1371
1395
  const statusResult = await commandForNode(ctx, node, "git_status", {
1372
1396
  workspace: node.workspace,
1397
+ refreshUpstream: true,
1373
1398
  includeSubmodules: autoDiscover,
1374
1399
  submoduleIgnorePaths: node.policy?.submoduleIgnorePaths || void 0
1375
1400
  });
@@ -2036,7 +2061,7 @@ async function meshGitStatus(ctx, args) {
2036
2061
  const submoduleIgnorePaths = node.policy?.submoduleIgnorePaths || [];
2037
2062
  try {
2038
2063
  if (!isLocalTransport(ctx.transport) && node.daemonId) {
2039
- const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true);
2064
+ const result = await ctx.transport.gitStatus(node.daemonId, node.workspace, true, true);
2040
2065
  return JSON.stringify({
2041
2066
  nodeId: args.node_id,
2042
2067
  workspace: node.workspace,
@@ -2048,6 +2073,7 @@ async function meshGitStatus(ctx, args) {
2048
2073
  } else if (isLocalTransport(ctx.transport)) {
2049
2074
  const statusResult = await commandForNode(ctx, node, "git_status", {
2050
2075
  workspace: node.workspace,
2076
+ refreshUpstream: true,
2051
2077
  includeSubmodules: autoDiscoverSubmodules,
2052
2078
  submoduleIgnorePaths: submoduleIgnorePaths.length > 0 ? submoduleIgnorePaths : void 0
2053
2079
  });
@@ -2519,8 +2545,8 @@ var CloudTransport = class {
2519
2545
  if (!res.ok) throw new Error(`Approve failed: ${res.status}`);
2520
2546
  return res.json();
2521
2547
  }
2522
- async gitStatus(daemonId, workspace, includeDiff = true) {
2523
- const params = new URLSearchParams({ workspace, includeDiff: String(includeDiff) });
2548
+ async gitStatus(daemonId, workspace, includeDiff = true, refreshUpstream = false) {
2549
+ const params = new URLSearchParams({ workspace, includeDiff: String(includeDiff), refreshUpstream: String(refreshUpstream) });
2524
2550
  const res = await fetch(
2525
2551
  `${this.baseUrl}/api/v1/shortcuts/${encodeURIComponent(daemonId)}/git-status?${params}`,
2526
2552
  { headers: this.headers() }