@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/index.mjs CHANGED
@@ -44,6 +44,211 @@ var init_repo_mesh_types = __esm({
44
44
  }
45
45
  });
46
46
 
47
+ // src/git/git-executor.ts
48
+ var git_executor_exports = {};
49
+ __export(git_executor_exports, {
50
+ GitCommandError: () => GitCommandError,
51
+ isPathInside: () => isPathInside,
52
+ normalizeGitOutput: () => normalizeGitOutput,
53
+ resolveGitRepository: () => resolveGitRepository,
54
+ runGit: () => runGit
55
+ });
56
+ import { execFile } from "child_process";
57
+ import { constants } from "fs";
58
+ import { access, realpath, stat } from "fs/promises";
59
+ import * as path from "path";
60
+ import { promisify } from "util";
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 realpath(repo.repoRoot);
92
+ const canonicalCwd = await 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 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 access(normalizedWorkspace, 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 execFileAsync, DEFAULT_TIMEOUT_MS, DEFAULT_MAX_BUFFER, GitCommandError;
220
+ var init_git_executor = __esm({
221
+ "src/git/git-executor.ts"() {
222
+ "use strict";
223
+ execFileAsync = promisify(execFile);
224
+ DEFAULT_TIMEOUT_MS = 5e3;
225
+ DEFAULT_MAX_BUFFER = 1024 * 1024;
226
+ GitCommandError = class extends Error {
227
+ reason;
228
+ stdout;
229
+ stderr;
230
+ exitCode;
231
+ signal;
232
+ argv;
233
+ cwd;
234
+ constructor(reason, message, details = {}) {
235
+ super(message);
236
+ if (details.cause !== void 0) {
237
+ this.cause = details.cause;
238
+ }
239
+ this.name = "GitCommandError";
240
+ this.reason = reason;
241
+ this.stdout = normalizeGitOutput(details.stdout);
242
+ this.stderr = normalizeGitOutput(details.stderr);
243
+ this.exitCode = details.exitCode;
244
+ this.signal = details.signal;
245
+ this.argv = details.argv ? [...details.argv] : void 0;
246
+ this.cwd = details.cwd;
247
+ }
248
+ };
249
+ }
250
+ });
251
+
47
252
  // src/git/git-worktree.ts
48
253
  var git_worktree_exports = {};
49
254
  __export(git_worktree_exports, {
@@ -5461,206 +5666,24 @@ ${lastSnapshot}`;
5461
5666
  // src/index.ts
5462
5667
  init_repo_mesh_types();
5463
5668
 
5464
- // src/git/git-executor.ts
5465
- import { execFile } from "child_process";
5466
- import { constants } from "fs";
5467
- import { access, realpath, stat } from "fs/promises";
5468
- import * as path from "path";
5469
- import { promisify } from "util";
5470
- var execFileAsync = promisify(execFile);
5471
- var DEFAULT_TIMEOUT_MS = 5e3;
5472
- var DEFAULT_MAX_BUFFER = 1024 * 1024;
5473
- var GitCommandError = class extends Error {
5474
- reason;
5475
- stdout;
5476
- stderr;
5477
- exitCode;
5478
- signal;
5479
- argv;
5480
- cwd;
5481
- constructor(reason, message, details = {}) {
5482
- super(message);
5483
- if (details.cause !== void 0) {
5484
- this.cause = details.cause;
5485
- }
5486
- this.name = "GitCommandError";
5487
- this.reason = reason;
5488
- this.stdout = normalizeGitOutput(details.stdout);
5489
- this.stderr = normalizeGitOutput(details.stderr);
5490
- this.exitCode = details.exitCode;
5491
- this.signal = details.signal;
5492
- this.argv = details.argv ? [...details.argv] : void 0;
5493
- this.cwd = details.cwd;
5494
- }
5495
- };
5496
- async function resolveGitRepository(workspace, options = {}) {
5497
- const normalizedWorkspace = await validateWorkspace(workspace);
5498
- const result = await execGitRaw(normalizedWorkspace, ["rev-parse", "--show-toplevel"], options, {
5499
- mapNotGitRepo: true
5500
- });
5501
- const repoRoot = path.resolve(result.stdout.trim());
5502
- if (!repoRoot) {
5503
- throw new GitCommandError("not_git_repo", "Git did not return a repository root", {
5504
- stdout: result.stdout,
5505
- stderr: result.stderr,
5506
- argv: ["rev-parse", "--show-toplevel"],
5507
- cwd: normalizedWorkspace
5508
- });
5509
- }
5510
- return {
5511
- workspace: normalizedWorkspace,
5512
- repoRoot,
5513
- isGitRepo: true
5514
- };
5515
- }
5516
- async function runGit(repoOrWorkspace, argv, options = {}) {
5517
- validateGitArgv(argv);
5518
- const repo = typeof repoOrWorkspace === "string" ? await resolveGitRepository(repoOrWorkspace, options) : repoOrWorkspace;
5519
- if (!repo.repoRoot || !repo.isGitRepo) {
5520
- throw new GitCommandError("not_git_repo", "Workspace is not a Git repository", {
5521
- argv,
5522
- cwd: repo.workspace
5523
- });
5524
- }
5525
- const cwd = options.cwd ? await validateWorkspace(options.cwd) : await validateWorkspace(repo.workspace);
5526
- const canonicalRepoRoot = await realpath(repo.repoRoot);
5527
- const canonicalCwd = await realpath(cwd);
5528
- if (!isPathInside(canonicalRepoRoot, canonicalCwd)) {
5529
- throw new GitCommandError("path_outside_repo", "Git cwd is outside the repository root", {
5530
- argv,
5531
- cwd
5532
- });
5533
- }
5534
- return execGitRaw(cwd, argv, options);
5535
- }
5536
- function normalizeGitOutput(value) {
5537
- if (typeof value === "string") return value.replace(/\r\n/g, "\n");
5538
- if (Buffer.isBuffer(value)) return value.toString("utf8").replace(/\r\n/g, "\n");
5539
- if (value == null) return "";
5540
- return String(value).replace(/\r\n/g, "\n");
5541
- }
5542
- function isPathInside(parent, child) {
5543
- const relative3 = path.relative(path.resolve(parent), path.resolve(child));
5544
- return relative3 === "" || !relative3.startsWith("..") && !path.isAbsolute(relative3);
5545
- }
5546
- async function validateWorkspace(workspace) {
5547
- if (typeof workspace !== "string" || workspace.length === 0 || workspace.includes("\0")) {
5548
- throw new GitCommandError("invalid_args", "Workspace must be a non-empty path");
5549
- }
5550
- if (!path.isAbsolute(workspace)) {
5551
- throw new GitCommandError("invalid_args", "Workspace must be an absolute path", { cwd: workspace });
5552
- }
5553
- const normalizedWorkspace = path.resolve(workspace);
5554
- try {
5555
- const info = await stat(normalizedWorkspace);
5556
- if (!info.isDirectory()) {
5557
- throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
5558
- cwd: normalizedWorkspace
5559
- });
5560
- }
5561
- await access(normalizedWorkspace, constants.R_OK);
5562
- } catch (error) {
5563
- if (error instanceof GitCommandError) throw error;
5564
- throw new GitCommandError("invalid_args", "Workspace must be an existing directory", {
5565
- cwd: normalizedWorkspace,
5566
- cause: error
5567
- });
5568
- }
5569
- return normalizedWorkspace;
5570
- }
5571
- function validateGitArgv(argv) {
5572
- if (!Array.isArray(argv) || argv.length === 0) {
5573
- throw new GitCommandError("invalid_args", "Git argv must be a non-empty string array", { argv });
5574
- }
5575
- for (const arg of argv) {
5576
- if (typeof arg !== "string" || arg.length === 0 || arg.includes("\0")) {
5577
- throw new GitCommandError("invalid_args", "Git argv contains an invalid argument", { argv });
5578
- }
5579
- }
5580
- if (argv.includes("-C") || argv.some((arg) => arg.startsWith("--git-dir") || arg.startsWith("--work-tree"))) {
5581
- throw new GitCommandError("invalid_args", "Git argv contains unsafe repository override arguments", {
5582
- argv
5583
- });
5584
- }
5585
- }
5586
- async function execGitRaw(cwd, argv, options, behavior = {}) {
5587
- validateGitArgv(argv);
5588
- try {
5589
- const result = await execFileAsync("git", [...argv], {
5590
- cwd,
5591
- encoding: "utf8",
5592
- timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
5593
- maxBuffer: options.maxBuffer ?? DEFAULT_MAX_BUFFER,
5594
- windowsHide: true
5595
- });
5596
- return {
5597
- stdout: normalizeGitOutput(result.stdout),
5598
- stderr: normalizeGitOutput(result.stderr)
5599
- };
5600
- } catch (error) {
5601
- throw mapExecError(error, cwd, argv, behavior);
5602
- }
5603
- }
5604
- function mapExecError(error, cwd, argv, behavior) {
5605
- const execError = error;
5606
- const stdout = normalizeGitOutput(execError.stdout);
5607
- const stderr = normalizeGitOutput(execError.stderr);
5608
- const code = execError.code;
5609
- const signal = execError.signal;
5610
- const message = [stderr.trim(), execError.message].filter(Boolean).join("\n");
5611
- if (code === "ENOENT") {
5612
- return new GitCommandError("git_not_installed", "Git executable was not found", {
5613
- stdout,
5614
- stderr,
5615
- exitCode: code,
5616
- signal,
5617
- argv,
5618
- cwd,
5619
- cause: error
5620
- });
5621
- }
5622
- if (execError.killed || /timed out/i.test(execError.message)) {
5623
- return new GitCommandError("timeout", "Git command timed out", {
5624
- stdout,
5625
- stderr,
5626
- exitCode: code,
5627
- signal,
5628
- argv,
5629
- cwd,
5630
- cause: error
5631
- });
5632
- }
5633
- if (behavior.mapNotGitRepo && /not a git repository/i.test(stderr + "\n" + execError.message)) {
5634
- return new GitCommandError("not_git_repo", "Workspace is not a Git repository", {
5635
- stdout,
5636
- stderr,
5637
- exitCode: code,
5638
- signal,
5639
- argv,
5640
- cwd,
5641
- cause: error
5642
- });
5643
- }
5644
- return new GitCommandError("git_command_failed", message || "Git command failed", {
5645
- stdout,
5646
- stderr,
5647
- exitCode: code,
5648
- signal,
5649
- argv,
5650
- cwd,
5651
- cause: error
5652
- });
5653
- }
5669
+ // src/git/index.ts
5670
+ init_git_executor();
5654
5671
 
5655
5672
  // src/git/git-status.ts
5673
+ init_git_executor();
5656
5674
  async function getGitRepoStatus(workspace, options = {}) {
5657
5675
  const lastCheckedAt = Date.now();
5676
+ const includeSubmodules = options.includeSubmodules !== false;
5658
5677
  try {
5659
5678
  const repo = await resolveGitRepository(workspace, options);
5660
5679
  const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
5661
5680
  const parsed = parsePorcelainV2Status(statusOutput.stdout);
5662
5681
  const head = await readHead(repo, options);
5663
5682
  const stashCount = await readStashCount(repo, options);
5683
+ let submodules;
5684
+ if (includeSubmodules) {
5685
+ submodules = await getSubmoduleStatuses(repo, options);
5686
+ }
5664
5687
  return {
5665
5688
  workspace: repo.workspace,
5666
5689
  repoRoot: repo.repoRoot,
@@ -5679,7 +5702,8 @@ async function getGitRepoStatus(workspace, options = {}) {
5679
5702
  hasConflicts: parsed.conflictFiles.length > 0,
5680
5703
  conflictFiles: parsed.conflictFiles,
5681
5704
  stashCount,
5682
- lastCheckedAt
5705
+ lastCheckedAt,
5706
+ submodules
5683
5707
  };
5684
5708
  } catch (error) {
5685
5709
  if (error instanceof GitCommandError) {
@@ -5801,8 +5825,40 @@ function emptyStatus(workspace, lastCheckedAt, error) {
5801
5825
  reason: error.reason
5802
5826
  };
5803
5827
  }
5828
+ async function getSubmoduleStatuses(repo, options) {
5829
+ if (!repo.repoRoot) return [];
5830
+ try {
5831
+ const result = await runGit(repo, ["submodule", "status", "--recursive"], options);
5832
+ return parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
5833
+ } catch {
5834
+ return [];
5835
+ }
5836
+ }
5837
+ function parseSubmoduleStatusOutput(output, repoRoot, ignorePaths) {
5838
+ const submodules = [];
5839
+ const ignoreSet = new Set(ignorePaths || []);
5840
+ for (const line of output.split("\n")) {
5841
+ if (!line.trim()) continue;
5842
+ const match = line.match(/^([\-+\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
5843
+ if (!match) continue;
5844
+ const prefix = match[1];
5845
+ const commit = match[2];
5846
+ const path28 = match[3];
5847
+ if (ignoreSet.has(path28)) continue;
5848
+ submodules.push({
5849
+ path: path28,
5850
+ commit,
5851
+ repoPath: repoRoot + "/" + path28,
5852
+ dirty: prefix === "+",
5853
+ outOfSync: prefix === "-",
5854
+ lastCheckedAt: Date.now()
5855
+ });
5856
+ }
5857
+ return submodules;
5858
+ }
5804
5859
 
5805
5860
  // src/git/git-diff.ts
5861
+ init_git_executor();
5806
5862
  import { readFile, realpath as realpath2 } from "fs/promises";
5807
5863
  import * as path2 from "path";
5808
5864
  var DEFAULT_MAX_FILES = 200;
@@ -6333,6 +6389,7 @@ function createGitWorkspaceMonitor(options = {}) {
6333
6389
 
6334
6390
  // src/git/git-commands.ts
6335
6391
  import * as path3 from "path";
6392
+ init_git_executor();
6336
6393
  var GIT_COMMAND_NAMES = /* @__PURE__ */ new Set([
6337
6394
  "git_status",
6338
6395
  "git_diff_summary",
@@ -25376,12 +25433,25 @@ var DaemonCommandRouter = class {
25376
25433
  });
25377
25434
  if (!node) return { success: false, error: "Failed to register worktree node" };
25378
25435
  }
25436
+ const initSubmodules = sourceNode.policy?.initSubmodulesOnClone !== false;
25437
+ if (initSubmodules) {
25438
+ try {
25439
+ const { runGit: runGit2 } = await Promise.resolve().then(() => (init_git_executor(), git_executor_exports));
25440
+ await runGit2(
25441
+ { workspace: result.worktreePath, repoRoot: result.worktreePath, isGitRepo: true },
25442
+ ["submodule", "update", "--init", "--recursive"],
25443
+ { timeoutMs: 12e4 }
25444
+ );
25445
+ } catch (subErr) {
25446
+ console.warn("[mesh] Submodule init failed for worktree:", subErr.message);
25447
+ }
25448
+ }
25379
25449
  try {
25380
25450
  const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
25381
25451
  appendLedgerEntry2(meshId, {
25382
25452
  kind: "node_cloned",
25383
25453
  nodeId: node.id,
25384
- payload: { sourceNodeId, branch: result.branch, worktreePath: result.worktreePath }
25454
+ payload: { sourceNodeId, branch: result.branch, worktreePath: result.worktreePath, submodulesInitialized: initSubmodules }
25385
25455
  });
25386
25456
  } catch {
25387
25457
  }