@adhdev/daemon-core 0.9.82-rc.4 → 0.9.82-rc.5

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/daemon-core",
3
- "version": "0.9.82-rc.4",
3
+ "version": "0.9.82-rc.5",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,6 +26,8 @@ import { getSavedProviderSessions } from '../config/saved-sessions.js';
26
26
  import { listProviderHistorySessions } from '../config/chat-history.js';
27
27
  import { detectIDEs } from '../detection/ide-detector.js';
28
28
  import { detectCLI } from '../detection/cli-detector.js';
29
+ import { getGitRepoStatus } from '../git/git-status.js';
30
+ import type { GitSubmoduleStatus } from '../git/git-types.js';
29
31
  import { SessionRegistry } from '../sessions/registry.js';
30
32
  import { LOG } from '../logging/logger.js';
31
33
  import { logCommand } from '../logging/command-log.js';
@@ -112,6 +114,29 @@ function readBooleanValue(...values: unknown[]): boolean | undefined {
112
114
  return undefined;
113
115
  }
114
116
 
117
+ function readGitSubmodules(value: unknown): GitSubmoduleStatus[] | undefined {
118
+ if (!Array.isArray(value)) return undefined;
119
+ const submodules = value
120
+ .map(entry => {
121
+ const submodule = readObjectRecord(entry);
122
+ const path = readStringValue(submodule.path);
123
+ const commit = readStringValue(submodule.commit);
124
+ const repoPath = readStringValue(submodule.repoPath, submodule.repo_root);
125
+ if (!path || !commit || !repoPath) return null;
126
+ return {
127
+ path,
128
+ commit,
129
+ repoPath,
130
+ dirty: readBooleanValue(submodule.dirty) ?? false,
131
+ outOfSync: readBooleanValue(submodule.outOfSync, submodule.out_of_sync) ?? false,
132
+ lastCheckedAt: readNumberValue(submodule.lastCheckedAt, submodule.last_checked_at) ?? Date.now(),
133
+ ...(readStringValue(submodule.error) ? { error: readStringValue(submodule.error) } : {}),
134
+ };
135
+ })
136
+ .filter((entry): entry is GitSubmoduleStatus => entry !== null);
137
+ return submodules.length > 0 ? submodules : undefined;
138
+ }
139
+
115
140
  function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | undefined {
116
141
  const cachedStatus = readObjectRecord(node?.cachedStatus);
117
142
  const cachedGit = readObjectRecord(cachedStatus.git);
@@ -123,6 +148,7 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
123
148
  const hasConflicts = readBooleanValue(cachedGit.hasConflicts) ?? conflictCount > 0;
124
149
  const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
125
150
  if (isGitRepo !== undefined) {
151
+ const submodules = readGitSubmodules(cachedGit.submodules);
126
152
  return {
127
153
  workspace: readStringValue(cachedGit.workspace, node?.workspace) || '',
128
154
  repoRoot: readStringValue(cachedGit.repoRoot, node?.repoRoot, node?.workspace) || null,
@@ -142,6 +168,7 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
142
168
  conflictFiles,
143
169
  stashCount: readNumberValue(cachedGit.stashCount) ?? 0,
144
170
  lastCheckedAt: readNumberValue(cachedGit.lastCheckedAt) ?? Date.now(),
171
+ ...(submodules ? { submodules } : {}),
145
172
  };
146
173
  }
147
174
  }
@@ -171,6 +198,7 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
171
198
  : [];
172
199
  const conflictCount = readNumberValue(status.conflicts) ?? conflictFiles.length;
173
200
  const hasConflicts = readBooleanValue(status.hasConflicts) ?? conflictCount > 0;
201
+ const submodules = readGitSubmodules(status.submodules);
174
202
  return {
175
203
  workspace: readStringValue(status.workspace, node?.workspace) || '',
176
204
  repoRoot: readStringValue(status.repoRoot, node?.repoRoot, node?.workspace) || null,
@@ -190,6 +218,7 @@ function buildCachedInlineMeshGitStatus(node: any): Record<string, unknown> | un
190
218
  conflictFiles,
191
219
  stashCount: readNumberValue(status.stashCount) ?? 0,
192
220
  lastCheckedAt: Date.now(),
221
+ ...(submodules ? { submodules } : {}),
193
222
  };
