mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-05 16:39:04 +08:00
Compare commits
7 Commits
rcc/render
...
b9e00f87d5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9e00f87d5 | ||
|
|
d621f5d5d8 | ||
|
|
c941e95fc7 | ||
|
|
c9f7b96e7d | ||
|
|
66d9c1e420 | ||
|
|
caad05016e | ||
|
|
2394140007 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: instructkr
|
||||
9
.port_sessions/00083e63395f4f3bb24fd6e7ed26439d.json
Normal file
9
.port_sessions/00083e63395f4f3bb24fd6e7ed26439d.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "00083e63395f4f3bb24fd6e7ed26439d",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
8
.port_sessions/053db140f7694d1abfb52c96e62fdede.json
Normal file
8
.port_sessions/053db140f7694d1abfb52c96e62fdede.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "053db140f7694d1abfb52c96e62fdede",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
9
.port_sessions/05a9bc9a33c24f80b7e6540d4306d59d.json
Normal file
9
.port_sessions/05a9bc9a33c24f80b7e6540d4306d59d.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "05a9bc9a33c24f80b7e6540d4306d59d",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/08dcdbf3d76b4ed7898c27c1a45dde73.json
Normal file
9
.port_sessions/08dcdbf3d76b4ed7898c27c1a45dde73.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "08dcdbf3d76b4ed7898c27c1a45dde73",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/0ca1cdd3176041a1916729d76effe10d.json
Normal file
9
.port_sessions/0ca1cdd3176041a1916729d76effe10d.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "0ca1cdd3176041a1916729d76effe10d",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/11fb4a2aa69b460ba53031ea6643969f.json
Normal file
9
.port_sessions/11fb4a2aa69b460ba53031ea6643969f.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "11fb4a2aa69b460ba53031ea6643969f",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/156dd69bbb3142e687d1a56cb97633da.json
Normal file
9
.port_sessions/156dd69bbb3142e687d1a56cb97633da.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "156dd69bbb3142e687d1a56cb97633da",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/1a9711e7305f42a8bc30d8ade03a221d.json
Normal file
9
.port_sessions/1a9711e7305f42a8bc30d8ade03a221d.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "1a9711e7305f42a8bc30d8ade03a221d",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/1b2fe9ba9b804b2896e33ddc7c7d91ae.json
Normal file
9
.port_sessions/1b2fe9ba9b804b2896e33ddc7c7d91ae.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "1b2fe9ba9b804b2896e33ddc7c7d91ae",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
8
.port_sessions/1d07428c6df44c859e6056ef13aa422d.json
Normal file
8
.port_sessions/1d07428c6df44c859e6056ef13aa422d.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "1d07428c6df44c859e6056ef13aa422d",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
9
.port_sessions/1e068ad0e9234d8b9e85735556ad436c.json
Normal file
9
.port_sessions/1e068ad0e9234d8b9e85735556ad436c.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "1e068ad0e9234d8b9e85735556ad436c",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/2763324a1d7e4832a12f14e7108ce552.json
Normal file
9
.port_sessions/2763324a1d7e4832a12f14e7108ce552.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "2763324a1d7e4832a12f14e7108ce552",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
8
.port_sessions/27d5203bdda3480681732c1f94291599.json
Normal file
8
.port_sessions/27d5203bdda3480681732c1f94291599.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "27d5203bdda3480681732c1f94291599",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
9
.port_sessions/28510de9575e40919ddb607d82e95cc1.json
Normal file
9
.port_sessions/28510de9575e40919ddb607d82e95cc1.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "28510de9575e40919ddb607d82e95cc1",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/2e574cba82bb43eda5aafe7ae364210b.json
Normal file
9
.port_sessions/2e574cba82bb43eda5aafe7ae364210b.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "2e574cba82bb43eda5aafe7ae364210b",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/317de978222e4e6c93f7e5430d44d130.json
Normal file
9
.port_sessions/317de978222e4e6c93f7e5430d44d130.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "317de978222e4e6c93f7e5430d44d130",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/338275156d1d405f8c627be621b3191e.json
Normal file
9
.port_sessions/338275156d1d405f8c627be621b3191e.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "338275156d1d405f8c627be621b3191e",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/3b93d8e47dff4a0d90782d19e33a81b6.json
Normal file
9
.port_sessions/3b93d8e47dff4a0d90782d19e33a81b6.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "3b93d8e47dff4a0d90782d19e33a81b6",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/3c60859387044f7b8111557a4b252745.json
Normal file
9
.port_sessions/3c60859387044f7b8111557a4b252745.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "3c60859387044f7b8111557a4b252745",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/439e9f7d2bfa41cbbfed6edf44074311.json
Normal file
9
.port_sessions/439e9f7d2bfa41cbbfed6edf44074311.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "439e9f7d2bfa41cbbfed6edf44074311",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/4c75b6e3a46b4f599e32eaf147d92c4d.json
Normal file
9
.port_sessions/4c75b6e3a46b4f599e32eaf147d92c4d.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "4c75b6e3a46b4f599e32eaf147d92c4d",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/4e4aae9acdf945839798f37e01c04f1b.json
Normal file
9
.port_sessions/4e4aae9acdf945839798f37e01c04f1b.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "4e4aae9acdf945839798f37e01c04f1b",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/5230052e7d2749be9cf318de519d9c42.json
Normal file
9
.port_sessions/5230052e7d2749be9cf318de519d9c42.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "5230052e7d2749be9cf318de519d9c42",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/575c96ef2c1f45cf9deb85b06c552bde.json
Normal file
9
.port_sessions/575c96ef2c1f45cf9deb85b06c552bde.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "575c96ef2c1f45cf9deb85b06c552bde",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/577b74a297fd4fed956a78b5debd8025.json
Normal file
9
.port_sessions/577b74a297fd4fed956a78b5debd8025.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "577b74a297fd4fed956a78b5debd8025",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/59d15a4390b648b9b19c6ebae1c5d2d8.json
Normal file
9
.port_sessions/59d15a4390b648b9b19c6ebae1c5d2d8.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "59d15a4390b648b9b19c6ebae1c5d2d8",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/5cb2ee43a1c547f79ffbc74cb311247c.json
Normal file
9
.port_sessions/5cb2ee43a1c547f79ffbc74cb311247c.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "5cb2ee43a1c547f79ffbc74cb311247c",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
8
.port_sessions/609a20c55c6d47c282ddc00d0c3b6dfb.json
Normal file
8
.port_sessions/609a20c55c6d47c282ddc00d0c3b6dfb.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "609a20c55c6d47c282ddc00d0c3b6dfb",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
9
.port_sessions/663f870db32944b3a0d2b863c19a5708.json
Normal file
9
.port_sessions/663f870db32944b3a0d2b863c19a5708.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "663f870db32944b3a0d2b863c19a5708",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/6d0759c31bd942b6b86f3969ba8bbf28.json
Normal file
9
.port_sessions/6d0759c31bd942b6b86f3969ba8bbf28.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "6d0759c31bd942b6b86f3969ba8bbf28",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/6dd0b48373c64449b756fd53425b4031.json
Normal file
9
.port_sessions/6dd0b48373c64449b756fd53425b4031.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "6dd0b48373c64449b756fd53425b4031",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/6f8b4a9d5a0e47f79bd732f61a6eb543.json
Normal file
9
.port_sessions/6f8b4a9d5a0e47f79bd732f61a6eb543.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "6f8b4a9d5a0e47f79bd732f61a6eb543",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/6f9f4e28691f46dab19d99d78127c94e.json
Normal file
9
.port_sessions/6f9f4e28691f46dab19d99d78127c94e.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "6f9f4e28691f46dab19d99d78127c94e",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
8
.port_sessions/72b9dcbd91054fd78d7cfdcf09de3c0b.json
Normal file
8
.port_sessions/72b9dcbd91054fd78d7cfdcf09de3c0b.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "72b9dcbd91054fd78d7cfdcf09de3c0b",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
8
.port_sessions/76411759fe1e42e5bc5ac4279a68f700.json
Normal file
8
.port_sessions/76411759fe1e42e5bc5ac4279a68f700.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "76411759fe1e42e5bc5ac4279a68f700",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
9
.port_sessions/776aeb73811d4868ad20b6562eba6502.json
Normal file
9
.port_sessions/776aeb73811d4868ad20b6562eba6502.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "776aeb73811d4868ad20b6562eba6502",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
8
.port_sessions/7afd286cb9d14d62b56ba7ff86bcc08b.json
Normal file
8
.port_sessions/7afd286cb9d14d62b56ba7ff86bcc08b.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "7afd286cb9d14d62b56ba7ff86bcc08b",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
9
.port_sessions/7e96b6c76ef64983a4ba3be162bd7cdd.json
Normal file
9
.port_sessions/7e96b6c76ef64983a4ba3be162bd7cdd.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "7e96b6c76ef64983a4ba3be162bd7cdd",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/800a5672a570499883260b597ed174bd.json
Normal file
9
.port_sessions/800a5672a570499883260b597ed174bd.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "800a5672a570499883260b597ed174bd",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/834e4208013d4505a6957901a0ff151c.json
Normal file
9
.port_sessions/834e4208013d4505a6957901a0ff151c.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "834e4208013d4505a6957901a0ff151c",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/884dc57a7bba40d59a1bcc1dd80cb28b.json
Normal file
9
.port_sessions/884dc57a7bba40d59a1bcc1dd80cb28b.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "884dc57a7bba40d59a1bcc1dd80cb28b",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/8d517af648b840928016d4cc613f1133.json
Normal file
9
.port_sessions/8d517af648b840928016d4cc613f1133.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "8d517af648b840928016d4cc613f1133",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/8d84e1795c9d414190bc1a9c719bc63e.json
Normal file
9
.port_sessions/8d84e1795c9d414190bc1a9c719bc63e.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "8d84e1795c9d414190bc1a9c719bc63e",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/90d94293814443edb376ba3b68568c46.json
Normal file
9
.port_sessions/90d94293814443edb376ba3b68568c46.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "90d94293814443edb376ba3b68568c46",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/9155bcf5da0348dbac0c2374ef60c9e5.json
Normal file
9
.port_sessions/9155bcf5da0348dbac0c2374ef60c9e5.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "9155bcf5da0348dbac0c2374ef60c9e5",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
8
.port_sessions/91d1f071ab8148b28b7e29513cc1804f.json
Normal file
8
.port_sessions/91d1f071ab8148b28b7e29513cc1804f.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "91d1f071ab8148b28b7e29513cc1804f",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
9
.port_sessions/933e1cd8e24042d1968959aa2c67f3d6.json
Normal file
9
.port_sessions/933e1cd8e24042d1968959aa2c67f3d6.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "933e1cd8e24042d1968959aa2c67f3d6",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/94d871d03ed6410a81a8339ecba4f3dd.json
Normal file
9
.port_sessions/94d871d03ed6410a81a8339ecba4f3dd.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "94d871d03ed6410a81a8339ecba4f3dd",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/994767f1199c44d8b2052e758b4d4d3c.json
Normal file
9
.port_sessions/994767f1199c44d8b2052e758b4d4d3c.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "994767f1199c44d8b2052e758b4d4d3c",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/9bd50e453f24444d970dedddf3c85027.json
Normal file
9
.port_sessions/9bd50e453f24444d970dedddf3c85027.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "9bd50e453f24444d970dedddf3c85027",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/9d8eecbecd7d452697d2e8b5be415bd4.json
Normal file
9
.port_sessions/9d8eecbecd7d452697d2e8b5be415bd4.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "9d8eecbecd7d452697d2e8b5be415bd4",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/9f87d3fe67bb4aebbe0effd034382685.json
Normal file
9
.port_sessions/9f87d3fe67bb4aebbe0effd034382685.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "9f87d3fe67bb4aebbe0effd034382685",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/a0e9fbca75ab44e7b89022156f18f60a.json
Normal file
9
.port_sessions/a0e9fbca75ab44e7b89022156f18f60a.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "a0e9fbca75ab44e7b89022156f18f60a",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/a1d0491f8c92424fb2a2c3e2333f39d0.json
Normal file
9
.port_sessions/a1d0491f8c92424fb2a2c3e2333f39d0.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "a1d0491f8c92424fb2a2c3e2333f39d0",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/a277471dc4cf4d7f86ff065fd77ccb88.json
Normal file
9
.port_sessions/a277471dc4cf4d7f86ff065fd77ccb88.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "a277471dc4cf4d7f86ff065fd77ccb88",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/a3b47cec34694898919020369ab6af13.json
Normal file
9
.port_sessions/a3b47cec34694898919020369ab6af13.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "a3b47cec34694898919020369ab6af13",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/a6e9e4b6daac4f5a82e366cb12313eee.json
Normal file
9
.port_sessions/a6e9e4b6daac4f5a82e366cb12313eee.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "a6e9e4b6daac4f5a82e366cb12313eee",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/ad7f38d318df458bb1f0f0492fa71f02.json
Normal file
9
.port_sessions/ad7f38d318df458bb1f0f0492fa71f02.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "ad7f38d318df458bb1f0f0492fa71f02",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/b3b5eb702163461bb0f0dd43a82528d8.json
Normal file
9
.port_sessions/b3b5eb702163461bb0f0dd43a82528d8.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "b3b5eb702163461bb0f0dd43a82528d8",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/b4829661195449c1af0c37e8c87ce0a5.json
Normal file
9
.port_sessions/b4829661195449c1af0c37e8c87ce0a5.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "b4829661195449c1af0c37e8c87ce0a5",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/b4f0e18929d54d948ad29bd1dd43a85d.json
Normal file
9
.port_sessions/b4f0e18929d54d948ad29bd1dd43a85d.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "b4f0e18929d54d948ad29bd1dd43a85d",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/bd2f9891b77d4ab29b022f6d830c6234.json
Normal file
9
.port_sessions/bd2f9891b77d4ab29b022f6d830c6234.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "bd2f9891b77d4ab29b022f6d830c6234",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/cbe249de5950459da16440988e2ddc54.json
Normal file
9
.port_sessions/cbe249de5950459da16440988e2ddc54.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "cbe249de5950459da16440988e2ddc54",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
8
.port_sessions/db0aea45e8984d57898e91803bdcdc7c.json
Normal file
8
.port_sessions/db0aea45e8984d57898e91803bdcdc7c.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "db0aea45e8984d57898e91803bdcdc7c",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
9
.port_sessions/dbd0d1c8f734444a8ed02f77f3d9fc1a.json
Normal file
9
.port_sessions/dbd0d1c8f734444a8ed02f77f3d9fc1a.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "dbd0d1c8f734444a8ed02f77f3d9fc1a",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/e8fff9feb2ae4d8ca94575ed8fa5a03f.json
Normal file
9
.port_sessions/e8fff9feb2ae4d8ca94575ed8fa5a03f.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "e8fff9feb2ae4d8ca94575ed8fa5a03f",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/e9433328bba545d8ac94e2bbac488aec.json
Normal file
9
.port_sessions/e9433328bba545d8ac94e2bbac488aec.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "e9433328bba545d8ac94e2bbac488aec",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/f074c37ae3b84a959da394aec7ec3c68.json
Normal file
9
.port_sessions/f074c37ae3b84a959da394aec7ec3c68.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "f074c37ae3b84a959da394aec7ec3c68",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/f2efb0de41f949859b57854e58efad70.json
Normal file
9
.port_sessions/f2efb0de41f949859b57854e58efad70.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "f2efb0de41f949859b57854e58efad70",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
8
.port_sessions/f48004afd90f41d081a6129e96ad7651.json
Normal file
8
.port_sessions/f48004afd90f41d081a6129e96ad7651.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "f48004afd90f41d081a6129e96ad7651",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
9
.port_sessions/f5def6f008f64a918bb0f5b0d6ec3db6.json
Normal file
9
.port_sessions/f5def6f008f64a918bb0f5b0d6ec3db6.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "f5def6f008f64a918bb0f5b0d6ec3db6",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
9
.port_sessions/f5efe1bbcb124e3191e4417becbe1f81.json
Normal file
9
.port_sessions/f5efe1bbcb124e3191e4417becbe1f81.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "f5efe1bbcb124e3191e4417becbe1f81",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
8
.port_sessions/f689195c4cf94eb998a461da808075a3.json
Normal file
8
.port_sessions/f689195c4cf94eb998a461da808075a3.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "f689195c4cf94eb998a461da808075a3",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
8
.port_sessions/f77644ede86245eba5198322de05e4a4.json
Normal file
8
.port_sessions/f77644ede86245eba5198322de05e4a4.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"session_id": "f77644ede86245eba5198322de05e4a4",
|
||||
"messages": [
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 3,
|
||||
"output_tokens": 13
|
||||
}
|
||||
9
.port_sessions/ffbf6be4ed074ead98bcf18e7710e6a1.json
Normal file
9
.port_sessions/ffbf6be4ed074ead98bcf18e7710e6a1.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"session_id": "ffbf6be4ed074ead98bcf18e7710e6a1",
|
||||
"messages": [
|
||||
"review MCP tool",
|
||||
"review MCP tool"
|
||||
],
|
||||
"input_tokens": 6,
|
||||
"output_tokens": 32
|
||||
}
|
||||
316
README.md
316
README.md
@@ -1,123 +1,261 @@
|
||||
# Claude Code Python Porting Workspace
|
||||
# Claude Code — Leaked Source (2026-03-31)
|
||||
|
||||
> The primary `src/` tree in this repository is now dedicated to **Python porting work**. The March 31, 2026 Claude Code source exposure is part of the project's background, but the tracked repository is now centered on Python source rather than the exposed TypeScript snapshot.
|
||||
> **On March 31, 2026, the full source code of Anthropic's Claude Code CLI was leaked** via a `.map` file exposed in their npm registry.
|
||||
|
||||
---
|
||||
|
||||
## Porting Status
|
||||
## How It Leaked
|
||||
|
||||
The main source tree is now Python-first.
|
||||
[Chaofan Shou (@Fried_rice)](https://x.com/Fried_rice) discovered the leak and posted it publicly:
|
||||
|
||||
- `src/` contains the active Python porting workspace
|
||||
- `tests/` verifies the current Python workspace
|
||||
- the exposed snapshot is no longer part of the tracked repository state
|
||||
> **"Claude code source code has been leaked via a map file in their npm registry!"**
|
||||
>
|
||||
> — [@Fried_rice, March 31, 2026](https://x.com/Fried_rice/status/2038894956459290963)
|
||||
|
||||
The current Python workspace is not yet a complete one-to-one replacement for the original system, but the primary implementation surface is now Python.
|
||||
The source map file in the published npm package contained a reference to the full, unobfuscated TypeScript source, which was downloadable as a zip archive from Anthropic's R2 storage bucket.
|
||||
|
||||
## Why this rewrite exists
|
||||
---
|
||||
|
||||
I originally studied the exposed codebase to understand its harness, tool wiring, and agent workflow. After spending more time with the legal and ethical questions—and after reading the essay linked below—I did not want the exposed snapshot itself to remain the main tracked source tree.
|
||||
## Overview
|
||||
|
||||
This repository now focuses on Python porting work instead.
|
||||
Claude Code is Anthropic's official CLI tool that lets you interact with Claude directly from the terminal to perform software engineering tasks — editing files, running commands, searching codebases, managing git workflows, and more.
|
||||
|
||||
## Repository Layout
|
||||
This repository contains the leaked `src/` directory.
|
||||
|
||||
```text
|
||||
.
|
||||
├── src/ # Python porting workspace
|
||||
│ ├── __init__.py
|
||||
│ ├── commands.py
|
||||
│ ├── main.py
|
||||
│ ├── models.py
|
||||
│ ├── port_manifest.py
|
||||
│ ├── query_engine.py
|
||||
│ ├── task.py
|
||||
│ └── tools.py
|
||||
├── tests/ # Python verification
|
||||
├── assets/omx/ # OmX workflow screenshots
|
||||
├── 2026-03-09-is-legal-the-same-as-legitimate-ai-reimplementation-and-the-erosion-of-copyleft.md
|
||||
└── README.md
|
||||
- **Leaked on**: 2026-03-31
|
||||
- **Language**: TypeScript
|
||||
- **Runtime**: Bun
|
||||
- **Terminal UI**: React + [Ink](https://github.com/vadimdemedes/ink) (React for CLI)
|
||||
- **Scale**: ~1,900 files, 512,000+ lines of code
|
||||
|
||||
---
|
||||
|
||||
## Rust port foundation
|
||||
|
||||
A compatibility-first Rust port workspace now lives in [`rust/`](rust/). Start with [`rust/README.md`](rust/README.md) for the current milestone scope, workspace layout, and verification commands.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.tsx # Entrypoint (Commander.js-based CLI parser)
|
||||
├── commands.ts # Command registry
|
||||
├── tools.ts # Tool registry
|
||||
├── Tool.ts # Tool type definitions
|
||||
├── QueryEngine.ts # LLM query engine (core Anthropic API caller)
|
||||
├── context.ts # System/user context collection
|
||||
├── cost-tracker.ts # Token cost tracking
|
||||
│
|
||||
├── commands/ # Slash command implementations (~50)
|
||||
├── tools/ # Agent tool implementations (~40)
|
||||
├── components/ # Ink UI components (~140)
|
||||
├── hooks/ # React hooks
|
||||
├── services/ # External service integrations
|
||||
├── screens/ # Full-screen UIs (Doctor, REPL, Resume)
|
||||
├── types/ # TypeScript type definitions
|
||||
├── utils/ # Utility functions
|
||||
│
|
||||
├── bridge/ # IDE integration bridge (VS Code, JetBrains)
|
||||
├── coordinator/ # Multi-agent coordinator
|
||||
├── plugins/ # Plugin system
|
||||
├── skills/ # Skill system
|
||||
├── keybindings/ # Keybinding configuration
|
||||
├── vim/ # Vim mode
|
||||
├── voice/ # Voice input
|
||||
├── remote/ # Remote sessions
|
||||
├── server/ # Server mode
|
||||
├── memdir/ # Memory directory (persistent memory)
|
||||
├── tasks/ # Task management
|
||||
├── state/ # State management
|
||||
├── migrations/ # Config migrations
|
||||
├── schemas/ # Config schemas (Zod)
|
||||
├── entrypoints/ # Initialization logic
|
||||
├── ink/ # Ink renderer wrapper
|
||||
├── buddy/ # Companion sprite (Easter egg)
|
||||
├── native-ts/ # Native TypeScript utils
|
||||
├── outputStyles/ # Output styling
|
||||
├── query/ # Query pipeline
|
||||
└── upstreamproxy/ # Proxy configuration
|
||||
```
|
||||
|
||||
## Python Workspace Overview
|
||||
---
|
||||
|
||||
The new Python `src/` tree currently provides:
|
||||
## Core Architecture
|
||||
|
||||
- **`port_manifest.py`** — summarizes the current Python workspace structure
|
||||
- **`models.py`** — dataclasses for subsystems, modules, and backlog state
|
||||
- **`commands.py`** — Python-side command port metadata
|
||||
- **`tools.py`** — Python-side tool port metadata
|
||||
- **`query_engine.py`** — renders a Python porting summary from the active workspace
|
||||
- **`main.py`** — a CLI entrypoint for manifest and summary output
|
||||
### 1. Tool System (`src/tools/`)
|
||||
|
||||
## Quickstart
|
||||
Every tool Claude Code can invoke is implemented as a self-contained module. Each tool defines its input schema, permission model, and execution logic.
|
||||
|
||||
Render the Python porting summary:
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `BashTool` | Shell command execution |
|
||||
| `FileReadTool` | File reading (images, PDFs, notebooks) |
|
||||
| `FileWriteTool` | File creation / overwrite |
|
||||
| `FileEditTool` | Partial file modification (string replacement) |
|
||||
| `GlobTool` | File pattern matching search |
|
||||
| `GrepTool` | ripgrep-based content search |
|
||||
| `WebFetchTool` | Fetch URL content |
|
||||
| `WebSearchTool` | Web search |
|
||||
| `AgentTool` | Sub-agent spawning |
|
||||
| `SkillTool` | Skill execution |
|
||||
| `MCPTool` | MCP server tool invocation |
|
||||
| `LSPTool` | Language Server Protocol integration |
|
||||
| `NotebookEditTool` | Jupyter notebook editing |
|
||||
| `TaskCreateTool` / `TaskUpdateTool` | Task creation and management |
|
||||
| `SendMessageTool` | Inter-agent messaging |
|
||||
| `TeamCreateTool` / `TeamDeleteTool` | Team agent management |
|
||||
| `EnterPlanModeTool` / `ExitPlanModeTool` | Plan mode toggle |
|
||||
| `EnterWorktreeTool` / `ExitWorktreeTool` | Git worktree isolation |
|
||||
| `ToolSearchTool` | Deferred tool discovery |
|
||||
| `CronCreateTool` | Scheduled trigger creation |
|
||||
| `RemoteTriggerTool` | Remote trigger |
|
||||
| `SleepTool` | Proactive mode wait |
|
||||
| `SyntheticOutputTool` | Structured output generation |
|
||||
|
||||
```bash
|
||||
python3 -m src.main summary
|
||||
### 2. Command System (`src/commands/`)
|
||||
|
||||
User-facing slash commands invoked with `/` prefix.
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `/commit` | Create a git commit |
|
||||
| `/review` | Code review |
|
||||
| `/compact` | Context compression |
|
||||
| `/mcp` | MCP server management |
|
||||
| `/config` | Settings management |
|
||||
| `/doctor` | Environment diagnostics |
|
||||
| `/login` / `/logout` | Authentication |
|
||||
| `/memory` | Persistent memory management |
|
||||
| `/skills` | Skill management |
|
||||
| `/tasks` | Task management |
|
||||
| `/vim` | Vim mode toggle |
|
||||
| `/diff` | View changes |
|
||||
| `/cost` | Check usage cost |
|
||||
| `/theme` | Change theme |
|
||||
| `/context` | Context visualization |
|
||||
| `/pr_comments` | View PR comments |
|
||||
| `/resume` | Restore previous session |
|
||||
| `/share` | Share session |
|
||||
| `/desktop` | Desktop app handoff |
|
||||
| `/mobile` | Mobile app handoff |
|
||||
|
||||
### 3. Service Layer (`src/services/`)
|
||||
|
||||
| Service | Description |
|
||||
|---|---|
|
||||
| `api/` | Anthropic API client, file API, bootstrap |
|
||||
| `mcp/` | Model Context Protocol server connection and management |
|
||||
| `oauth/` | OAuth 2.0 authentication flow |
|
||||
| `lsp/` | Language Server Protocol manager |
|
||||
| `analytics/` | GrowthBook-based feature flags and analytics |
|
||||
| `plugins/` | Plugin loader |
|
||||
| `compact/` | Conversation context compression |
|
||||
| `policyLimits/` | Organization policy limits |
|
||||
| `remoteManagedSettings/` | Remote managed settings |
|
||||
| `extractMemories/` | Automatic memory extraction |
|
||||
| `tokenEstimation.ts` | Token count estimation |
|
||||
| `teamMemorySync/` | Team memory synchronization |
|
||||
|
||||
### 4. Bridge System (`src/bridge/`)
|
||||
|
||||
A bidirectional communication layer connecting IDE extensions (VS Code, JetBrains) with the Claude Code CLI.
|
||||
|
||||
- `bridgeMain.ts` — Bridge main loop
|
||||
- `bridgeMessaging.ts` — Message protocol
|
||||
- `bridgePermissionCallbacks.ts` — Permission callbacks
|
||||
- `replBridge.ts` — REPL session bridge
|
||||
- `jwtUtils.ts` — JWT-based authentication
|
||||
- `sessionRunner.ts` — Session execution management
|
||||
|
||||
### 5. Permission System (`src/hooks/toolPermission/`)
|
||||
|
||||
Checks permissions on every tool invocation. Either prompts the user for approval/denial or automatically resolves based on the configured permission mode (`default`, `plan`, `bypassPermissions`, `auto`, etc.).
|
||||
|
||||
### 6. Feature Flags
|
||||
|
||||
Dead code elimination via Bun's `bun:bundle` feature flags:
|
||||
|
||||
```typescript
|
||||
import { feature } from 'bun:bundle'
|
||||
|
||||
// Inactive code is completely stripped at build time
|
||||
const voiceCommand = feature('VOICE_MODE')
|
||||
? require('./commands/voice/index.js').default
|
||||
: null
|
||||
```
|
||||
|
||||
Print the current Python workspace manifest:
|
||||
Notable flags: `PROACTIVE`, `KAIROS`, `BRIDGE_MODE`, `DAEMON`, `VOICE_MODE`, `AGENT_TRIGGERS`, `MONITOR_TOOL`
|
||||
|
||||
```bash
|
||||
python3 -m src.main manifest
|
||||
---
|
||||
|
||||
## Key Files in Detail
|
||||
|
||||
### `QueryEngine.ts` (~46K lines)
|
||||
|
||||
The core engine for LLM API calls. Handles streaming responses, tool-call loops, thinking mode, retry logic, and token counting.
|
||||
|
||||
### `Tool.ts` (~29K lines)
|
||||
|
||||
Defines base types and interfaces for all tools — input schemas, permission models, and progress state types.
|
||||
|
||||
### `commands.ts` (~25K lines)
|
||||
|
||||
Manages registration and execution of all slash commands. Uses conditional imports to load different command sets per environment.
|
||||
|
||||
### `main.tsx`
|
||||
|
||||
Commander.js-based CLI parser + React/Ink renderer initialization. At startup, parallelizes MDM settings, keychain prefetch, and GrowthBook initialization for faster boot.
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Category | Technology |
|
||||
|---|---|
|
||||
| Runtime | [Bun](https://bun.sh) |
|
||||
| Language | TypeScript (strict) |
|
||||
| Terminal UI | [React](https://react.dev) + [Ink](https://github.com/vadimdemedes/ink) |
|
||||
| CLI Parsing | [Commander.js](https://github.com/tj/commander.js) (extra-typings) |
|
||||
| Schema Validation | [Zod v4](https://zod.dev) |
|
||||
| Code Search | [ripgrep](https://github.com/BurntSushi/ripgrep) (via GrepTool) |
|
||||
| Protocols | [MCP SDK](https://modelcontextprotocol.io), LSP |
|
||||
| API | [Anthropic SDK](https://docs.anthropic.com) |
|
||||
| Telemetry | OpenTelemetry + gRPC |
|
||||
| Feature Flags | GrowthBook |
|
||||
| Auth | OAuth 2.0, JWT, macOS Keychain |
|
||||
|
||||
---
|
||||
|
||||
## Notable Design Patterns
|
||||
|
||||
### Parallel Prefetch
|
||||
|
||||
Startup time is optimized by prefetching MDM settings, keychain reads, and API preconnect in parallel — before heavy module evaluation begins.
|
||||
|
||||
```typescript
|
||||
// main.tsx — fired as side-effects before other imports
|
||||
startMdmRawRead()
|
||||
startKeychainPrefetch()
|
||||
```
|
||||
|
||||
List the current Python modules:
|
||||
### Lazy Loading
|
||||
|
||||
```bash
|
||||
python3 -m src.main subsystems --limit 16
|
||||
```
|
||||
Heavy modules (OpenTelemetry ~400KB, gRPC ~700KB) are deferred via dynamic `import()` until actually needed.
|
||||
|
||||
Run verification:
|
||||
### Agent Swarms
|
||||
|
||||
```bash
|
||||
python3 -m unittest discover -s tests -v
|
||||
```
|
||||
Sub-agents are spawned via `AgentTool`, with `coordinator/` handling multi-agent orchestration. `TeamCreateTool` enables team-level parallel work.
|
||||
|
||||
Run the parity audit against the local ignored archive (when present):
|
||||
### Skill System
|
||||
|
||||
```bash
|
||||
python3 -m src.main parity-audit
|
||||
```
|
||||
Reusable workflows defined in `skills/` and executed through `SkillTool`. Users can add custom skills.
|
||||
|
||||
Inspect mirrored command/tool inventories:
|
||||
### Plugin Architecture
|
||||
|
||||
```bash
|
||||
python3 -m src.main commands --limit 10
|
||||
python3 -m src.main tools --limit 10
|
||||
```
|
||||
Built-in and third-party plugins are loaded through the `plugins/` subsystem.
|
||||
|
||||
## Current Parity Checkpoint
|
||||
---
|
||||
|
||||
The port now mirrors the archived root-entry file surface, top-level subsystem names, and command/tool inventories much more closely than before. However, it is **not yet** a full runtime-equivalent replacement for the original TypeScript system; the Python tree still contains fewer executable runtime slices than the archived source.
|
||||
## Disclaimer
|
||||
|
||||
## Related Essay
|
||||
|
||||
- [*Is legal the same as legitimate: AI reimplementation and the erosion of copyleft*](https://writings.hongminhee.org/2026/03/legal-vs-legitimate/)
|
||||
|
||||
The essay is dated **March 9, 2026**, so it should be read as companion analysis that predates the **March 31, 2026** source exposure that motivated this rewrite direction.
|
||||
|
||||
## Built with `oh-my-codex`
|
||||
|
||||
The restructuring and documentation work on this repository was AI-assisted and orchestrated with Yeachan Heo's [oh-my-codex (OmX)](https://github.com/Yeachan-Heo/oh-my-codex), layered on top of Codex.
|
||||
|
||||
- **`$team` mode:** used for coordinated parallel review and architectural feedback
|
||||
- **`$ralph` mode:** used for persistent execution, verification, and completion discipline
|
||||
- **Codex-driven workflow:** used to turn the main `src/` tree into a Python-first porting workspace
|
||||
|
||||
### OmX workflow screenshots
|
||||
|
||||

|
||||
|
||||
*Ralph/team orchestration view while the README and essay context were being reviewed in terminal panes.*
|
||||
|
||||

|
||||
|
||||
*Split-pane review and verification flow during the final README wording pass.*
|
||||
|
||||
## Ownership / Affiliation Disclaimer
|
||||
|
||||
- This repository does **not** claim ownership of the original Claude Code source material.
|
||||
- This repository is **not affiliated with, endorsed by, or maintained by Anthropic**.
|
||||
This repository archives source code that was leaked from Anthropic's npm registry on **2026-03-31**. All original source code is the property of [Anthropic](https://www.anthropic.com).
|
||||
|
||||
BIN
assets/clawd-hero.jpeg
Normal file
BIN
assets/clawd-hero.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 233 KiB |
BIN
assets/instructkr.png
Normal file
BIN
assets/instructkr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/star-history.png
Normal file
BIN
assets/star-history.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 312 KiB |
BIN
assets/tweet-screenshot.png
Normal file
BIN
assets/tweet-screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 812 KiB |
BIN
assets/wsj-feature.png
Normal file
BIN
assets/wsj-feature.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 873 KiB |
2
rust/.gitignore
vendored
Normal file
2
rust/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
target/
|
||||
.omx/
|
||||
883
rust/Cargo.lock
generated
Normal file
883
rust/Cargo.lock
generated
Normal file
@@ -0,0 +1,883 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "commands"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "compat-harness"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"commands",
|
||||
"runtime",
|
||||
"tools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crossterm_winapi",
|
||||
"derive_more",
|
||||
"document-features",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"rustix",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
|
||||
dependencies = [
|
||||
"derive_more-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more-impl"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
|
||||
dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "998b056554fbe42e03ae0e152895cd1a7e1002aec800fdc6635d20270260c46f"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.183"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plist"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"indexmap",
|
||||
"quick-xml",
|
||||
"serde",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c3a14896dfa883796f1cb410461aef38810ea05f2b2c33c5aded3649095fdad"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"getopts",
|
||||
"memchr",
|
||||
"pulldown-cmark-escape",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark-escape"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.38.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "runtime"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusty-claude-cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"compat-harness",
|
||||
"crossterm",
|
||||
"pulldown-cmark",
|
||||
"runtime",
|
||||
"syntect",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syntect"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "656b45c05d95a5704399aeef6bd0ddec7b2b3531b7c9e900abbf7c4d2190c925"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"fancy-regex",
|
||||
"flate2",
|
||||
"fnv",
|
||||
"once_cell",
|
||||
"plist",
|
||||
"regex-syntax",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"walkdir",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde_core",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tools"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
19
rust/Cargo.toml
Normal file
19
rust/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[workspace]
|
||||
members = ["crates/*"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
publish = false
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "forbid"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
all = { level = "warn", priority = -1 }
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
module_name_repetitions = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
54
rust/README.md
Normal file
54
rust/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Rust port foundation
|
||||
|
||||
This directory contains the first compatibility-first Rust foundation for a drop-in Claude Code CLI replacement.
|
||||
|
||||
## Current milestone
|
||||
|
||||
This initial milestone focuses on **harness-first scaffolding**, not full feature parity:
|
||||
|
||||
- a Cargo workspace aligned to major upstream seams
|
||||
- a placeholder CLI crate (`rusty-claude-cli`)
|
||||
- runtime, command, and tool registry skeleton crates
|
||||
- a `compat-harness` crate that reads the upstream TypeScript sources in `../src/`
|
||||
- tests that prove upstream manifests/bootstrap hints can be extracted from the leaked TypeScript codebase
|
||||
|
||||
## Workspace layout
|
||||
|
||||
```text
|
||||
rust/
|
||||
├── Cargo.toml
|
||||
├── README.md
|
||||
├── crates/
|
||||
│ ├── rusty-claude-cli/
|
||||
│ ├── runtime/
|
||||
│ ├── commands/
|
||||
│ ├── tools/
|
||||
│ └── compat-harness/
|
||||
└── tests/
|
||||
```
|
||||
|
||||
## How to use
|
||||
|
||||
From this directory:
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo check --workspace
|
||||
cargo test --workspace
|
||||
cargo run -p rusty-claude-cli -- --help
|
||||
cargo run -p rusty-claude-cli -- dump-manifests
|
||||
cargo run -p rusty-claude-cli -- bootstrap-plan
|
||||
```
|
||||
|
||||
## Design notes
|
||||
|
||||
The shape follows the PRD's harness-first recommendation:
|
||||
|
||||
1. Extract observable upstream command/tool/bootstrap facts first.
|
||||
2. Keep Rust module boundaries recognizable.
|
||||
3. Grow runtime compatibility behind proof artifacts.
|
||||
4. Document explicit gaps instead of implying drop-in parity too early.
|
||||
|
||||
## Relationship to the root README
|
||||
|
||||
The repository root README explains the leaked TypeScript codebase. This document tracks the Rust replacement effort that lives in `rust/`.
|
||||
9
rust/crates/commands/Cargo.toml
Normal file
9
rust/crates/commands/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "commands"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
29
rust/crates/commands/src/lib.rs
Normal file
29
rust/crates/commands/src/lib.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CommandManifestEntry {
|
||||
pub name: String,
|
||||
pub source: CommandSource,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CommandSource {
|
||||
Builtin,
|
||||
InternalOnly,
|
||||
FeatureGated,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct CommandRegistry {
|
||||
entries: Vec<CommandManifestEntry>,
|
||||
}
|
||||
|
||||
impl CommandRegistry {
|
||||
#[must_use]
|
||||
pub fn new(entries: Vec<CommandManifestEntry>) -> Self {
|
||||
Self { entries }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn entries(&self) -> &[CommandManifestEntry] {
|
||||
&self.entries
|
||||
}
|
||||
}
|
||||
14
rust/crates/compat-harness/Cargo.toml
Normal file
14
rust/crates/compat-harness/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "compat-harness"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
commands = { path = "../commands" }
|
||||
tools = { path = "../tools" }
|
||||
runtime = { path = "../runtime" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
308
rust/crates/compat-harness/src/lib.rs
Normal file
308
rust/crates/compat-harness/src/lib.rs
Normal file
@@ -0,0 +1,308 @@
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use commands::{CommandManifestEntry, CommandRegistry, CommandSource};
|
||||
use runtime::{BootstrapPhase, BootstrapPlan};
|
||||
use tools::{ToolManifestEntry, ToolRegistry, ToolSource};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UpstreamPaths {
|
||||
repo_root: PathBuf,
|
||||
}
|
||||
|
||||
impl UpstreamPaths {
|
||||
#[must_use]
|
||||
pub fn from_repo_root(repo_root: impl Into<PathBuf>) -> Self {
|
||||
Self {
|
||||
repo_root: repo_root.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_workspace_dir(workspace_dir: impl AsRef<Path>) -> Self {
|
||||
let workspace_dir = workspace_dir
|
||||
.as_ref()
|
||||
.canonicalize()
|
||||
.unwrap_or_else(|_| workspace_dir.as_ref().to_path_buf());
|
||||
let repo_root = workspace_dir
|
||||
.parent()
|
||||
.map_or_else(|| PathBuf::from(".."), Path::to_path_buf);
|
||||
Self { repo_root }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn commands_path(&self) -> PathBuf {
|
||||
self.repo_root.join("src/commands.ts")
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn tools_path(&self) -> PathBuf {
|
||||
self.repo_root.join("src/tools.ts")
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn cli_path(&self) -> PathBuf {
|
||||
self.repo_root.join("src/entrypoints/cli.tsx")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExtractedManifest {
|
||||
pub commands: CommandRegistry,
|
||||
pub tools: ToolRegistry,
|
||||
pub bootstrap: BootstrapPlan,
|
||||
}
|
||||
|
||||
pub fn extract_manifest(paths: &UpstreamPaths) -> std::io::Result<ExtractedManifest> {
|
||||
let commands_source = fs::read_to_string(paths.commands_path())?;
|
||||
let tools_source = fs::read_to_string(paths.tools_path())?;
|
||||
let cli_source = fs::read_to_string(paths.cli_path())?;
|
||||
|
||||
Ok(ExtractedManifest {
|
||||
commands: extract_commands(&commands_source),
|
||||
tools: extract_tools(&tools_source),
|
||||
bootstrap: extract_bootstrap_plan(&cli_source),
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn extract_commands(source: &str) -> CommandRegistry {
|
||||
let mut entries = Vec::new();
|
||||
let mut in_internal_block = false;
|
||||
|
||||
for raw_line in source.lines() {
|
||||
let line = raw_line.trim();
|
||||
|
||||
if line.starts_with("export const INTERNAL_ONLY_COMMANDS = [") {
|
||||
in_internal_block = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if in_internal_block {
|
||||
if line.starts_with(']') {
|
||||
in_internal_block = false;
|
||||
continue;
|
||||
}
|
||||
if let Some(name) = first_identifier(line) {
|
||||
entries.push(CommandManifestEntry {
|
||||
name,
|
||||
source: CommandSource::InternalOnly,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if line.starts_with("import ") {
|
||||
for imported in imported_symbols(line) {
|
||||
entries.push(CommandManifestEntry {
|
||||
name: imported,
|
||||
source: CommandSource::Builtin,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if line.contains("feature('") && line.contains("./commands/") {
|
||||
if let Some(name) = first_assignment_identifier(line) {
|
||||
entries.push(CommandManifestEntry {
|
||||
name,
|
||||
source: CommandSource::FeatureGated,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dedupe_commands(entries)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn extract_tools(source: &str) -> ToolRegistry {
|
||||
let mut entries = Vec::new();
|
||||
|
||||
for raw_line in source.lines() {
|
||||
let line = raw_line.trim();
|
||||
if line.starts_with("import ") && line.contains("./tools/") {
|
||||
for imported in imported_symbols(line) {
|
||||
if imported.ends_with("Tool") {
|
||||
entries.push(ToolManifestEntry {
|
||||
name: imported,
|
||||
source: ToolSource::Base,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if line.contains("feature('") && line.contains("Tool") {
|
||||
if let Some(name) = first_assignment_identifier(line) {
|
||||
if name.ends_with("Tool") || name.ends_with("Tools") {
|
||||
entries.push(ToolManifestEntry {
|
||||
name,
|
||||
source: ToolSource::Conditional,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dedupe_tools(entries)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn extract_bootstrap_plan(source: &str) -> BootstrapPlan {
|
||||
let mut phases = vec![BootstrapPhase::CliEntry];
|
||||
|
||||
if source.contains("--version") {
|
||||
phases.push(BootstrapPhase::FastPathVersion);
|
||||
}
|
||||
if source.contains("startupProfiler") {
|
||||
phases.push(BootstrapPhase::StartupProfiler);
|
||||
}
|
||||
if source.contains("--dump-system-prompt") {
|
||||
phases.push(BootstrapPhase::SystemPromptFastPath);
|
||||
}
|
||||
if source.contains("--claude-in-chrome-mcp") {
|
||||
phases.push(BootstrapPhase::ChromeMcpFastPath);
|
||||
}
|
||||
if source.contains("--daemon-worker") {
|
||||
phases.push(BootstrapPhase::DaemonWorkerFastPath);
|
||||
}
|
||||
if source.contains("remote-control") {
|
||||
phases.push(BootstrapPhase::BridgeFastPath);
|
||||
}
|
||||
if source.contains("args[0] === 'daemon'") {
|
||||
phases.push(BootstrapPhase::DaemonFastPath);
|
||||
}
|
||||
if source.contains("args[0] === 'ps'") || source.contains("args.includes('--bg')") {
|
||||
phases.push(BootstrapPhase::BackgroundSessionFastPath);
|
||||
}
|
||||
if source.contains("args[0] === 'new' || args[0] === 'list' || args[0] === 'reply'") {
|
||||
phases.push(BootstrapPhase::TemplateFastPath);
|
||||
}
|
||||
if source.contains("environment-runner") {
|
||||
phases.push(BootstrapPhase::EnvironmentRunnerFastPath);
|
||||
}
|
||||
phases.push(BootstrapPhase::MainRuntime);
|
||||
|
||||
BootstrapPlan::from_phases(phases)
|
||||
}
|
||||
|
||||
fn imported_symbols(line: &str) -> Vec<String> {
|
||||
let Some(after_import) = line.strip_prefix("import ") else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let before_from = after_import
|
||||
.split(" from ")
|
||||
.next()
|
||||
.unwrap_or_default()
|
||||
.trim();
|
||||
if before_from.starts_with('{') {
|
||||
return before_from
|
||||
.trim_matches(|c| c == '{' || c == '}')
|
||||
.split(',')
|
||||
.filter_map(|part| {
|
||||
let trimmed = part.trim();
|
||||
if trimmed.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(trimmed.split_whitespace().next()?.to_string())
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
let first = before_from.split(',').next().unwrap_or_default().trim();
|
||||
if first.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
vec![first.to_string()]
|
||||
}
|
||||
}
|
||||
|
||||
fn first_assignment_identifier(line: &str) -> Option<String> {
|
||||
let trimmed = line.trim_start();
|
||||
let candidate = trimmed.split('=').next()?.trim();
|
||||
first_identifier(candidate)
|
||||
}
|
||||
|
||||
fn first_identifier(line: &str) -> Option<String> {
|
||||
let mut out = String::new();
|
||||
for ch in line.chars() {
|
||||
if ch.is_ascii_alphanumeric() || ch == '_' || ch == '-' {
|
||||
out.push(ch);
|
||||
} else if !out.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(!out.is_empty()).then_some(out)
|
||||
}
|
||||
|
||||
fn dedupe_commands(entries: Vec<CommandManifestEntry>) -> CommandRegistry {
|
||||
let mut deduped = Vec::new();
|
||||
for entry in entries {
|
||||
let exists = deduped.iter().any(|seen: &CommandManifestEntry| {
|
||||
seen.name == entry.name && seen.source == entry.source
|
||||
});
|
||||
if !exists {
|
||||
deduped.push(entry);
|
||||
}
|
||||
}
|
||||
CommandRegistry::new(deduped)
|
||||
}
|
||||
|
||||
fn dedupe_tools(entries: Vec<ToolManifestEntry>) -> ToolRegistry {
|
||||
let mut deduped = Vec::new();
|
||||
for entry in entries {
|
||||
let exists = deduped
|
||||
.iter()
|
||||
.any(|seen: &ToolManifestEntry| seen.name == entry.name && seen.source == entry.source);
|
||||
if !exists {
|
||||
deduped.push(entry);
|
||||
}
|
||||
}
|
||||
ToolRegistry::new(deduped)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn fixture_paths() -> UpstreamPaths {
|
||||
let workspace_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
|
||||
UpstreamPaths::from_workspace_dir(workspace_dir)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_non_empty_manifests_from_upstream_repo() {
|
||||
let manifest = extract_manifest(&fixture_paths()).expect("manifest should load");
|
||||
assert!(!manifest.commands.entries().is_empty());
|
||||
assert!(!manifest.tools.entries().is_empty());
|
||||
assert!(!manifest.bootstrap.phases().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_known_upstream_command_symbols() {
|
||||
let commands = extract_commands(
|
||||
&fs::read_to_string(fixture_paths().commands_path()).expect("commands.ts"),
|
||||
);
|
||||
let names: Vec<_> = commands
|
||||
.entries()
|
||||
.iter()
|
||||
.map(|entry| entry.name.as_str())
|
||||
.collect();
|
||||
assert!(names.contains(&"addDir"));
|
||||
assert!(names.contains(&"review"));
|
||||
assert!(!names.contains(&"INTERNAL_ONLY_COMMANDS"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_known_upstream_tool_symbols() {
|
||||
let tools =
|
||||
extract_tools(&fs::read_to_string(fixture_paths().tools_path()).expect("tools.ts"));
|
||||
let names: Vec<_> = tools
|
||||
.entries()
|
||||
.iter()
|
||||
.map(|entry| entry.name.as_str())
|
||||
.collect();
|
||||
assert!(names.contains(&"AgentTool"));
|
||||
assert!(names.contains(&"BashTool"));
|
||||
}
|
||||
}
|
||||
9
rust/crates/runtime/Cargo.toml
Normal file
9
rust/crates/runtime/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "runtime"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
56
rust/crates/runtime/src/lib.rs
Normal file
56
rust/crates/runtime/src/lib.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BootstrapPhase {
|
||||
CliEntry,
|
||||
FastPathVersion,
|
||||
StartupProfiler,
|
||||
SystemPromptFastPath,
|
||||
ChromeMcpFastPath,
|
||||
DaemonWorkerFastPath,
|
||||
BridgeFastPath,
|
||||
DaemonFastPath,
|
||||
BackgroundSessionFastPath,
|
||||
TemplateFastPath,
|
||||
EnvironmentRunnerFastPath,
|
||||
MainRuntime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct BootstrapPlan {
|
||||
phases: Vec<BootstrapPhase>,
|
||||
}
|
||||
|
||||
impl BootstrapPlan {
|
||||
#[must_use]
|
||||
pub fn claude_code_default() -> Self {
|
||||
Self::from_phases(vec![
|
||||
BootstrapPhase::CliEntry,
|
||||
BootstrapPhase::FastPathVersion,
|
||||
BootstrapPhase::StartupProfiler,
|
||||
BootstrapPhase::SystemPromptFastPath,
|
||||
BootstrapPhase::ChromeMcpFastPath,
|
||||
BootstrapPhase::DaemonWorkerFastPath,
|
||||
BootstrapPhase::BridgeFastPath,
|
||||
BootstrapPhase::DaemonFastPath,
|
||||
BootstrapPhase::BackgroundSessionFastPath,
|
||||
BootstrapPhase::TemplateFastPath,
|
||||
BootstrapPhase::EnvironmentRunnerFastPath,
|
||||
BootstrapPhase::MainRuntime,
|
||||
])
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_phases(phases: Vec<BootstrapPhase>) -> Self {
|
||||
let mut deduped = Vec::new();
|
||||
for phase in phases {
|
||||
if !deduped.contains(&phase) {
|
||||
deduped.push(phase);
|
||||
}
|
||||
}
|
||||
Self { phases: deduped }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn phases(&self) -> &[BootstrapPhase] {
|
||||
&self.phases
|
||||
}
|
||||
}
|
||||
17
rust/crates/rusty-claude-cli/Cargo.toml
Normal file
17
rust/crates/rusty-claude-cli/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "rusty-claude-cli"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.38", features = ["derive"] }
|
||||
compat-harness = { path = "../compat-harness" }
|
||||
crossterm = "0.29.0"
|
||||
pulldown-cmark = "0.13.0"
|
||||
runtime = { path = "../runtime" }
|
||||
syntect = { version = "5.2.0", default-features = false, features = ["default-fancy"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
246
rust/crates/rusty-claude-cli/src/app.rs
Normal file
246
rust/crates/rusty-claude-cli/src/app.rs
Normal file
@@ -0,0 +1,246 @@
|
||||
use std::io::{self, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::args::{OutputFormat, PermissionMode};
|
||||
use crate::input::LineEditor;
|
||||
use crate::render::{Spinner, TerminalRenderer};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SessionConfig {
|
||||
pub model: String,
|
||||
pub permission_mode: PermissionMode,
|
||||
pub config: Option<PathBuf>,
|
||||
pub output_format: OutputFormat,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SessionState {
|
||||
pub turns: usize,
|
||||
pub compacted_messages: usize,
|
||||
pub last_model: String,
|
||||
}
|
||||
|
||||
impl SessionState {
|
||||
#[must_use]
|
||||
pub fn new(model: impl Into<String>) -> Self {
|
||||
Self {
|
||||
turns: 0,
|
||||
compacted_messages: 0,
|
||||
last_model: model.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CommandResult {
|
||||
Continue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum SlashCommand {
|
||||
Help,
|
||||
Status,
|
||||
Compact,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl SlashCommand {
|
||||
#[must_use]
|
||||
pub fn parse(input: &str) -> Option<Self> {
|
||||
let trimmed = input.trim();
|
||||
if !trimmed.starts_with('/') {
|
||||
return None;
|
||||
}
|
||||
|
||||
let command = trimmed
|
||||
.trim_start_matches('/')
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.unwrap_or_default();
|
||||
Some(match command {
|
||||
"help" => Self::Help,
|
||||
"status" => Self::Status,
|
||||
"compact" => Self::Compact,
|
||||
other => Self::Unknown(other.to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CliApp {
|
||||
config: SessionConfig,
|
||||
renderer: TerminalRenderer,
|
||||
state: SessionState,
|
||||
}
|
||||
|
||||
impl CliApp {
|
||||
#[must_use]
|
||||
pub fn new(config: SessionConfig) -> Self {
|
||||
let state = SessionState::new(config.model.clone());
|
||||
Self {
|
||||
config,
|
||||
renderer: TerminalRenderer::new(),
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_repl(&mut self) -> io::Result<()> {
|
||||
let editor = LineEditor::new("› ");
|
||||
println!("Rusty Claude CLI interactive mode");
|
||||
println!("Type /help for commands. Shift+Enter or Ctrl+J inserts a newline.");
|
||||
|
||||
while let Some(input) = editor.read_line()? {
|
||||
if input.trim().is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.handle_submission(&input, &mut io::stdout())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_prompt(&mut self, prompt: &str, out: &mut impl Write) -> io::Result<()> {
|
||||
self.render_response(prompt, out)
|
||||
}
|
||||
|
||||
pub fn handle_submission(
|
||||
&mut self,
|
||||
input: &str,
|
||||
out: &mut impl Write,
|
||||
) -> io::Result<CommandResult> {
|
||||
if let Some(command) = SlashCommand::parse(input) {
|
||||
return self.handle_command(command, out);
|
||||
}
|
||||
|
||||
self.state.turns += 1;
|
||||
self.render_response(input, out)?;
|
||||
Ok(CommandResult::Continue)
|
||||
}
|
||||
|
||||
fn handle_command(
|
||||
&mut self,
|
||||
command: SlashCommand,
|
||||
out: &mut impl Write,
|
||||
) -> io::Result<CommandResult> {
|
||||
match command {
|
||||
SlashCommand::Help => {
|
||||
writeln!(
|
||||
out,
|
||||
"Available commands:\n /help Show command help\n /status Show current session status\n /compact Compact local session history"
|
||||
)?;
|
||||
}
|
||||
SlashCommand::Status => {
|
||||
writeln!(
|
||||
out,
|
||||
"status: turns={} model={} permission-mode={:?} output-format={:?} config={}",
|
||||
self.state.turns,
|
||||
self.state.last_model,
|
||||
self.config.permission_mode,
|
||||
self.config.output_format,
|
||||
self.config
|
||||
.config
|
||||
.as_ref()
|
||||
.map_or_else(|| String::from("<none>"), |path| path.display().to_string())
|
||||
)?;
|
||||
}
|
||||
SlashCommand::Compact => {
|
||||
self.state.compacted_messages += self.state.turns;
|
||||
self.state.turns = 0;
|
||||
writeln!(
|
||||
out,
|
||||
"Compacted session history into a local summary ({} messages total compacted).",
|
||||
self.state.compacted_messages
|
||||
)?;
|
||||
}
|
||||
SlashCommand::Unknown(name) => {
|
||||
writeln!(out, "Unknown slash command: /{name}")?;
|
||||
}
|
||||
}
|
||||
Ok(CommandResult::Continue)
|
||||
}
|
||||
|
||||
fn render_response(&mut self, input: &str, out: &mut impl Write) -> io::Result<()> {
|
||||
let mut spinner = Spinner::new();
|
||||
for _ in 0..3 {
|
||||
spinner.tick("Generating response", out)?;
|
||||
thread::sleep(Duration::from_millis(24));
|
||||
}
|
||||
spinner.finish("Streaming response", out)?;
|
||||
|
||||
let response = demo_response(input, &self.config);
|
||||
match self.config.output_format {
|
||||
OutputFormat::Text => self.renderer.stream_markdown(&response, out)?,
|
||||
OutputFormat::Json => writeln!(out, "{{\"message\":{response:?}}}")?,
|
||||
OutputFormat::Ndjson => {
|
||||
writeln!(out, "{{\"type\":\"message\",\"text\":{response:?}}}")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn demo_response(input: &str, config: &SessionConfig) -> String {
|
||||
format!(
|
||||
"## Assistant\n\nModel: `{}` \nPermission mode: `{}`\n\nYou said:\n\n> {}\n\n```rust\nfn main() {{\n println!(\"streaming from rusty-claude-cli\");\n}}\n```",
|
||||
config.model,
|
||||
permission_mode_label(config.permission_mode),
|
||||
input.trim()
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn permission_mode_label(mode: PermissionMode) -> &'static str {
|
||||
match mode {
|
||||
PermissionMode::ReadOnly => "read-only",
|
||||
PermissionMode::WorkspaceWrite => "workspace-write",
|
||||
PermissionMode::DangerFullAccess => "danger-full-access",
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::args::{OutputFormat, PermissionMode};
|
||||
|
||||
use super::{CliApp, CommandResult, SessionConfig, SlashCommand};
|
||||
|
||||
#[test]
|
||||
fn parses_required_slash_commands() {
|
||||
assert_eq!(SlashCommand::parse("/help"), Some(SlashCommand::Help));
|
||||
assert_eq!(SlashCommand::parse(" /status "), Some(SlashCommand::Status));
|
||||
assert_eq!(
|
||||
SlashCommand::parse("/compact now"),
|
||||
Some(SlashCommand::Compact)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_and_compact_commands_update_state() {
|
||||
let config = SessionConfig {
|
||||
model: "claude".into(),
|
||||
permission_mode: PermissionMode::WorkspaceWrite,
|
||||
config: Some(PathBuf::from("settings.toml")),
|
||||
output_format: OutputFormat::Text,
|
||||
};
|
||||
let mut app = CliApp::new(config);
|
||||
let mut out = Vec::new();
|
||||
|
||||
let result = app
|
||||
.handle_submission("hello", &mut out)
|
||||
.expect("submission succeeds");
|
||||
assert_eq!(result, CommandResult::Continue);
|
||||
|
||||
app.handle_submission("/status", &mut out)
|
||||
.expect("status succeeds");
|
||||
app.handle_submission("/compact", &mut out)
|
||||
.expect("compact succeeds");
|
||||
|
||||
let output = String::from_utf8_lossy(&out);
|
||||
assert!(output.contains("status: turns=1"));
|
||||
assert!(output.contains("Compacted session history"));
|
||||
}
|
||||
}
|
||||
89
rust/crates/rusty-claude-cli/src/args.rs
Normal file
89
rust/crates/rusty-claude-cli/src/args.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
|
||||
#[derive(Debug, Clone, Parser, PartialEq, Eq)]
|
||||
#[command(
|
||||
name = "rusty-claude-cli",
|
||||
version,
|
||||
about = "Rust Claude CLI prototype"
|
||||
)]
|
||||
pub struct Cli {
|
||||
#[arg(long, default_value = "claude-3-7-sonnet")]
|
||||
pub model: String,
|
||||
|
||||
#[arg(long, value_enum, default_value_t = PermissionMode::WorkspaceWrite)]
|
||||
pub permission_mode: PermissionMode,
|
||||
|
||||
#[arg(long)]
|
||||
pub config: Option<PathBuf>,
|
||||
|
||||
#[arg(long, value_enum, default_value_t = OutputFormat::Text)]
|
||||
pub output_format: OutputFormat,
|
||||
|
||||
#[command(subcommand)]
|
||||
pub command: Option<Command>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Subcommand, PartialEq, Eq)]
|
||||
pub enum Command {
|
||||
/// Read upstream TS sources and print extracted counts
|
||||
DumpManifests,
|
||||
/// Print the current bootstrap phase skeleton
|
||||
BootstrapPlan,
|
||||
/// Run a non-interactive prompt and exit
|
||||
Prompt { prompt: Vec<String> },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq)]
|
||||
pub enum PermissionMode {
|
||||
ReadOnly,
|
||||
WorkspaceWrite,
|
||||
DangerFullAccess,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq)]
|
||||
pub enum OutputFormat {
|
||||
Text,
|
||||
Json,
|
||||
Ndjson,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use clap::Parser;
|
||||
|
||||
use super::{Cli, Command, OutputFormat, PermissionMode};
|
||||
|
||||
#[test]
|
||||
fn parses_requested_flags() {
|
||||
let cli = Cli::parse_from([
|
||||
"rusty-claude-cli",
|
||||
"--model",
|
||||
"claude-3-5-haiku",
|
||||
"--permission-mode",
|
||||
"read-only",
|
||||
"--config",
|
||||
"/tmp/config.toml",
|
||||
"--output-format",
|
||||
"ndjson",
|
||||
"prompt",
|
||||
"hello",
|
||||
"world",
|
||||
]);
|
||||
|
||||
assert_eq!(cli.model, "claude-3-5-haiku");
|
||||
assert_eq!(cli.permission_mode, PermissionMode::ReadOnly);
|
||||
assert_eq!(
|
||||
cli.config.as_deref(),
|
||||
Some(std::path::Path::new("/tmp/config.toml"))
|
||||
);
|
||||
assert_eq!(cli.output_format, OutputFormat::Ndjson);
|
||||
assert_eq!(
|
||||
cli.command,
|
||||
Some(Command::Prompt {
|
||||
prompt: vec!["hello".into(), "world".into()]
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
248
rust/crates/rusty-claude-cli/src/input.rs
Normal file
248
rust/crates/rusty-claude-cli/src/input.rs
Normal file
@@ -0,0 +1,248 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
use crossterm::cursor::MoveToColumn;
|
||||
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers};
|
||||
use crossterm::queue;
|
||||
use crossterm::style::Print;
|
||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InputBuffer {
|
||||
buffer: String,
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
impl InputBuffer {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
buffer: String::new(),
|
||||
cursor: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, ch: char) {
|
||||
self.buffer.insert(self.cursor, ch);
|
||||
self.cursor += ch.len_utf8();
|
||||
}
|
||||
|
||||
pub fn insert_newline(&mut self) {
|
||||
self.insert('\n');
|
||||
}
|
||||
|
||||
pub fn backspace(&mut self) {
|
||||
if self.cursor == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let previous = self.buffer[..self.cursor]
|
||||
.char_indices()
|
||||
.last()
|
||||
.map_or(0, |(idx, _)| idx);
|
||||
self.buffer.drain(previous..self.cursor);
|
||||
self.cursor = previous;
|
||||
}
|
||||
|
||||
pub fn move_left(&mut self) {
|
||||
if self.cursor == 0 {
|
||||
return;
|
||||
}
|
||||
self.cursor = self.buffer[..self.cursor]
|
||||
.char_indices()
|
||||
.last()
|
||||
.map_or(0, |(idx, _)| idx);
|
||||
}
|
||||
|
||||
pub fn move_right(&mut self) {
|
||||
if self.cursor >= self.buffer.len() {
|
||||
return;
|
||||
}
|
||||
if let Some(next) = self.buffer[self.cursor..].chars().next() {
|
||||
self.cursor += next.len_utf8();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_home(&mut self) {
|
||||
self.cursor = 0;
|
||||
}
|
||||
|
||||
pub fn move_end(&mut self) {
|
||||
self.cursor = self.buffer.len();
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.buffer
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[must_use]
|
||||
pub fn cursor(&self) -> usize {
|
||||
self.cursor
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.buffer.clear();
|
||||
self.cursor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LineEditor {
|
||||
prompt: String,
|
||||
}
|
||||
|
||||
impl LineEditor {
|
||||
#[must_use]
|
||||
pub fn new(prompt: impl Into<String>) -> Self {
|
||||
Self {
|
||||
prompt: prompt.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_line(&self) -> io::Result<Option<String>> {
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
let mut input = InputBuffer::new();
|
||||
self.redraw(&mut stdout, &input)?;
|
||||
|
||||
loop {
|
||||
let event = event::read()?;
|
||||
if let Event::Key(key) = event {
|
||||
match Self::handle_key(key, &mut input) {
|
||||
EditorAction::Continue => self.redraw(&mut stdout, &input)?,
|
||||
EditorAction::Submit => {
|
||||
disable_raw_mode()?;
|
||||
writeln!(stdout)?;
|
||||
return Ok(Some(input.as_str().to_owned()));
|
||||
}
|
||||
EditorAction::Cancel => {
|
||||
disable_raw_mode()?;
|
||||
writeln!(stdout)?;
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_key(key: KeyEvent, input: &mut InputBuffer) -> EditorAction {
|
||||
match key {
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('c'),
|
||||
modifiers,
|
||||
..
|
||||
} if modifiers.contains(KeyModifiers::CONTROL) => EditorAction::Cancel,
|
||||
KeyEvent {
|
||||
code: KeyCode::Char('j'),
|
||||
modifiers,
|
||||
..
|
||||
} if modifiers.contains(KeyModifiers::CONTROL) => {
|
||||
input.insert_newline();
|
||||
EditorAction::Continue
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Enter,
|
||||
modifiers,
|
||||
..
|
||||
} if modifiers.contains(KeyModifiers::SHIFT) => {
|
||||
input.insert_newline();
|
||||
EditorAction::Continue
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Enter,
|
||||
..
|
||||
} => EditorAction::Submit,
|
||||
KeyEvent {
|
||||
code: KeyCode::Backspace,
|
||||
..
|
||||
} => {
|
||||
input.backspace();
|
||||
EditorAction::Continue
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Left,
|
||||
..
|
||||
} => {
|
||||
input.move_left();
|
||||
EditorAction::Continue
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Right,
|
||||
..
|
||||
} => {
|
||||
input.move_right();
|
||||
EditorAction::Continue
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Home,
|
||||
..
|
||||
} => {
|
||||
input.move_home();
|
||||
EditorAction::Continue
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::End, ..
|
||||
} => {
|
||||
input.move_end();
|
||||
EditorAction::Continue
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Esc, ..
|
||||
} => {
|
||||
input.clear();
|
||||
EditorAction::Cancel
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Char(ch),
|
||||
modifiers,
|
||||
..
|
||||
} if modifiers.is_empty() || modifiers == KeyModifiers::SHIFT => {
|
||||
input.insert(ch);
|
||||
EditorAction::Continue
|
||||
}
|
||||
_ => EditorAction::Continue,
|
||||
}
|
||||
}
|
||||
|
||||
fn redraw(&self, out: &mut impl Write, input: &InputBuffer) -> io::Result<()> {
|
||||
let display = input.as_str().replace('\n', "\\n\n> ");
|
||||
queue!(
|
||||
out,
|
||||
MoveToColumn(0),
|
||||
Clear(ClearType::CurrentLine),
|
||||
Print(&self.prompt),
|
||||
Print(display),
|
||||
)?;
|
||||
out.flush()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum EditorAction {
|
||||
Continue,
|
||||
Submit,
|
||||
Cancel,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::InputBuffer;
|
||||
|
||||
#[test]
|
||||
fn supports_basic_line_editing() {
|
||||
let mut input = InputBuffer::new();
|
||||
input.insert('h');
|
||||
input.insert('i');
|
||||
input.move_end();
|
||||
input.insert_newline();
|
||||
input.insert('x');
|
||||
|
||||
assert_eq!(input.as_str(), "hi\nx");
|
||||
assert_eq!(input.cursor(), 4);
|
||||
|
||||
input.move_left();
|
||||
input.backspace();
|
||||
assert_eq!(input.as_str(), "hix");
|
||||
assert_eq!(input.cursor(), 2);
|
||||
}
|
||||
}
|
||||
63
rust/crates/rusty-claude-cli/src/main.rs
Normal file
63
rust/crates/rusty-claude-cli/src/main.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
mod app;
|
||||
mod args;
|
||||
mod input;
|
||||
mod render;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use app::{CliApp, SessionConfig};
|
||||
use args::{Cli, Command};
|
||||
use clap::Parser;
|
||||
use compat_harness::{extract_manifest, UpstreamPaths};
|
||||
use runtime::BootstrapPlan;
|
||||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
let result = match &cli.command {
|
||||
Some(Command::DumpManifests) => dump_manifests(),
|
||||
Some(Command::BootstrapPlan) => {
|
||||
print_bootstrap_plan();
|
||||
Ok(())
|
||||
}
|
||||
Some(Command::Prompt { prompt }) => {
|
||||
let joined = prompt.join(" ");
|
||||
let mut app = CliApp::new(build_session_config(&cli));
|
||||
app.run_prompt(&joined, &mut std::io::stdout())
|
||||
}
|
||||
None => {
|
||||
let mut app = CliApp::new(build_session_config(&cli));
|
||||
app.run_repl()
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(error) = result {
|
||||
eprintln!("{error}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_session_config(cli: &Cli) -> SessionConfig {
|
||||
SessionConfig {
|
||||
model: cli.model.clone(),
|
||||
permission_mode: cli.permission_mode,
|
||||
config: cli.config.clone(),
|
||||
output_format: cli.output_format,
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_manifests() -> std::io::Result<()> {
|
||||
let workspace_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..");
|
||||
let paths = UpstreamPaths::from_workspace_dir(&workspace_dir);
|
||||
let manifest = extract_manifest(&paths)?;
|
||||
println!("commands: {}", manifest.commands.entries().len());
|
||||
println!("tools: {}", manifest.tools.entries().len());
|
||||
println!("bootstrap phases: {}", manifest.bootstrap.phases().len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_bootstrap_plan() {
|
||||
for phase in BootstrapPlan::claude_code_default().phases() {
|
||||
println!("- {phase:?}");
|
||||
}
|
||||
}
|
||||
244
rust/crates/rusty-claude-cli/src/render.rs
Normal file
244
rust/crates/rusty-claude-cli/src/render.rs
Normal file
@@ -0,0 +1,244 @@
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::io::{self, Write};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossterm::cursor::{MoveToColumn, RestorePosition, SavePosition};
|
||||
use crossterm::style::{Color, Print, ResetColor, SetForegroundColor, Stylize};
|
||||
use crossterm::terminal::{Clear, ClearType};
|
||||
use crossterm::{execute, queue};
|
||||
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};
|
||||
use syntect::easy::HighlightLines;
|
||||
use syntect::highlighting::{Theme, ThemeSet};
|
||||
use syntect::parsing::SyntaxSet;
|
||||
use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct Spinner {
|
||||
frame_index: usize,
|
||||
}
|
||||
|
||||
impl Spinner {
|
||||
const FRAMES: [&str; 10] = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
||||
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, label: &str, out: &mut impl Write) -> io::Result<()> {
|
||||
let frame = Self::FRAMES[self.frame_index % Self::FRAMES.len()];
|
||||
self.frame_index += 1;
|
||||
queue!(
|
||||
out,
|
||||
SavePosition,
|
||||
MoveToColumn(0),
|
||||
Clear(ClearType::CurrentLine),
|
||||
SetForegroundColor(Color::Blue),
|
||||
Print(format!("{frame} {label}")),
|
||||
ResetColor,
|
||||
RestorePosition
|
||||
)?;
|
||||
out.flush()
|
||||
}
|
||||
|
||||
pub fn finish(&mut self, label: &str, out: &mut impl Write) -> io::Result<()> {
|
||||
self.frame_index = 0;
|
||||
execute!(
|
||||
out,
|
||||
MoveToColumn(0),
|
||||
Clear(ClearType::CurrentLine),
|
||||
SetForegroundColor(Color::Green),
|
||||
Print(format!("✔ {label}\n")),
|
||||
ResetColor
|
||||
)?;
|
||||
out.flush()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TerminalRenderer {
|
||||
syntax_set: SyntaxSet,
|
||||
theme: Theme,
|
||||
}
|
||||
|
||||
impl Default for TerminalRenderer {
|
||||
fn default() -> Self {
|
||||
let syntax_set = SyntaxSet::load_defaults_newlines();
|
||||
let theme = ThemeSet::load_defaults()
|
||||
.themes
|
||||
.remove("base16-ocean.dark")
|
||||
.unwrap_or_default();
|
||||
Self { syntax_set, theme }
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalRenderer {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn render_markdown(&self, markdown: &str) -> String {
|
||||
let mut output = String::new();
|
||||
let parser = Parser::new_ext(markdown, Options::all());
|
||||
let mut in_code_block = false;
|
||||
let mut code_language = String::new();
|
||||
let mut code_buffer = String::new();
|
||||
let mut list_depth = 0usize;
|
||||
|
||||
for event in parser {
|
||||
match event {
|
||||
Event::Start(Tag::Heading { level, .. }) => {
|
||||
output.push('\n');
|
||||
let prefix = match level as u8 {
|
||||
1 => "# ",
|
||||
2 => "## ",
|
||||
3 => "### ",
|
||||
_ => "#### ",
|
||||
};
|
||||
output.push_str(prefix);
|
||||
}
|
||||
Event::End(TagEnd::Heading(..) | TagEnd::Paragraph) => {
|
||||
output.push_str("\n\n");
|
||||
}
|
||||
Event::Start(Tag::BlockQuote(..)) => output.push_str("│ "),
|
||||
Event::End(TagEnd::BlockQuote(..) | TagEnd::Item)
|
||||
| Event::SoftBreak
|
||||
| Event::HardBreak => output.push('\n'),
|
||||
Event::Start(Tag::List(_)) => list_depth += 1,
|
||||
Event::End(TagEnd::List(..)) => {
|
||||
list_depth = list_depth.saturating_sub(1);
|
||||
output.push('\n');
|
||||
}
|
||||
Event::Start(Tag::Item) => {
|
||||
output.push_str(&" ".repeat(list_depth.saturating_sub(1)));
|
||||
output.push_str("• ");
|
||||
}
|
||||
Event::Start(Tag::CodeBlock(kind)) => {
|
||||
in_code_block = true;
|
||||
code_language = match kind {
|
||||
CodeBlockKind::Indented => String::from("text"),
|
||||
CodeBlockKind::Fenced(lang) => lang.to_string(),
|
||||
};
|
||||
code_buffer.clear();
|
||||
}
|
||||
Event::End(TagEnd::CodeBlock) => {
|
||||
output.push_str(&self.highlight_code(&code_buffer, &code_language));
|
||||
output.push('\n');
|
||||
in_code_block = false;
|
||||
code_language.clear();
|
||||
code_buffer.clear();
|
||||
}
|
||||
Event::Code(code) => {
|
||||
let _ = write!(output, "{}", format!("`{code}`").with(Color::Yellow));
|
||||
}
|
||||
Event::Rule => output.push_str("---\n"),
|
||||
Event::Text(text) => {
|
||||
if in_code_block {
|
||||
code_buffer.push_str(&text);
|
||||
} else {
|
||||
output.push_str(&text);
|
||||
}
|
||||
}
|
||||
Event::Html(html) | Event::InlineHtml(html) => output.push_str(&html),
|
||||
Event::FootnoteReference(reference) => {
|
||||
let _ = write!(output, "[{reference}]");
|
||||
}
|
||||
Event::TaskListMarker(done) => {
|
||||
output.push_str(if done { "[x] " } else { "[ ] " });
|
||||
}
|
||||
Event::InlineMath(math) | Event::DisplayMath(math) => output.push_str(&math),
|
||||
Event::Start(Tag::Link { dest_url, .. }) => {
|
||||
let _ = write!(output, "[{dest_url}]");
|
||||
}
|
||||
Event::Start(Tag::Image { dest_url, .. }) => {
|
||||
let _ = write!(output, "[image:{dest_url}]");
|
||||
}
|
||||
Event::Start(
|
||||
Tag::Paragraph
|
||||
| Tag::Emphasis
|
||||
| Tag::Strong
|
||||
| Tag::Table(..)
|
||||
| Tag::TableHead
|
||||
| Tag::TableRow
|
||||
| Tag::TableCell
|
||||
| Tag::MetadataBlock(..)
|
||||
| _,
|
||||
)
|
||||
| Event::End(
|
||||
TagEnd::Emphasis
|
||||
| TagEnd::Strong
|
||||
| TagEnd::Link
|
||||
| TagEnd::Image
|
||||
| TagEnd::Table
|
||||
| TagEnd::TableHead
|
||||
| TagEnd::TableRow
|
||||
| TagEnd::TableCell
|
||||
| TagEnd::MetadataBlock(..)
|
||||
| _,
|
||||
) => {}
|
||||
}
|
||||
}
|
||||
|
||||
output.trim_end().to_string()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn highlight_code(&self, code: &str, language: &str) -> String {
|
||||
let syntax = self
|
||||
.syntax_set
|
||||
.find_syntax_by_token(language)
|
||||
.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text());
|
||||
let mut syntax_highlighter = HighlightLines::new(syntax, &self.theme);
|
||||
let mut colored_output = String::new();
|
||||
|
||||
for line in LinesWithEndings::from(code) {
|
||||
match syntax_highlighter.highlight_line(line, &self.syntax_set) {
|
||||
Ok(ranges) => {
|
||||
colored_output.push_str(&as_24_bit_terminal_escaped(&ranges[..], false));
|
||||
}
|
||||
Err(_) => colored_output.push_str(line),
|
||||
}
|
||||
}
|
||||
|
||||
colored_output
|
||||
}
|
||||
|
||||
pub fn stream_markdown(&self, markdown: &str, out: &mut impl Write) -> io::Result<()> {
|
||||
let rendered_markdown = self.render_markdown(markdown);
|
||||
for chunk in rendered_markdown.split_inclusive(char::is_whitespace) {
|
||||
write!(out, "{chunk}")?;
|
||||
out.flush()?;
|
||||
thread::sleep(Duration::from_millis(8));
|
||||
}
|
||||
writeln!(out)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Spinner, TerminalRenderer};
|
||||
|
||||
#[test]
|
||||
fn renders_basic_markdown_features() {
|
||||
let terminal_renderer = TerminalRenderer::new();
|
||||
let markdown_output = terminal_renderer.render_markdown("# Heading\n\n- item\n\n`code`");
|
||||
|
||||
assert!(markdown_output.contains("# Heading"));
|
||||
assert!(markdown_output.contains("• item"));
|
||||
assert!(markdown_output.contains("code"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spinner_advances_frames() {
|
||||
let mut spinner = Spinner::new();
|
||||
let mut out = Vec::new();
|
||||
spinner.tick("Working", &mut out).expect("tick succeeds");
|
||||
spinner.tick("Working", &mut out).expect("tick succeeds");
|
||||
|
||||
let output = String::from_utf8_lossy(&out);
|
||||
assert!(output.contains("Working"));
|
||||
}
|
||||
}
|
||||
9
rust/crates/tools/Cargo.toml
Normal file
9
rust/crates/tools/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "tools"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
28
rust/crates/tools/src/lib.rs
Normal file
28
rust/crates/tools/src/lib.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ToolManifestEntry {
|
||||
pub name: String,
|
||||
pub source: ToolSource,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ToolSource {
|
||||
Base,
|
||||
Conditional,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct ToolRegistry {
|
||||
entries: Vec<ToolManifestEntry>,
|
||||
}
|
||||
|
||||
impl ToolRegistry {
|
||||
#[must_use]
|
||||
pub fn new(entries: Vec<ToolManifestEntry>) -> Self {
|
||||
Self { entries }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn entries(&self) -> &[ToolManifestEntry] {
|
||||
&self.entries
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user