moth-cli 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -111
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,177 +1,120 @@
|
|
|
1
1
|
# moth
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/nikolasgioannou/moth/actions/workflows/ci.yml) [](https://www.npmjs.com/package/moth-cli) [](LICENSE)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
An issue tracker that lives in your repository. Tickets are markdown files with a schema the CLI enforces — no account, no server, no sign-up.
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
$ moth list
|
|
9
9
|
backlog
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
in-progress
|
|
14
|
-
001 Parse the frontmatter none
|
|
10
|
+
280f4d Ship the binary high
|
|
11
|
+
8e0642 Parse the frontmatter none
|
|
15
12
|
```
|
|
16
13
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
A folder of markdown files is free, but it is a convention rather than a tool, and nothing enforces it. Session one's agent writes `status: todo`. Session three's agent, having never seen that file, writes `state: in_progress`. By session ten the folder is unqueryable, and answering "what's blocked?" means re-deriving a grep every time.
|
|
20
|
-
|
|
21
|
-
moth's value is the constraint. It refuses writes a bare filesystem would accept: an unrecognised status, a field nobody declared, a parent that would create a cycle. An agent cannot invent `status: blocked` on a whim, because the write fails and tells it what is legal. And it can ask: `moth schema --json` returns every legal field and value for the repository, so a session with no memory of previous sessions can discover the rules in one call.
|
|
22
|
-
|
|
23
|
-
Because the shape of the data is known, querying it is a command rather than a grep somebody has to get right.
|
|
24
|
-
|
|
25
|
-
## What it deliberately does not do
|
|
26
|
-
|
|
27
|
-
The refusals are the design, not gaps waiting to be filled:
|
|
28
|
-
|
|
29
|
-
- **No assignees, and no accounts.** Moving a ticket into a started status is how you claim it.
|
|
30
|
-
- **No custom statuses outside six fixed categories.** You name your own statuses; every one belongs to `backlog`, `unstarted`, `started`, `completed`, `canceled`, or `duplicate`. Queries against categories work in any repository; queries against your status names work in yours.
|
|
31
|
-
- **No undeclared fields.** Custom fields are allowed, but they must be declared in config first, so an agent can never introduce one.
|
|
32
|
-
- **No comments and no activity log.** `git log -p` on a ticket file is already a complete, attributed, tamper-evident history. Notes append to the body.
|
|
33
|
-
- **No cycles, sprints, estimates, projects, or manual ordering.**
|
|
34
|
-
- **No web UI, and no TUI.** The CLI is the interface.
|
|
35
|
-
|
|
36
|
-
The full list, each with its reasoning, is in [the spec](docs/spec-v1.md#out-of-scope).
|
|
37
|
-
|
|
38
|
-
## Installing
|
|
39
|
-
|
|
40
|
-
### Homebrew
|
|
14
|
+
## Install
|
|
41
15
|
|
|
42
16
|
```sh
|
|
43
17
|
brew install nikolasgioannou/tap/moth
|
|
44
18
|
```
|
|
45
19
|
|
|
46
|
-
### Shell
|
|
47
|
-
|
|
48
20
|
```sh
|
|
49
21
|
curl -fsSL https://raw.githubusercontent.com/nikolasgioannou/moth/main/install.sh | sh
|
|
50
22
|
```
|
|
51
23
|
|
|
52
|
-
Installs to `~/.local/bin` by default. Set `MOTH_INSTALL_DIR` to change that, or `MOTH_VERSION` to pin a release. The download is checked against the release's published checksums.
|
|
53
|
-
|
|
54
|
-
### npm
|
|
55
|
-
|
|
56
24
|
```sh
|
|
57
25
|
npm install -g moth-cli
|
|
58
26
|
```
|
|
59
27
|
|
|
60
|
-
Or
|
|
28
|
+
Or grab a binary from [releases](https://github.com/nikolasgioannou/moth/releases). macOS, Linux and Windows.
|
|
61
29
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Binaries for macOS, Linux and Windows are attached to every [release](https://github.com/nikolasgioannou/moth/releases), alongside a `SHA256SUMS` file. Download one, `chmod +x` it, and put it on your `PATH`.
|
|
65
|
-
|
|
66
|
-
### From source
|
|
30
|
+
## Use it
|
|
67
31
|
|
|
68
32
|
```sh
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
33
|
+
moth init # one question per status, Enter accepts each
|
|
34
|
+
moth new "Parse the frontmatter" # prints the id it assigned
|
|
35
|
+
moth new "Ship the binary" --body "Needs the parser first."
|
|
36
|
+
moth edit 280f4d --blocked-by 8e0642 --priority high --label release
|
|
73
37
|
```
|
|
74
38
|
|
|
75
|
-
|
|
39
|
+
Then ask what you can actually start, rather than what merely exists:
|
|
76
40
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Statuses in 'backlog' [backlog]
|
|
82
|
-
Statuses in 'unstarted' [todo]
|
|
83
|
-
Statuses in 'started' [in-progress]
|
|
84
|
-
Statuses in 'completed' [done]
|
|
85
|
-
Statuses in 'canceled' [canceled]
|
|
86
|
-
Statuses in 'duplicate' [duplicate]
|
|
41
|
+
```
|
|
42
|
+
$ moth list --unblocked
|
|
43
|
+
backlog
|
|
44
|
+
8e0642 Parse the frontmatter none
|
|
87
45
|
```
|
|
88
46
|
|
|
89
|
-
|
|
47
|
+
`280f4d` is missing because it is waiting on `8e0642`.
|
|
90
48
|
|
|
91
|
-
|
|
92
|
-
$ moth new "Parse the frontmatter"
|
|
93
|
-
001 Parse the frontmatter
|
|
49
|
+
Name a ticket however you remember it. `moth show 280f4d`, `moth show 280` and `moth show "ship the binary"` all find the same one, and an ambiguous reference lists the candidates rather than guessing.
|
|
94
50
|
|
|
95
|
-
|
|
96
|
-
002 Handle quoted strings
|
|
51
|
+
`moth schema --json` reports exactly what this repository considers a legal ticket, which is how an agent learns the rules in one call.
|
|
97
52
|
|
|
98
|
-
|
|
99
|
-
003 Ship the first binary
|
|
100
|
-
```
|
|
53
|
+
## Commands
|
|
101
54
|
|
|
102
|
-
|
|
55
|
+
Ten, and that is the whole surface:
|
|
103
56
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
57
|
+
- `init` — set up a repository
|
|
58
|
+
- `new` — file a ticket
|
|
59
|
+
- `list` — the default view, priority then age
|
|
60
|
+
- `show` — one ticket in full
|
|
61
|
+
- `move` — change status, which is how you claim work
|
|
62
|
+
- `edit` — change any field, the body included
|
|
63
|
+
- `delete` — remove a ticket
|
|
64
|
+
- `board` — a markdown board, grouped by status
|
|
65
|
+
- `check` — validate every ticket against the schema
|
|
66
|
+
- `schema` — this repository's rules, as JSON
|
|
107
67
|
|
|
108
|
-
|
|
109
|
-
001 Parse the frontmatter in-progress
|
|
110
|
-
```
|
|
68
|
+
Each one's `--help` carries its flags and a worked example, and `--json` is available on everything that returns a ticket.
|
|
111
69
|
|
|
112
|
-
|
|
70
|
+
## Why it exists
|
|
113
71
|
|
|
114
|
-
|
|
115
|
-
$ moth list --unblocked --category backlog
|
|
116
|
-
backlog
|
|
117
|
-
002 Handle quoted strings none ↳ 001
|
|
118
|
-
```
|
|
72
|
+
A folder of markdown files is free, but nothing enforces it. Session one's agent writes `status: todo`. Session three's agent, having never seen that file, writes `state: in_progress`. By session ten the folder is unqueryable, and "what's blocked?" means re-deriving a grep every time.
|
|
119
73
|
|
|
120
|
-
|
|
74
|
+
moth refuses writes a bare filesystem would accept: an unrecognised status, a field nobody declared, a parent that would form a cycle. An agent cannot invent `status: blocked`, because the write fails and says what is legal. Because the shape is known, querying it is a command rather than a grep somebody has to get right.
|
|
121
75
|
|
|
122
|
-
|
|
123
|
-
$ moth show 3
|
|
124
|
-
003 Ship the first binary
|
|
125
|
-
status backlog
|
|
126
|
-
priority high
|
|
127
|
-
labels release
|
|
128
|
-
blocked by 001
|
|
129
|
-
created 2026-08-31T00:35:11.462Z
|
|
130
|
-
updated 2026-08-31T00:35:11.484Z
|
|
76
|
+
## What it refuses to do
|
|
131
77
|
|
|
132
|
-
|
|
133
|
-
```
|
|
78
|
+
The refusals are the design, not gaps:
|
|
134
79
|
|
|
135
|
-
|
|
80
|
+
- **No assignees, no accounts.** Moving a ticket into a started status is how you claim it.
|
|
81
|
+
- **No statuses outside six fixed categories.** Name your own; each belongs to `backlog`, `unstarted`, `started`, `completed`, `canceled` or `duplicate`. Queries by category work in any repository.
|
|
82
|
+
- **No undeclared fields.** Custom fields are allowed, but must be declared in config first, so an agent can never introduce one.
|
|
83
|
+
- **No comments, no activity log.** `git log -p` on a ticket is already a complete, attributed history.
|
|
84
|
+
- **No cycles, sprints, estimates, projects or manual ordering. No web UI, no TUI.**
|
|
136
85
|
|
|
137
|
-
|
|
86
|
+
Every rejection, with its reasoning, is in [the spec](docs/spec-v1.md#out-of-scope).
|
|
138
87
|
|
|
139
|
-
|
|
140
|
-
moth.config.yml statuses, custom fields, where tickets live
|
|
141
|
-
.moth/
|
|
142
|
-
001-parse-the-frontmatter.md
|
|
143
|
-
002-handle-quoted-strings.md
|
|
144
|
-
```
|
|
88
|
+
## Storage
|
|
145
89
|
|
|
146
|
-
|
|
90
|
+
`moth.config.yml` at the root, and one markdown file per ticket in `.moth/`, named for its title so a directory listing reads like a backlog — `fix-the-login-redirect-280f4d.md`:
|
|
147
91
|
|
|
148
92
|
```markdown
|
|
149
93
|
---
|
|
150
|
-
id:
|
|
94
|
+
id: "280f4d"
|
|
151
95
|
title: Ship the first binary
|
|
152
96
|
status: backlog
|
|
153
97
|
priority: high
|
|
154
98
|
labels:
|
|
155
99
|
- release
|
|
156
|
-
created_at: 2026-08-
|
|
157
|
-
updated_at: 2026-08-
|
|
100
|
+
created_at: 2026-08-31T02:31:59.759Z
|
|
101
|
+
updated_at: 2026-08-31T02:31:59.787Z
|
|
158
102
|
blocked_by:
|
|
159
|
-
-
|
|
103
|
+
- "8e0642"
|
|
160
104
|
---
|
|
161
105
|
|
|
162
106
|
Blocked on the parser landing.
|
|
163
107
|
```
|
|
164
108
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
Run `moth --help` for the list, and `moth <command> --help` for a worked example of any one of them. `moth schema --json` reports what this repository considers a legal ticket.
|
|
109
|
+
Flat, with nothing central that every write touches, so two branches creating tickets merge cleanly. Commit them with your code and they travel through branches, clones and pull requests.
|
|
168
110
|
|
|
169
|
-
## Design
|
|
111
|
+
## Design
|
|
170
112
|
|
|
171
113
|
- [The v1 spec](docs/spec-v1.md) — what was built, and every rejected alternative
|
|
172
|
-
- [Architecture decisions](docs/adr/) —
|
|
114
|
+
- [Architecture decisions](docs/adr/) — four decisions, each with the alternatives that lost
|
|
115
|
+
- [Contributing](CONTRIBUTING.md)
|
|
173
116
|
|
|
174
|
-
moth tracks its own development in moth: the backlog is
|
|
117
|
+
moth tracks its own development in moth: the backlog is [`.moth/`](.moth/).
|
|
175
118
|
|
|
176
119
|
## Licence
|
|
177
120
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moth-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "An opinionated issue tracker that lives in your repo. Tickets are markdown files with an enforced schema.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"issue-tracker",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"bin"
|
|
23
23
|
],
|
|
24
24
|
"optionalDependencies": {
|
|
25
|
-
"moth-cli-darwin-arm64": "0.
|
|
26
|
-
"moth-cli-darwin-x64": "0.
|
|
27
|
-
"moth-cli-linux-arm64": "0.
|
|
28
|
-
"moth-cli-linux-x64": "0.
|
|
29
|
-
"moth-cli-windows-x64": "0.
|
|
25
|
+
"moth-cli-darwin-arm64": "0.3.0",
|
|
26
|
+
"moth-cli-darwin-x64": "0.3.0",
|
|
27
|
+
"moth-cli-linux-arm64": "0.3.0",
|
|
28
|
+
"moth-cli-linux-x64": "0.3.0",
|
|
29
|
+
"moth-cli-windows-x64": "0.3.0"
|
|
30
30
|
}
|
|
31
31
|
}
|