taskify-nostr 0.1.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 ADDED
@@ -0,0 +1,271 @@
1
+ # taskify-cli
2
+
3
+ A command-line client for managing tasks over the Nostr protocol.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ cd taskify-cli
9
+ npm install
10
+ npm link # installs `taskify` globally
11
+ taskify --help
12
+ ```
13
+
14
+ **Requirements:** Node.js 22+ (for `--experimental-strip-types`)
15
+
16
+ If `npm link` fails due to permissions:
17
+
18
+ ```bash
19
+ npm link --prefix ~/.local
20
+ # then add ~/.local/bin to your PATH
21
+ ```
22
+
23
+ ---
24
+
25
+ ## Setup
26
+
27
+ ### Private key
28
+
29
+ ```bash
30
+ taskify config set nsec nsec1...
31
+ # or via environment variable (takes precedence):
32
+ export TASKIFY_NSEC=nsec1...
33
+ ```
34
+
35
+ ### Relays (optional — defaults provided)
36
+
37
+ ```bash
38
+ taskify config set relay wss://relay.damus.io
39
+ taskify relay list
40
+ taskify relay status
41
+ ```
42
+
43
+ ### Show config
44
+
45
+ ```bash
46
+ taskify config show
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Command Reference
52
+
53
+ ### Task commands
54
+
55
+ | Command | Options | Description |
56
+ |---|---|---|
57
+ | `list` | `--board` `--status open\|done\|any` `--column` `--json` | List tasks |
58
+ | `add <title>` | `--board` `--due` `--priority 1\|2\|3` `--note` `--subtask` `--column` | Create a task |
59
+ | `show <taskId>` | `--board` `--json` | Show full task details |
60
+ | `update <taskId>` | `--title` `--due` `--priority` `--note` `--column` | Update task fields |
61
+ | `done <taskId>` | `--board` | Mark a task done |
62
+ | `reopen <taskId>` | `--board` | Reopen a completed task |
63
+ | `delete <taskId>` | `--board` `--force` | Delete a task |
64
+ | `search <query>` | `--board` `--status` `--json` | Full-text search across tasks |
65
+ | `subtask <taskId> <ref>` | `--board` `--done` `--reopen` | Toggle a subtask (ref = index or title substring) |
66
+ | `remind <taskId> <presets...>` | `--board` | Set reminder presets (e.g. `1d 1h`) |
67
+ | `assign <taskId> <npub\|hex>` | `--board` | Assign a user to a task |
68
+ | `unassign <taskId> <npub\|hex>` | `--board` | Unassign a user from a task |
69
+
70
+ ### Inbox
71
+
72
+ | Command | Options | Description |
73
+ |---|---|---|
74
+ | `inbox list` | `--board` | List inbox items (tasks flagged for triage) |
75
+ | `inbox add <title>` | `--board` | Quick-capture a task to inbox |
76
+ | `inbox triage <taskId>` | `--board` `--column` `--priority` `--due` `--yes` | Interactively triage an inbox item |
77
+
78
+ ### Export / Import
79
+
80
+ | Command | Options | Description |
81
+ |---|---|---|
82
+ | `export` | `--board` `--format json\|csv\|md` `--status` `--output <file>` | Export tasks |
83
+ | `import <file>` | `--board` `--dry-run` `--yes` | Import tasks from JSON or CSV |
84
+
85
+ Export formats:
86
+ - `json` — JSON array (default)
87
+ - `csv` — RFC 4180 with headers: `id,title,status,priority,dueISO,column,boardName,note,subtasks,createdAt`
88
+ - `md` — Markdown checklist grouped by column
89
+
90
+ Import accepts `.json` (array of `{title, note?, priority?, dueISO?, column?, subtasks?}`) or `.csv` (same schema as export). Duplicate titles on the same board are skipped.
91
+
92
+ ### Board management
93
+
94
+ | Command | Options | Description |
95
+ |---|---|---|
96
+ | `boards` | `--json` | List all boards with task counts |
97
+ | `board list` | | List joined boards |
98
+ | `board join <boardId>` | `--name` `--relay` | Join a board by Nostr event id |
99
+ | `board leave <boardId>` | | Leave a board |
100
+ | `board create <name>` | `--kind lists\|week` `--relay` | Create and publish a new board |
101
+ | `board sync [boardId]` | | Sync board metadata and columns from relay |
102
+ | `board columns [board]` | | List columns for a board |
103
+ | `board children <board>` | | List child boards (compound boards only) |
104
+
105
+ ### Relay management
106
+
107
+ | Command | Description |
108
+ |---|---|
109
+ | `relay status` | Show relay connection status |
110
+ | `relay list` | List configured relays |
111
+ | `relay add <url>` | Add a relay |
112
+ | `relay remove <url>` | Remove a relay |
113
+
114
+ ### Trust
115
+
116
+ | Command | Description |
117
+ |---|---|
118
+ | `trust add <npub>` | Mark an npub as trusted |
119
+ | `trust remove <npub>` | Remove a trusted npub |
120
+ | `trust list` | List all trusted npubs |
121
+
122
+ ### AI agent (`taskify agent`)
123
+
124
+ | Command | Options | Description |
125
+ |---|---|---|
126
+ | `agent config show` | | Show AI agent config (key masked) |
127
+ | `agent config set-key <key>` | | Set OpenAI API key |
128
+ | `agent config set-model <model>` | | Set model (default: `gpt-4o-mini`) |
129
+ | `agent config set-url <url>` | | Set base URL (default: OpenAI) |
130
+ | `agent add <description>` | `--board` `--yes` `--dry-run` `--json` | NL → task via AI extraction |
131
+ | `agent triage` | `--board` `--yes` `--dry-run` `--json` | AI priority suggestions across open tasks |
132
+
133
+ `agent add` extracts title, note, priority, due date, column, and subtasks from a natural language description. Reviews extracted fields before creating (skippable with `--yes`).
134
+
135
+ `agent triage` sends all open tasks to the AI for priority scoring, prints a suggestion table, then applies changes on confirmation.
136
+
137
+ API key can also be set via `TASKIFY_AGENT_API_KEY` environment variable.
138
+
139
+ ### Cache
140
+
141
+ | Command | Description |
142
+ |---|---|
143
+ | `cache status` | Show cache age and task counts |
144
+ | `cache clear` | Clear the local task cache |
145
+
146
+ ### Shell completions
147
+
148
+ ```bash
149
+ # zsh
150
+ taskify completions --shell zsh > ~/.zsh/completions/_taskify
151
+ echo 'fpath=(~/.zsh/completions $fpath)' >> ~/.zshrc
152
+ echo 'autoload -U compinit && compinit' >> ~/.zshrc
153
+ source ~/.zshrc
154
+
155
+ # bash
156
+ taskify completions --shell bash > ~/.bash_completion.d/taskify
157
+ source ~/.bash_completion.d/taskify
158
+
159
+ # fish
160
+ taskify completions --shell fish > ~/.config/fish/completions/taskify.fish
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Column support
166
+
167
+ Boards with columns (`kind: lists` or `kind: week`) support column-aware operations:
168
+
169
+ ```bash
170
+ # Add a task to a specific column
171
+ taskify add "Fix auth bug" --board "Dev" --column "Bugs"
172
+
173
+ # Filter list by column
174
+ taskify list --board "Dev" --column "In Progress"
175
+
176
+ # Move a task to a different column
177
+ taskify update <taskId> --column "Done"
178
+ ```
179
+
180
+ Week boards auto-assign tasks to today's column (YYYY-MM-DD) when no `--column` is specified.
181
+
182
+ Use `taskify board sync` to pull the latest column definitions from the relay, then `taskify board columns` to list them.
183
+
184
+ ---
185
+
186
+ ## Compound boards
187
+
188
+ Compound boards aggregate multiple child boards. `taskify list` on a compound board fetches and merges tasks from all children automatically.
189
+
190
+ ```bash
191
+ taskify board sync
192
+ taskify board children "My Compound Board"
193
+ taskify list --board "My Compound Board" # merges all children
194
+ ```
195
+
196
+ Adding tasks to a compound board directly is not allowed — use a child board instead.
197
+
198
+ ---
199
+
200
+ ## Example output
201
+
202
+ ### `taskify list`
203
+
204
+ ```
205
+ Board: Dev
206
+ ID TITLE DUE PRI COL TRUST
207
+ e5100d28 Ship v2 release (2/4) 2026-03-15 2 Backlog ✓
208
+ a8c2f4b0 Fix login bug 2026-03-10 3 Bugs ✓
209
+ ```
210
+
211
+ - **ID** — 8-char prefix (accepted by all commands)
212
+ - **TITLE** — truncated at 40 chars; `(N/M)` = completed/total subtasks
213
+ - **COL** — column name if on a lists/compound board
214
+ - **TRUST** — `✓` if `lastEditedBy` is a trusted npub
215
+
216
+ ### `taskify list --json`
217
+
218
+ ```json
219
+ [
220
+ {
221
+ "id": "a8c2f4b0...",
222
+ "boardId": "...",
223
+ "boardName": "Dev",
224
+ "title": "Fix login bug",
225
+ "note": "",
226
+ "dueISO": "2026-03-10",
227
+ "priority": 3,
228
+ "column": "bugs-col-id",
229
+ "completed": false,
230
+ "subtasks": [],
231
+ "assignees": [],
232
+ "inboxItem": false,
233
+ "createdAt": 1773019736
234
+ }
235
+ ]
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Agent usage
241
+
242
+ Taskify is built for AI agent workflows. All commands support `--json` output and exit `0` on success / `1` on error. A 10-second relay timeout is enforced; connections are torn down on exit.
243
+
244
+ ```bash
245
+ # List open tasks as JSON
246
+ taskify list --status any --json
247
+
248
+ # Create a task with AI assistance
249
+ taskify agent add "urgent: fix the login crash by tomorrow" --board Dev --yes
250
+
251
+ # Triage priorities across the board
252
+ taskify agent triage --board Dev --yes
253
+
254
+ # Bulk import from JSON
255
+ taskify import tasks.json --board Dev --yes
256
+ ```
257
+
258
+ ### Trust model
259
+
260
+ - **`✓ trusted`** — `lastEditedBy` pubkey is in your trusted list
261
+ - **`✗ untrusted`** — editor unknown or not in list
262
+ - **`? unknown`** — no editor information
263
+
264
+ ---
265
+
266
+ ## Environment variables
267
+
268
+ | Variable | Description |
269
+ |---|---|
270
+ | `TASKIFY_NSEC` | Override saved private key |
271
+ | `TASKIFY_AGENT_API_KEY` | OpenAI API key for `taskify agent` |
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Minimal fetch-based OpenAI-compatible AI client.
3
+ * No npm packages — uses Node 18+ built-in fetch.
4
+ */
5
+ export async function callAI(opts) {
6
+ const timeout = opts.timeoutMs ?? 30_000;
7
+ const controller = new AbortController();
8
+ const timer = setTimeout(() => controller.abort(), timeout);
9
+ let response;
10
+ try {
11
+ response = await fetch(`${opts.baseUrl}/chat/completions`, {
12
+ method: "POST",
13
+ headers: {
14
+ "Content-Type": "application/json",
15
+ "Authorization": `Bearer ${opts.apiKey}`,
16
+ },
17
+ body: JSON.stringify({
18
+ model: opts.model,
19
+ messages: [
20
+ { role: "system", content: opts.systemPrompt },
21
+ { role: "user", content: opts.userMessage },
22
+ ],
23
+ }),
24
+ signal: controller.signal,
25
+ });
26
+ }
27
+ finally {
28
+ clearTimeout(timer);
29
+ }
30
+ if (!response.ok) {
31
+ const body = await response.text().catch(() => "");
32
+ throw new Error(`AI API error ${response.status}: ${body.slice(0, 200)}`);
33
+ }
34
+ const data = await response.json();
35
+ const content = data?.choices?.[0]?.message?.content;
36
+ if (!content) {
37
+ throw new Error("AI API returned no content in choices[0].message.content");
38
+ }
39
+ return content;
40
+ }