@adhdev/daemon-core 0.9.81-rc.1 → 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
package/dist/git/git-status.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { GitRepoStatus } from './git-types.js';
|
|
2
2
|
export interface GitStatusOptions {
|
|
3
3
|
timeoutMs?: number;
|
|
4
|
+
/** When true, include submodule status in the result. Defaults to true. */
|
|
5
|
+
includeSubmodules?: boolean;
|
|
6
|
+
/** Optional filter to exclude specific submodule paths from status */
|
|
7
|
+
submoduleIgnorePaths?: string[];
|
|
4
8
|
}
|
|
5
9
|
export declare function getGitRepoStatus(workspace: string, options?: GitStatusOptions): Promise<GitRepoStatus>;
|
|
6
10
|
interface ParsedPorcelainStatus {
|
package/dist/git/git-types.d.ts
CHANGED
|
@@ -11,6 +11,22 @@ export interface GitRepoIdentity {
|
|
|
11
11
|
repoRoot: string | null;
|
|
12
12
|
isGitRepo: boolean;
|
|
13
13
|
}
|
|
14
|
+
export interface GitSubmoduleStatus {
|
|
15
|
+
/** Submodule path relative to repo root */
|
|
16
|
+
path: string;
|
|
17
|
+
/** Current commit SHA the submodule is at */
|
|
18
|
+
commit: string;
|
|
19
|
+
/** Path to the submodule repo (absolute) */
|
|
20
|
+
repoPath: string;
|
|
21
|
+
/** Whether the submodule has uncommitted changes */
|
|
22
|
+
dirty: boolean;
|
|
23
|
+
/** Whether the submodule commit differs from what the parent repo expects */
|
|
24
|
+
outOfSync: boolean;
|
|
25
|
+
/** Last checked timestamp */
|
|
26
|
+
lastCheckedAt: number;
|
|
27
|
+
/** Error message if submodule status could not be read */
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
14
30
|
export interface GitRepoStatus extends GitRepoIdentity {
|
|
15
31
|
branch: string | null;
|
|
16
32
|
headCommit: string | null;
|
|
@@ -27,6 +43,8 @@ export interface GitRepoStatus extends GitRepoIdentity {
|
|
|
27
43
|
conflictFiles: string[];
|
|
28
44
|
stashCount: number;
|
|
29
45
|
lastCheckedAt: number;
|
|
46
|
+
/** Submodule statuses when auto-discover is enabled */
|
|
47
|
+
submodules?: GitSubmoduleStatus[];
|
|
30
48
|
error?: string;
|
|
31
49
|
reason?: GitFailureReason;
|
|
32
50
|
}
|
package/dist/index.js
CHANGED
|
@@ -49,6 +49,211 @@ var init_repo_mesh_types = __esm({
|
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
// src/git/git-executor.ts
|
|
53
|
+
var git_executor_exports = {};
|
|
54
|
+
__export(git_executor_exports, {
|
|
55
|
+
GitCommandError: () => GitCommandError,
|
|
56
|
+
isPathInside: () => isPathInside,
|
|
57
|
+
normalizeGitOutput: () => normalizeGitOutput,
|
|
58
|
+
resolveGitRepository: () => resolveGitRepository,
|
|
59
|
+
runGit: () => runGit
|
|
60
|
+
});
|
|
61
|
+
async function resolveGitRepository(workspace, options = {}) {
|
|
62
|
+
const normalizedWorkspace = await validateWorkspace(workspace);
|
|
63
|
+
const result = await execGitRaw(normalizedWorkspace, ["rev-parse", "--show-toplevel"], options, {
|
|
64
|
+
mapNotGitRepo: true
|
|
65
|
+
});
|
|
66
|
+
const repoRoot = path.resolve(result.stdout.trim());
|
|
67
|
+
if (!repoRoot) {
|
|
68
|
+
throw new GitCommandError("not_git_repo", "Git did not return a repository root", {
|
|
69
|
+
stdout: result.stdout,
|
|
70
|
+
stderr: result.stderr,
|
|
71
|
+
argv: ["rev-parse", "--show-toplevel"],
|
|
72
|
+
cwd: normalizedWorkspace
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
workspace: normalizedWorkspace,
|
|
77
|
+
repoRoot,
|
|
78
|
+
isGitRepo: true
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function runGit(repoOrWorkspace, argv, options = {}) {
|
|
82
|
+
validateGitArgv(argv);
|
|
83
|
+
const repo = typeof repoOrWorkspace === "string" ? await resolveGitRepository(repoOrWorkspace, options) : repoOrWorkspace;
|
|
84
|
+
if (!repo.repoRoot || !repo.isGitRepo) {
|
|
85
|
+
throw new GitCommandError("not_git_repo", "Workspace is not a Git repository", {
|
|
86
|
+
argv,
|
|
87
|
+
cwd: repo.workspace
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const cwd = options.cwd ? await validateWorkspace(options.cwd) : await validateWorkspace(repo.workspace);
|
|
91
|
+
const canonicalRepoRoot = await (0, import_promises.realpath)(repo.repoRoot);
|
|
92
|
+
const canonicalCwd = await (0, import_promises.realpath)(cwd);
|
|
93
|
+
if (!isPathInside(canonicalRepoRoot, canonicalCwd)) {
|
|
94
|
+
throw new GitCommandError("path_outside_repo", "Git cwd is outside the repository root", {
|
|
95
|
+
argv,
|
|
96
|
+
cwd
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return execGitRaw(cwd, argv, options);
|
|
100
|
+
}
|
|
101
|
+
function normalizeGitOutput(value) {
|
|
102
|
+
if (typeof value === "string") return value.replace(/\r\n/g, "\n");
|
|
103
|
+
if (Buffer.isBuffer(value)) return value.toString("utf8").replace(/\r\n/g, "\n");
|
|
104
|
+
if (value == null) return "";
|
|
105
|
+
return String(value).replace(/\r\n/g, "\n");
|
|
106
|
+
}
|
|
107
|
+
function isPathInside(parent, child) {
|
|
108
|
+
const relative3 = path.relative(path.resolve(parent), path.resolve(child));
|
|
109
|
+
return relative3 === "" || !relative3.startsWith("..") && !path.isAbsolute(relative3);
|
|
110
|
+
}
|
|
111
|
+
async function validateWorkspace(workspace) {
|
|
112
|
+
if (typeof workspace !== "string" || workspace.length === 0 || workspace.includes("\0")) {
|
|
113
|
+
throw new GitCommandError("invalid_args", "Workspace must be a non-empty path");
|
|
114
|
+
}
|
|
115
|
+
if (!path.isAbsolute(workspace)) {
|
|
116
|
+
throw new GitCommandError("invalid_args", "Workspace must be an absolute path", { cwd: workspace });
|
|
117
|
+
}
|
|
118
|
+
const normalizedWorkspace = path.resolve(workspace);
|
|
119
|
+
try {
|
|
120
|
+
const info = await (0, import_promises.stat)(normalizedWorkspace);
|
|
121
|
+
if (!info.isDirectory()) {
|
|
122
|
+
throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
|
|
123
|
+
cwd: normalizedWorkspace
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
await (0, import_promises.access)(normalizedWorkspace, import_node_fs.constants.R_OK);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (error instanceof GitCommandError) throw error;
|
|
129
|
+
throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
|
|
130
|
+
cwd: normalizedWorkspace,
|
|
131
|
+
cause: error
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return normalizedWorkspace;
|
|
135
|
+
}
|
|
136
|
+
function validateGitArgv(argv) {
|
|
137
|
+
if (!Array.isArray(argv) || argv.length === 0) {
|
|
138
|
+
throw new GitCommandError("invalid_args", "Git argv must be a non-empty string array", { argv });
|
|
139
|
+
}
|
|
140
|
+
for (const arg of argv) {
|
|
141
|
+
if (typeof arg !== "string" || arg.length === 0 || arg.includes("\0")) {
|
|
142
|
+
throw new GitCommandError("invalid_args", "Git argv contains an invalid argument", { argv });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (argv.includes("-C") || argv.some((arg) => arg.startsWith("--git-dir") || arg.startsWith("--work-tree"))) {
|
|
146
|
+
throw new GitCommandError("invalid_args", "Git argv contains unsafe repository override arguments", {
|
|
147
|
+
argv
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function execGitRaw(cwd, argv, options, behavior = {}) {
|
|
152
|
+
validateGitArgv(argv);
|
|
153
|
+
try {
|
|
154
|
+
const result = await execFileAsync("git", [...argv], {
|
|
155
|
+
cwd,
|
|
156
|
+
encoding: "utf8",
|
|
157
|
+
timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
158
|
+
maxBuffer: options.maxBuffer ?? DEFAULT_MAX_BUFFER,
|
|
159
|
+
windowsHide: true
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
stdout: normalizeGitOutput(result.stdout),
|
|
163
|
+
stderr: normalizeGitOutput(result.stderr)
|
|
164
|
+
};
|
|
165
|
+
} catch (error) {
|
|
166
|
+
throw mapExecError(error, cwd, argv, behavior);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function mapExecError(error, cwd, argv, behavior) {
|
|
170
|
+
const execError = error;
|
|
171
|
+
const stdout = normalizeGitOutput(execError.stdout);
|
|
172
|
+
const stderr = normalizeGitOutput(execError.stderr);
|
|
173
|
+
const code = execError.code;
|
|
174
|
+
const signal = execError.signal;
|
|
175
|
+
const message = [stderr.trim(), execError.message].filter(Boolean).join("\n");
|
|
176
|
+
if (code === "ENOENT") {
|
|
177
|
+
return new GitCommandError("git_not_installed", "Git executable was not found", {
|
|
178
|
+
stdout,
|
|
179
|
+
stderr,
|
|
180
|
+
exitCode: code,
|
|
181
|
+
signal,
|
|
182
|
+
argv,
|
|
183
|
+
cwd,
|
|
184
|
+
cause: error
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (execError.killed || /timed out/i.test(execError.message)) {
|
|
188
|
+
return new GitCommandError("timeout", "Git command timed out", {
|
|
189
|
+
stdout,
|
|
190
|
+
stderr,
|
|
191
|
+
exitCode: code,
|
|
192
|
+
signal,
|
|
193
|
+
argv,
|
|
194
|
+
cwd,
|
|
195
|
+
cause: error
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (behavior.mapNotGitRepo && /not a git repository/i.test(stderr + "\n" + execError.message)) {
|
|
199
|
+
return new GitCommandError("not_git_repo", "Workspace is not a Git repository", {
|
|
200
|
+
stdout,
|
|
201
|
+
stderr,
|
|
202
|
+
exitCode: code,
|
|
203
|
+
signal,
|
|
204
|
+
argv,
|
|
205
|
+
cwd,
|
|
206
|
+
cause: error
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return new GitCommandError("git_command_failed", message || "Git command failed", {
|
|
210
|
+
stdout,
|
|
211
|
+
stderr,
|
|
212
|
+
exitCode: code,
|
|
213
|
+
signal,
|
|
214
|
+
argv,
|
|
215
|
+
cwd,
|
|
216
|
+
cause: error
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
var import_node_child_process, import_node_fs, import_promises, path, import_node_util, execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError;
|
|
220
|
+
var init_git_executor = __esm({
|
|
221
|
+
"src/git/git-executor.ts"() {
|
|
222
|
+
"use strict";
|
|
223
|
+
import_node_child_process = require("child_process");
|
|
224
|
+
import_node_fs = require("fs");
|
|
225
|
+
import_promises = require("fs/promises");
|
|
226
|
+
path = __toESM(require("path"));
|
|
227
|
+
import_node_util = require("util");
|
|
228
|
+
execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
229
|
+
DEFAULT_TIMEOUT_MS = 5e3;
|
|
230
|
+
DEFAULT_MAX_BUFFER = 1024 * 1024;
|
|
231
|
+
GitCommandError = class extends Error {
|
|
232
|
+
reason;
|
|
233
|
+
stdout;
|
|
234
|
+
stderr;
|
|
235
|
+
exitCode;
|
|
236
|
+
signal;
|
|
237
|
+
argv;
|
|
238
|
+
cwd;
|
|
239
|
+
constructor(reason, message, details = {}) {
|
|
240
|
+
super(message);
|
|
241
|
+
if (details.cause !== void 0) {
|
|
242
|
+
this.cause = details.cause;
|
|
243
|
+
}
|
|
244
|
+
this.name = "GitCommandError";
|
|
245
|
+
this.reason = reason;
|
|
246
|
+
this.stdout = normalizeGitOutput(details.stdout);
|
|
247
|
+
this.stderr = normalizeGitOutput(details.stderr);
|
|
248
|
+
this.exitCode = details.exitCode;
|
|
249
|
+
this.signal = details.signal;
|
|
250
|
+
this.argv = details.argv ? [...details.argv] : void 0;
|
|
251
|
+
this.cwd = details.cwd;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
52
257
|
// src/git/git-worktree.ts
|
|
53
258
|
var git_worktree_exports = {};
|
|
54
259
|
__export(git_worktree_exports, {
|
|
@@ -5698,206 +5903,24 @@ __export(index_exports, {
|
|
|
5698
5903
|
module.exports = __toCommonJS(index_exports);
|
|
5699
5904
|
init_repo_mesh_types();
|
|
5700
5905
|
|
|
5701
|
-
// src/git/
|
|
5702
|
-
|
|
5703
|
-
var import_node_fs = require("fs");
|
|
5704
|
-
var import_promises = require("fs/promises");
|
|
5705
|
-
var path = __toESM(require("path"));
|
|
5706
|
-
var import_node_util = require("util");
|
|
5707
|
-
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
5708
|
-
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
5709
|
-
var DEFAULT_MAX_BUFFER = 1024 * 1024;
|
|
5710
|
-
var GitCommandError = class extends Error {
|
|
5711
|
-
reason;
|
|
5712
|
-
stdout;
|
|
5713
|
-
stderr;
|
|
5714
|
-
exitCode;
|
|
5715
|
-
signal;
|
|
5716
|
-
argv;
|
|
5717
|
-
cwd;
|
|
5718
|
-
constructor(reason, message, details = {}) {
|
|
5719
|
-
super(message);
|
|
5720
|
-
if (details.cause !== void 0) {
|
|
5721
|
-
this.cause = details.cause;
|
|
5722
|
-
}
|
|
5723
|
-
this.name = "GitCommandError";
|
|
5724
|
-
this.reason = reason;
|
|
5725
|
-
this.stdout = normalizeGitOutput(details.stdout);
|
|
5726
|
-
this.stderr = normalizeGitOutput(details.stderr);
|
|
5727
|
-
this.exitCode = details.exitCode;
|
|
5728
|
-
this.signal = details.signal;
|
|
5729
|
-
this.argv = details.argv ? [...details.argv] : void 0;
|
|
5730
|
-
this.cwd = details.cwd;
|
|
5731
|
-
}
|
|
5732
|
-
};
|
|
5733
|
-
async function resolveGitRepository(workspace, options = {}) {
|
|
5734
|
-
const normalizedWorkspace = await validateWorkspace(workspace);
|
|
5735
|
-
const result = await execGitRaw(normalizedWorkspace, ["rev-parse", "--show-toplevel"], options, {
|
|
5736
|
-
mapNotGitRepo: true
|
|
5737
|
-
});
|
|
5738
|
-
const repoRoot = path.resolve(result.stdout.trim());
|
|
5739
|
-
if (!repoRoot) {
|
|
5740
|
-
throw new GitCommandError("not_git_repo", "Git did not return a repository root", {
|
|
5741
|
-
stdout: result.stdout,
|
|
5742
|
-
stderr: result.stderr,
|
|
5743
|
-
argv: ["rev-parse", "--show-toplevel"],
|
|
5744
|
-
cwd: normalizedWorkspace
|
|
5745
|
-
});
|
|
5746
|
-
}
|
|
5747
|
-
return {
|
|
5748
|
-
workspace: normalizedWorkspace,
|
|
5749
|
-
repoRoot,
|
|
5750
|
-
isGitRepo: true
|
|
5751
|
-
};
|
|
5752
|
-
}
|
|
5753
|
-
async function runGit(repoOrWorkspace, argv, options = {}) {
|
|
5754
|
-
validateGitArgv(argv);
|
|
5755
|
-
const repo = typeof repoOrWorkspace === "string" ? await resolveGitRepository(repoOrWorkspace, options) : repoOrWorkspace;
|
|
5756
|
-
if (!repo.repoRoot || !repo.isGitRepo) {
|
|
5757
|
-
throw new GitCommandError("not_git_repo", "Workspace is not a Git repository", {
|
|
5758
|
-
argv,
|
|
5759
|
-
cwd: repo.workspace
|
|
5760
|
-
});
|
|
5761
|
-
}
|
|
5762
|
-
const cwd = options.cwd ? await validateWorkspace(options.cwd) : await validateWorkspace(repo.workspace);
|
|
5763
|
-
const canonicalRepoRoot = await (0, import_promises.realpath)(repo.repoRoot);
|
|
5764
|
-
const canonicalCwd = await (0, import_promises.realpath)(cwd);
|
|
5765
|
-
if (!isPathInside(canonicalRepoRoot, canonicalCwd)) {
|
|
5766
|
-
throw new GitCommandError("path_outside_repo", "Git cwd is outside the repository root", {
|
|
5767
|
-
argv,
|
|
5768
|
-
cwd
|
|
5769
|
-
});
|
|
5770
|
-
}
|
|
5771
|
-
return execGitRaw(cwd, argv, options);
|
|
5772
|
-
}
|
|
5773
|
-
function normalizeGitOutput(value) {
|
|
5774
|
-
if (typeof value === "string") return value.replace(/\r\n/g, "\n");
|
|
5775
|
-
if (Buffer.isBuffer(value)) return value.toString("utf8").replace(/\r\n/g, "\n");
|
|
5776
|
-
if (value == null) return "";
|
|
5777
|
-
return String(value).replace(/\r\n/g, "\n");
|
|
5778
|
-
}
|
|
5779
|
-
function isPathInside(parent, child) {
|
|
5780
|
-
const relative3 = path.relative(path.resolve(parent), path.resolve(child));
|
|
5781
|
-
return relative3 === "" || !relative3.startsWith("..") && !path.isAbsolute(relative3);
|
|
5782
|
-
}
|
|
5783
|
-
async function validateWorkspace(workspace) {
|
|
5784
|
-
if (typeof workspace !== "string" || workspace.length === 0 || workspace.includes("\0")) {
|
|
5785
|
-
throw new GitCommandError("invalid_args", "Workspace must be a non-empty path");
|
|
5786
|
-
}
|
|
5787
|
-
if (!path.isAbsolute(workspace)) {
|
|
5788
|
-
throw new GitCommandError("invalid_args", "Workspace must be an absolute path", { cwd: workspace });
|
|
5789
|
-
}
|
|
5790
|
-
const normalizedWorkspace = path.resolve(workspace);
|
|
5791
|
-
try {
|
|
5792
|
-
const info = await (0, import_promises.stat)(normalizedWorkspace);
|
|
5793
|
-
if (!info.isDirectory()) {
|
|
5794
|
-
throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
|
|
5795
|
-
cwd: normalizedWorkspace
|
|
5796
|
-
});
|
|
5797
|
-
}
|
|
5798
|
-
await (0, import_promises.access)(normalizedWorkspace, import_node_fs.constants.R_OK);
|
|
5799
|
-
} catch (error) {
|
|
5800
|
-
if (error instanceof GitCommandError) throw error;
|
|
5801
|
-
throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
|
|
5802
|
-
cwd: normalizedWorkspace,
|
|
5803
|
-
cause: error
|
|
5804
|
-
});
|
|
5805
|
-
}
|
|
5806
|
-
return normalizedWorkspace;
|
|
5807
|
-
}
|
|
5808
|
-
function validateGitArgv(argv) {
|
|
5809
|
-
if (!Array.isArray(argv) || argv.length === 0) {
|
|
5810
|
-
throw new GitCommandError("invalid_args", "Git argv must be a non-empty string array", { argv });
|
|
5811
|
-
}
|
|
5812
|
-
for (const arg of argv) {
|
|
5813
|
-
if (typeof arg !== "string" || arg.length === 0 || arg.includes("\0")) {
|
|
5814
|
-
throw new GitCommandError("invalid_args", "Git argv contains an invalid argument", { argv });
|
|
5815
|
-
}
|
|
5816
|
-
}
|
|
5817
|
-
if (argv.includes("-C") || argv.some((arg) => arg.startsWith("--git-dir") || arg.startsWith("--work-tree"))) {
|
|
5818
|
-
throw new GitCommandError("invalid_args", "Git argv contains unsafe repository override arguments", {
|
|
5819
|
-
argv
|
|
5820
|
-
});
|
|
5821
|
-
}
|
|
5822
|
-
}
|
|
5823
|
-
async function execGitRaw(cwd, argv, options, behavior = {}) {
|
|
5824
|
-
validateGitArgv(argv);
|
|
5825
|
-
try {
|
|
5826
|
-
const result = await execFileAsync("git", [...argv], {
|
|
5827
|
-
cwd,
|
|
5828
|
-
encoding: "utf8",
|
|
5829
|
-
timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
5830
|
-
maxBuffer: options.maxBuffer ?? DEFAULT_MAX_BUFFER,
|
|
5831
|
-
windowsHide: true
|
|
5832
|
-
});
|
|
5833
|
-
return {
|
|
5834
|
-
stdout: normalizeGitOutput(result.stdout),
|
|
5835
|
-
stderr: normalizeGitOutput(result.stderr)
|
|
5836
|
-
};
|
|
5837
|
-
} catch (error) {
|
|
5838
|
-
throw mapExecError(error, cwd, argv, behavior);
|
|
5839
|
-
}
|
|
5840
|
-
}
|
|
5841
|
-
function mapExecError(error, cwd, argv, behavior) {
|
|
5842
|
-
const execError = error;
|
|
5843
|
-
const stdout = normalizeGitOutput(execError.stdout);
|
|
5844
|
-
const stderr = normalizeGitOutput(execError.stderr);
|
|
5845
|
-
const code = execError.code;
|
|
5846
|
-
const signal = execError.signal;
|
|
5847
|
-
const message = [stderr.trim(), execError.message].filter(Boolean).join("\n");
|
|
5848
|
-
if (code === "ENOENT") {
|
|
5849
|
-
return new GitCommandError("git_not_installed", "Git executable was not found", {
|
|
5850
|
-
stdout,
|
|
5851
|
-
stderr,
|
|
5852
|
-
exitCode: code,
|
|
5853
|
-
signal,
|
|
5854
|
-
argv,
|
|
5855
|
-
cwd,
|
|
5856
|
-
cause: error
|
|
5857
|
-
});
|
|
5858
|
-
}
|
|
5859
|
-
if (execError.killed || /timed out/i.test(execError.message)) {
|
|
5860
|
-
return new GitCommandError("timeout", "Git command timed out", {
|
|
5861
|
-
stdout,
|
|
5862
|
-
stderr,
|
|
5863
|
-
exitCode: code,
|
|
5864
|
-
signal,
|
|
5865
|
-
argv,
|
|
5866
|
-
cwd,
|
|
5867
|
-
cause: error
|
|
5868
|
-
});
|
|
5869
|
-
}
|
|
5870
|
-
if (behavior.mapNotGitRepo && /not a git repository/i.test(stderr + "\n" + execError.message)) {
|
|
5871
|
-
return new GitCommandError("not_git_repo", "Workspace is not a Git repository", {
|
|
5872
|
-
stdout,
|
|
5873
|
-
stderr,
|
|
5874
|
-
exitCode: code,
|
|
5875
|
-
signal,
|
|
5876
|
-
argv,
|
|
5877
|
-
cwd,
|
|
5878
|
-
cause: error
|
|
5879
|
-
});
|
|
5880
|
-
}
|
|
5881
|
-
return new GitCommandError("git_command_failed", message || "Git command failed", {
|
|
5882
|
-
stdout,
|
|
5883
|
-
stderr,
|
|
5884
|
-
exitCode: code,
|
|
5885
|
-
signal,
|
|
5886
|
-
argv,
|
|
5887
|
-
cwd,
|
|
5888
|
-
cause: error
|
|
5889
|
-
});
|
|
5890
|
-
}
|
|
5906
|
+
// src/git/index.ts
|
|
5907
|
+
init_git_executor();
|
|
5891
5908
|
|
|
5892
5909
|
// src/git/git-status.ts
|
|
5910
|
+
init_git_executor();
|
|
5893
5911
|
async function getGitRepoStatus(workspace, options = {}) {
|
|
5894
5912
|
const lastCheckedAt = Date.now();
|
|
5913
|
+
const includeSubmodules = options.includeSubmodules !== false;
|
|
5895
5914
|
try {
|
|
5896
5915
|
const repo = await resolveGitRepository(workspace, options);
|
|
5897
5916
|
const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
|
|
5898
5917
|
const parsed = parsePorcelainV2Status(statusOutput.stdout);
|
|
5899
5918
|
const head = await readHead(repo, options);
|
|
5900
5919
|
const stashCount = await readStashCount(repo, options);
|
|
5920
|
+
let submodules;
|
|
5921
|
+
if (includeSubmodules) {
|
|
5922
|
+
submodules = await getSubmoduleStatuses(repo, options);
|
|
5923
|
+
}
|
|
5901
5924
|
return {
|
|
5902
5925
|
workspace: repo.workspace,
|
|
5903
5926
|
repoRoot: repo.repoRoot,
|
|
@@ -5916,7 +5939,8 @@ async function getGitRepoStatus(workspace, options = {}) {
|
|
|
5916
5939
|
hasConflicts: parsed.conflictFiles.length > 0,
|
|
5917
5940
|
conflictFiles: parsed.conflictFiles,
|
|
5918
5941
|
stashCount,
|
|
5919
|
-
lastCheckedAt
|
|
5942
|
+
lastCheckedAt,
|
|
5943
|
+
submodules
|
|
5920
5944
|
};
|
|
5921
5945
|
} catch (error) {
|
|
5922
5946
|
if (error instanceof GitCommandError) {
|
|
@@ -6038,10 +6062,42 @@ function emptyStatus(workspace, lastCheckedAt, error) {
|
|
|
6038
6062
|
reason: error.reason
|
|
6039
6063
|
};
|
|
6040
6064
|
}
|
|
6065
|
+
async function getSubmoduleStatuses(repo, options) {
|
|
6066
|
+
if (!repo.repoRoot) return [];
|
|
6067
|
+
try {
|
|
6068
|
+
const result = await runGit(repo, ["submodule", "status", "--recursive"], options);
|
|
6069
|
+
return parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
|
|
6070
|
+
} catch {
|
|
6071
|
+
return [];
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
function parseSubmoduleStatusOutput(output, repoRoot, ignorePaths) {
|
|
6075
|
+
const submodules = [];
|
|
6076
|
+
const ignoreSet = new Set(ignorePaths || []);
|
|
6077
|
+
for (const line of output.split("\n")) {
|
|
6078
|
+
if (!line.trim()) continue;
|
|
6079
|
+
const match = line.match(/^([\-+\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
|
|
6080
|
+
if (!match) continue;
|
|
6081
|
+
const prefix = match[1];
|
|
6082
|
+
const commit = match[2];
|
|
6083
|
+
const path28 = match[3];
|
|
6084
|
+
if (ignoreSet.has(path28)) continue;
|
|
6085
|
+
submodules.push({
|
|
6086
|
+
path: path28,
|
|
6087
|
+
commit,
|
|
6088
|
+
repoPath: repoRoot + "/" + path28,
|
|
6089
|
+
dirty: prefix === "+",
|
|
6090
|
+
outOfSync: prefix === "-",
|
|
6091
|
+
lastCheckedAt: Date.now()
|
|
6092
|
+
});
|
|
6093
|
+
}
|
|
6094
|
+
return submodules;
|
|
6095
|
+
}
|
|
6041
6096
|
|
|
6042
6097
|
// src/git/git-diff.ts
|
|
6043
6098
|
var import_promises2 = require("fs/promises");
|
|
6044
6099
|
var path2 = __toESM(require("path"));
|
|
6100
|
+
init_git_executor();
|
|
6045
6101
|
var DEFAULT_MAX_FILES = 200;
|
|
6046
6102
|
var DEFAULT_MAX_BYTES = 2e5;
|
|
6047
6103
|
async function getGitDiffSummary(workspace, options = {}) {
|
|
@@ -6570,6 +6626,7 @@ function createGitWorkspaceMonitor(options = {}) {
|
|
|
6570
6626
|
|
|
6571
6627
|
// src/git/git-commands.ts
|
|
6572
6628
|
var path3 = __toESM(require("path"));
|
|
6629
|
+
init_git_executor();
|
|
6573
6630
|
var GIT_COMMAND_NAMES = /* @__PURE__ */ new Set([
|
|
6574
6631
|
"git_status",
|
|
6575
6632
|
"git_diff_summary",
|
|
@@ -25608,12 +25665,25 @@ var DaemonCommandRouter = class {
|
|
|
25608
25665
|
});
|
|
25609
25666
|
if (!node) return { success: false, error: "Failed to register worktree node" };
|
|
25610
25667
|
}
|
|
25668
|
+
const initSubmodules = sourceNode.policy?.initSubmodulesOnClone !== false;
|
|
25669
|
+
if (initSubmodules) {
|
|
25670
|
+
try {
|
|
25671
|
+
const { runGit: runGit2 } = await Promise.resolve().then(() => (init_git_executor(), git_executor_exports));
|
|
25672
|
+
await runGit2(
|
|
25673
|
+
{ workspace: result.worktreePath, repoRoot: result.worktreePath, isGitRepo: true },
|
|
25674
|
+
["submodule", "update", "--init", "--recursive"],
|
|
25675
|
+
{ timeoutMs: 12e4 }
|
|
25676
|
+
);
|
|
25677
|
+
} catch (subErr) {
|
|
25678
|
+
console.warn("[mesh] Submodule init failed for worktree:", subErr.message);
|
|
25679
|
+
}
|
|
25680
|
+
}
|
|
25611
25681
|
try {
|
|
25612
25682
|
const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
|
|
25613
25683
|
appendLedgerEntry2(meshId, {
|
|
25614
25684
|
kind: "node_cloned",
|
|
25615
25685
|
nodeId: node.id,
|
|
25616
|
-
payload: { sourceNodeId, branch: result.branch, worktreePath: result.worktreePath }
|
|
25686
|
+
payload: { sourceNodeId, branch: result.branch, worktreePath: result.worktreePath, submodulesInitialized: initSubmodules }
|
|
25617
25687
|
});
|
|
25618
25688
|
} catch {
|
|
25619
25689
|
}
|