@mcoda/integrations 0.1.8 → 0.1.10

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.
@@ -5,6 +5,8 @@ export declare class CliQaAdapter implements QaAdapter {
5
5
  private resolveCwd;
6
6
  ensureInstalled(profile: QaProfile, ctx: QaContext): Promise<QaEnsureResult>;
7
7
  private persistLogs;
8
+ private formatCommandHeader;
9
+ private runCommand;
8
10
  invoke(profile: QaProfile, ctx: QaContext): Promise<QaRunResult>;
9
11
  }
10
12
  //# sourceMappingURL=CliQaAdapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CliQaAdapter.d.ts","sourceRoot":"","sources":["../../src/qa/CliQaAdapter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAKtE,qBAAa,YAAa,YAAW,SAAS;IAC5C,OAAO,CAAC,UAAU;IASZ,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC;YAapE,WAAW;IAYnB,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;CAiDvE"}
1
+ {"version":3,"file":"CliQaAdapter.d.ts","sourceRoot":"","sources":["../../src/qa/CliQaAdapter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAKtE,qBAAa,YAAa,YAAW,SAAS;IAC5C,OAAO,CAAC,UAAU;IASZ,eAAe,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC;YAapE,WAAW;IAkBzB,OAAO,CAAC,mBAAmB;YAWb,UAAU;IAiDlB,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;CAoEvE"}
@@ -26,33 +26,25 @@ export class CliQaAdapter {
26
26
  return { ok: false, message: error?.message ?? 'QA install failed' };
27
27
  }
28
28
  }
