Counts lines, words and bytes, like wc, with output that lines up.
cargo install --path .\\ntally notes.txt\\necho \\\"one two\\\" | tally\\n\\n| Column | Meaning |
|---|---|
| 1 | lines |
| 2 | words, split on whitespace |
| 3 | bytes |
See the design notes for why it reads the whole file.
\\nMIT
\\n\"}" }, "/xrpc/dev.knotless.getFile?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&ref=main%2Fassets%2Flogo.svg": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"ref\":\"main\",\"path\":\"assets/logo.svg\",\"kind\":\"image\",\"size\":282,\"lines\":1}" }, "/xrpc/dev.knotless.getFile?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&ref=main%2Fdocs%2FDESIGN.md": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"ref\":\"main\",\"path\":\"docs/DESIGN.md\",\"kind\":\"text\",\"size\":341,\"lines\":14,\"text\":\"# Design\\n\\ntally reads the whole input before counting. Inputs are small, and one\\npass over a string is simpler than a streaming reader.\\n\\n```mermaid\\nflowchart LR\\n input[File or stdin] --> read[Read it whole]\\n read --> count{Count}\\n count --> lines\\n count --> words\\n count --> bytes\\n lines & words & bytes --> row[One row, lined up]\\n```\\n\",\"ranges\":\"comment:137,3,197,3;function:0,9;attr:140,7\",\"html\":\"tally reads the whole input before counting. Inputs are small, and one\\npass over a string is simpler than a streaming reader.
\\nflowchart LR\\n input[File or stdin] --> read[Read it whole]\\n read --> count{Count}\\n count --> lines\\n count --> words\\n count --> bytes\\n lines & words & bytes --> row[One row, lined up]\\n\\n\"}"
},
"/xrpc/dev.knotless.getFile?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&ref=main%2Fnowhere": {
"status": 404,
"headers": {
"content-type": "application/json",
"cache-control": "no-store"
},
"body": "{\"error\":\"NotFound\",\"message\":\"no nowhere at main\"}"
},
"/xrpc/dev.knotless.getFile?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&ref=main%2Fsrc%2Flib.rs": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"ref\":\"main\",\"path\":\"src/lib.rs\",\"kind\":\"text\",\"size\":783,\"lines\":37,\"text\":\"//! Counting, kept apart from reading so it can be tested.\\n\\nmod parse;\\n\\n/// What a text holds.\\n#[derive(Debug, Default, PartialEq)]\\npub struct Counts {\\n pub lines: usize,\\n pub words: usize,\\n pub bytes: usize,\\n}\\n\\n/// Counts `text` in one pass.\\npub fn count(text: &str) -> Counts {\\n let mut counts = Counts { bytes: text.len(), ..Counts::default() };\\n for line in parse::lines(text) {\\n counts.lines += 1;\\n counts.words += parse::words(line);\\n }\\n counts\\n}\\n\\n#[cfg(test)]\\nmod tests {\\n use super::*;\\n\\n #[test]\\n fn counts() {\\n let c = count(\\\"one two\\\\nthree\\\\n\\\");\\n assert_eq!(c, Counts { lines: 2, words: 3, bytes: 14 });\\n }\\n\\n #[test]\\n fn a_last_line_without_a_newline() {\\n assert_eq!(count(\\\"a\\\\nb\\\").lines, 2);\\n }\\n}\\n\",\"ranges\":\"comment:0,59,13,23,126,31;string:585,18,153,6;keyword:60,3,69,3,1,6,14,3,19,3,19,3,49,3,1,2,35,3,1,3,65,3,6,2,126,3,13,3,1,5,22,2,20,3,120,2;number:422,1,221,1,9,1,9,2,105,1;function:259,5,67,3,14,7,29,5,67,5,91,6,21,5,30,10,73,29,13,10;type:104,5,2,7,2,9,14,6,18,5,17,5,17,5,56,3,5,6,24,6,24,6,282,6;attr:95,9,5,2,7,2,9,2,358,12,36,7,138,7\"}"
},
"/xrpc/dev.knotless.getLastCommits?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Fscratch": {
"status": 404,
"headers": {
"content-type": "application/json",
"cache-control": "no-store"
},
"body": "{\"error\":\"NotFound\",\"message\":\"no revision main: couldn't parse revision, input=\\\"main\\\": Reference \\\"main\\\" could not be found: NotFound\"}"
},
"/xrpc/dev.knotless.getLastCommits?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"ref\":\"main\",\"path\":\"\",\"entries\":[{\"name\":\".gitignore\",\"sha\":\"cac02c464843db4d55fecfa69d327ef7b12702c0\"},{\"name\":\"Cargo.toml\",\"sha\":\"cac02c464843db4d55fecfa69d327ef7b12702c0\"},{\"name\":\"README.md\",\"sha\":\"4abc93fa7cb85e41149e17f70dfcc4d3830ba0f2\"},{\"name\":\"assets\",\"sha\":\"4b29199f359ce31aa300ec409cdbdfa44d347ad5\"},{\"name\":\"docs\",\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\"},{\"name\":\"src\",\"sha\":\"1be297a440088f5808a7cdcf5254ac71c024458f\"}],\"commits\":[{\"sha\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"parents\":[{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\"}],\"author\":{\"name\":\"Bob Tanaka\",\"actor\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}},\"coauthors\":[],\"committer\":{\"name\":\"alice.test\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"date\":\"2026-10-09T09:31:00+00:00\",\"committedAt\":\"2026-10-09T15:00:00+00:00\",\"message\":\"Count no lines in empty input\\n\\n`tally` printed one line for an empty file. `lines` now yields nothing when the text is empty, and a test covers it.\\n\\nFixes [the empty file's count](https://knotless.dev/alice.test/tally/issue/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwh7kezpru).\",\"pull\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwh7my3ltr\",\"title\":\"Count no lines in empty input\"}},{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\",\"parents\":[{\"sha\":\"4abc93fa7cb85e41149e17f70dfcc4d3830ba0f2\"}],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-08T11:47:00+00:00\",\"committedAt\":\"2026-10-08T11:47:00+00:00\",\"message\":\"Split parsing into its own module\"},{\"sha\":\"4abc93fa7cb85e41149e17f70dfcc4d3830ba0f2\",\"parents\":[{\"sha\":\"4b29199f359ce31aa300ec409cdbdfa44d347ad5\"}],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-06T10:02:00+00:00\",\"committedAt\":\"2026-10-06T10:02:00+00:00\",\"message\":\"Document usage and output\"},{\"sha\":\"4b29199f359ce31aa300ec409cdbdfa44d347ad5\",\"parents\":[{\"sha\":\"7818a5bc1bef9184ad313cf79175da3d87d4b9cf\"}],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-02T11:30:00+00:00\",\"committedAt\":\"2026-10-05T17:21:00+00:00\",\"message\":\"Add a logo\"},{\"sha\":\"cac02c464843db4d55fecfa69d327ef7b12702c0\",\"parents\":[],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-01T09:12:00+00:00\",\"committedAt\":\"2026-10-01T09:12:00+00:00\",\"message\":\"Start tally\"}],\"commitCount\":7}"
},
"/xrpc/dev.knotless.getLastCommits?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&ref=main%2Fsrc": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"ref\":\"main\",\"path\":\"src\",\"entries\":[{\"name\":\"lib.rs\",\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\"},{\"name\":\"main.rs\",\"sha\":\"7818a5bc1bef9184ad313cf79175da3d87d4b9cf\"},{\"name\":\"parse.rs\",\"sha\":\"1be297a440088f5808a7cdcf5254ac71c024458f\"}],\"commits\":[{\"sha\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"parents\":[{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\"}],\"author\":{\"name\":\"Bob Tanaka\",\"actor\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}},\"coauthors\":[],\"committer\":{\"name\":\"alice.test\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"date\":\"2026-10-09T09:31:00+00:00\",\"committedAt\":\"2026-10-09T15:00:00+00:00\",\"message\":\"Count no lines in empty input\\n\\n`tally` printed one line for an empty file. `lines` now yields nothing when the text is empty, and a test covers it.\\n\\nFixes [the empty file's count](https://knotless.dev/alice.test/tally/issue/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwh7kezpru).\",\"pull\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwh7my3ltr\",\"title\":\"Count no lines in empty input\"}},{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\",\"parents\":[{\"sha\":\"4abc93fa7cb85e41149e17f70dfcc4d3830ba0f2\"}],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-08T11:47:00+00:00\",\"committedAt\":\"2026-10-08T11:47:00+00:00\",\"message\":\"Split parsing into its own module\"},{\"sha\":\"7818a5bc1bef9184ad313cf79175da3d87d4b9cf\",\"parents\":[{\"sha\":\"a7e467c6f9acd7b1be0603afdec0b8cfdcf3cf49\"}],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-03T08:05:00+00:00\",\"committedAt\":\"2026-10-03T08:05:00+00:00\",\"message\":\"Read from stdin when no file is given\\n\\nSo tally works at the end of a pipe, as wc does. The name shown\\nfor stdin is \\\"-\\\".\"}]}"
},
"/xrpc/dev.knotless.getLastCommits?repo=at%3A%2F%2Fbob.test%2Fdev.knotless.repo%2Ftally": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"ref\":\"main\",\"path\":\"\",\"entries\":[{\"name\":\".gitignore\",\"sha\":\"cac02c464843db4d55fecfa69d327ef7b12702c0\"},{\"name\":\".knotless\",\"sha\":\"4c4f3483836ee19f1f64f7976d1bc44ebc9db642\"},{\"name\":\"Cargo.toml\",\"sha\":\"cac02c464843db4d55fecfa69d327ef7b12702c0\"},{\"name\":\"README.md\",\"sha\":\"4abc93fa7cb85e41149e17f70dfcc4d3830ba0f2\"},{\"name\":\"assets\",\"sha\":\"4b29199f359ce31aa300ec409cdbdfa44d347ad5\"},{\"name\":\"docs\",\"sha\":\"226488be4afaf037c2331c7480d4a48790f20eba\"},{\"name\":\"src\",\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\"}],\"commits\":[{\"sha\":\"4c4f3483836ee19f1f64f7976d1bc44ebc9db642\",\"parents\":[{\"sha\":\"226488be4afaf037c2331c7480d4a48790f20eba\"}],\"author\":{\"name\":\"Bob Tanaka\",\"actor\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}},\"coauthors\":[],\"date\":\"2026-10-20T12:00:00+00:00\",\"committedAt\":\"2026-10-20T12:00:00+00:00\",\"message\":\"Release from tags when asked\"},{\"sha\":\"226488be4afaf037c2331c7480d4a48790f20eba\",\"parents\":[{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\"}],\"author\":{\"name\":\"Bob Tanaka\",\"actor\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}},\"coauthors\":[],\"date\":\"2026-10-11T08:00:00+00:00\",\"committedAt\":\"2026-10-11T08:00:00+00:00\",\"message\":\"Show usage on --help\"},{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\",\"parents\":[{\"sha\":\"4abc93fa7cb85e41149e17f70dfcc4d3830ba0f2\"}],\"author\":{\"name\":\"Alice Moreau\"},\"coauthors\":[],\"date\":\"2026-10-08T11:47:00+00:00\",\"committedAt\":\"2026-10-08T11:47:00+00:00\",\"message\":\"Split parsing into its own module\"},{\"sha\":\"4abc93fa7cb85e41149e17f70dfcc4d3830ba0f2\",\"parents\":[{\"sha\":\"4b29199f359ce31aa300ec409cdbdfa44d347ad5\"}],\"author\":{\"name\":\"Alice Moreau\"},\"coauthors\":[],\"date\":\"2026-10-06T10:02:00+00:00\",\"committedAt\":\"2026-10-06T10:02:00+00:00\",\"message\":\"Document usage and output\"},{\"sha\":\"4b29199f359ce31aa300ec409cdbdfa44d347ad5\",\"parents\":[{\"sha\":\"7818a5bc1bef9184ad313cf79175da3d87d4b9cf\"}],\"author\":{\"name\":\"Alice Moreau\"},\"coauthors\":[],\"date\":\"2026-10-02T11:30:00+00:00\",\"committedAt\":\"2026-10-05T17:21:00+00:00\",\"message\":\"Add a logo\"},{\"sha\":\"cac02c464843db4d55fecfa69d327ef7b12702c0\",\"parents\":[],\"author\":{\"name\":\"Alice Moreau\"},\"coauthors\":[],\"date\":\"2026-10-01T09:12:00+00:00\",\"committedAt\":\"2026-10-01T09:12:00+00:00\",\"message\":\"Start tally\"}],\"commitCount\":8}"
},
"/xrpc/dev.knotless.getProfile?actor=alice.test": {
"status": 200,
"headers": {
"content-type": "application/json"
},
"body": "{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\",\"displayName\":\"Alice Moreau\",\"description\":\"Small tools for text. Writes tally.\",\"followersCount\":1,\"followsCount\":0,\"badges\":[{\"kind\":\"forked-and-fixed\",\"tier\":1,\"count\":1},{\"kind\":\"shipped\",\"tier\":1,\"count\":4,\"next\":10},{\"kind\":\"carry-your-own\",\"tier\":1,\"count\":1},{\"kind\":\"first-light\",\"tier\":1,\"count\":1}]}"
},
"/xrpc/dev.knotless.getProfile?actor=bob.test": {
"status": 200,
"headers": {
"content-type": "application/json"
},
"body": "{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\",\"followersCount\":0,\"followsCount\":1,\"orgs\":[{\"actor\":{\"did\":\"did:plc:2ejydq2mzgqz2tzdtgk3figi\",\"handle\":\"textkit.test\"}}],\"badges\":[{\"kind\":\"carry-your-own\",\"tier\":1,\"count\":1},{\"kind\":\"first-light\",\"tier\":1,\"count\":1}]}"
},
"/xrpc/dev.knotless.getQueue?repo=at%3A%2F%2Fbob.test%2Fdev.knotless.repo%2Ftally": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"branches\":[{\"branch\":\"main\",\"pulls\":[{\"pull\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwhbuzv5n3\",\"kind\":\"pull\",\"title\":\"Count bytes in files that are not UTF-8\",\"state\":\"open\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T12:31:00.000Z\",\"comments\":0},\"queuedAt\":\"2026-10-15T09:30:00.000Z\",\"said\":\"In a batch of 2 being checked\",\"stage\":\"checking\",\"checks\":[],\"batch\":0},{\"pull\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwhc5eopws\",\"kind\":\"pull\",\"title\":\"Line the columns up with tabs\",\"state\":\"open\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T14:22:00.000Z\",\"comments\":0},\"queuedAt\":\"2026-10-16T09:30:00.000Z\",\"said\":\"In a batch of 2 being checked\",\"stage\":\"checking\",\"checks\":[],\"batch\":0},{\"pull\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwhcffbj7l\",\"kind\":\"pull\",\"title\":\"Count words in any script\",\"state\":\"open\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T16:13:00.000Z\",\"comments\":0},\"queuedAt\":\"2026-10-17T09:30:00.000Z\",\"said\":\"Queued, 3rd of 3, running its checks\",\"stage\":\"preflight\",\"checks\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.check/qdjbmvovu2tnodagwluxk6jxmq.3myvwh72yvtgz.x7xwerv7qozd33kq5cq5j54zdt.f834a66404f8eb1f5ccf94b3ef31e47246fd30ab.226488be4afaf037c2331c7480d4a48790f20eba.test\",\"repo\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\",\"name\":\"tally\"},\"workflow\":\"test\",\"status\":\"running\",\"commit\":\"f834a66404f8eb1f5ccf94b3ef31e47246fd30ab\",\"startedAt\":\"2026-10-20T13:57:00Z\",\"images\":[]}]}],\"batches\":[{\"commit\":\"ee347cbe693bbc8c7f70dbb7b30bc329f34dcfa6\",\"state\":\"checking\",\"size\":2,\"startedAt\":\"2026-10-20T10:00:00Z\",\"checks\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.check/qdjbmvovu2tnodagwluxk6jxmq.3myvwh72yvtgz.gkn5el4746pzeyvms3btnm2gqt.ee347cbe693bbc8c7f70dbb7b30bc329f34dcfa6.226488be4afaf037c2331c7480d4a48790f20eba.build\",\"repo\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\",\"name\":\"tally\"},\"workflow\":\"build\",\"status\":\"success\",\"commit\":\"ee347cbe693bbc8c7f70dbb7b30bc329f34dcfa6\",\"startedAt\":\"2026-10-20T10:00:00Z\",\"finishedAt\":\"2026-10-20T10:01:35.000Z\",\"images\":[]},{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.check/qdjbmvovu2tnodagwluxk6jxmq.3myvwh72yvtgz.gkn5el4746pzeyvms3btnm2gqt.ee347cbe693bbc8c7f70dbb7b30bc329f34dcfa6.226488be4afaf037c2331c7480d4a48790f20eba.test\",\"repo\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\",\"name\":\"tally\"},\"workflow\":\"test\",\"status\":\"running\",\"commit\":\"ee347cbe693bbc8c7f70dbb7b30bc329f34dcfa6\",\"startedAt\":\"2026-10-20T10:00:00Z\",\"images\":[]}]}]}],\"ejected\":[{\"pull\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwhcnn4cic\",\"kind\":\"pull\",\"title\":\"Print a total for many files\",\"state\":\"open\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T18:04:00.000Z\",\"comments\":0},\"ejectedAt\":\"2026-10-19T15:02:00.000Z\",\"reason\":\"its batch failed its checks at 6f89484c63a3, on main at 226488be4afa: test (failure)\",\"checks\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.check/qdjbmvovu2tnodagwluxk6jxmq.3myvwh72yvtgz.gkn5el4746pzeyvms3btnm2gqt.6f89484c63a36649168e3d77a1e631f695469b74.226488be4afaf037c2331c7480d4a48790f20eba.test\",\"repo\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\",\"name\":\"tally\"},\"workflow\":\"test\",\"status\":\"failure\",\"commit\":\"6f89484c63a36649168e3d77a1e631f695469b74\",\"startedAt\":\"2026-10-19T15:00:00Z\",\"finishedAt\":\"2026-10-19T15:01:35.000Z\",\"images\":[]}]}],\"landed\":[{\"branch\":\"main\",\"commit\":\"226488be4afaf037c2331c7480d4a48790f20eba\",\"landedAt\":\"2026-10-19T09:01:35.000Z\",\"pulls\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwhbacgmx6\",\"kind\":\"pull\",\"title\":\"Show usage on --help\",\"state\":\"merged\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T08:49:00.000Z\",\"comments\":0}]}],\"typical\":{\"batchSeconds\":95,\"preflightSeconds\":360}}"
},
"/xrpc/dev.knotless.getRepo?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Fscratch": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.repo/scratch\",\"owner\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"},\"name\":\"scratch\",\"key\":\"3mz2z2irwxiwv\",\"defaultBranch\":\"main\",\"openPulls\":0,\"openIssues\":0,\"card\":\"/alice.test/scratch/card.png?v=8d5b3f136b93e209\",\"starCount\":0,\"forkCount\":0}"
},
"/xrpc/dev.knotless.getRepo?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.repo/tally\",\"owner\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"},\"name\":\"tally\",\"key\":\"3mz2z2gkqpvhk\",\"description\":\"Counts lines, words and bytes, with output that lines up\",\"links\":[{\"url\":\"https://tally.dev\"},{\"url\":\"https://docs.tally.dev\",\"title\":\"Docs\"}],\"defaultBranch\":\"main\",\"openPulls\":1,\"openIssues\":1,\"card\":\"/alice.test/tally/card.png?v=8da376836ab23847\",\"starCount\":1,\"forkCount\":1,\"releases\":{\"count\":4,\"release\":{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.release/3myvwh5dhhixv.wapcnu7vuujqxhmnqiofvhlb47.chxchoh4f7dbtvxkwytxvw22kj\",\"package\":\"tally\",\"version\":\"0.1.1\",\"latest\":true,\"createdAt\":\"2026-10-10T19:18:00.000Z\"},\"packages\":[{\"name\":\"tally\",\"latest\":\"0.1.1\",\"releaseCount\":3},{\"name\":\"tally-server\",\"latest\":\"0.1.0\",\"image\":true,\"releaseCount\":1}]}}"
},
"/xrpc/dev.knotless.getRepo?repo=at%3A%2F%2Fbob.test%2Fdev.knotless.repo%2Ftally": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.repo/tally\",\"owner\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"name\":\"tally\",\"key\":\"3mz2z2jedyqxn\",\"description\":\"Counts lines, words and bytes, with output that lines up\",\"forkOf\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\",\"name\":\"tally\"},\"defaultBranch\":\"main\",\"openPulls\":4,\"openIssues\":0,\"mergeQueue\":true,\"card\":\"/bob.test/tally/card.png?v=68776149d6ea2b31\",\"starCount\":0,\"forkCount\":0}"
},
"/xrpc/dev.knotless.getRepoSettings?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.repo/tally\",\"description\":\"Counts lines, words and bytes, with output that lines up\",\"links\":[{\"url\":\"https://tally.dev\"},{\"url\":\"https://docs.tally.dev\",\"title\":\"Docs\"}],\"defaultBranch\":\"main\",\"rules\":[{\"branch\":\"main\",\"history\":\"linear\"}],\"runners\":true,\"labels\":[{\"name\":\"kind/bug\",\"description\":\"Something is broken\",\"color\":\"#d73a4a\"},{\"name\":\"priority/high\",\"color\":\"#fbca04\",\"exclusive\":true},{\"name\":\"priority/low\",\"exclusive\":true}],\"collaborators\":[],\"triagers\":[]}"
},
"/xrpc/dev.knotless.getRepoStorage?repo=at%3A%2F%2Fdid%3Aplc%3Aabe24xewga4qdbzjcxmuzdt3%2Fdev.knotless.repo%2Ftally": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"packs\":2,\"objects\":35,\"bytes\":5031,\"blobBytes\":0}"
},
"/xrpc/dev.knotless.getStats": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "public, max-age=300"
},
"body": "{\"days\":[{\"day\":\"2026-09-29\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-09-30\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-01\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-02\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-03\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-04\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-05\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-06\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-07\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-08\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-09\",\"visitors\":0,\"views\":0,\"repos\":4,\"pushes\":7,\"pulls\":0,\"issues\":1,\"comments\":2,\"checks\":1,\"authors\":2},{\"day\":\"2026-10-10\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":9,\"pulls\":7,\"issues\":2,\"comments\":2,\"checks\":4,\"authors\":2},{\"day\":\"2026-10-11\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":2,\"authors\":0},{\"day\":\"2026-10-12\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-13\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-14\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-15\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":1},{\"day\":\"2026-10-16\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":1},{\"day\":\"2026-10-17\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":1},{\"day\":\"2026-10-18\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":1},{\"day\":\"2026-10-19\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":4,\"authors\":1},{\"day\":\"2026-10-20\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":2,\"authors\":0},{\"day\":\"2026-10-21\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-22\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-23\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-24\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-25\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-26\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-27\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0},{\"day\":\"2026-10-28\",\"visitors\":0,\"views\":0,\"repos\":0,\"pushes\":0,\"pulls\":0,\"issues\":0,\"comments\":0,\"checks\":0,\"authors\":0}],\"network\":{\"accounts\":2,\"repos\":4,\"authorsWeek\":0,\"authorsMonth\":2}}"
},
"/xrpc/dev.knotless.getThread?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&uri=at%3A%2F%2Fdid%3Aplc%3A55j6e4wxzlzndxm4nov2lz7x%2Fdev.knotless.issue%2F3myvwh7kezpru&filesOnly=true": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"item\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.issue/3myvwh7kezpru\",\"kind\":\"issue\",\"title\":\"Counts one line for an empty file\",\"state\":\"closed\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-09T22:20:00.000Z\",\"comments\":0},\"comments\":[],\"closes\":[],\"closedBy\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwh7my3ltr\",\"title\":\"Count no lines in empty input\"},\"checks\":[],\"card\":\"/alice.test/tally/issue/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwh7kezpru/card.png?v=46a09b63af6fcadf\"}"
},
"/xrpc/dev.knotless.getThread?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&uri=at%3A%2F%2Fdid%3Aplc%3A55j6e4wxzlzndxm4nov2lz7x%2Fdev.knotless.issue%2F3myvwhak63fo4&filesOnly=true": {
"status": 200,
"headers": {
"content-type": "application/json",
"cache-control": "no-cache"
},
"body": "{\"item\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.issue/3myvwhak63fo4\",\"kind\":\"issue\",\"title\":\"Fails on files that are not UTF-8\",\"state\":\"open\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T05:07:00.000Z\",\"comments\":1,\"labels\":[{\"name\":\"kind/bug\",\"description\":\"Something is broken\",\"color\":\"#d73a4a\"},{\"name\":\"priority/high\",\"color\":\"#fbca04\",\"exclusive\":true}]},\"body\":\"Running `tally image.png` panics:\\n\\n```\\nthread 'main' panicked at src/main.rs:8:56:\\nstream did not contain valid UTF-8\\n```\\n\\nwc counts bytes for any file.\",\"html\":\"Running tally image.png panics:
thread 'main' panicked at src/main.rs:8:56:\\nstream did not contain valid UTF-8\\n\\nwc counts bytes for any file.
\\n\",\"comments\":[{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.comment/3myvwhakyq3pz\",\"author\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"},\"createdAt\":\"2026-10-10T04:30:00.000Z\",\"updatedAt\":\"2026-10-10T04:42:00.000Z\",\"body\":\"Confirmed. Reading bytes and counting words lossily would fix it.\",\"html\":\"Confirmed. Reading bytes and counting words lossily would fix it.
\\n\"}],\"closes\":[],\"checks\":[],\"card\":\"/alice.test/tally/issue/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwhak63fo4/card.png?v=d70e31b0efe160a5\"}" }, "/xrpc/dev.knotless.getThread?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&uri=at%3A%2F%2Fdid%3Aplc%3A55j6e4wxzlzndxm4nov2lz7x%2Fdev.knotless.issue%2F3myvwhaqw6os3&filesOnly=true": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"item\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.issue/3myvwhaqw6os3\",\"kind\":\"issue\",\"title\":\"Typo in the README ✏️\",\"state\":\"closed\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T06:21:00.000Z\",\"comments\":0},\"comments\":[],\"closes\":[],\"checks\":[],\"card\":\"/alice.test/tally/issue/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwhaqw6os3/card.png?v=88e0042fce25624d\"}" }, "/xrpc/dev.knotless.getThread?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&uri=at%3A%2F%2Fdid%3Aplc%3A55j6e4wxzlzndxm4nov2lz7x%2Fdev.knotless.pull%2F3myvwh7my3ltr&filesOnly=true": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"item\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwh7my3ltr\",\"kind\":\"pull\",\"title\":\"Count no lines in empty input\",\"state\":\"merged\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T00:48:00.000Z\",\"comments\":3},\"body\":\"`tally` printed one line for an empty file. `lines` now yields nothing when the text is empty, and a test covers it.\\n\\nFixes [the empty file's count](https://knotless.dev/alice.test/tally/issue/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwh7kezpru).\",\"html\":\"tally printed one line for an empty file. lines now yields nothing when the text is empty, and a test covers it.
Fixes the empty file's count.
\\n\",\"base\":\"main\",\"head\":{\"repo\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\",\"name\":\"tally\"},\"branch\":\"empty-input\"},\"merged\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"comments\":[{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.comment/3myvwh7ohg4vo\",\"author\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"},\"createdAt\":\"2026-10-09T22:57:00.000Z\",\"body\":\"Could `lines` check for emptiness once, rather than in the filter?\",\"html\":\"Could lines check for emptiness once, rather than in the filter?
It only runs per line, so the cost is one comparison each. Happy to change it if you prefer.
\\n\"},{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.comment/3myvwh7pws4vr\",\"author\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"},\"createdAt\":\"2026-10-09T23:34:00.000Z\",\"body\":\"Fair enough. Thanks!\",\"html\":\"Fair enough. Thanks!
\\n\",\"verdict\":\"approve\"}],\"closes\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.issue/3myvwh7kezpru\",\"kind\":\"issue\",\"title\":\"Counts one line for an empty file\",\"state\":\"closed\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-09T22:20:00.000Z\",\"comments\":0}],\"checks\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.check/qdjbmvovu2tnodagwluxk6jxmq.3myvwh72yvtgz.eyz4we3pck2i4z63rw5caruhiv.d1f87fe9540ab153762a7caf292887855d3e1a90.test\",\"repo\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\",\"name\":\"tally\"},\"workflow\":\"test\",\"status\":\"success\",\"commit\":\"d1f87fe9540ab153762a7caf292887855d3e1a90\",\"startedAt\":\"2026-10-09T09:32:00Z\",\"finishedAt\":\"2026-10-09T09:32:41.500Z\",\"images\":[]}],\"minutes\":[{\"runner\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"},\"minutes\":1640,\"allowance\":2000,\"freeing\":[{\"at\":\"2026-11-02T00:00:00Z\",\"minutes\":310}],\"updatedAt\":\"2026-10-25T17:52:00Z\"}],\"changes\":{\"repo\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\",\"name\":\"tally\"},\"commits\":[{\"sha\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"parents\":[{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\"}],\"author\":{\"name\":\"Bob Tanaka\",\"actor\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}},\"coauthors\":[],\"committer\":{\"name\":\"alice.test\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"date\":\"2026-10-09T09:31:00Z\",\"committedAt\":\"2026-10-09T15:00:00Z\",\"message\":\"Count no lines in empty input\\n\\n`tally` printed one line for an empty file. `lines` now yields nothing when the text is empty, and a test covers it.\\n\\nFixes [the empty file's count](https://knotless.dev/alice.test/tally/issue/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwh7kezpru).\",\"pull\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwh7my3ltr\",\"title\":\"Count no lines in empty input\"}}],\"files\":[{\"old\":\"src/parse.rs\",\"new\":\"src/parse.rs\",\"status\":\"modified\",\"added\":11,\"removed\":2,\"rows\":[],\"rowCount\":19,\"hunkCount\":1}]},\"card\":\"/alice.test/tally/pull/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwh7my3ltr/card.png?v=6530fbcb6cb5067c\"}" }, "/xrpc/dev.knotless.getThread?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&uri=at%3A%2F%2Fdid%3Aplc%3A55j6e4wxzlzndxm4nov2lz7x%2Fdev.knotless.pull%2F3myvwh7my3ltr&filesOnly=true&firstRows=true": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"item\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwh7my3ltr\",\"kind\":\"pull\",\"title\":\"Count no lines in empty input\",\"state\":\"merged\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T00:48:00.000Z\",\"comments\":3},\"body\":\"`tally` printed one line for an empty file. `lines` now yields nothing when the text is empty, and a test covers it.\\n\\nFixes [the empty file's count](https://knotless.dev/alice.test/tally/issue/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwh7kezpru).\",\"html\":\"tally printed one line for an empty file. lines now yields nothing when the text is empty, and a test covers it.
Fixes the empty file's count.
\\n\",\"base\":\"main\",\"head\":{\"repo\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\",\"name\":\"tally\"},\"branch\":\"empty-input\"},\"merged\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"comments\":[{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.comment/3myvwh7ohg4vo\",\"author\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"},\"createdAt\":\"2026-10-09T22:57:00.000Z\",\"body\":\"Could `lines` check for emptiness once, rather than in the filter?\",\"html\":\"Could lines check for emptiness once, rather than in the filter?
It only runs per line, so the cost is one comparison each. Happy to change it if you prefer.
\\n\"},{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.comment/3myvwh7pws4vr\",\"author\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"},\"createdAt\":\"2026-10-09T23:34:00.000Z\",\"body\":\"Fair enough. Thanks!\",\"html\":\"Fair enough. Thanks!
\\n\",\"verdict\":\"approve\"}],\"closes\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.issue/3myvwh7kezpru\",\"kind\":\"issue\",\"title\":\"Counts one line for an empty file\",\"state\":\"closed\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-09T22:20:00.000Z\",\"comments\":0}],\"checks\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.check/qdjbmvovu2tnodagwluxk6jxmq.3myvwh72yvtgz.eyz4we3pck2i4z63rw5caruhiv.d1f87fe9540ab153762a7caf292887855d3e1a90.test\",\"repo\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\",\"name\":\"tally\"},\"workflow\":\"test\",\"status\":\"success\",\"commit\":\"d1f87fe9540ab153762a7caf292887855d3e1a90\",\"startedAt\":\"2026-10-09T09:32:00Z\",\"finishedAt\":\"2026-10-09T09:32:41.500Z\",\"images\":[]}],\"minutes\":[{\"runner\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"},\"minutes\":1640,\"allowance\":2000,\"freeing\":[{\"at\":\"2026-11-02T00:00:00Z\",\"minutes\":310}],\"updatedAt\":\"2026-10-25T17:52:00Z\"}],\"changes\":{\"repo\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\",\"name\":\"tally\"},\"commits\":[{\"sha\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"parents\":[{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\"}],\"author\":{\"name\":\"Bob Tanaka\",\"actor\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}},\"coauthors\":[],\"committer\":{\"name\":\"alice.test\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"date\":\"2026-10-09T09:31:00Z\",\"committedAt\":\"2026-10-09T15:00:00Z\",\"message\":\"Count no lines in empty input\\n\\n`tally` printed one line for an empty file. `lines` now yields nothing when the text is empty, and a test covers it.\\n\\nFixes [the empty file's count](https://knotless.dev/alice.test/tally/issue/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwh7kezpru).\",\"pull\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwh7my3ltr\",\"title\":\"Count no lines in empty input\"}}],\"files\":[{\"old\":\"src/parse.rs\",\"new\":\"src/parse.rs\",\"status\":\"modified\",\"added\":11,\"removed\":2,\"ranges\":\"comment:39,68,1,72,1,25;string:435,2;keyword:207,3,1,2,22,4,104,3,25,2,49,5;number:448,1;function:9,16,3,5,181,5,54,5,12,5,3,6,11,8,51,24,13,10;type:227,3,10,8,1,4,4,3;attr:330,12,17,7;xins:175,5,117,29;xdel:106,1\",\"rows\":[{\"kind\":\"h\",\"line\":false,\"text\":\"@@ -5,7 +5,16 @@ pub fn words(line: &str) -> usize {\"},{\"kind\":\"\",\"line\":true,\"old\":5,\"new\":5,\"text\":\" line.split_whitespace().count()\"},{\"kind\":\"\",\"line\":true,\"old\":6,\"new\":6,\"text\":\"}\"},{\"kind\":\"\",\"line\":true,\"old\":7,\"new\":7,\"text\":\"\"},{\"kind\":\"d\",\"line\":true,\"old\":8,\"text\":\"/// The lines of `text`. A last line without a newline still counts.\"},{\"kind\":\"a\",\"line\":true,\"new\":8,\"text\":\"/// The lines of `text`. A last line without a newline still counts, and\"},{\"kind\":\"a\",\"line\":true,\"new\":9,\"text\":\"/// empty input has none.\"},{\"kind\":\"\",\"line\":true,\"old\":9,\"new\":10,\"text\":\"pub fn lines(text: &str) -> impl IteratorFor scripts. The object has lines, words, bytes and name.
Counts lines, words and bytes, like wc, with output that lines up.
cargo install --path .\\ntally notes.txt\\necho \\\"one two\\\" | tally\\n\\n| Column | Meaning |
|---|---|
| 1 | lines |
| 2 | words, split on whitespace |
| 3 | bytes |
See the design notes for why it reads the whole file.
\\nMIT
\\n\"},\"checks\":[{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.check/ppk3ktprvyuf4xa3s63c2nt3kd.3myvwh5dhhixv.7eq32bpgrmbxidcfbzlf4dtbop.1be297a440088f5808a7cdcf5254ac71c024458f.lint\",\"repo\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\",\"name\":\"tally\"},\"workflow\":\"lint\",\"status\":\"success\",\"commit\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"startedAt\":\"2026-10-11T09:00:00Z\",\"finishedAt\":\"2026-10-11T09:00:41.500Z\",\"images\":[]},{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.check/ppk3ktprvyuf4xa3s63c2nt3kd.3myvwh5dhhixv.7eq32bpgrmbxidcfbzlf4dtbop.1be297a440088f5808a7cdcf5254ac71c024458f.test\",\"repo\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\",\"name\":\"tally\"},\"workflow\":\"test\",\"status\":\"success\",\"commit\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"startedAt\":\"2026-10-11T09:00:00Z\",\"finishedAt\":\"2026-10-11T09:00:41.500Z\",\"images\":[]}],\"languages\":[{\"name\":\"Rust\",\"color\":\"#dea584\",\"bytes\":1785,\"permille\":1000}]}" }, "/xrpc/dev.knotless.getTree?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&ref=main%2Fsrc": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"ref\":\"main\",\"path\":\"src\",\"lastCommit\":{\"sha\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"parents\":[{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\"}],\"author\":{\"name\":\"Bob Tanaka\",\"actor\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}},\"coauthors\":[],\"committer\":{\"name\":\"alice.test\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"date\":\"2026-10-09T09:31:00+00:00\",\"committedAt\":\"2026-10-09T15:00:00+00:00\",\"message\":\"Count no lines in empty input\\n\\n`tally` printed one line for an empty file. `lines` now yields nothing when the text is empty, and a test covers it.\\n\\nFixes [the empty file's count](https://knotless.dev/alice.test/tally/issue/did:plc:55j6e4wxzlzndxm4nov2lz7x/3myvwh7kezpru).\",\"pull\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwh7my3ltr\",\"title\":\"Count no lines in empty input\"}},\"entries\":[{\"name\":\"lib.rs\",\"kind\":\"blob\",\"size\":783},{\"name\":\"main.rs\",\"kind\":\"blob\",\"size\":484},{\"name\":\"parse.rs\",\"kind\":\"blob\",\"size\":518}],\"checks\":[{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.check/ppk3ktprvyuf4xa3s63c2nt3kd.3myvwh5dhhixv.7eq32bpgrmbxidcfbzlf4dtbop.1be297a440088f5808a7cdcf5254ac71c024458f.lint\",\"repo\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\",\"name\":\"tally\"},\"workflow\":\"lint\",\"status\":\"success\",\"commit\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"startedAt\":\"2026-10-11T09:00:00Z\",\"finishedAt\":\"2026-10-11T09:00:41.500Z\",\"images\":[]},{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.check/ppk3ktprvyuf4xa3s63c2nt3kd.3myvwh5dhhixv.7eq32bpgrmbxidcfbzlf4dtbop.1be297a440088f5808a7cdcf5254ac71c024458f.test\",\"repo\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\",\"name\":\"tally\"},\"workflow\":\"test\",\"status\":\"success\",\"commit\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"startedAt\":\"2026-10-11T09:00:00Z\",\"finishedAt\":\"2026-10-11T09:00:41.500Z\",\"images\":[]}],\"languages\":[]}" }, "/xrpc/dev.knotless.getTree?repo=at%3A%2F%2Fbob.test%2Fdev.knotless.repo%2Ftally": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"ref\":\"main\",\"path\":\"\",\"lastCommit\":{\"sha\":\"4c4f3483836ee19f1f64f7976d1bc44ebc9db642\",\"parents\":[{\"sha\":\"226488be4afaf037c2331c7480d4a48790f20eba\"}],\"author\":{\"name\":\"Bob Tanaka\",\"actor\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}},\"coauthors\":[],\"date\":\"2026-10-20T12:00:00+00:00\",\"committedAt\":\"2026-10-20T12:00:00+00:00\",\"message\":\"Release from tags when asked\"},\"entries\":[{\"name\":\".knotless\",\"kind\":\"tree\"},{\"name\":\"assets\",\"kind\":\"tree\"},{\"name\":\"docs\",\"kind\":\"tree\"},{\"name\":\"src\",\"kind\":\"tree\"},{\"name\":\".gitignore\",\"kind\":\"blob\",\"size\":8},{\"name\":\"Cargo.toml\",\"kind\":\"blob\",\"size\":122},{\"name\":\"README.md\",\"kind\":\"blob\",\"size\":388}],\"readme\":{\"name\":\"README.md\",\"html\":\"Counts lines, words and bytes, like wc, with output that lines up.
cargo install --path .\\ntally notes.txt\\necho \\\"one two\\\" | tally\\n\\n| Column | Meaning |
|---|---|
| 1 | lines |
| 2 | words, split on whitespace |
| 3 | bytes |
See the design notes for why it reads the whole file.
\\nMIT
\\n\"},\"checks\":[],\"languages\":[{\"name\":\"Rust\",\"color\":\"#dea584\",\"bytes\":1595,\"permille\":1000}]}" }, "/xrpc/dev.knotless.getViewer": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"login\":false}" }, "/xrpc/dev.knotless.listBranches?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Fscratch&limit=1000": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"branches\":[]}" }, "/xrpc/dev.knotless.listBranches?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&limit=1000": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"branches\":[{\"name\":\"main\"}]}" }, "/xrpc/dev.knotless.listBranches?repo=at%3A%2F%2Fbob.test%2Fdev.knotless.repo%2Ftally&limit=1000": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"branches\":[{\"name\":\"main\"},{\"name\":\"bytes\"},{\"name\":\"empty-input\"},{\"name\":\"json\"},{\"name\":\"tabs\"},{\"name\":\"totals\"},{\"name\":\"words\"}]}" }, "/xrpc/dev.knotless.listCommits?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&ref=main&subjectOnly=true": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"ref\":\"main\",\"path\":\"\",\"commits\":[{\"sha\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"parents\":[{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\"}],\"author\":{\"name\":\"Bob Tanaka\",\"actor\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}},\"coauthors\":[],\"committer\":{\"name\":\"alice.test\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"date\":\"2026-10-09T09:31:00+00:00\",\"committedAt\":\"2026-10-09T15:00:00+00:00\",\"message\":\"Count no lines in empty input\",\"pull\":{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwh7my3ltr\",\"title\":\"Count no lines in empty input\"}},{\"sha\":\"052e48d313c5ebeb08f596cd683ba1eade3fb1ac\",\"parents\":[{\"sha\":\"4abc93fa7cb85e41149e17f70dfcc4d3830ba0f2\"}],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-08T11:47:00+00:00\",\"committedAt\":\"2026-10-08T11:47:00+00:00\",\"message\":\"Split parsing into its own module\"},{\"sha\":\"4abc93fa7cb85e41149e17f70dfcc4d3830ba0f2\",\"parents\":[{\"sha\":\"4b29199f359ce31aa300ec409cdbdfa44d347ad5\"}],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-06T10:02:00+00:00\",\"committedAt\":\"2026-10-06T10:02:00+00:00\",\"message\":\"Document usage and output\"},{\"sha\":\"4b29199f359ce31aa300ec409cdbdfa44d347ad5\",\"parents\":[{\"sha\":\"7818a5bc1bef9184ad313cf79175da3d87d4b9cf\"}],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-02T11:30:00+00:00\",\"committedAt\":\"2026-10-05T17:21:00+00:00\",\"message\":\"Add a logo\"},{\"sha\":\"7818a5bc1bef9184ad313cf79175da3d87d4b9cf\",\"parents\":[{\"sha\":\"a7e467c6f9acd7b1be0603afdec0b8cfdcf3cf49\"}],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-03T08:05:00+00:00\",\"committedAt\":\"2026-10-03T08:05:00+00:00\",\"message\":\"Read from stdin when no file is given\"},{\"sha\":\"a7e467c6f9acd7b1be0603afdec0b8cfdcf3cf49\",\"parents\":[{\"sha\":\"cac02c464843db4d55fecfa69d327ef7b12702c0\"}],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-02T14:40:00+00:00\",\"committedAt\":\"2026-10-02T14:40:00+00:00\",\"message\":\"Count words, lines and bytes\"},{\"sha\":\"cac02c464843db4d55fecfa69d327ef7b12702c0\",\"parents\":[],\"author\":{\"name\":\"Alice Moreau\",\"actor\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\"}},\"coauthors\":[],\"date\":\"2026-10-01T09:12:00+00:00\",\"committedAt\":\"2026-10-01T09:12:00+00:00\",\"message\":\"Start tally\"}],\"checks\":[{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.check/ppk3ktprvyuf4xa3s63c2nt3kd.3myvwh5dhhixv.7eq32bpgrmbxidcfbzlf4dtbop.1be297a440088f5808a7cdcf5254ac71c024458f.lint\",\"repo\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\",\"name\":\"tally\"},\"workflow\":\"lint\",\"status\":\"success\",\"commit\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"startedAt\":\"2026-10-11T09:00:00Z\",\"finishedAt\":\"2026-10-11T09:00:41.500Z\",\"images\":[]},{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.check/ppk3ktprvyuf4xa3s63c2nt3kd.3myvwh5dhhixv.7eq32bpgrmbxidcfbzlf4dtbop.1be297a440088f5808a7cdcf5254ac71c024458f.test\",\"repo\":{\"did\":\"did:plc:abe24xewga4qdbzjcxmuzdt3\",\"handle\":\"alice.test\",\"name\":\"tally\"},\"workflow\":\"test\",\"status\":\"success\",\"commit\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"startedAt\":\"2026-10-11T09:00:00Z\",\"finishedAt\":\"2026-10-11T09:00:41.500Z\",\"images\":[]}]}" }, "/xrpc/dev.knotless.listItems?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&kind=issue": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"items\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.issue/3myvwhak63fo4\",\"kind\":\"issue\",\"title\":\"Fails on files that are not UTF-8\",\"state\":\"open\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T05:07:00.000Z\",\"comments\":1,\"labels\":[{\"name\":\"kind/bug\",\"description\":\"Something is broken\",\"color\":\"#d73a4a\"},{\"name\":\"priority/high\",\"color\":\"#fbca04\",\"exclusive\":true}]}],\"openCount\":1,\"closedCount\":2,\"labels\":[{\"name\":\"kind/bug\",\"description\":\"Something is broken\",\"color\":\"#d73a4a\"},{\"name\":\"priority/high\",\"color\":\"#fbca04\",\"exclusive\":true},{\"name\":\"priority/low\",\"exclusive\":true}],\"authors\":[{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}]}" }, "/xrpc/dev.knotless.listItems?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&kind=issue&state=closed": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"items\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.issue/3myvwhaqw6os3\",\"kind\":\"issue\",\"title\":\"Typo in the README ✏️\",\"state\":\"closed\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T06:21:00.000Z\",\"comments\":0},{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.issue/3myvwh7kezpru\",\"kind\":\"issue\",\"title\":\"Counts one line for an empty file\",\"state\":\"closed\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-09T22:20:00.000Z\",\"comments\":0}],\"openCount\":1,\"closedCount\":2,\"labels\":[{\"name\":\"kind/bug\",\"description\":\"Something is broken\",\"color\":\"#d73a4a\"},{\"name\":\"priority/high\",\"color\":\"#fbca04\",\"exclusive\":true},{\"name\":\"priority/low\",\"exclusive\":true}],\"authors\":[{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}]}" }, "/xrpc/dev.knotless.listItems?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&kind=pull": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"items\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwhahturhe\",\"kind\":\"pull\",\"title\":\"Print counts as JSON with --json\",\"state\":\"open\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T03:53:00.000Z\",\"comments\":0}],\"openCount\":1,\"closedCount\":1,\"labels\":[{\"name\":\"kind/bug\",\"description\":\"Something is broken\",\"color\":\"#d73a4a\"},{\"name\":\"priority/high\",\"color\":\"#fbca04\",\"exclusive\":true},{\"name\":\"priority/low\",\"exclusive\":true}],\"authors\":[{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}]}" }, "/xrpc/dev.knotless.listItems?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally&kind=pull&state=closed": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"items\":[{\"uri\":\"at://did:plc:55j6e4wxzlzndxm4nov2lz7x/dev.knotless.pull/3myvwh7my3ltr\",\"kind\":\"pull\",\"title\":\"Count no lines in empty input\",\"state\":\"merged\",\"author\":{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"},\"createdAt\":\"2026-10-10T00:48:00.000Z\",\"comments\":3}],\"openCount\":1,\"closedCount\":1,\"labels\":[{\"name\":\"kind/bug\",\"description\":\"Something is broken\",\"color\":\"#d73a4a\"},{\"name\":\"priority/high\",\"color\":\"#fbca04\",\"exclusive\":true},{\"name\":\"priority/low\",\"exclusive\":true}],\"authors\":[{\"did\":\"did:plc:55j6e4wxzlzndxm4nov2lz7x\",\"handle\":\"bob.test\"}]}" }, "/xrpc/dev.knotless.listReleases?repo=at%3A%2F%2Falice.test%2Fdev.knotless.repo%2Ftally": { "status": 200, "headers": { "content-type": "application/json", "cache-control": "no-cache" }, "body": "{\"releases\":[{\"uri\":\"at://did:plc:abe24xewga4qdbzjcxmuzdt3/dev.knotless.release/3myvwh5dhhixv.wapcnu7vuujqxhmnqiofvhlb47.4iqtjhkd5xtc6nvxjtcplav3mh\",\"package\":\"tally\",\"version\":\"0.2.0-rc.1\",\"commit\":\"1be297a440088f5808a7cdcf5254ac71c024458f\",\"notes\":\"A first look at 0.2.0: try it before it is the latest.\\n\\n- `tally --json` prints the counts as JSON.\",\"html\":\"A first look at 0.2.0: try it before it is the latest.
\\ntally --json prints the counts as JSON.Serves counts over HTTP: POST /count with the text, and the counts come back as JSON.
The first release: tally counts lines, words and bytes, as wc does.
Serves counts over HTTP: POST /count with the text, and the counts come back as JSON.