@adhdev/daemon-core 0.9.80 → 0.9.81
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/dist/git/git-status.d.ts +4 -0
- package/dist/git/git-types.d.ts +18 -0
- package/dist/index.js +262 -192
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +262 -192
- package/dist/index.mjs.map +1 -1
- package/dist/repo-mesh-types.d.ts +15 -0
- package/package.json +1 -1
- package/src/commands/router.ts +17 -1
- package/src/git/git-status.ts +63 -1
- package/src/git/git-types.ts +19 -0
- package/src/repo-mesh-types.ts +15 -0
|
@@ -87,6 +87,21 @@ export interface RepoMeshNodePolicy {
|
|
|
87
87
|
* sibling paths so freshness checks stay fail-closed and non-surprising.
|
|
88
88
|
*/
|
|
89
89
|
relatedRepos?: RepoMeshRelatedRepo[];
|
|
90
|
+
/**
|
|
91
|
+
* When true (default), mesh_git_status automatically discovers git submodules
|
|
92
|
+
* and includes their status. Set to false to disable auto-discovery.
|
|
93
|
+
*/
|
|
94
|
+
autoDiscoverSubmodules?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Submodule paths to ignore when autoDiscoverSubmodules is true.
|
|
97
|
+
* Useful for vendored dependencies that change frequently but are not deploy-critical.
|
|
98
|
+
*/
|
|
99
|
+
submoduleIgnorePaths?: string[];
|
|
100
|
+
/**
|
|
101
|
+
* When true (default), mesh_clone_node runs `git submodule update --init --recursive`
|
|
102
|
+
* after creating a worktree. Set to false to skip submodule initialization.
|
|
103
|
+
*/
|
|
104
|
+
initSubmodulesOnClone?: boolean;
|
|
90
105
|
}
|
|
91
106
|
export declare const DEFAULT_MESH_POLICY: RepoMeshPolicy;
|
|
92
107
|
export interface RepoMeshNodeCapabilities {
|
package/package.json
CHANGED
package/src/commands/router.ts
CHANGED
|
@@ -2303,13 +2303,29 @@ export class DaemonCommandRouter {
|
|
|
2303
2303
|
if (!node) return { success: false, error: 'Failed to register worktree node' };
|
|
2304
2304
|
}
|
|
2305
2305
|
|
|
2306
|
+
// Initialize submodules if policy allows (default: true)
|
|
2307
|
+
const initSubmodules = (sourceNode.policy as any)?.initSubmodulesOnClone !== false;
|
|
2308
|
+
if (initSubmodules) {
|
|
2309
|
+
try {
|
|
2310
|
+
const { runGit } = await import('../git/git-executor.js');
|
|
2311
|
+
await runGit(
|
|
2312
|
+
{ workspace: result.worktreePath, repoRoot: result.worktreePath, isGitRepo: true },
|
|
2313
|
+
['submodule', 'update', '--init', '--recursive'],
|
|
2314
|
+
{ timeoutMs: 120000 },
|
|
2315
|
+
);
|
|
2316
|
+
} catch (subErr: any) {
|
|
2317
|
+
// Submodule init is best-effort; don't fail the clone
|
|
2318
|
+
console.warn('[mesh] Submodule init failed for worktree:', subErr.message);
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2306
2322
|
// Record in task ledger
|
|
2307
2323
|
try {
|
|
2308
2324
|
const { appendLedgerEntry } = await import('../mesh/mesh-ledger.js');
|
|
2309
2325
|
appendLedgerEntry(meshId, {
|
|
2310
2326
|
kind: 'node_cloned',
|
|
2311
2327
|
nodeId: node.id,
|
|
2312
|
-
payload: { sourceNodeId, branch: result.branch, worktreePath: result.worktreePath },
|
|
2328
|
+
payload: { sourceNodeId, branch: result.branch, worktreePath: result.worktreePath, submodulesInitialized: initSubmodules },
|
|
2313
2329
|
});
|
|
2314
2330
|
} catch { /* ledger append is best-effort */ }
|
|
2315
2331
|
|
package/src/git/git-status.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import type { GitRepoStatus } from './git-types.js';
|
|
1
|
+
import type { GitRepoStatus, GitSubmoduleStatus } from './git-types.js';
|
|
2
2
|
import { GitCommandError, resolveGitRepository, runGit } from './git-executor.js';
|
|
3
3
|
|
|
4
4
|
export interface GitStatusOptions {
|
|
5
5
|
timeoutMs?: number;
|
|
6
|
+
/** When true, include submodule status in the result. Defaults to true. */
|
|
7
|
+
includeSubmodules?: boolean;
|
|
8
|
+
/** Optional filter to exclude specific submodule paths from status */
|
|
9
|
+
submoduleIgnorePaths?: string[];
|
|
6
10
|
}
|
|
7
11
|
|
|
8
12
|
export async function getGitRepoStatus(
|
|
@@ -10,6 +14,7 @@ export async function getGitRepoStatus(
|
|
|
10
14
|
options: GitStatusOptions = {},
|
|
11
15
|
): Promise<GitRepoStatus> {
|
|
12
16
|
const lastCheckedAt = Date.now();
|
|
17
|
+
const includeSubmodules = options.includeSubmodules !== false;
|
|
13
18
|
|
|
14
19
|
try {
|
|
15
20
|
const repo = await resolveGitRepository(workspace, options);
|
|
@@ -18,6 +23,11 @@ export async function getGitRepoStatus(
|
|
|
18
23
|
const head = await readHead(repo, options);
|
|
19
24
|
const stashCount = await readStashCount(repo, options);
|
|
20
25
|
|
|
26
|
+
let submodules: GitSubmoduleStatus[] | undefined;
|
|
27
|
+
if (includeSubmodules) {
|
|
28
|
+
submodules = await getSubmoduleStatuses(repo, options);
|
|
29
|
+
}
|
|
30
|
+
|
|
21
31
|
return {
|
|
22
32
|
workspace: repo.workspace,
|
|
23
33
|
repoRoot: repo.repoRoot,
|
|
@@ -37,6 +47,7 @@ export async function getGitRepoStatus(
|
|
|
37
47
|
conflictFiles: parsed.conflictFiles,
|
|
38
48
|
stashCount,
|
|
39
49
|
lastCheckedAt,
|
|
50
|
+
submodules,
|
|
40
51
|
};
|
|
41
52
|
} catch (error) {
|
|
42
53
|
if (error instanceof GitCommandError) {
|
|
@@ -191,3 +202,54 @@ function emptyStatus(workspace: string, lastCheckedAt: number, error: GitCommand
|
|
|
191
202
|
reason: error.reason,
|
|
192
203
|
};
|
|
193
204
|
}
|
|
205
|
+
|
|
206
|
+
// ─── Submodule Status ───────────────────────────
|
|
207
|
+
|
|
208
|
+
async function getSubmoduleStatuses(
|
|
209
|
+
repo: { workspace: string; repoRoot: string | null; isGitRepo: boolean },
|
|
210
|
+
options: GitStatusOptions,
|
|
211
|
+
): Promise<GitSubmoduleStatus[]> {
|
|
212
|
+
if (!repo.repoRoot) return [];
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const result = await runGit(repo, ['submodule', 'status', '--recursive'], options);
|
|
216
|
+
return parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
|
|
217
|
+
} catch {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function parseSubmoduleStatusOutput(
|
|
223
|
+
output: string,
|
|
224
|
+
repoRoot: string,
|
|
225
|
+
ignorePaths?: string[],
|
|
226
|
+
): GitSubmoduleStatus[] {
|
|
227
|
+
const submodules: GitSubmoduleStatus[] = [];
|
|
228
|
+
const ignoreSet = new Set(ignorePaths || []);
|
|
229
|
+
|
|
230
|
+
for (const line of output.split('\n')) {
|
|
231
|
+
if (!line.trim()) continue;
|
|
232
|
+
|
|
233
|
+
// Format: [+- ]<commit> <path> (<branch>)
|
|
234
|
+
// - = out of sync, + = dirty, ' ' = clean
|
|
235
|
+
const match = line.match(/^([\-+\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
|
|
236
|
+
if (!match) continue;
|
|
237
|
+
|
|
238
|
+
const prefix = match[1];
|
|
239
|
+
const commit = match[2];
|
|
240
|
+
const path = match[3];
|
|
241
|
+
|
|
242
|
+
if (ignoreSet.has(path)) continue;
|
|
243
|
+
|
|
244
|
+
submodules.push({
|
|
245
|
+
path,
|
|
246
|
+
commit,
|
|
247
|
+
repoPath: repoRoot + '/' + path,
|
|
248
|
+
dirty: prefix === '+',
|
|
249
|
+
outOfSync: prefix === '-',
|
|
250
|
+
lastCheckedAt: Date.now(),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return submodules;
|
|
255
|
+
}
|
package/src/git/git-types.ts
CHANGED
|
@@ -23,6 +23,23 @@ export interface GitRepoIdentity {
|
|
|
23
23
|
isGitRepo: boolean;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export interface GitSubmoduleStatus {
|
|
27
|
+
/** Submodule path relative to repo root */
|
|
28
|
+
path: string;
|
|
29
|
+
/** Current commit SHA the submodule is at */
|
|
30
|
+
commit: string;
|
|
31
|
+
/** Path to the submodule repo (absolute) */
|
|
32
|
+
repoPath: string;
|
|
33
|
+
/** Whether the submodule has uncommitted changes */
|
|
34
|
+
dirty: boolean;
|
|
35
|
+
/** Whether the submodule commit differs from what the parent repo expects */
|
|
36
|
+
outOfSync: boolean;
|
|
37
|
+
/** Last checked timestamp */
|
|
38
|
+
lastCheckedAt: number;
|
|
39
|
+
/** Error message if submodule status could not be read */
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
26
43
|
export interface GitRepoStatus extends GitRepoIdentity {
|
|
27
44
|
branch: string | null;
|
|
28
45
|
headCommit: string | null;
|
|
@@ -39,6 +56,8 @@ export interface GitRepoStatus extends GitRepoIdentity {
|
|
|
39
56
|
conflictFiles: string[];
|
|
40
57
|
stashCount: number;
|
|
41
58
|
lastCheckedAt: number;
|
|
59
|
+
/** Submodule statuses when auto-discover is enabled */
|
|
60
|
+
submodules?: GitSubmoduleStatus[];
|
|
42
61
|
error?: string;
|
|
43
62
|
reason?: GitFailureReason;
|
|
44
63
|
}
|
package/src/repo-mesh-types.ts
CHANGED
|
@@ -105,6 +105,21 @@ export interface RepoMeshNodePolicy {
|
|
|
105
105
|
* sibling paths so freshness checks stay fail-closed and non-surprising.
|
|
106
106
|
*/
|
|
107
107
|
relatedRepos?: RepoMeshRelatedRepo[];
|
|
108
|
+
/**
|
|
109
|
+
* When true (default), mesh_git_status automatically discovers git submodules
|
|
110
|
+
* and includes their status. Set to false to disable auto-discovery.
|
|
111
|
+
*/
|
|
112
|
+
autoDiscoverSubmodules?: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Submodule paths to ignore when autoDiscoverSubmodules is true.
|
|
115
|
+
* Useful for vendored dependencies that change frequently but are not deploy-critical.
|
|
116
|
+
*/
|
|
117
|
+
submoduleIgnorePaths?: string[];
|
|
118
|
+
/**
|
|
119
|
+
* When true (default), mesh_clone_node runs `git submodule update --init --recursive`
|
|
120
|
+
* after creating a worktree. Set to false to skip submodule initialization.
|
|
121
|
+
*/
|
|
122
|
+
initSubmodulesOnClone?: boolean;
|
|
108
123
|
}
|
|
109
124
|
|
|
110
125
|
export const DEFAULT_MESH_POLICY: RepoMeshPolicy = {
|