@dugleelabs/copair 1.0.1 → 1.0.2

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.js CHANGED
@@ -545,33 +545,41 @@ Error: ${chunk.error}
545
545
  */
546
546
  showGitDiff(output) {
547
547
  if (!output.trim()) return;
548
- if (this.inkMode) return;
549
- const maxLines = 80;
550
- const lines = output.split("\n");
551
- const display = lines.slice(0, maxLines);
552
- process.stderr.write("\n");
553
- for (const line of display) {
554
- if (line.startsWith("+++") || line.startsWith("---")) {
555
- process.stderr.write(chalk3.bold.white(line) + "\n");
556
- } else if (line.startsWith("+")) {
557
- process.stderr.write(chalk3.bgGreen.black(line) + "\n");
558
- } else if (line.startsWith("-")) {
559
- process.stderr.write(chalk3.bgRedBright.black(line) + "\n");
560
- } else if (line.startsWith("@@")) {
561
- process.stderr.write(chalk3.cyan(line) + "\n");
562
- } else if (line.startsWith("diff ")) {
563
- process.stderr.write(chalk3.bold.yellow(line) + "\n");
564
- } else if (line.startsWith("index ")) {
565
- process.stderr.write(chalk3.gray(line) + "\n");
566
- } else {
567
- process.stderr.write(chalk3.gray(line) + "\n");
548
+ if (!this.inkMode) {
549
+ const maxLines = 80;
550
+ const lines = output.split("\n");
551
+ const display = lines.slice(0, maxLines);
552
+ process.stderr.write("\n");
553
+ for (const line of display) {
554
+ if (line.startsWith("+++") || line.startsWith("---")) {
555
+ process.stderr.write(chalk3.bold.white(line) + "\n");
556
+ } else if (line.startsWith("+")) {
557
+ process.stderr.write(chalk3.bgGreen.black(line) + "\n");
558
+ } else if (line.startsWith("-")) {
559
+ process.stderr.write(chalk3.bgRedBright.black(line) + "\n");
560
+ } else if (line.startsWith("@@")) {
561
+ process.stderr.write(chalk3.cyan(line) + "\n");
562
+ } else if (line.startsWith("diff ")) {
563
+ process.stderr.write(chalk3.bold.yellow(line) + "\n");
564
+ } else if (line.startsWith("index ")) {
565
+ process.stderr.write(chalk3.gray(line) + "\n");
566
+ } else {
567
+ process.stderr.write(chalk3.gray(line) + "\n");
568
+ }
568
569
  }
569
- }
570
- if (lines.length > maxLines) {
571
- process.stderr.write(chalk3.gray(` ... ${lines.length - maxLines} more lines
570
+ if (lines.length > maxLines) {
571
+ process.stderr.write(chalk3.gray(` ... ${lines.length - maxLines} more lines
572
572
  `));
573
+ }
574
+ process.stderr.write("\n");
575
+ }
576
+ if (this.bridge) {
577
+ const lines = output.split("\n");
578
+ this.bridge.emit("diff", {
579
+ filePath: extractDiffFilePath(lines),
580
+ hunks: [{ oldStart: 0, newStart: 0, lines }]
581
+ });
573
582
  }
574
- process.stderr.write("\n");
575
583
  }
576
584
  /**
577
585
  * Show a diff snippet for file mutations (write/edit).
@@ -694,6 +702,15 @@ function formatDuration(ms) {
694
702
  if (ms < 1e3) return `${Math.round(ms)}ms`;
695
703
  return `${(ms / 1e3).toFixed(1)}s`;
696
704
  }
705
+ function extractDiffFilePath(lines) {
706
+ for (const line of lines) {
707
+ if (line.startsWith("diff --git")) {
708
+ const match = line.match(/b\/(.+)$/);
709
+ if (match) return match[1];
710
+ }
711
+ }
712
+ return "git diff";
713
+ }
697
714
 
698
715
  // src/core/formats/fenced-block.ts
699
716
  function tryParseToolCall(json) {
@@ -4656,7 +4673,7 @@ function ApprovalHandler({ bridge }) {
4656
4673
  }
4657
4674
 
4658
4675
  // src/cli/ui/app.tsx
4659
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
4676
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
4660
4677
  var DEFAULT_UI_CONFIG = {
4661
4678
  bordered_input: true,
4662
4679
  status_bar: true,
@@ -4689,6 +4706,139 @@ function useSpinner(active) {
4689
4706
  const elapsedStr = secs < 60 ? `${secs}s` : `${Math.floor(secs / 60)}m ${String(secs % 60).padStart(2, "0")}s`;
4690
4707
  return { frame: SPINNER_FRAMES[frameIdx], elapsed: elapsedStr };
4691
4708
  }
4709
+ function renderInline(text) {
4710
+ const parts = [];
4711
+ let remaining = text;
4712
+ let key = 0;
4713
+ while (remaining.length > 0) {
4714
+ const boldMatch = remaining.match(/^\*\*(.+?)\*\*/);
4715
+ if (boldMatch) {
4716
+ parts.push(/* @__PURE__ */ jsx6(Text6, { bold: true, children: boldMatch[1] }, key++));
4717
+ remaining = remaining.slice(boldMatch[0].length);
4718
+ continue;
4719
+ }
4720
+ const italicMatch = remaining.match(/^\*(.+?)\*/);
4721
+ if (italicMatch) {
4722
+ parts.push(/* @__PURE__ */ jsx6(Text6, { italic: true, children: italicMatch[1] }, key++));
4723
+ remaining = remaining.slice(italicMatch[0].length);
4724
+ continue;
4725
+ }
4726
+ const codeMatch = remaining.match(/^`([^`]+)`/);
4727
+ if (codeMatch) {
4728
+ parts.push(/* @__PURE__ */ jsx6(Text6, { color: "cyan", bold: true, children: codeMatch[1] }, key++));
4729
+ remaining = remaining.slice(codeMatch[0].length);
4730
+ continue;
4731
+ }
4732
+ const nextSpecial = remaining.search(/[*`]/);
4733
+ if (nextSpecial === -1) {
4734
+ parts.push(remaining);
4735
+ break;
4736
+ }
4737
+ if (nextSpecial === 0) {
4738
+ parts.push(remaining[0]);
4739
+ remaining = remaining.slice(1);
4740
+ } else {
4741
+ parts.push(remaining.slice(0, nextSpecial));
4742
+ remaining = remaining.slice(nextSpecial);
4743
+ }
4744
+ }
4745
+ return parts.length === 1 ? parts[0] : /* @__PURE__ */ jsx6(Fragment2, { children: parts });
4746
+ }
4747
+ function renderMarkdownBlocks(text) {
4748
+ const lines = text.split("\n");
4749
+ const elements = [];
4750
+ let key = 0;
4751
+ let i = 0;
4752
+ while (i < lines.length) {
4753
+ const line = lines[i];
4754
+ const trimmed = line.trim();
4755
+ if (trimmed.startsWith("```")) {
4756
+ const lang = trimmed.slice(3).trim();
4757
+ const codeLines = [];
4758
+ i++;
4759
+ while (i < lines.length && !lines[i].trim().startsWith("```")) {
4760
+ codeLines.push(lines[i]);
4761
+ i++;
4762
+ }
4763
+ if (i < lines.length) i++;
4764
+ elements.push(
4765
+ /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginY: 1, children: [
4766
+ lang && /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: lang }),
4767
+ /* @__PURE__ */ jsx6(Box6, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "column", children: codeLines.map((cl, ci) => /* @__PURE__ */ jsx6(Text6, { color: "white", children: cl }, ci)) })
4768
+ ] }, key++)
4769
+ );
4770
+ continue;
4771
+ }
4772
+ const headerMatch = trimmed.match(/^(#{1,6})\s+(.+)/);
4773
+ if (headerMatch) {
4774
+ const level = headerMatch[1].length;
4775
+ const content = headerMatch[2];
4776
+ elements.push(
4777
+ /* @__PURE__ */ jsxs6(Text6, { bold: true, color: level <= 2 ? "white" : void 0, children: [
4778
+ level <= 2 ? "\n" : "",
4779
+ content
4780
+ ] }, key++)
4781
+ );
4782
+ i++;
4783
+ continue;
4784
+ }
4785
+ if (/^[-*_]{3,}$/.test(trimmed)) {
4786
+ elements.push(
4787
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2500".repeat(40) }, key++)
4788
+ );
4789
+ i++;
4790
+ continue;
4791
+ }
4792
+ const ulMatch = trimmed.match(/^[-*+]\s+(.*)/);
4793
+ if (ulMatch) {
4794
+ elements.push(
4795
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "wrap", children: [
4796
+ " ",
4797
+ "\u2022",
4798
+ " ",
4799
+ renderInline(ulMatch[1])
4800
+ ] }, key++)
4801
+ );
4802
+ i++;
4803
+ continue;
4804
+ }
4805
+ const olMatch = trimmed.match(/^(\d+)[.)]\s+(.*)/);
4806
+ if (olMatch) {
4807
+ elements.push(
4808
+ /* @__PURE__ */ jsxs6(Text6, { wrap: "wrap", children: [
4809
+ " ",
4810
+ olMatch[1],
4811
+ ". ",
4812
+ renderInline(olMatch[2])
4813
+ ] }, key++)
4814
+ );
4815
+ i++;
4816
+ continue;
4817
+ }
4818
+ if (trimmed.startsWith(">")) {
4819
+ const content = trimmed.replace(/^>\s?/, "");
4820
+ elements.push(
4821
+ /* @__PURE__ */ jsxs6(Text6, { dimColor: true, wrap: "wrap", children: [
4822
+ " ",
4823
+ "\u2502",
4824
+ " ",
4825
+ renderInline(content)
4826
+ ] }, key++)
4827
+ );
4828
+ i++;
4829
+ continue;
4830
+ }
4831
+ if (trimmed === "") {
4832
+ i++;
4833
+ continue;
4834
+ }
4835
+ elements.push(
4836
+ /* @__PURE__ */ jsx6(Text6, { wrap: "wrap", children: renderInline(line) }, key++)
4837
+ );
4838
+ i++;
4839
+ }
4840
+ return elements;
4841
+ }
4692
4842
  var CopairApp = forwardRef(function CopairApp2({
4693
4843
  bridge,
4694
4844
  model,
@@ -4786,6 +4936,12 @@ var CopairApp = forwardRef(function CopairApp2({
4786
4936
  { id: nextId.current++, type: "error", content: `\u2717 ${tool.label} denied` }
4787
4937
  ]);
4788
4938
  };
4939
+ const onDiff = (diff) => {
4940
+ setStaticItems((items) => [
4941
+ ...items,
4942
+ { id: nextId.current++, type: "diff", content: "", diff }
4943
+ ]);
4944
+ };
4789
4945
  const onError = (message) => {
4790
4946
  setStaticItems((items) => [
4791
4947
  ...items,
@@ -4816,6 +4972,7 @@ var CopairApp = forwardRef(function CopairApp2({
4816
4972
  bridge.on("tool-start", onToolStart);
4817
4973
  bridge.on("tool-complete", onToolComplete);
4818
4974
  bridge.on("tool-denied", onToolDenied);
4975
+ bridge.on("diff", onDiff);
4819
4976
  bridge.on("error", onError);
4820
4977
  bridge.on("usage", onUsage);
4821
4978
  bridge.on("turn-complete", onTurnComplete);
@@ -4825,6 +4982,7 @@ var CopairApp = forwardRef(function CopairApp2({
4825
4982
  bridge.off("tool-start", onToolStart);
4826
4983
  bridge.off("tool-complete", onToolComplete);
4827
4984
  bridge.off("tool-denied", onToolDenied);
4985
+ bridge.off("diff", onDiff);
4828
4986
  bridge.off("error", onError);
4829
4987
  bridge.off("usage", onUsage);
4830
4988
  bridge.off("turn-complete", onTurnComplete);
@@ -4860,9 +5018,11 @@ var CopairApp = forwardRef(function CopairApp2({
4860
5018
  " ",
4861
5019
  item.content
4862
5020
  ] }, item.id);
5021
+ case "diff":
5022
+ return item.diff ? /* @__PURE__ */ jsx6(DiffView, { diff: item.diff }, item.id) : null;
4863
5023
  case "text":
4864
5024
  default:
4865
- return /* @__PURE__ */ jsx6(Text6, { wrap: "wrap", children: item.content }, item.id);
5025
+ return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", children: renderMarkdownBlocks(item.content) }, item.id);
4866
5026
  }
4867
5027
  } }),
4868
5028
  state.phase === "thinking" && /* @__PURE__ */ jsxs6(Text6, { children: [
@@ -4874,7 +5034,7 @@ var CopairApp = forwardRef(function CopairApp2({
4874
5034
  /* @__PURE__ */ jsx6(Text6, { color: "gray", children: spinner.elapsed })
4875
5035
  ] })
4876
5036
  ] }),
4877
- liveText && /* @__PURE__ */ jsx6(Text6, { wrap: "wrap", children: liveText }),
5037
+ liveText && /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", children: renderMarkdownBlocks(liveText) }),
4878
5038
  liveTool && /* @__PURE__ */ jsxs6(Text6, { color: "green", children: [
4879
5039
  " ",
4880
5040
  "\u25CF",
@@ -5084,7 +5244,7 @@ import chalk6 from "chalk";
5084
5244
  // package.json
5085
5245
  var package_default = {
5086
5246
  name: "@dugleelabs/copair",
5087
- version: "1.0.1",
5247
+ version: "1.0.2",
5088
5248
  description: "Model-agnostic AI coding agent for the terminal",
5089
5249
  type: "module",
5090
5250
  main: "dist/index.js",