@gemdoq/codi 0.1.4 → 0.1.6
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/cli.js +162 -63
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -778,6 +778,19 @@ function completer(line) {
|
|
|
778
778
|
}
|
|
779
779
|
|
|
780
780
|
// src/repl.ts
|
|
781
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
782
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
783
|
+
import * as path5 from "path";
|
|
784
|
+
var __filename2 = fileURLToPath2(import.meta.url);
|
|
785
|
+
var __dirname2 = path5.dirname(__filename2);
|
|
786
|
+
function getVersion() {
|
|
787
|
+
try {
|
|
788
|
+
const pkg = JSON.parse(readFileSync3(path5.join(__dirname2, "..", "package.json"), "utf-8"));
|
|
789
|
+
return `v${pkg.version}`;
|
|
790
|
+
} catch {
|
|
791
|
+
return "v0.1.4";
|
|
792
|
+
}
|
|
793
|
+
}
|
|
781
794
|
var Repl = class {
|
|
782
795
|
rl = null;
|
|
783
796
|
keyBindings = new KeyBindingManager();
|
|
@@ -810,9 +823,25 @@ var Repl = class {
|
|
|
810
823
|
completer: (line) => completer(line),
|
|
811
824
|
terminal: true
|
|
812
825
|
});
|
|
813
|
-
if (process.stdin.isTTY) {
|
|
826
|
+
if (process.stdin.isTTY && os2.platform() !== "win32") {
|
|
814
827
|
process.stdout.write("\x1B[?2004h");
|
|
815
828
|
}
|
|
829
|
+
if (os2.platform() === "win32" && process.stdin.isTTY) {
|
|
830
|
+
process.stdin.on("keypress", (_str, key) => {
|
|
831
|
+
if (key && key.sequence === "") {
|
|
832
|
+
try {
|
|
833
|
+
const clip = execSync("powershell -command Get-Clipboard", {
|
|
834
|
+
encoding: "utf-8",
|
|
835
|
+
timeout: 3e3
|
|
836
|
+
}).replace(/\r\n$/, "");
|
|
837
|
+
if (clip && this.rl) {
|
|
838
|
+
this.rl.write(clip);
|
|
839
|
+
}
|
|
840
|
+
} catch {
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
}
|
|
816
845
|
this.printWelcome();
|
|
817
846
|
while (this.running) {
|
|
818
847
|
try {
|
|
@@ -877,7 +906,7 @@ var Repl = class {
|
|
|
877
906
|
}
|
|
878
907
|
}
|
|
879
908
|
}
|
|
880
|
-
if (process.stdin.isTTY) {
|
|
909
|
+
if (process.stdin.isTTY && os2.platform() !== "win32") {
|
|
881
910
|
process.stdout.write("\x1B[?2004l");
|
|
882
911
|
}
|
|
883
912
|
}
|
|
@@ -916,19 +945,38 @@ var Repl = class {
|
|
|
916
945
|
return;
|
|
917
946
|
}
|
|
918
947
|
let message = input3;
|
|
919
|
-
const
|
|
948
|
+
const IMAGE_EXTS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".svg"]);
|
|
949
|
+
const atMatches = input3.match(/@([\w.\/\\:~-]+)/g);
|
|
920
950
|
if (atMatches) {
|
|
921
951
|
for (const match of atMatches) {
|
|
922
952
|
const filePath = match.slice(1);
|
|
923
953
|
try {
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
|
|
954
|
+
const ext = path5.extname(filePath).toLowerCase();
|
|
955
|
+
if (IMAGE_EXTS.has(ext)) {
|
|
956
|
+
const data = readFileSync3(filePath);
|
|
957
|
+
const base64 = data.toString("base64");
|
|
958
|
+
const mimeMap = {
|
|
959
|
+
".png": "image/png",
|
|
960
|
+
".jpg": "image/jpeg",
|
|
961
|
+
".jpeg": "image/jpeg",
|
|
962
|
+
".gif": "image/gif",
|
|
963
|
+
".webp": "image/webp",
|
|
964
|
+
".bmp": "image/bmp",
|
|
965
|
+
".svg": "image/svg+xml"
|
|
966
|
+
};
|
|
967
|
+
const mime = mimeMap[ext] || "image/png";
|
|
968
|
+
message = message.replace(match, `
|
|
969
|
+
[Image: ${filePath}](data:${mime};base64,${base64})
|
|
970
|
+
`);
|
|
971
|
+
} else {
|
|
972
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
973
|
+
message = message.replace(match, `
|
|
927
974
|
[File: ${filePath}]
|
|
928
975
|
\`\`\`
|
|
929
976
|
${content}
|
|
930
977
|
\`\`\`
|
|
931
978
|
`);
|
|
979
|
+
}
|
|
932
980
|
} catch {
|
|
933
981
|
}
|
|
934
982
|
}
|
|
@@ -961,7 +1009,8 @@ ${content}
|
|
|
961
1009
|
printWelcome() {
|
|
962
1010
|
console.log("");
|
|
963
1011
|
console.log(chalk4.cyan.bold(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
|
|
964
|
-
|
|
1012
|
+
const versionPad = ` Codi (\uCF54\uB514) ${getVersion()}`.padEnd(29);
|
|
1013
|
+
console.log(chalk4.cyan.bold(" \u2502") + chalk4.white.bold(versionPad) + chalk4.cyan.bold("\u2502"));
|
|
965
1014
|
console.log(chalk4.cyan.bold(" \u2502") + chalk4.dim(" AI Code Agent for Terminal ") + chalk4.cyan.bold("\u2502"));
|
|
966
1015
|
console.log(chalk4.cyan.bold(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
967
1016
|
console.log("");
|
|
@@ -1456,7 +1505,56 @@ function sleep(ms) {
|
|
|
1456
1505
|
init_esm_shims();
|
|
1457
1506
|
import * as os3 from "os";
|
|
1458
1507
|
import { execSync as execSync2 } from "child_process";
|
|
1459
|
-
var ROLE_DEFINITION = `You are Codi (\uCF54\uB514), a terminal-based AI coding agent. You help users with software engineering tasks including writing code, debugging, refactoring, and explaining code. You have access to tools for file manipulation, code search, shell execution, and more
|
|
1508
|
+
var ROLE_DEFINITION = `You are Codi (\uCF54\uB514), a terminal-based AI coding agent. You help users with software engineering tasks including writing code, debugging, refactoring, and explaining code. You have access to tools for file manipulation, code search, shell execution, and more.
|
|
1509
|
+
|
|
1510
|
+
# How Users Interact with You
|
|
1511
|
+
- Users type natural language messages to you. They do NOT type tool calls directly.
|
|
1512
|
+
- Tools (read_file, bash, grep, etc.) are for YOU to use internally. NEVER tell users to type tool calls like "read_file(path)" or "bash(command)".
|
|
1513
|
+
- When users ask "how should I do X?" or "what should I type?", give them natural language prompts they can type to you, NOT tool call syntax.
|
|
1514
|
+
- When users ask a QUESTION about how to do something, ANSWER with an explanation. Do NOT immediately execute actions.
|
|
1515
|
+
- Only execute actions when the user clearly REQUESTS you to do something (e.g., "clone this repo", "analyze this code", "fix this bug").
|
|
1516
|
+
|
|
1517
|
+
# Codi CLI Features (you must know these)
|
|
1518
|
+
Users can start Codi with these command-line options:
|
|
1519
|
+
- codi --yolo : Skip ALL permission checks (like Claude Code's --dangerously-skip-permissions)
|
|
1520
|
+
- codi --plan : Start in read-only plan mode (analysis only, no changes)
|
|
1521
|
+
- codi -p "prompt" : Run a single prompt and exit
|
|
1522
|
+
- codi -c / --continue : Continue the last session
|
|
1523
|
+
- codi -r <id> / --resume <id> : Resume a specific session
|
|
1524
|
+
- codi -m <model> : Switch to a different model
|
|
1525
|
+
- codi --provider <name> : Switch provider (openai, anthropic, ollama)
|
|
1526
|
+
|
|
1527
|
+
# Slash Commands (available inside Codi)
|
|
1528
|
+
Users can type these commands while using Codi:
|
|
1529
|
+
- /help : Show all available commands
|
|
1530
|
+
- /quit or /exit : Exit Codi
|
|
1531
|
+
- /clear : Clear conversation history
|
|
1532
|
+
- /model <name> : Switch model (e.g., /model gpt-4o)
|
|
1533
|
+
- /compact : Compress conversation to save context
|
|
1534
|
+
- /cost : Show token usage and cost
|
|
1535
|
+
- /plan : Toggle plan mode (read-only analysis)
|
|
1536
|
+
- /commit : Generate commit message from git diff and commit
|
|
1537
|
+
- /review : AI code review of current changes
|
|
1538
|
+
- /fix <command> : Run command, auto-fix if it fails
|
|
1539
|
+
- /search <keyword> : Search past sessions
|
|
1540
|
+
- /save : Save current session
|
|
1541
|
+
- /resume : Resume a saved session
|
|
1542
|
+
- /memory : Show auto memory
|
|
1543
|
+
- /tasks : Show task list
|
|
1544
|
+
- /context : Show context window usage
|
|
1545
|
+
- /rewind : Undo to previous checkpoint
|
|
1546
|
+
- /diff : Show git diff
|
|
1547
|
+
|
|
1548
|
+
# Input Prefixes
|
|
1549
|
+
- ! command : Execute a shell command directly (e.g., ! git status)
|
|
1550
|
+
- @file.ts : Attach file content to your message
|
|
1551
|
+
- \\ at end of line : Continue typing on next line (multiline input)`;
|
|
1552
|
+
var CONVERSATION_RULES = `# Conversation Rules
|
|
1553
|
+
- When a user asks "how do I..." or "what should I type...", give a clear EXPLANATION with example prompts they can type.
|
|
1554
|
+
- Do NOT execute commands or use tools when the user is just asking for information.
|
|
1555
|
+
- When giving examples of what to type, show them as natural language, e.g.: "You can type: \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uAD6C\uC870\uB97C \uBD84\uC11D\uD574\uC918"
|
|
1556
|
+
- Only use tools when the user explicitly requests an action.
|
|
1557
|
+
- If the user's intent is ambiguous, ASK for clarification before acting.`;
|
|
1460
1558
|
var TOOL_HIERARCHY = `# Tool Usage Rules
|
|
1461
1559
|
- Use read_file instead of bash cat/head/tail
|
|
1462
1560
|
- Use edit_file instead of bash sed/awk
|
|
@@ -1515,6 +1613,7 @@ function buildSystemPrompt(context) {
|
|
|
1515
1613
|
const fragments = [];
|
|
1516
1614
|
fragments.push(ROLE_DEFINITION);
|
|
1517
1615
|
fragments.push(buildEnvironmentInfo(context));
|
|
1616
|
+
fragments.push(CONVERSATION_RULES);
|
|
1518
1617
|
fragments.push(TOOL_HIERARCHY);
|
|
1519
1618
|
if (os3.platform() === "win32") {
|
|
1520
1619
|
fragments.push(WINDOWS_RULES);
|
|
@@ -1619,15 +1718,15 @@ ${summaryContent}`;
|
|
|
1619
1718
|
// src/agent/memory.ts
|
|
1620
1719
|
init_esm_shims();
|
|
1621
1720
|
import * as fs4 from "fs";
|
|
1622
|
-
import * as
|
|
1721
|
+
import * as path6 from "path";
|
|
1623
1722
|
import * as crypto from "crypto";
|
|
1624
1723
|
var MemoryManager = class {
|
|
1625
1724
|
memoryDir;
|
|
1626
1725
|
constructor() {
|
|
1627
1726
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
1628
1727
|
const projectHash = crypto.createHash("md5").update(process.cwd()).digest("hex").slice(0, 8);
|
|
1629
|
-
const projectName =
|
|
1630
|
-
this.memoryDir =
|
|
1728
|
+
const projectName = path6.basename(process.cwd());
|
|
1729
|
+
this.memoryDir = path6.join(home, ".codi", "projects", `${projectName}-${projectHash}`, "memory");
|
|
1631
1730
|
}
|
|
1632
1731
|
ensureDir() {
|
|
1633
1732
|
if (!fs4.existsSync(this.memoryDir)) {
|
|
@@ -1638,7 +1737,7 @@ var MemoryManager = class {
|
|
|
1638
1737
|
return this.memoryDir;
|
|
1639
1738
|
}
|
|
1640
1739
|
loadIndex() {
|
|
1641
|
-
const indexPath =
|
|
1740
|
+
const indexPath = path6.join(this.memoryDir, "MEMORY.md");
|
|
1642
1741
|
if (!fs4.existsSync(indexPath)) return "";
|
|
1643
1742
|
const content = fs4.readFileSync(indexPath, "utf-8");
|
|
1644
1743
|
const lines = content.split("\n");
|
|
@@ -1646,17 +1745,17 @@ var MemoryManager = class {
|
|
|
1646
1745
|
}
|
|
1647
1746
|
saveIndex(content) {
|
|
1648
1747
|
this.ensureDir();
|
|
1649
|
-
const indexPath =
|
|
1748
|
+
const indexPath = path6.join(this.memoryDir, "MEMORY.md");
|
|
1650
1749
|
fs4.writeFileSync(indexPath, content, "utf-8");
|
|
1651
1750
|
}
|
|
1652
1751
|
loadTopic(name) {
|
|
1653
|
-
const topicPath =
|
|
1752
|
+
const topicPath = path6.join(this.memoryDir, `${name}.md`);
|
|
1654
1753
|
if (!fs4.existsSync(topicPath)) return null;
|
|
1655
1754
|
return fs4.readFileSync(topicPath, "utf-8");
|
|
1656
1755
|
}
|
|
1657
1756
|
saveTopic(name, content) {
|
|
1658
1757
|
this.ensureDir();
|
|
1659
|
-
const topicPath =
|
|
1758
|
+
const topicPath = path6.join(this.memoryDir, `${name}.md`);
|
|
1660
1759
|
fs4.writeFileSync(topicPath, content, "utf-8");
|
|
1661
1760
|
}
|
|
1662
1761
|
listTopics() {
|
|
@@ -1681,13 +1780,13 @@ var memoryManager = new MemoryManager();
|
|
|
1681
1780
|
// src/agent/session.ts
|
|
1682
1781
|
init_esm_shims();
|
|
1683
1782
|
import * as fs5 from "fs";
|
|
1684
|
-
import * as
|
|
1783
|
+
import * as path7 from "path";
|
|
1685
1784
|
import * as crypto2 from "crypto";
|
|
1686
1785
|
var SessionManager = class {
|
|
1687
1786
|
sessionsDir;
|
|
1688
1787
|
constructor() {
|
|
1689
1788
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
1690
|
-
this.sessionsDir =
|
|
1789
|
+
this.sessionsDir = path7.join(home, ".codi", "sessions");
|
|
1691
1790
|
}
|
|
1692
1791
|
ensureDir() {
|
|
1693
1792
|
if (!fs5.existsSync(this.sessionsDir)) {
|
|
@@ -1697,7 +1796,7 @@ var SessionManager = class {
|
|
|
1697
1796
|
save(conversation, name, model) {
|
|
1698
1797
|
this.ensureDir();
|
|
1699
1798
|
const id = name || crypto2.randomUUID().slice(0, 8);
|
|
1700
|
-
const filePath =
|
|
1799
|
+
const filePath = path7.join(this.sessionsDir, `${id}.jsonl`);
|
|
1701
1800
|
const data = conversation.serialize();
|
|
1702
1801
|
const meta = {
|
|
1703
1802
|
id,
|
|
@@ -1717,7 +1816,7 @@ var SessionManager = class {
|
|
|
1717
1816
|
return id;
|
|
1718
1817
|
}
|
|
1719
1818
|
load(id) {
|
|
1720
|
-
const filePath =
|
|
1819
|
+
const filePath = path7.join(this.sessionsDir, `${id}.jsonl`);
|
|
1721
1820
|
if (!fs5.existsSync(filePath)) return null;
|
|
1722
1821
|
const content = fs5.readFileSync(filePath, "utf-8");
|
|
1723
1822
|
const lines = content.trim().split("\n").filter(Boolean);
|
|
@@ -1747,7 +1846,7 @@ var SessionManager = class {
|
|
|
1747
1846
|
const files = fs5.readdirSync(this.sessionsDir).filter((f) => f.endsWith(".jsonl"));
|
|
1748
1847
|
const sessions = [];
|
|
1749
1848
|
for (const file of files) {
|
|
1750
|
-
const filePath =
|
|
1849
|
+
const filePath = path7.join(this.sessionsDir, file);
|
|
1751
1850
|
try {
|
|
1752
1851
|
const firstLine = fs5.readFileSync(filePath, "utf-8").split("\n")[0];
|
|
1753
1852
|
if (firstLine) {
|
|
@@ -1767,7 +1866,7 @@ var SessionManager = class {
|
|
|
1767
1866
|
return sessions[0] ?? null;
|
|
1768
1867
|
}
|
|
1769
1868
|
delete(id) {
|
|
1770
|
-
const filePath =
|
|
1869
|
+
const filePath = path7.join(this.sessionsDir, `${id}.jsonl`);
|
|
1771
1870
|
if (fs5.existsSync(filePath)) {
|
|
1772
1871
|
fs5.unlinkSync(filePath);
|
|
1773
1872
|
return true;
|
|
@@ -1872,21 +1971,21 @@ var checkpointManager = new CheckpointManager();
|
|
|
1872
1971
|
// src/agent/codi-md.ts
|
|
1873
1972
|
init_esm_shims();
|
|
1874
1973
|
import * as fs6 from "fs";
|
|
1875
|
-
import * as
|
|
1974
|
+
import * as path8 from "path";
|
|
1876
1975
|
function loadCodiMd() {
|
|
1877
1976
|
const fragments = [];
|
|
1878
1977
|
let dir = process.cwd();
|
|
1879
|
-
const root =
|
|
1978
|
+
const root = path8.parse(dir).root;
|
|
1880
1979
|
while (dir !== root) {
|
|
1881
1980
|
loadFromDir(dir, fragments);
|
|
1882
|
-
const parent =
|
|
1981
|
+
const parent = path8.dirname(dir);
|
|
1883
1982
|
if (parent === dir) break;
|
|
1884
1983
|
dir = parent;
|
|
1885
1984
|
}
|
|
1886
1985
|
return fragments.join("\n\n---\n\n");
|
|
1887
1986
|
}
|
|
1888
1987
|
function loadFromDir(dir, fragments) {
|
|
1889
|
-
const codiPath =
|
|
1988
|
+
const codiPath = path8.join(dir, "CODI.md");
|
|
1890
1989
|
if (fs6.existsSync(codiPath)) {
|
|
1891
1990
|
try {
|
|
1892
1991
|
let content = fs6.readFileSync(codiPath, "utf-8");
|
|
@@ -1896,7 +1995,7 @@ ${content}`);
|
|
|
1896
1995
|
} catch {
|
|
1897
1996
|
}
|
|
1898
1997
|
}
|
|
1899
|
-
const localPath =
|
|
1998
|
+
const localPath = path8.join(dir, "CODI.local.md");
|
|
1900
1999
|
if (fs6.existsSync(localPath)) {
|
|
1901
2000
|
try {
|
|
1902
2001
|
const content = fs6.readFileSync(localPath, "utf-8");
|
|
@@ -1905,12 +2004,12 @@ ${content}`);
|
|
|
1905
2004
|
} catch {
|
|
1906
2005
|
}
|
|
1907
2006
|
}
|
|
1908
|
-
const rulesDir =
|
|
2007
|
+
const rulesDir = path8.join(dir, ".codi", "rules");
|
|
1909
2008
|
if (fs6.existsSync(rulesDir)) {
|
|
1910
2009
|
try {
|
|
1911
2010
|
const files = fs6.readdirSync(rulesDir).filter((f) => f.endsWith(".md")).sort();
|
|
1912
2011
|
for (const file of files) {
|
|
1913
|
-
const content = fs6.readFileSync(
|
|
2012
|
+
const content = fs6.readFileSync(path8.join(rulesDir, file), "utf-8");
|
|
1914
2013
|
fragments.push(`[Rule: ${file}]
|
|
1915
2014
|
${content}`);
|
|
1916
2015
|
}
|
|
@@ -1920,7 +2019,7 @@ ${content}`);
|
|
|
1920
2019
|
}
|
|
1921
2020
|
function processImports(content, baseDir) {
|
|
1922
2021
|
return content.replace(/@([\w./-]+)/g, (match, importPath) => {
|
|
1923
|
-
const resolved =
|
|
2022
|
+
const resolved = path8.resolve(baseDir, importPath);
|
|
1924
2023
|
if (fs6.existsSync(resolved)) {
|
|
1925
2024
|
try {
|
|
1926
2025
|
return fs6.readFileSync(resolved, "utf-8");
|
|
@@ -2189,7 +2288,7 @@ var hookManager = new HookManager();
|
|
|
2189
2288
|
init_esm_shims();
|
|
2190
2289
|
init_tool();
|
|
2191
2290
|
import * as fs7 from "fs";
|
|
2192
|
-
import * as
|
|
2291
|
+
import * as path9 from "path";
|
|
2193
2292
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2194
2293
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
2195
2294
|
import chalk9 from "chalk";
|
|
@@ -2211,8 +2310,8 @@ var McpManager = class {
|
|
|
2211
2310
|
const configs = {};
|
|
2212
2311
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
2213
2312
|
const paths = [
|
|
2214
|
-
|
|
2215
|
-
|
|
2313
|
+
path9.join(home, ".codi", "mcp.json"),
|
|
2314
|
+
path9.join(process.cwd(), ".codi", "mcp.json")
|
|
2216
2315
|
];
|
|
2217
2316
|
for (const p of paths) {
|
|
2218
2317
|
try {
|
|
@@ -2414,7 +2513,7 @@ var subAgentTool = {
|
|
|
2414
2513
|
init_esm_shims();
|
|
2415
2514
|
import * as fs8 from "fs";
|
|
2416
2515
|
import * as os5 from "os";
|
|
2417
|
-
import * as
|
|
2516
|
+
import * as path10 from "path";
|
|
2418
2517
|
import chalk11 from "chalk";
|
|
2419
2518
|
function createBuiltinCommands() {
|
|
2420
2519
|
return [
|
|
@@ -2608,12 +2707,12 @@ Topics: ${topics.join(", ")}`));
|
|
|
2608
2707
|
name: "/init",
|
|
2609
2708
|
description: "Initialize CODI.md in the current project",
|
|
2610
2709
|
handler: async () => {
|
|
2611
|
-
const codiPath =
|
|
2710
|
+
const codiPath = path10.join(process.cwd(), "CODI.md");
|
|
2612
2711
|
if (fs8.existsSync(codiPath)) {
|
|
2613
2712
|
console.log(chalk11.yellow("CODI.md already exists"));
|
|
2614
2713
|
return true;
|
|
2615
2714
|
}
|
|
2616
|
-
const content = `# Project: ${
|
|
2715
|
+
const content = `# Project: ${path10.basename(process.cwd())}
|
|
2617
2716
|
|
|
2618
2717
|
## Overview
|
|
2619
2718
|
<!-- Describe your project here -->
|
|
@@ -2825,7 +2924,7 @@ ${diff}
|
|
|
2825
2924
|
return true;
|
|
2826
2925
|
}
|
|
2827
2926
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
2828
|
-
const sessionsDir =
|
|
2927
|
+
const sessionsDir = path10.join(home, ".codi", "sessions");
|
|
2829
2928
|
if (!fs8.existsSync(sessionsDir)) {
|
|
2830
2929
|
console.log(chalk11.dim("\nNo sessions found.\n"));
|
|
2831
2930
|
return true;
|
|
@@ -2835,7 +2934,7 @@ ${diff}
|
|
|
2835
2934
|
const keyword = args.toLowerCase();
|
|
2836
2935
|
for (const file of files) {
|
|
2837
2936
|
if (results.length >= 10) break;
|
|
2838
|
-
const filePath =
|
|
2937
|
+
const filePath = path10.join(sessionsDir, file);
|
|
2839
2938
|
const lines = fs8.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
|
|
2840
2939
|
for (const line of lines) {
|
|
2841
2940
|
if (results.length >= 10) break;
|
|
@@ -2929,18 +3028,18 @@ function loadCustomCommands() {
|
|
|
2929
3028
|
const commands = [];
|
|
2930
3029
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
2931
3030
|
const dirs = [
|
|
2932
|
-
|
|
2933
|
-
|
|
3031
|
+
path10.join(home, ".codi", "commands"),
|
|
3032
|
+
path10.join(process.cwd(), ".codi", "commands")
|
|
2934
3033
|
];
|
|
2935
3034
|
for (const dir of dirs) {
|
|
2936
3035
|
if (!fs8.existsSync(dir)) continue;
|
|
2937
3036
|
const files = fs8.readdirSync(dir).filter((f) => f.endsWith(".md"));
|
|
2938
3037
|
for (const file of files) {
|
|
2939
3038
|
const name = "/" + file.replace(".md", "");
|
|
2940
|
-
const filePath =
|
|
3039
|
+
const filePath = path10.join(dir, file);
|
|
2941
3040
|
commands.push({
|
|
2942
3041
|
name,
|
|
2943
|
-
description: `Custom command from ${
|
|
3042
|
+
description: `Custom command from ${path10.relative(process.cwd(), filePath)}`,
|
|
2944
3043
|
handler: async (_args, ctx) => {
|
|
2945
3044
|
let content = fs8.readFileSync(filePath, "utf-8");
|
|
2946
3045
|
content = content.replace(/\{\{cwd\}\}/g, process.cwd()).replace(/\{\{date\}\}/g, (/* @__PURE__ */ new Date()).toISOString().split("T")[0]).replace(/\{\{file_path\}\}/g, _args || "");
|
|
@@ -2957,7 +3056,7 @@ function loadCustomCommands() {
|
|
|
2957
3056
|
init_esm_shims();
|
|
2958
3057
|
init_tool();
|
|
2959
3058
|
import * as fs9 from "fs";
|
|
2960
|
-
import * as
|
|
3059
|
+
import * as path11 from "path";
|
|
2961
3060
|
var fileReadTool = {
|
|
2962
3061
|
name: "read_file",
|
|
2963
3062
|
description: `Read a file from the filesystem. Supports text files with line numbers (cat -n format), PDF files, images (returns base64 for multimodal), and Jupyter notebooks (.ipynb). Use offset/limit for large files.`,
|
|
@@ -2977,7 +3076,7 @@ var fileReadTool = {
|
|
|
2977
3076
|
const filePath = String(input3["file_path"]);
|
|
2978
3077
|
const offset = input3["offset"];
|
|
2979
3078
|
const limit = input3["limit"];
|
|
2980
|
-
const resolved =
|
|
3079
|
+
const resolved = path11.resolve(filePath);
|
|
2981
3080
|
if (!fs9.existsSync(resolved)) {
|
|
2982
3081
|
return makeToolError(`File not found: ${resolved}`);
|
|
2983
3082
|
}
|
|
@@ -2985,7 +3084,7 @@ var fileReadTool = {
|
|
|
2985
3084
|
if (stat.isDirectory()) {
|
|
2986
3085
|
return makeToolError(`Path is a directory, not a file: ${resolved}. Use list_dir instead.`);
|
|
2987
3086
|
}
|
|
2988
|
-
const ext =
|
|
3087
|
+
const ext = path11.extname(resolved).toLowerCase();
|
|
2989
3088
|
if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".svg"].includes(ext)) {
|
|
2990
3089
|
const data = fs9.readFileSync(resolved);
|
|
2991
3090
|
const base64 = data.toString("base64");
|
|
@@ -2998,7 +3097,7 @@ var fileReadTool = {
|
|
|
2998
3097
|
".bmp": "image/bmp",
|
|
2999
3098
|
".svg": "image/svg+xml"
|
|
3000
3099
|
};
|
|
3001
|
-
return makeToolResult(`[Image: ${
|
|
3100
|
+
return makeToolResult(`[Image: ${path11.basename(resolved)}]`, {
|
|
3002
3101
|
filePath: resolved,
|
|
3003
3102
|
isImage: true,
|
|
3004
3103
|
imageData: base64,
|
|
@@ -3084,7 +3183,7 @@ var fileReadTool = {
|
|
|
3084
3183
|
init_esm_shims();
|
|
3085
3184
|
init_tool();
|
|
3086
3185
|
import * as fs10 from "fs";
|
|
3087
|
-
import * as
|
|
3186
|
+
import * as path12 from "path";
|
|
3088
3187
|
var fileWriteTool = {
|
|
3089
3188
|
name: "write_file",
|
|
3090
3189
|
description: `Create a new file or overwrite an existing file. Creates parent directories if needed. For modifying existing files, prefer edit_file instead.`,
|
|
@@ -3101,9 +3200,9 @@ var fileWriteTool = {
|
|
|
3101
3200
|
async execute(input3) {
|
|
3102
3201
|
const filePath = String(input3["file_path"]);
|
|
3103
3202
|
const content = String(input3["content"]);
|
|
3104
|
-
const resolved =
|
|
3203
|
+
const resolved = path12.resolve(filePath);
|
|
3105
3204
|
try {
|
|
3106
|
-
const dir =
|
|
3205
|
+
const dir = path12.dirname(resolved);
|
|
3107
3206
|
if (!fs10.existsSync(dir)) {
|
|
3108
3207
|
fs10.mkdirSync(dir, { recursive: true });
|
|
3109
3208
|
}
|
|
@@ -3125,7 +3224,7 @@ var fileWriteTool = {
|
|
|
3125
3224
|
init_esm_shims();
|
|
3126
3225
|
init_tool();
|
|
3127
3226
|
import * as fs11 from "fs";
|
|
3128
|
-
import * as
|
|
3227
|
+
import * as path13 from "path";
|
|
3129
3228
|
var fileEditTool = {
|
|
3130
3229
|
name: "edit_file",
|
|
3131
3230
|
description: `Perform exact string replacement in a file. The old_string must be unique in the file unless replace_all is true. Preserves indentation exactly.`,
|
|
@@ -3146,7 +3245,7 @@ var fileEditTool = {
|
|
|
3146
3245
|
const oldString = String(input3["old_string"]);
|
|
3147
3246
|
const newString = String(input3["new_string"]);
|
|
3148
3247
|
const replaceAll = input3["replace_all"] === true;
|
|
3149
|
-
const resolved =
|
|
3248
|
+
const resolved = path13.resolve(filePath);
|
|
3150
3249
|
if (!fs11.existsSync(resolved)) {
|
|
3151
3250
|
return makeToolError(`File not found: ${resolved}`);
|
|
3152
3251
|
}
|
|
@@ -3205,7 +3304,7 @@ Did you mean this line?
|
|
|
3205
3304
|
init_esm_shims();
|
|
3206
3305
|
init_tool();
|
|
3207
3306
|
import * as fs12 from "fs";
|
|
3208
|
-
import * as
|
|
3307
|
+
import * as path14 from "path";
|
|
3209
3308
|
var fileMultiEditTool = {
|
|
3210
3309
|
name: "multi_edit",
|
|
3211
3310
|
description: `Apply multiple edits to a single file atomically. Each edit is an old_string \u2192 new_string replacement. All edits are validated before any are applied.`,
|
|
@@ -3233,7 +3332,7 @@ var fileMultiEditTool = {
|
|
|
3233
3332
|
async execute(input3) {
|
|
3234
3333
|
const filePath = String(input3["file_path"]);
|
|
3235
3334
|
const edits = input3["edits"];
|
|
3236
|
-
const resolved =
|
|
3335
|
+
const resolved = path14.resolve(filePath);
|
|
3237
3336
|
if (!fs12.existsSync(resolved)) {
|
|
3238
3337
|
return makeToolError(`File not found: ${resolved}`);
|
|
3239
3338
|
}
|
|
@@ -3281,7 +3380,7 @@ Searching for: ${edit2.old_string.slice(0, 100)}...`
|
|
|
3281
3380
|
init_esm_shims();
|
|
3282
3381
|
init_tool();
|
|
3283
3382
|
import * as fs13 from "fs";
|
|
3284
|
-
import * as
|
|
3383
|
+
import * as path15 from "path";
|
|
3285
3384
|
import { globby } from "globby";
|
|
3286
3385
|
var globTool = {
|
|
3287
3386
|
name: "glob",
|
|
@@ -3299,7 +3398,7 @@ var globTool = {
|
|
|
3299
3398
|
async execute(input3) {
|
|
3300
3399
|
const pattern = String(input3["pattern"]);
|
|
3301
3400
|
const searchPath = input3["path"] ? String(input3["path"]) : process.cwd();
|
|
3302
|
-
const resolved =
|
|
3401
|
+
const resolved = path15.resolve(searchPath);
|
|
3303
3402
|
try {
|
|
3304
3403
|
const files = await globby(pattern, {
|
|
3305
3404
|
cwd: resolved,
|
|
@@ -3336,7 +3435,7 @@ init_esm_shims();
|
|
|
3336
3435
|
init_tool();
|
|
3337
3436
|
import { execFile } from "child_process";
|
|
3338
3437
|
import * as fs14 from "fs";
|
|
3339
|
-
import * as
|
|
3438
|
+
import * as path16 from "path";
|
|
3340
3439
|
var grepTool = {
|
|
3341
3440
|
name: "grep",
|
|
3342
3441
|
description: `Search file contents using regex patterns. Uses ripgrep (rg) if available, falls back to grep, then to a built-in Node.js search. Supports context lines, file type filters, and multiple output modes.`,
|
|
@@ -3366,7 +3465,7 @@ var grepTool = {
|
|
|
3366
3465
|
readOnly: true,
|
|
3367
3466
|
async execute(input3) {
|
|
3368
3467
|
const pattern = String(input3["pattern"]);
|
|
3369
|
-
const searchPath =
|
|
3468
|
+
const searchPath = path16.resolve(input3["path"] ? String(input3["path"]) : process.cwd());
|
|
3370
3469
|
const outputMode = input3["output_mode"] || "files_with_matches";
|
|
3371
3470
|
const headLimit = input3["head_limit"] || 0;
|
|
3372
3471
|
const hasRg = await hasCommand("rg");
|
|
@@ -3556,11 +3655,11 @@ function collectFiles(dirPath, typeFilter, globFilter) {
|
|
|
3556
3655
|
for (const entry of entries) {
|
|
3557
3656
|
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
3558
3657
|
if (entry.name.startsWith(".") && entry.name !== ".") continue;
|
|
3559
|
-
const fullPath =
|
|
3658
|
+
const fullPath = path16.join(dir, entry.name);
|
|
3560
3659
|
if (entry.isDirectory()) {
|
|
3561
3660
|
walk(fullPath);
|
|
3562
3661
|
} else if (entry.isFile()) {
|
|
3563
|
-
const ext =
|
|
3662
|
+
const ext = path16.extname(entry.name).toLowerCase();
|
|
3564
3663
|
if (BINARY_EXTENSIONS.has(ext)) continue;
|
|
3565
3664
|
if (allowedExtensions && !allowedExtensions.has(ext)) continue;
|
|
3566
3665
|
if (globRegex && !globRegex.test(entry.name)) continue;
|
|
@@ -3687,7 +3786,7 @@ Use task_output tool to check results.`);
|
|
|
3687
3786
|
init_esm_shims();
|
|
3688
3787
|
init_tool();
|
|
3689
3788
|
import * as fs15 from "fs";
|
|
3690
|
-
import * as
|
|
3789
|
+
import * as path17 from "path";
|
|
3691
3790
|
var listDirTool = {
|
|
3692
3791
|
name: "list_dir",
|
|
3693
3792
|
description: `List directory contents with file/folder distinction and basic metadata.`,
|
|
@@ -3701,7 +3800,7 @@ var listDirTool = {
|
|
|
3701
3800
|
dangerous: false,
|
|
3702
3801
|
readOnly: true,
|
|
3703
3802
|
async execute(input3) {
|
|
3704
|
-
const dirPath =
|
|
3803
|
+
const dirPath = path17.resolve(input3["path"] ? String(input3["path"]) : process.cwd());
|
|
3705
3804
|
if (!fs15.existsSync(dirPath)) {
|
|
3706
3805
|
return makeToolError(`Directory not found: ${dirPath}`);
|
|
3707
3806
|
}
|
|
@@ -3721,7 +3820,7 @@ var listDirTool = {
|
|
|
3721
3820
|
dirs.push(`${entry.name}/`);
|
|
3722
3821
|
} else if (entry.isSymbolicLink()) {
|
|
3723
3822
|
try {
|
|
3724
|
-
const target = fs15.readlinkSync(
|
|
3823
|
+
const target = fs15.readlinkSync(path17.join(dirPath, entry.name));
|
|
3725
3824
|
files.push(`${entry.name} -> ${target}`);
|
|
3726
3825
|
} catch {
|
|
3727
3826
|
files.push(`${entry.name} -> (broken link)`);
|
|
@@ -3934,7 +4033,7 @@ ${formatted}`);
|
|
|
3934
4033
|
init_esm_shims();
|
|
3935
4034
|
init_tool();
|
|
3936
4035
|
import * as fs16 from "fs";
|
|
3937
|
-
import * as
|
|
4036
|
+
import * as path18 from "path";
|
|
3938
4037
|
var notebookEditTool = {
|
|
3939
4038
|
name: "notebook_edit",
|
|
3940
4039
|
description: `Edit Jupyter notebook (.ipynb) cells. Supports replacing, inserting, and deleting cells.`,
|
|
@@ -3952,7 +4051,7 @@ var notebookEditTool = {
|
|
|
3952
4051
|
dangerous: true,
|
|
3953
4052
|
readOnly: false,
|
|
3954
4053
|
async execute(input3) {
|
|
3955
|
-
const nbPath =
|
|
4054
|
+
const nbPath = path18.resolve(String(input3["notebook_path"]));
|
|
3956
4055
|
const cellNumber = input3["cell_number"];
|
|
3957
4056
|
const newSource = String(input3["new_source"]);
|
|
3958
4057
|
const cellType = input3["cell_type"] || "code";
|