cognary-cli 1.0.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,88 @@
1
+ # Cognary CLI
2
+
3
+ Command-line interface for Cognary Tasks.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ cd cognary-cli
9
+ npm install
10
+ npm link # Optional: makes 'cognary-cli' available globally
11
+ ```
12
+
13
+ ## Authentication
14
+
15
+ The CLI requires an API key to authenticate with the Cognary Tasks API. You can provide it in two ways:
16
+
17
+ 1. **Environment variable** (recommended):
18
+ ```bash
19
+ export COGNARY_API_KEY=nts_your_api_key_here
20
+ ```
21
+
22
+ 2. **Command line option**:
23
+ ```bash
24
+ cognary-cli --api-key nts_your_api_key_here tasks list
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ You can also configure the API URL:
30
+
31
+ ```bash
32
+ # Environment variable
33
+ export COGNARY_API_URL=https://your-api-url.com
34
+
35
+ # Or command line option
36
+ cognary-cli --api-url https://your-api-url.com tasks list
37
+ ```
38
+
39
+ ## Commands
40
+
41
+ ### List Tasks
42
+
43
+ ```bash
44
+ cognary-cli tasks list
45
+ ```
46
+
47
+ Options:
48
+ - `-l, --limit <number>` - Number of tasks to return (default: 20)
49
+ - `-p, --page <number>` - Page number (default: 1)
50
+ - `-s, --status <status>` - Filter by status: active, completed, all (default: all)
51
+ - `-c, --category <category>` - Filter by category
52
+ - `--priority <priority>` - Filter by priority: High, Medium, Low
53
+ - `--search <query>` - Search tasks
54
+ - `--sort <field>` - Sort by: createdAt, updatedAt, dueDate, priority, title
55
+ - `--order <order>` - Sort order: asc, desc (default: desc)
56
+ - `--json` - Output as JSON
57
+
58
+ Examples:
59
+ ```bash
60
+ # List all tasks
61
+ cognary-cli tasks list
62
+
63
+ # List only active tasks
64
+ cognary-cli tasks list --status active
65
+
66
+ # Search for tasks containing "meeting"
67
+ cognary-cli tasks list --search meeting
68
+
69
+ # List high priority tasks sorted by due date
70
+ cognary-cli tasks list --priority High --sort dueDate --order asc
71
+
72
+ # Output as JSON
73
+ cognary-cli tasks list --json
74
+ ```
75
+
76
+ ### Get a Single Task
77
+
78
+ ```bash
79
+ cognary-cli tasks get <task-id>
80
+ ```
81
+
82
+ Options:
83
+ - `--json` - Output as JSON
84
+
85
+ Example:
86
+ ```bash
87
+ cognary-cli tasks get abc123-def456-ghi789
88
+ ```
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import '../src/index.js';
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "cognary-cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI for Cognary AI Tasks, Notes and Leads",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "bin": {
8
+ "cognary-cli": "./bin/cognary-cli.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node bin/cognary-cli.js"
12
+ },
13
+ "keywords": [
14
+ "cognary",
15
+ "tasks",
16
+ "cli"
17
+ ],
18
+ "author": "",
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "chalk": "^5.3.0",
22
+ "commander": "^12.1.0",
23
+ "dotenv": "^16.4.5"
24
+ }
25
+ }
package/src/api.js ADDED
@@ -0,0 +1,105 @@
1
+ /**
2
+ * API client for Cognary Tasks
3
+ */
4
+ export class CognaryApi {
5
+ constructor(apiUrl, apiKey) {
6
+ this.apiUrl = apiUrl;
7
+ this.apiKey = apiKey;
8
+ this.baseEndpoint = '/api/v1/public';
9
+ }
10
+
11
+ async request(endpoint, options = {}) {
12
+ const url = `${this.apiUrl}${this.baseEndpoint}${endpoint}`;
13
+
14
+ const response = await fetch(url, {
15
+ ...options,
16
+ headers: {
17
+ 'Authorization': `Bearer ${this.apiKey}`,
18
+ 'Content-Type': 'application/json',
19
+ ...options.headers,
20
+ },
21
+ });
22
+
23
+ if (!response.ok) {
24
+ const errorBody = await response.json().catch(() => ({}));
25
+ const error = new Error(errorBody.message || `API request failed with status ${response.status}`);
26
+ error.status = response.status;
27
+ error.code = errorBody.error;
28
+ throw error;
29
+ }
30
+
31
+ return response.json();
32
+ }
33
+
34
+ /**
35
+ * List tasks with optional filters
36
+ */
37
+ async listTasks(options = {}) {
38
+ const params = new URLSearchParams();
39
+
40
+ if (options.page) params.set('page', options.page);
41
+ if (options.limit) params.set('limit', options.limit);
42
+ if (options.status) params.set('status', options.status);
43
+ if (options.category) params.set('category', options.category);
44
+ if (options.priority) params.set('priority', options.priority);
45
+ if (options.search) params.set('search', options.search);
46
+ if (options.sort) params.set('sort', options.sort);
47
+ if (options.order) params.set('order', options.order);
48
+ if (options.completedLimit) params.set('completed_limit', options.completedLimit);
49
+
50
+ const queryString = params.toString();
51
+ const endpoint = `/tasks${queryString ? `?${queryString}` : ''}`;
52
+
53
+ return this.request(endpoint);
54
+ }
55
+
56
+ /**
57
+ * Get a single task by ID
58
+ */
59
+ async getTask(taskId) {
60
+ return this.request(`/tasks/${taskId}`);
61
+ }
62
+
63
+ /**
64
+ * Create a new task
65
+ */
66
+ async createTask({ title, notes, category, priority, dueDate }) {
67
+ const body = { title };
68
+ if (notes) body.notes = notes;
69
+ if (category) body.category = category;
70
+ if (priority) body.priority = priority;
71
+ if (dueDate) body.dueDate = dueDate;
72
+
73
+ return this.request('/tasks', {
74
+ method: 'POST',
75
+ body: JSON.stringify(body),
76
+ });
77
+ }
78
+
79
+ /**
80
+ * Update an existing task
81
+ */
82
+ async updateTask(taskId, fields) {
83
+ const body = {};
84
+ if (fields.title !== undefined) body.title = fields.title;
85
+ if (fields.notes !== undefined) body.notes = fields.notes;
86
+ if (fields.category !== undefined) body.category = fields.category;
87
+ if (fields.priority !== undefined) body.priority = fields.priority;
88
+ if (fields.dueDate !== undefined) body.dueDate = fields.dueDate;
89
+ if (fields.isCompleted !== undefined) body.isCompleted = fields.isCompleted;
90
+
91
+ return this.request(`/tasks/${taskId}`, {
92
+ method: 'PATCH',
93
+ body: JSON.stringify(body),
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Delete a task
99
+ */
100
+ async deleteTask(taskId) {
101
+ return this.request(`/tasks/${taskId}`, {
102
+ method: 'DELETE',
103
+ });
104
+ }
105
+ }
@@ -0,0 +1,339 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { getConfig } from '../config.js';
4
+ import { CognaryApi } from '../api.js';
5
+
6
+ export const tasksCommand = new Command('tasks')
7
+ .description('Manage tasks');
8
+
9
+ // List tasks command
10
+ tasksCommand
11
+ .command('list')
12
+ .description('List tasks')
13
+ .option('-l, --limit <number>', 'Number of tasks to return', '20')
14
+ .option('-p, --page <number>', 'Page number', '1')
15
+ .option('-s, --status <status>', 'Filter by status (active, completed, all)', 'all')
16
+ .option('-c, --category <category>', 'Filter by category')
17
+ .option('--priority <priority>', 'Filter by priority (High, Medium, Low)')
18
+ .option('--search <query>', 'Search tasks')
19
+ .option('--sort <field>', 'Sort by field (createdAt, updatedAt, dueDate, priority, title)', 'createdAt')
20
+ .option('--order <order>', 'Sort order (asc, desc)', 'desc')
21
+ .option('--active-only', 'Show only active tasks')
22
+ .option('--completed-limit <number>', 'Limit number of completed tasks returned')
23
+ .option('--json', 'Output as JSON')
24
+ .action(async (options, cmd) => {
25
+ try {
26
+ const config = getConfig(cmd);
27
+ const api = new CognaryApi(config.apiUrl, config.apiKey);
28
+
29
+ const status = options.activeOnly ? 'active' : options.status;
30
+
31
+ const result = await api.listTasks({
32
+ limit: parseInt(options.limit, 10),
33
+ page: parseInt(options.page, 10),
34
+ status,
35
+ completedLimit: options.completedLimit ? parseInt(options.completedLimit, 10) : undefined,
36
+ category: options.category,
37
+ priority: options.priority,
38
+ search: options.search,
39
+ sort: options.sort,
40
+ order: options.order,
41
+ });
42
+
43
+ if (options.json) {
44
+ console.log(JSON.stringify(result, null, 2));
45
+ return;
46
+ }
47
+
48
+ // Display tasks in a readable format
49
+ const { data: tasks, pagination } = result;
50
+
51
+ if (tasks.length === 0) {
52
+ console.log(chalk.yellow('No tasks found.'));
53
+ return;
54
+ }
55
+
56
+ console.log(chalk.bold(`\nTasks (${pagination.total} total)\n`));
57
+
58
+ for (const task of tasks) {
59
+ const status = task.isCompleted
60
+ ? chalk.green('✓')
61
+ : chalk.gray('○');
62
+
63
+ const priority = formatPriority(task.priority);
64
+ const dueDate = task.dueDate
65
+ ? chalk.cyan(new Date(task.dueDate).toLocaleDateString())
66
+ : chalk.gray('No due date');
67
+
68
+ console.log(`${status} ${chalk.bold(task.title)}`);
69
+ console.log(` ${chalk.gray('ID:')} ${task.id}`);
70
+ console.log(` ${chalk.gray('Category:')} ${task.category} | ${chalk.gray('Priority:')} ${priority} | ${chalk.gray('Due:')} ${dueDate}`);
71
+
72
+ if (task.notes) {
73
+ const truncatedNotes = task.notes.length > 100
74
+ ? task.notes.substring(0, 100) + '...'
75
+ : task.notes;
76
+ console.log(` ${chalk.gray('Notes:')} ${truncatedNotes}`);
77
+ }
78
+ console.log('');
79
+ }
80
+
81
+ // Pagination info
82
+ console.log(chalk.gray(`Page ${pagination.page} of ${pagination.totalPages} (${pagination.limit} per page)`));
83
+
84
+ if (pagination.hasMore) {
85
+ console.log(chalk.gray(`Use --page ${pagination.page + 1} to see more`));
86
+ }
87
+
88
+ } catch (error) {
89
+ handleError(error);
90
+ }
91
+ });
92
+
93
+ // Get single task command
94
+ tasksCommand
95
+ .command('get <id>')
96
+ .description('Get a task by ID')
97
+ .option('--json', 'Output as JSON')
98
+ .action(async (id, options, cmd) => {
99
+ try {
100
+ const config = getConfig(cmd);
101
+ const api = new CognaryApi(config.apiUrl, config.apiKey);
102
+
103
+ const result = await api.getTask(id);
104
+
105
+ if (options.json) {
106
+ console.log(JSON.stringify(result, null, 2));
107
+ return;
108
+ }
109
+
110
+ const task = result.data;
111
+ const status = task.isCompleted
112
+ ? chalk.green('✓ Completed')
113
+ : chalk.yellow('○ Active');
114
+
115
+ console.log(`\n${chalk.bold(task.title)}`);
116
+ console.log(chalk.gray('─'.repeat(50)));
117
+ console.log(`${chalk.gray('ID:')} ${task.id}`);
118
+ console.log(`${chalk.gray('Status:')} ${status}`);
119
+ console.log(`${chalk.gray('Category:')} ${task.category}`);
120
+ console.log(`${chalk.gray('Priority:')} ${formatPriority(task.priority)}`);
121
+
122
+ if (task.dueDate) {
123
+ console.log(`${chalk.gray('Due Date:')} ${new Date(task.dueDate).toLocaleString()}`);
124
+ }
125
+
126
+ console.log(`${chalk.gray('Created:')} ${new Date(task.createdAt).toLocaleString()}`);
127
+ console.log(`${chalk.gray('Updated:')} ${new Date(task.updatedAt).toLocaleString()}`);
128
+
129
+ if (task.notes) {
130
+ console.log(`\n${chalk.gray('Notes:')}`);
131
+ console.log(task.notes);
132
+ }
133
+
134
+ if (task.labels && task.labels.length > 0) {
135
+ console.log(`\n${chalk.gray('Labels:')} ${task.labels.map(l => l.name).join(', ')}`);
136
+ }
137
+
138
+ console.log('');
139
+
140
+ } catch (error) {
141
+ handleError(error);
142
+ }
143
+ });
144
+
145
+ // Add task command
146
+ tasksCommand
147
+ .command('add <title>')
148
+ .description('Create a new task')
149
+ .option('-n, --notes <notes>', 'Task notes')
150
+ .option('-c, --category <category>', 'Task category')
151
+ .option('--priority <priority>', 'Task priority (High, Medium, Low)')
152
+ .option('--due-date <date>', 'Due date')
153
+ .option('--json', 'Output as JSON')
154
+ .action(async (title, options, cmd) => {
155
+ try {
156
+ const config = getConfig(cmd);
157
+ const api = new CognaryApi(config.apiUrl, config.apiKey);
158
+
159
+ const result = await api.createTask({
160
+ title,
161
+ notes: options.notes,
162
+ category: options.category,
163
+ priority: options.priority,
164
+ dueDate: options.dueDate,
165
+ });
166
+
167
+ if (options.json) {
168
+ console.log(JSON.stringify(result, null, 2));
169
+ return;
170
+ }
171
+
172
+ const task = result.data;
173
+ console.log(chalk.green(`\n✓ Task created successfully!\n`));
174
+ console.log(`${chalk.bold(task.title)}`);
175
+ console.log(` ${chalk.gray('ID:')} ${task.id}`);
176
+ if (task.category) console.log(` ${chalk.gray('Category:')} ${task.category}`);
177
+ if (task.priority) console.log(` ${chalk.gray('Priority:')} ${formatPriority(task.priority)}`);
178
+ if (task.dueDate) console.log(` ${chalk.gray('Due:')} ${chalk.cyan(new Date(task.dueDate).toLocaleDateString())}`);
179
+ if (task.notes) console.log(` ${chalk.gray('Notes:')} ${task.notes}`);
180
+ console.log('');
181
+
182
+ } catch (error) {
183
+ handleError(error);
184
+ }
185
+ });
186
+
187
+ // Update task command
188
+ tasksCommand
189
+ .command('update <id>')
190
+ .description('Update an existing task')
191
+ .option('-t, --title <title>', 'New title')
192
+ .option('-n, --notes <notes>', 'New notes')
193
+ .option('-c, --category <category>', 'New category')
194
+ .option('--priority <priority>', 'New priority (High, Medium, Low)')
195
+ .option('--due-date <date>', 'New due date')
196
+ .option('--json', 'Output as JSON')
197
+ .action(async (id, options, cmd) => {
198
+ try {
199
+ const config = getConfig(cmd);
200
+ const api = new CognaryApi(config.apiUrl, config.apiKey);
201
+
202
+ const result = await api.updateTask(id, {
203
+ title: options.title,
204
+ notes: options.notes,
205
+ category: options.category,
206
+ priority: options.priority,
207
+ dueDate: options.dueDate,
208
+ });
209
+
210
+ if (options.json) {
211
+ console.log(JSON.stringify(result, null, 2));
212
+ return;
213
+ }
214
+
215
+ const task = result.data;
216
+ console.log(chalk.green(`\n✓ Task updated successfully!\n`));
217
+ console.log(`${chalk.bold(task.title)}`);
218
+ console.log(` ${chalk.gray('ID:')} ${task.id}`);
219
+ if (task.category) console.log(` ${chalk.gray('Category:')} ${task.category}`);
220
+ if (task.priority) console.log(` ${chalk.gray('Priority:')} ${formatPriority(task.priority)}`);
221
+ if (task.dueDate) console.log(` ${chalk.gray('Due:')} ${chalk.cyan(new Date(task.dueDate).toLocaleDateString())}`);
222
+ console.log('');
223
+
224
+ } catch (error) {
225
+ handleError(error);
226
+ }
227
+ });
228
+
229
+ // Delete task command
230
+ tasksCommand
231
+ .command('delete <id>')
232
+ .description('Delete a task')
233
+ .option('--json', 'Output as JSON')
234
+ .action(async (id, options, cmd) => {
235
+ try {
236
+ const config = getConfig(cmd);
237
+ const api = new CognaryApi(config.apiUrl, config.apiKey);
238
+
239
+ const result = await api.deleteTask(id);
240
+
241
+ if (options.json) {
242
+ console.log(JSON.stringify(result, null, 2));
243
+ return;
244
+ }
245
+
246
+ console.log(chalk.green(`\n✓ Task ${id} deleted successfully!\n`));
247
+
248
+ } catch (error) {
249
+ handleError(error);
250
+ }
251
+ });
252
+
253
+ // Complete task command
254
+ tasksCommand
255
+ .command('complete <id>')
256
+ .description('Mark a task as completed')
257
+ .option('--json', 'Output as JSON')
258
+ .action(async (id, options, cmd) => {
259
+ try {
260
+ const config = getConfig(cmd);
261
+ const api = new CognaryApi(config.apiUrl, config.apiKey);
262
+
263
+ const result = await api.updateTask(id, { isCompleted: true });
264
+
265
+ if (options.json) {
266
+ console.log(JSON.stringify(result, null, 2));
267
+ return;
268
+ }
269
+
270
+ const task = result.data;
271
+ console.log(chalk.green(`\n✓ Task marked as completed!\n`));
272
+ console.log(`${chalk.green('✓')} ${chalk.bold(task.title)}`);
273
+ console.log(` ${chalk.gray('ID:')} ${task.id}`);
274
+ console.log('');
275
+
276
+ } catch (error) {
277
+ handleError(error);
278
+ }
279
+ });
280
+
281
+ // Uncomplete task command
282
+ tasksCommand
283
+ .command('uncomplete <id>')
284
+ .description('Mark a task as active')
285
+ .option('--json', 'Output as JSON')
286
+ .action(async (id, options, cmd) => {
287
+ try {
288
+ const config = getConfig(cmd);
289
+ const api = new CognaryApi(config.apiUrl, config.apiKey);
290
+
291
+ const result = await api.updateTask(id, { isCompleted: false });
292
+
293
+ if (options.json) {
294
+ console.log(JSON.stringify(result, null, 2));
295
+ return;
296
+ }
297
+
298
+ const task = result.data;
299
+ console.log(chalk.green(`\n✓ Task marked as active!\n`));
300
+ console.log(`${chalk.yellow('○')} ${chalk.bold(task.title)}`);
301
+ console.log(` ${chalk.gray('ID:')} ${task.id}`);
302
+ console.log('');
303
+
304
+ } catch (error) {
305
+ handleError(error);
306
+ }
307
+ });
308
+
309
+ function formatPriority(priority) {
310
+ if (!priority) return chalk.gray('None');
311
+
312
+ switch (priority) {
313
+ case 'High':
314
+ return chalk.red('High');
315
+ case 'Medium':
316
+ return chalk.yellow('Medium');
317
+ case 'Low':
318
+ return chalk.blue('Low');
319
+ default:
320
+ return priority;
321
+ }
322
+ }
323
+
324
+ function handleError(error) {
325
+ if (error.status === 401) {
326
+ console.error(chalk.red('Error: Authentication failed. Please check your API key.'));
327
+ } else if (error.status === 403) {
328
+ console.error(chalk.red('Error: Access denied. Your API key may not have the required permissions.'));
329
+ } else if (error.status === 404) {
330
+ console.error(chalk.red('Error: Resource not found.'));
331
+ } else if (error.status === 429) {
332
+ console.error(chalk.red('Error: Rate limit exceeded. Please try again later.'));
333
+ } else if (error.code === 'ECONNREFUSED') {
334
+ console.error(chalk.red('Error: Could not connect to the API. Is the server running?'));
335
+ } else {
336
+ console.error(chalk.red(`Error: ${error.message}`));
337
+ }
338
+ process.exit(1);
339
+ }
package/src/config.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Get configuration from command options or environment variables
3
+ */
4
+ export function getConfig(cmd) {
5
+ // Walk up the command chain to find the root program options
6
+ let root = cmd;
7
+ while (root.parent) {
8
+ root = root.parent;
9
+ }
10
+ const opts = root.opts();
11
+
12
+ const apiKey = opts.apiKey || process.env.COGNARY_API_KEY;
13
+ const apiUrl = opts.apiUrl || process.env.COGNARY_API_URL || 'https://tasks.cognary.ai';
14
+
15
+ if (!apiKey) {
16
+ console.error('Error: API key is required.');
17
+ console.error('Provide it via --api-key option or set COGNARY_API_KEY environment variable.');
18
+ process.exit(1);
19
+ }
20
+
21
+ return {
22
+ apiKey,
23
+ apiUrl: apiUrl.replace(/\/$/, ''), // Remove trailing slash
24
+ };
25
+ }
package/src/index.js ADDED
@@ -0,0 +1,20 @@
1
+ import { Command } from 'commander';
2
+ import dotenv from 'dotenv';
3
+ import { tasksCommand } from './commands/tasks.js';
4
+
5
+ // Load environment variables from .env file
6
+ dotenv.config();
7
+
8
+ const program = new Command();
9
+
10
+ program
11
+ .name('cognary-cli')
12
+ .description('CLI for Cognary Tasks')
13
+ .version('1.0.0')
14
+ .option('-k, --api-key <key>', 'API key for authentication (or set COGNARY_API_KEY env var)')
15
+ .option('-u, --api-url <url>', 'API base URL (or set COGNARY_API_URL env var)');
16
+
17
+ // Add commands
18
+ program.addCommand(tasksCommand);
19
+
20
+ program.parse();