@dugleelabs/copair 1.0.0 → 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/README.md +4 -4
- package/dist/index.js +188 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<a href="https://www.npmjs.com/package/copair"><img src="https://img.shields.io/npm/v/copair" alt="npm version"></a>
|
|
7
|
-
<a href="https://github.com/dugleelabs/copair/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/copair" alt="license"></a>
|
|
6
|
+
<a href="https://www.npmjs.com/package/@dugleelabs/copair"><img src="https://img.shields.io/npm/v/@dugleelabs/copair" alt="npm version"></a>
|
|
7
|
+
<a href="https://github.com/dugleelabs/copair/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@dugleelabs/copair" alt="license"></a>
|
|
8
8
|
<a href="https://github.com/dugleelabs/copair/actions"><img src="https://img.shields.io/github/actions/workflow/status/dugleelabs/copair/pull_request.yaml?branch=main&label=CI" alt="CI"></a>
|
|
9
|
-
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/copair" alt="node version"></a>
|
|
9
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/@dugleelabs/copair" alt="node version"></a>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
A model-agnostic AI coding agent for the terminal. Works like Claude Code but supports any LLM provider — commercial APIs, open source models, or self-hosted instances.
|
|
13
13
|
|
|
14
14
|
```
|
|
15
|
-
npm install -g copair
|
|
15
|
+
npm install -g @dugleelabs/copair
|
|
16
16
|
copair
|
|
17
17
|
```
|
|
18
18
|
|
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)
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
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",
|