29
- async persistLogs(ctx, stdout, stderr) {
29
+ async persistLogs(ctx, stdout, stderr, suffix) {
30
30
  const artifacts = [];
31
31
  if (!ctx.artifactDir)
32
32
  return artifacts;
33
33
  await fs.mkdir(ctx.artifactDir, { recursive: true });
34
- const outPath = path.join(ctx.artifactDir, 'stdout.log');
35
- const errPath = path.join(ctx.artifactDir, 'stderr.log');
34
+ const suffixLabel = suffix ? `-${suffix}` : '';
35
+ const outPath = path.join(ctx.artifactDir, `stdout${suffixLabel}.log`);
36
+ const errPath = path.join(ctx.artifactDir, `stderr${suffixLabel}.log`);
36
37
  await fs.writeFile(outPath, stdout ?? '', 'utf8');
37
38
  await fs.writeFile(errPath, stderr ?? '', 'utf8');
38
39
  artifacts.push(path.relative(ctx.workspaceRoot, outPath), path.relative(ctx.workspaceRoot, errPath));
39
40
  return artifacts;
40
41
  }
41
- async invoke(profile, ctx) {
42
- const command = ctx.testCommandOverride ?? profile.test_command;
42
+ formatCommandHeader(command, index, total, outcome, exitCode) {
43
+ const label = total > 1 ? `command ${index}/${total}` : 'command';
44
+ return `=== ${label} outcome=${outcome} exit=${exitCode ?? 'null'} cmd=${command} ===`;
45
+ }
46
+ async runCommand(command, profile, ctx, index, total) {
43
47
  const startedAt = new Date().toISOString();
44
- if (!command) {
45
- const finishedAt = new Date().toISOString();
46
- return {
47
- outcome: 'infra_issue',
48
- exitCode: null,
49
- stdout: '',
50
- stderr: 'No test_command configured for QA profile',
51
- artifacts: [],
52
- startedAt,
53
- finishedAt,
54
- };
55
- }
56
48
  const cwd = this.resolveCwd(profile, ctx);
57
49
  try {
58
50
  const { stdout, stderr } = await exec(command, {
@@ -60,15 +52,18 @@ export class CliQaAdapter {
60
52
  env: { ...process.env, ...profile.env, ...ctx.env },
61
53
  });
62
54
  const finishedAt = new Date().toISOString();
63
- const artifacts = await this.persistLogs(ctx, stdout, stderr);
55
+ const artifacts = await this.persistLogs(ctx, stdout, stderr, total > 1 ? String(index) : undefined);
64
56
  return {
65
- outcome: 'pass',
66
- exitCode: 0,
67
- stdout,
68
- stderr,
69
- artifacts,
70
- startedAt,
71
- finishedAt,
57
+ command,
58
+ result: {
59
+ outcome: 'pass',
60
+ exitCode: 0,
61
+ stdout,
62
+ stderr,
63
+ artifacts,
64
+ startedAt,
65
+ finishedAt,
66
+ },
72
67
  };
73
68
  }
74
69
  catch (error) {
@@ -76,16 +71,76 @@ export class CliQaAdapter {
76
71
  const stderr = error?.stderr ?? String(error);
77
72
  const exitCode = typeof error?.code === 'number' ? error.code : null;
78
73
  const finishedAt = new Date().toISOString();
79
- const artifacts = await this.persistLogs(ctx, stdout, stderr);
74
+ const artifacts = await this.persistLogs(ctx, stdout, stderr, total > 1 ? String(index) : undefined);
80
75
  return {
81
- outcome: exitCode === null ? 'infra_issue' : exitCode === 0 ? 'pass' : 'fail',
82
- exitCode,
83
- stdout,
84
- stderr,
85
- artifacts,
86
- startedAt,
87
- finishedAt,
76
+ command,
77
+ result: {
78
+ outcome: exitCode === null ? 'infra_issue' : exitCode === 0 ? 'pass' : 'fail',
79
+ exitCode,
80
+ stdout,
81
+ stderr,
82
+ artifacts,
83
+ startedAt,
84
+ finishedAt,
85
+ },
88
86
  };
89
87
  }
90
88
  }
89
+ async invoke(profile, ctx) {
90
+ const commandList = (ctx.commands ?? []).map((cmd) => cmd.trim()).filter(Boolean);
91
+ const fallback = ctx.testCommandOverride ?? profile.test_command;
92
+ const commands = commandList.length ? commandList : fallback ? [fallback] : [];
93
+ if (!commands.length) {
94
+ const now = new Date().toISOString();
95
+ return {
96
+ outcome: 'infra_issue',
97
+ exitCode: null,
98
+ stdout: '',
99
+ stderr: 'No test_command configured for QA profile',
100
+ artifacts: [],
101
+ startedAt: now,
102
+ finishedAt: now,
103
+ };
104
+ }
105
+ if (commands.length === 1) {
106
+ return (await this.runCommand(commands[0], profile, ctx, 1, 1)).result;
107
+ }
108
+ const runs = [];
109
+ for (let index = 0; index < commands.length; index += 1) {
110
+ runs.push(await this.runCommand(commands[index], profile, ctx, index + 1, commands.length));
111
+ }
112
+ const outcome = runs.some((run) => run.result.outcome === 'infra_issue')
113
+ ? 'infra_issue'
114
+ : runs.some((run) => run.result.outcome === 'fail')
115
+ ? 'fail'
116
+ : 'pass';
117
+ const exitCode = outcome === 'infra_issue'
118
+ ? null
119
+ : outcome === 'pass'
120
+ ? 0
121
+ : runs.find((run) => typeof run.result.exitCode === 'number' && run.result.exitCode !== 0)?.result
122
+ .exitCode ?? 1;
123
+ const stdout = runs
124
+ .map((run, index) => [this.formatCommandHeader(run.command, index + 1, runs.length, run.result.outcome, run.result.exitCode), run.result.stdout]
125
+ .filter(Boolean)
126
+ .join('\n'))
127
+ .join('\n\n');
128
+ const stderr = runs
129
+ .map((run, index) => [this.formatCommandHeader(run.command, index + 1, runs.length, run.result.outcome, run.result.exitCode), run.result.stderr]
130
+ .filter(Boolean)
131
+ .join('\n'))
132
+ .join('\n\n');
133
+ const artifacts = runs.flatMap((run) => run.result.artifacts ?? []);
134
+ const startedAt = runs.reduce((min, run) => (run.result.startedAt < min ? run.result.startedAt : min), runs[0].result.startedAt);
135
+ const finishedAt = runs.reduce((max, run) => (run.result.finishedAt > max ? run.result.finishedAt : max), runs[0].result.finishedAt);
136
+ return {
137
+ outcome,
138
+ exitCode,
139
+ stdout,
140
+ stderr,
141
+ artifacts,
142
+ startedAt,
143
+ finishedAt,
144
+ };
145
+ }
91
146
  }
@@ -1,3 +1,4 @@
1
+ import type { QaBrowserAction } from '@mcoda/shared/qa/QaPlan.js';
1
2
  export type QaOutcome = 'pass' | 'fail' | 'infra_issue';
2
3
  export interface QaRunResult {
3
4
  outcome: QaOutcome;
@@ -19,6 +20,9 @@ export interface QaContext {
19
20
  taskKey: string;
20
21
  env: NodeJS.ProcessEnv;
21
22
  testCommandOverride?: string;
23
+ commands?: string[];
24
+ browserActions?: QaBrowserAction[];
25
+ browserBaseUrl?: string;
22
26
  artifactDir?: string;
23
27
  }
24
28
  //# sourceMappingURL=QaTypes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"QaTypes.d.ts","sourceRoot":"","sources":["../../src/qa/QaTypes.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,SAAS,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
1
+ {"version":3,"file":"QaTypes.d.ts","sourceRoot":"","sources":["../../src/qa/QaTypes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAElE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,SAAS,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,CAAC,EAAE,eAAe,EAAE,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
@@ -11,6 +11,10 @@ export interface TokenUsageSummaryRow {
11
11
  tokens_prompt: number;
12
12
  tokens_completion: number;
13
13
  tokens_total: number;
14
+ tokens_cached?: number;
15
+ tokens_cache_read?: number;
16
+ tokens_cache_write?: number;
17
+ duration_ms?: number;
14
18
  cost_estimate: number | null;
15
19
  }
16
20
  export interface TokenUsageRow {
@@ -27,11 +31,20 @@ export interface TokenUsageRow {
27
31
  tokens_prompt: number | null;
28
32
  tokens_completion: number | null;
29
33
  tokens_total: number | null;
34
+ tokens_cached?: number | null;
35
+ tokens_cache_read?: number | null;
36
+ tokens_cache_write?: number | null;
30
37
  cost_estimate: number | null;
31
38
  duration_seconds: number | null;
39
+ duration_ms?: number | null;
40
+ started_at?: string | null;
41
+ finished_at?: string | null;
32
42
  timestamp: string;
33
43
  command_name?: string | null;
34
44
  action?: string | null;
45
+ invocation_kind?: string | null;
46
+ provider?: string | null;
47
+ currency?: string | null;
35
48
  error_kind?: string | null;
36
49
  metadata?: Record<string, unknown>;
37
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TelemetryClient.d.ts","sourceRoot":"","sources":["../../src/telemetry/TelemetryClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TelemetryClient.d.ts","sourceRoot":"","sources":["../../src/telemetry/TelemetryClient.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  export declare class VcsClient {
2
2
  private runGit;
3
3
  private gitDirExists;
4
+ isRepo(cwd: string): Promise<boolean>;
4
5
  ensureRepo(cwd: string): Promise<void>;
5
6
  hasRemote(cwd: string): Promise<boolean>;
6
7
  currentBranch(cwd: string): Promise<string | null>;
@@ -10,19 +11,37 @@ export declare class VcsClient {
10
11
  ensureBaseBranch(cwd: string, base: string): Promise<void>;
11
12
  checkoutBranch(cwd: string, branch: string): Promise<void>;
12
13
  createOrCheckoutBranch(cwd: string, branch: string, base: string): Promise<void>;
14
+ addWorktree(cwd: string, worktreePath: string, branch: string, options?: {
15
+ detach?: boolean;
16
+ }): Promise<void>;
17
+ removeWorktree(cwd: string, worktreePath: string): Promise<void>;
18
+ cherryPick(cwd: string, commit: string): Promise<void>;
19
+ abortCherryPick(cwd: string): Promise<void>;
13
20
  applyPatch(cwd: string, patch: string): Promise<void>;
21
+ applyPatchWithReject(cwd: string, patch: string): Promise<{
22
+ stdout?: string;
23
+ stderr?: string;
24
+ error?: string;
25
+ }>;
14
26
  stage(cwd: string, paths: string[]): Promise<void>;
15
27
  commit(cwd: string, message: string, options?: {
16
28
  noVerify?: boolean;
17
29
  noGpgSign?: boolean;
30
+ env?: NodeJS.ProcessEnv;
18
31
  }): Promise<void>;
19
32
  merge(cwd: string, source: string, target: string, ensureClean?: boolean): Promise<void>;
33
+ abortMerge(cwd: string): Promise<void>;
34
+ resolveMergeConflicts(cwd: string, strategy: "theirs" | "ours", paths?: string[]): Promise<string[]>;
20
35
  push(cwd: string, remote: string, branch: string): Promise<void>;
21
36
  pull(cwd: string, remote: string, branch: string, ffOnly?: boolean): Promise<void>;
22
37
  status(cwd: string): Promise<string>;
23
38
  dirtyPaths(cwd: string): Promise<string[]>;
24
39
  conflictPaths(cwd: string): Promise<string[]>;
25
- ensureClean(cwd: string, ignoreDotMcoda?: boolean): Promise<void>;
40
+ ensureClean(cwd: string, ignoreDotMcoda?: boolean, ignorePaths?: string[]): Promise<void>;
41
+ resetHard(cwd: string, options?: {
42
+ exclude?: string[];
43
+ }): Promise<void>;
44
+ restorePaths(cwd: string, paths: string[]): Promise<void>;
26
45
  lastCommitSha(cwd: string): Promise<string>;
27
46
  diff(cwd: string, base: string, head: string, paths?: string[]): Promise<string>;
28
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"VcsClient.d.ts","sourceRoot":"","sources":["../../src/vcs/VcsClient.ts"],"names":[],"mappings":"AAQA,qBAAa,SAAS;YACN,MAAM;YAKN,YAAY;IASpB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtC,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASxC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IASlD,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YASnD,UAAU;YASV,gBAAgB;IAaxB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1D,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQhF,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,MAAM,CACV,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAO,GACxD,OAAO,CAAC,IAAI,CAAC;IAOV,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAQtF,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKpC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAsB1C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAS7C,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9D,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK3C,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CAQvF"}
1
+ {"version":3,"file":"VcsClient.d.ts","sourceRoot":"","sources":["../../src/vcs/VcsClient.ts"],"names":[],"mappings":"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"}
@@ -5,9 +5,12 @@ import path from "node:path";
5
5
  const exec = promisify(execCb);
6
6
  const execFile = promisify(execFileCb);
7
7
  export class VcsClient {
8
- async runGit(cwd, args) {
9
- const { stdout, stderr } = await execFile("git", args, { cwd });
10
- return { stdout, stderr };
8
+ async runGit(cwd, args, options = {}) {
9
+ const { stdout, stderr } = await execFile("git", args, { cwd, ...options });
10
+ return {
11
+ stdout: typeof stdout === "string" ? stdout : stdout?.toString?.() ?? "",
12
+ stderr: typeof stderr === "string" ? stderr : stderr?.toString?.() ?? "",
13
+ };
11
14
  }
12
15
  async gitDirExists(cwd) {
13
16
  try {
@@ -18,6 +21,15 @@ export class VcsClient {
18
21
  return false;
19
22
  }
20
23
  }
24
+ async isRepo(cwd) {
25
+ try {
26
+ await this.runGit(cwd, ["rev-parse", "--is-inside-work-tree"]);
27
+ return true;
28
+ }
29
+ catch {
30
+ return this.gitDirExists(cwd);
31
+ }
32
+ }
21
33
  async ensureRepo(cwd) {
22
34
  if (await this.gitDirExists(cwd))
23
35
  return;
@@ -95,6 +107,28 @@ export class VcsClient {
95
107
  }
96
108
  await this.runGit(cwd, ["checkout", "-b", branch, base]);
97
109
  }
110
+ async addWorktree(cwd, worktreePath, branch, options) {
111
+ const args = ["worktree", "add", "--force"];
112
+ if (options?.detach) {
113
+ args.push("--detach");
114
+ }
115
+ args.push(worktreePath, branch);
116
+ await this.runGit(cwd, args);
117
+ }
118
+ async removeWorktree(cwd, worktreePath) {
119
+ try {
120
+ await this.runGit(cwd, ["worktree", "remove", "--force", worktreePath]);
121
+ }
122
+ catch {
123
+ // ignore; cleanup caller will remove directory
124
+ }
125
+ }
126
+ async cherryPick(cwd, commit) {
127
+ await this.runGit(cwd, ["cherry-pick", commit]);
128
+ }
129
+ async abortCherryPick(cwd) {
130
+ await this.runGit(cwd, ["cherry-pick", "--abort"]);
131
+ }
98
132
  async applyPatch(cwd, patch) {
99
133
  const opts = { cwd, shell: true };
100
134
  const applyCmd = `cat <<'__PATCH__' | git apply --whitespace=nowarn\n${patch}\n__PATCH__`;
@@ -103,17 +137,42 @@ export class VcsClient {
103
137
  return;
104
138
  }
105
139
  catch (error) {
106
- // If the patch is already applied, a reverse --check succeeds; treat that as a no-op.
107
- const reverseCheckCmd = `cat <<'__PATCH__' | git apply --reverse --check --whitespace=nowarn\n${patch}\n__PATCH__`;
140
+ // Retry with 3-way merge for drifted files.
141
+ const apply3wayCmd = `cat <<'__PATCH__' | git apply --3way --whitespace=nowarn\n${patch}\n__PATCH__`;
108
142
  try {
109
- await exec(reverseCheckCmd, opts);
143
+ await exec(apply3wayCmd, opts);
110
144
  return;
111
145
  }
112
146
  catch {
113
- throw error;
147
+ // If the patch is already applied, a reverse --check succeeds; treat that as a no-op.
148
+ const reverseCheckCmd = `cat <<'__PATCH__' | git apply --reverse --check --whitespace=nowarn\n${patch}\n__PATCH__`;
149
+ try {
150
+ await exec(reverseCheckCmd, opts);
151
+ return;
152
+ }
153
+ catch {
154
+ throw error;
155
+ }
114
156
  }
115
157
  }
116
158
  }
159
+ async applyPatchWithReject(cwd, patch) {
160
+ const opts = { cwd, shell: true };
161
+ const applyCmd = `cat <<'__PATCH__' | git apply --reject --whitespace=nowarn\n${patch}\n__PATCH__`;
162
+ try {
163
+ const { stdout, stderr } = await exec(applyCmd, opts);
164
+ return {
165
+ stdout: typeof stdout === "string" ? stdout : stdout?.toString?.() ?? "",
166
+ stderr: typeof stderr === "string" ? stderr : stderr?.toString?.() ?? "",
167
+ };
168
+ }
169
+ catch (error) {
170
+ const stdout = typeof error?.stdout === "string" ? error.stdout : error?.stdout?.toString?.() ?? "";
171
+ const stderr = typeof error?.stderr === "string" ? error.stderr : error?.stderr?.toString?.() ?? "";
172
+ const message = error instanceof Error ? error.message : String(error);
173
+ return { stdout, stderr, error: stderr || message };
174
+ }
175
+ }
117
176
  async stage(cwd, paths) {
118
177
  await this.runGit(cwd, ["add", ...paths]);
119
178
  }
@@ -123,7 +182,8 @@ export class VcsClient {
123
182
  args.push("--no-verify");
124
183
  if (options.noGpgSign)
125
184
  args.push("--no-gpg-sign");
126
- await this.runGit(cwd, args);
185
+ const execOptions = options.env ? { env: options.env } : {};
186
+ await this.runGit(cwd, args, execOptions);
127
187
  }
128
188
  async merge(cwd, source, target, ensureClean = false) {
129
189
  await this.checkoutBranch(cwd, target);
@@ -132,6 +192,24 @@ export class VcsClient {
132
192
  }
133
193
  await this.runGit(cwd, ["merge", "--no-edit", source]);
134
194
  }
195
+ async abortMerge(cwd) {
196
+ try {
197
+ await this.runGit(cwd, ["merge", "--abort"]);
198
+ }
199
+ catch {
200
+ // Ignore when no merge is in progress.
201
+ }
202
+ }
203
+ async resolveMergeConflicts(cwd, strategy, paths) {
204
+ const conflicts = paths?.length ? paths : await this.conflictPaths(cwd);
205
+ if (!conflicts.length)
206
+ return [];
207
+ const flag = strategy === "theirs" ? "--theirs" : "--ours";
208
+ await this.runGit(cwd, ["checkout", flag, "--", ...conflicts]);
209
+ await this.runGit(cwd, ["add", "--", ...conflicts]);
210
+ await this.runGit(cwd, ["commit", "--no-edit"]);
211
+ return conflicts;
212
+ }
135
213
  async push(cwd, remote, branch) {
136
214
  await this.runGit(cwd, ["push", remote, branch]);
137
215
  }
@@ -179,13 +257,48 @@ export class VcsClient {
179
257
  return [];
180
258
  }
181
259
  }
182
- async ensureClean(cwd, ignoreDotMcoda = true) {
260
+ async ensureClean(cwd, ignoreDotMcoda = true, ignorePaths = []) {
183
261
  const dirty = await this.dirtyPaths(cwd);
184
- const filtered = ignoreDotMcoda ? dirty.filter((p) => !p.startsWith(".mcoda")) : dirty;
262
+ const normalize = (value) => value.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/^\/+/, "");
263
+ const normalizedIgnore = ignorePaths
264
+ .map((entry) => normalize(entry.trim()))
265
+ .filter(Boolean)
266
+ .map((entry) => (entry.endsWith("/") ? `${entry.replace(/\/+$/, "")}/` : entry));
267
+ const isIgnored = (value) => {
268
+ const normalized = normalize(value);
269
+ if (ignoreDotMcoda && normalized.startsWith(".mcoda"))
270
+ return true;
271
+ return normalizedIgnore.some((entry) => {
272
+ if (entry.endsWith("/")) {
273
+ const dir = entry.slice(0, -1);
274
+ return normalized === dir || normalized.startsWith(entry);
275
+ }
276
+ return normalized === entry || normalized.startsWith(`${entry}/`);
277
+ });
278
+ };
279
+ const filtered = dirty.filter((p) => !isIgnored(p));
185
280
  if (filtered.length) {
186
281
  throw new Error(`Working tree dirty: ${filtered.join(", ")}`);
187
282
  }
188
283
  }
284
+ async resetHard(cwd, options) {
285
+ await this.runGit(cwd, ["reset", "--hard"]);
286
+ const args = ["clean", "-fd"];
287
+ (options?.exclude ?? [])
288
+ .map((entry) => entry.trim())
289
+ .filter(Boolean)
290
+ .forEach((entry) => {
291
+ args.push("-e", entry);
292
+ });
293
+ await this.runGit(cwd, args);
294
+ }
295
+ async restorePaths(cwd, paths) {
296
+ const cleaned = paths.map((entry) => entry.trim()).filter(Boolean);
297
+ if (!cleaned.length)
298
+ return;
299
+ await this.runGit(cwd, ["checkout", "--", ...cleaned]);
300
+ await this.runGit(cwd, ["clean", "-fd", "--", ...cleaned]);
301
+ }
189
302
  async lastCommitSha(cwd) {
190
303
  const { stdout } = await this.runGit(cwd, ["rev-parse", "HEAD"]);
191
304
  return stdout.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcoda/integrations",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "External integrations for mcoda (vcs, QA, telemetry).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -37,7 +37,8 @@
37
37
  "access": "public"
38
38
  },
39
39
  "dependencies": {
40
- "@mcoda/shared": "0.1.8"
40
+ "docdex": "^0.2.22",
41
+ "@mcoda/shared": "0.1.10"
41
42
  },
42
43
  "scripts": {
43
44
  "build": "tsc -p tsconfig.json",