open-azdo 0.3.3 → 0.3.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/README.md CHANGED
@@ -130,6 +130,7 @@ Key requirements:
130
130
 
131
131
  Attach the pipeline as a branch build-validation policy. Findings are posted as PR comments by default and do not fail the build.
132
132
  `open-azdo` does not install language-specific prerequisites itself. LSP prerequisites are provided by the pipeline environment, and the pnpm example enables OpenCode's experimental LSP tool while provisioning `.NET` plus `dotnet restore` for C# projects.
133
+ For nested JS or TS packages, install dependencies in the package directory that owns the lockfile before running `open-azdo`. `open-azdo` keeps the review workspace at the checkout root and relies on workspace-local TypeScript resolution.
133
134
 
134
135
  ```yaml
135
136
  trigger: none
package/dist/open-azdo.js CHANGED
@@ -66831,7 +66831,7 @@ var extractDiffLineMaps = (diffText) => {
66831
66831
  };
66832
66832
  var buildTargetRefCandidates = (targetBranch) => {
66833
66833
  const normalized = targetBranch.replace(/^refs\/heads\//, "");
66834
- return [targetBranch, `refs/remotes/origin/${normalized}`, `refs/heads/${normalized}`, normalized];
66834
+ return [`refs/remotes/origin/${normalized}`, targetBranch, `refs/heads/${normalized}`, normalized];
66835
66835
  };
66836
66836
  var runGit = (workspace, operation, args2, allowNonZeroExit = false) => exports_Effect.gen(function* () {
66837
66837
  const git = yield* GitExec;
@@ -66842,6 +66842,7 @@ var runGit = (workspace, operation, args2, allowNonZeroExit = false) => exports_
66842
66842
  allowNonZeroExit
66843
66843
  });
66844
66844
  });
66845
+ var resolveCommitRef = (workspace, operation, ref) => runGit(workspace, operation, ["rev-parse", "--verify", `${ref}^{commit}`]).pipe(exports_Effect.map((result4) => result4.stdout.trim()));
66845
66846
  var resolveHeadReviewedSourceCommitCandidate = (workspace) => runGit(workspace, "Git.resolveReviewedSourceCommit.revList", ["rev-list", "--parents", "-n", "1", "HEAD"]).pipe(exports_Effect.map((result4) => {
66846
66847
  const hashes = result4.stdout.trim().split(/\s+/);
66847
66848
  return hashes.length === 3 ? "HEAD^2" : "HEAD";
@@ -66876,6 +66877,36 @@ var isAncestor = ({ workspace, ancestorRef, headRef }) => exports_Effect.gen(fun
66876
66877
  remediation: "Use `checkout: self` with `fetchDepth: 0` so the relevant commit history is available locally."
66877
66878
  });
66878
66879
  });
66880
+ var hasTargetMergeCommitInRange = ({
66881
+ workspace,
66882
+ baseRef,
66883
+ headRef,
66884
+ currentTargetRef
66885
+ }) => exports_Effect.gen(function* () {
66886
+ const history = yield* runGit(workspace, "Git.hasTargetMergeCommitInRange.revList", [
66887
+ "rev-list",
66888
+ "--first-parent",
66889
+ "--parents",
66890
+ `${baseRef}..${headRef}`
66891
+ ]);
66892
+ for (const line of history.stdout.split(`
66893
+ `).map((entry) => entry.trim()).filter(Boolean)) {
66894
+ const [, , ...mergedParents] = line.split(/\s+/);
66895
+ if (mergedParents.length === 0) {
66896
+ continue;
66897
+ }
66898
+ for (const mergedParent of mergedParents) {
66899
+ if (yield* isAncestor({
66900
+ workspace,
66901
+ ancestorRef: mergedParent,
66902
+ headRef: currentTargetRef
66903
+ })) {
66904
+ return true;
66905
+ }
66906
+ }
66907
+ }
66908
+ return false;
66909
+ });
66879
66910
  var resolveDiffRange = ({ workspace, baseRef, headRef }) => exports_Effect.gen(function* () {
66880
66911
  const diff = yield* runGit(workspace, "Git.resolveDiffRange.diff", [
66881
66912
  "diff",
@@ -66924,6 +66955,8 @@ var resolveTargetRef = ({ workspace, targetBranch }) => exports_Effect.gen(funct
66924
66955
  });
66925
66956
  });
66926
66957
  var resolvePullRequestDiff = (input) => exports_Effect.gen(function* () {
66958
+ const targetRef = yield* resolveTargetRef(input);
66959
+ const targetCommit = yield* resolveCommitRef(input.workspace, "Git.resolvePullRequestDiff.targetCommit", targetRef);
66927
66960
  const parents = yield* runGit(input.workspace, "Git.resolvePullRequestDiff.revList", [
66928
66961
  "rev-list",
66929
66962
  "--parents",
@@ -66934,10 +66967,10 @@ var resolvePullRequestDiff = (input) => exports_Effect.gen(function* () {
66934
66967
  const hashes = parents.stdout.trim().split(/\s+/);
66935
66968
  let baseRef = "";
66936
66969
  const headRef = "HEAD";
66937
- if (hashes.length === 3) {
66938
- baseRef = "HEAD^1";
66970
+ const matchingTargetParent = hashes.slice(1).find((hash3) => hash3 === targetCommit);
66971
+ if (matchingTargetParent) {
66972
+ baseRef = matchingTargetParent;
66939
66973
  } else {
66940
- const targetRef = yield* resolveTargetRef(input);
66941
66974
  const mergeBase = yield* runGit(input.workspace, "Git.resolvePullRequestDiff.mergeBase", [
66942
66975
  "merge-base",
66943
66976
  targetRef,
@@ -70159,7 +70192,16 @@ var withSubscriptionLifetime = (client2, lifetime) => ({
70159
70192
  })
70160
70193
  });
70161
70194
  var sessionStatusKey = (status) => status.type === "retry" ? `${status.type}:${status.attempt}` : status.type;
70162
- var TOOL_TITLE_INPUT_KEYS = ["title", "filePath", "path", "command", "query", "url"];
70195
+ var TOOL_TITLE_INPUT_KEYS = ["title", "filePath", "command", "query", "pattern", "path", "url"];
70196
+ var TOOL_PROGRESS_ICONS = {
70197
+ bash: "\uD83D\uDD27",
70198
+ read: "\uD83D\uDCD6",
70199
+ glob: "\u2731",
70200
+ edit: "\u270F\uFE0F",
70201
+ grep: "\uD83D\uDD0E",
70202
+ list: "\uD83D\uDCC2",
70203
+ webfetch: "\uD83C\uDF10"
70204
+ };
70163
70205
  var titleFromToolInput = (input) => {
70164
70206
  for (const key of TOOL_TITLE_INPUT_KEYS) {
70165
70207
  const candidate = input[key];
@@ -70199,14 +70241,13 @@ var getToolProgressFields = (part) => {
70199
70241
  ...durationMs !== undefined ? { durationMs } : {}
70200
70242
  };
70201
70243
  };
70244
+ var getToolProgressIcon = (toolName) => TOOL_PROGRESS_ICONS[toolName] ?? "\u2699\uFE0F";
70202
70245
  var getToolProgressMessage = (phase, part, fields) => {
70203
70246
  const title = fields.title ? truncateForLog(String(fields.title), 160) : undefined;
70204
- const icon = part.tool === "bash" ? "\uD83D\uDD27" : part.tool === "read" ? "\uD83D\uDCD6" : part.tool === "edit" ? "\u270F\uFE0F" : part.tool === "grep" ? "\uD83D\uDD0E" : part.tool === "list" ? "\uD83D\uDCC2" : part.tool === "webfetch" ? "\uD83C\uDF10" : "\u2699\uFE0F";
70247
+ const icon = getToolProgressIcon(part.tool);
70205
70248
  switch (phase) {
70206
70249
  case "started":
70207
70250
  return title ? `${icon} ${title}` : `${icon} ${part.tool}`;
70208
- case "completed":
70209
- return title ? `${icon} done: ${title}` : `${icon} done`;
70210
70251
  case "failed":
70211
70252
  return title ? `\u26A0\uFE0F ${part.tool}: ${title}` : `\u26A0\uFE0F ${part.tool}`;
70212
70253
  }
@@ -70682,6 +70723,7 @@ var OpenCodeSdkRuntimeLive = exports_Layer.effect(OpenCodeSdkRuntime, makeOpenCo
70682
70723
 
70683
70724
  // ../../packages/core/src/opencode/Layers/OpenCodeRunner.ts
70684
70725
  var OPEN_CODE_CONFIG_SCHEMA = "https://opencode.ai/config.json";
70726
+ var OPEN_CODE_TYPESCRIPT_LSP_COMMAND = [process.execPath, "x", "typescript-language-server", "--stdio"];
70685
70727
  var openCodePermission = {
70686
70728
  edit: "deny",
70687
70729
  read: "allow",
@@ -70712,6 +70754,11 @@ var buildOpenCodeConfig = (agentName) => ({
70712
70754
  share: "disabled",
70713
70755
  autoupdate: false,
70714
70756
  default_agent: agentName,
70757
+ lsp: {
70758
+ typescript: {
70759
+ command: OPEN_CODE_TYPESCRIPT_LSP_COMMAND
70760
+ }
70761
+ },
70715
70762
  permission: openCodePermission,
70716
70763
  agent: {
70717
70764
  [agentName]: {
@@ -71645,7 +71692,8 @@ var buildReviewPrompt = (promptFile, reviewContext) => exports_Effect.gen(functi
71645
71692
  "If LSP access is available for the current file, use it selectively to validate symbol resolution, references, implementations, hover details, and diagnostics when that is faster or more reliable than manual file tracing.",
71646
71693
  "Treat LSP results as supporting evidence, not authority on their own; confirm findings against the changed code and nearby repository context before reporting them.",
71647
71694
  "Build an internal checklist containing every path in scoped changedFiles and review the files one by one until the checklist is exhausted.",
71648
- "For each scoped changed file, inspect the diff with `git diff <baseRef> <headRef> -- <path>` before deciding whether there is a finding.",
71695
+ "For each scoped changed file, inspect the diff with `git diff '<baseRef>' '<headRef>' -- '<path>'` before deciding whether there is a finding.",
71696
+ "Always shell-quote file paths and refs when you run read-only commands.",
71649
71697
  "Read nearby code and directly related files only when needed to validate behavior.",
71650
71698
  "Do not perform broad repository sweeps or unrelated searches.",
71651
71699
  "Use connected work items as supplemental product context only. Acceptance criteria, repro steps, and comments can explain intent, but they are not standalone evidence for a finding.",
@@ -71761,6 +71809,38 @@ var resolveReviewScope = ({
71761
71809
  return;
71762
71810
  }));
71763
71811
  if (previousCommitIsAncestor === true) {
71812
+ const followUpContainsTargetMerge = yield* hasTargetMergeCommitInRange({
71813
+ workspace: config.workspace,
71814
+ baseRef: previousReviewedCommit,
71815
+ headRef: reviewedSourceCommit,
71816
+ currentTargetRef: fullPullRequestDiff.baseRef
71817
+ }).pipe(exports_Effect.orElseSucceed(() => {
71818
+ return;
71819
+ }));
71820
+ if (followUpContainsTargetMerge === true) {
71821
+ yield* logInfo2("Follow-up range includes a merge from the target branch. Falling back to a full review to avoid reviewing target-only merge changes.", {
71822
+ previousReviewedCommit,
71823
+ reviewedSourceCommit,
71824
+ currentPullRequestBaseRef: fullPullRequestDiff.baseRef
71825
+ });
71826
+ return {
71827
+ reviewMode: "full",
71828
+ scopedDiff: fullPullRequestDiff,
71829
+ previousReviewedCommit
71830
+ };
71831
+ }
71832
+ if (followUpContainsTargetMerge === undefined) {
71833
+ yield* logInfo2("Could not validate whether the follow-up range includes a merge from the target branch. Falling back to a full review.", {
71834
+ previousReviewedCommit,
71835
+ reviewedSourceCommit,
71836
+ currentPullRequestBaseRef: fullPullRequestDiff.baseRef
71837
+ });
71838
+ return {
71839
+ reviewMode: "full",
71840
+ scopedDiff: fullPullRequestDiff,
71841
+ previousReviewedCommit
71842
+ };
71843
+ }
71764
71844
  return {
71765
71845
  reviewMode: "follow-up",
71766
71846
  scopedDiff: yield* resolveDiffRange({
@@ -73076,7 +73156,7 @@ var sandboxCaptureCommand = make34("capture", sandboxCaptureCommandConfig).pipe(
73076
73156
  var sandboxCommand = make34("sandbox").pipe(withDescription3("Sandbox tooling for live capture and local preview."), withSubcommands([sandboxCaptureCommand]));
73077
73157
  var openAzdoCli = make34("open-azdo").pipe(withDescription3("Secure Azure DevOps pull-request review CLI powered by OpenCode."), withSubcommands([reviewCommand, sandboxCommand]));
73078
73158
  // package.json
73079
- var version2 = "0.3.3";
73159
+ var version2 = "0.3.5";
73080
73160
 
73081
73161
  // src/Main.ts
73082
73162
  var cliProgram = run3(openAzdoCli, { version: version2 }).pipe(exports_Effect.scoped, exports_Effect.provide(BaseRuntimeLayer));
@@ -73085,4 +73165,4 @@ var main = () => runMain2(cliProgram, { disableErrorReporting: true });
73085
73165
  // bin/open-azdo.ts
73086
73166
  main();
73087
73167
 
73088
- //# debugId=19FF78D0D39C785464756E2164756E21
73168
+ //# debugId=BA409C50A45DF5C664756E2164756E21