194
223
  }
195
224
 
@@ -2906,68 +2935,15 @@ export class DaemonCommandRouter {
2906
2935
  continue;
2907
2936
  }
2908
2937
  try {
2909
- const { execFile } = await import('node:child_process');
2910
- const { promisify } = await import('node:util');
2911
- const execFileAsync = promisify(execFile);
2912
-
2913
- const runGit = async (args: string[]): Promise<string> => {
2914
- const result = await execFileAsync('git', ['-C', node.workspace as string, ...args], {
2915
- encoding: 'utf8',
2916
- timeout: 10_000,
2917
- });
2918
- return result.stdout.trim();
2919
- };
2920
-
2921
- const branch = await runGit(['branch', '--show-current']).catch(() => '');
2922
- const porc = await runGit(['status', '--porcelain']).catch(() => '');
2923
- const headCommit = await runGit(['rev-parse', '--short', 'HEAD']).catch(() => null);
2924
- const headMessage = await runGit(['log', '-1', '--format=%s']).catch(() => null);
2925
- const upstream = await runGit(['rev-parse', '--abbrev-ref', '@{upstream}']).catch(() => null);
2926
- const aheadBehind = await runGit(['rev-list', '--left-right', '--count', '@{upstream}...HEAD']).catch(() => '');
2927
- const stashCount = await runGit(['stash', 'list']).catch(() => '');
2928
-
2929
- let ahead = 0, behind = 0;
2930
- if (aheadBehind) {
2931
- const parts = aheadBehind.split(/\s+/);
2932
- if (parts.length >= 2) {
2933
- behind = parseInt(parts[0], 10) || 0;
2934
- ahead = parseInt(parts[1], 10) || 0;
2935
- }
2936
- }
2937
-
2938
- const dirty = porc.length > 0;
2939
- const lines = porc ? porc.split('\n').filter(Boolean) : [];
2940
- let staged = 0, modified = 0, untracked = 0, deleted = 0, renamed = 0;
2941
- for (const line of lines) {
2942
- const xy = line.slice(0, 2);
2943
- if (xy[0] !== ' ' && xy[0] !== '?') staged++;
2944
- if (xy[1] === 'M') modified++;
2945
- if (xy[1] === 'D') deleted++;
2946
- if (xy[0] === 'R' || xy[1] === 'R') renamed++;
2947
- if (xy === '??') untracked++;
2938
+ const gitStatus = await getGitRepoStatus(node.workspace as string, { timeoutMs: 10_000 });
2939
+ status.git = gitStatus;
2940
+ if (gitStatus.isGitRepo) {
2941
+ const dirty = (gitStatus.staged + gitStatus.modified + gitStatus.untracked + gitStatus.deleted + gitStatus.renamed) > 0;
2942
+ status.health = gitStatus.branch ? (dirty ? 'dirty' : 'online') : 'degraded';
2943
+ } else {
2944
+ status.health = 'degraded';
2945
+ if (gitStatus.error && !status.error) status.error = gitStatus.error;
2948
2946
  }
2949
-
2950
- status.git = {
2951
- workspace: node.workspace,
2952
- repoRoot: node.workspace,
2953
- isGitRepo: true,
2954
- branch: branch || null,
2955
- headCommit,
2956
- headMessage,
2957
- upstream,
2958
- ahead,
2959
- behind,
2960
- staged,
2961
- modified,
2962
- untracked,
2963
- deleted,
2964
- renamed,
2965
- hasConflicts: false,
2966
- conflictFiles: [],
2967
- stashCount: stashCount ? stashCount.split('\n').filter(Boolean).length : 0,
2968
- lastCheckedAt: Date.now(),
2969
- };
2970
- status.health = branch ? (dirty ? 'dirty' : 'online') : 'degraded';
2971
2947
  } catch {
2972
2948
  if (!applyCachedInlineMeshNodeStatus(status, node)) {
2973
2949
  status.health = 'degraded';