@plosson/agentio 0.1.25 → 0.1.27

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 CHANGED
@@ -72,6 +72,7 @@ Download from [GitHub Releases](https://github.com/plosson/agentio/releases/late
72
72
  | Google Chat | Available | `send`, `list`, `get` |
73
73
  | Slack | Available | `send` |
74
74
  | JIRA | Available | `projects`, `search`, `get`, `comment`, `transitions`, `transition` |
75
+ | RSS | Available | `articles`, `get`, `info` |
75
76
  | Linear | Planned | - |
76
77
 
77
78
  ## Usage
@@ -177,6 +178,23 @@ agentio jira transitions PROJ-123
177
178
  agentio jira transition PROJ-123 <transition-id>
178
179
  ```
179
180
 
181
+ ### RSS
182
+
183
+ ```bash
184
+ # List articles from a blog (feed URL auto-discovered)
185
+ agentio rss articles https://simonwillison.net
186
+ agentio rss articles https://steipete.me --limit 5
187
+
188
+ # Filter by date
189
+ agentio rss articles https://blog.fsck.com --since 2025-01-01
190
+
191
+ # Get feed info (shows discovered feed URL)
192
+ agentio rss info https://kau.sh
193
+
194
+ # Get a specific article
195
+ agentio rss get https://simonwillison.net <article-url>
196
+ ```
197
+
180
198
  ## Multi-Profile Support
181
199
 
182
200
  Each service supports multiple named profiles:
@@ -192,54 +210,52 @@ agentio gmail list --profile work
192
210
 
193
211
  ## Claude Code Integration
194
212
 
195
- agentio provides plugins for [Claude Code](https://claude.ai/download) with skills for Gmail, Telegram, and Google Chat operations.
213
+ agentio provides plugins for [Claude Code](https://claude.ai/download) with skills for Gmail, Telegram, Google Chat, JIRA, and RSS operations.
196
214
 
197
- ### Install the Plugin
215
+ ### Install Marketplaces and Plugins
198
216
 
199
217
  ```bash
200
- # Install from GitHub
201
- agentio claude plugin install plosson/agentio
218
+ # Add a marketplace (auto-detected from URL)
219
+ agentio claude install https://github.com/plosson/agentio
202
220
 
203
- # Or install from a full GitHub URL
204
- agentio claude plugin install https://github.com/plosson/agentio
221
+ # Install a plugin (auto-detected from name@marketplace format)
222
+ agentio claude install agentio-gmail@agentio
205
223
 
206
- # Install to a specific directory
207
- agentio claude plugin install plosson/agentio -d ~/myproject
208
-
209
- # Install only specific components (skills, commands, hooks, agents)
210
- agentio claude plugin install plosson/agentio --skills
211
- agentio claude plugin install plosson/agentio --agents
212
-
213
- # Force reinstall if already exists
214
- agentio claude plugin install plosson/agentio -f
215
-
216
- # Show detailed installation logs
217
- agentio claude plugin install plosson/agentio --verbose
224
+ # Install all from agentio.json
225
+ agentio claude install
218
226
  ```
219
227
 
220
- Once installed, Claude Code can use the agentio CLI skills to help you manage emails, send Telegram messages, and more.
221
-
222
228
  ### Manage Plugins
223
229
 
224
230
  ```bash
225
- # List installed plugins
226
- agentio claude plugin list
231
+ # List marketplaces and plugins from agentio.json
232
+ agentio claude list
227
233
 
228
- # Remove a plugin
229
- agentio claude plugin remove agentio
234
+ # Update marketplaces
235
+ agentio claude update
236
+ agentio claude update https://github.com/plosson/agentio
237
+
238
+ # Remove a marketplace or plugin
239
+ agentio claude remove https://github.com/plosson/agentio
240
+ agentio claude remove agentio-gmail@agentio
230
241
  ```
231
242
 
232
- ### Install from agentio.json
243
+ ### agentio.json
233
244
 
234
- If your project has an `agentio.json` file listing plugins, you can install all of them at once:
245
+ Projects can define marketplaces and plugins in an `agentio.json` file:
235
246
 
236
- ```bash
237
- # Install all plugins from agentio.json in current directory
238
- agentio claude plugin install
247
+ ```json
248
+ {
249
+ "marketplaces": [
250
+ "https://github.com/plosson/agentio"
251
+ ],
252
+ "plugins": [
253
+ "agentio-gmail@agentio",
254
+ "agentio-rss@agentio"
255
+ ]
256
+ }
239
257
  ```
240
258
 
241
- Plugins are installed to `.claude/` in the target directory (skills, commands, hooks, and agents subdirectories).
242
-
243
259
  ## Design
244
260
 
245
261
  agentio is designed for LLM consumption:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plosson/agentio",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "CLI for LLM agents to interact with communication and tracking services",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -10,6 +10,15 @@ import {
10
10
  removePlugin,
11
11
  } from '../services/claude-plugin/agentio-json';
12
12
 
13
+ /**
14
+ * Determine if the argument is a marketplace URL or a plugin name.
15
+ * - URL (contains :// or github.com/) -> marketplace
16
+ * - Contains @ (e.g., plugin@marketplace) -> plugin
17
+ */
18
+ function isMarketplaceUrl(arg: string): boolean {
19
+ return arg.includes('://') || arg.startsWith('github.com/');
20
+ }
21
+
13
22
  /**
14
23
  * Execute a claude CLI command and return the result.
15
24
  */
@@ -90,6 +99,82 @@ async function installPluginCmd(name: string): Promise<boolean> {
90
99
  return false;
91
100
  }
92
101
 
102
+ /**
103
+ * Get list of installed marketplaces with their names and sources.
104
+ */
105
+ async function getMarketplaces(): Promise<Array<{ name: string; source: string }>> {
106
+ const result = await execClaude(['plugin', 'marketplace', 'list']);
107
+ if (!result.success) {
108
+ return [];
109
+ }
110
+
111
+ const marketplaces: Array<{ name: string; source: string }> = [];
112
+ const lines = result.stdout.split('\n');
113
+
114
+ let currentName: string | null = null;
115
+ for (const line of lines) {
116
+ // Match marketplace name: " ❯ marketplace-name"
117
+ const nameMatch = line.match(/^\s*❯\s+(.+)$/);
118
+ if (nameMatch) {
119
+ currentName = nameMatch[1].trim();
120
+ continue;
121
+ }
122
+
123
+ // Match source line: " Source: GitHub (user/repo)" or " Source: Git (url)"
124
+ const sourceMatch = line.match(/^\s*Source:\s*(?:GitHub|Git)\s*\((.+)\)$/);
125
+ if (sourceMatch && currentName) {
126
+ marketplaces.push({ name: currentName, source: sourceMatch[1].trim() });
127
+ currentName = null;
128
+ }
129
+ }
130
+
131
+ return marketplaces;
132
+ }
133
+
134
+ /**
135
+ * Find marketplace name by URL.
136
+ */
137
+ async function findMarketplaceName(url: string): Promise<string | null> {
138
+ const marketplaces = await getMarketplaces();
139
+
140
+ // Normalize the URL for comparison
141
+ const normalizedUrl = url
142
+ .replace(/^https?:\/\//, '')
143
+ .replace(/^github\.com\//, '')
144
+ .replace(/\.git$/, '')
145
+ .toLowerCase();
146
+
147
+ for (const mp of marketplaces) {
148
+ const normalizedSource = mp.source
149
+ .replace(/^https?:\/\//, '')
150
+ .replace(/^github\.com\//, '')
151
+ .replace(/\.git$/, '')
152
+ .toLowerCase();
153
+
154
+ if (normalizedSource === normalizedUrl || normalizedSource.includes(normalizedUrl) || normalizedUrl.includes(normalizedSource)) {
155
+ return mp.name;
156
+ }
157
+ }
158
+
159
+ return null;
160
+ }
161
+
162
+ /**
163
+ * Update a marketplace by name.
164
+ */
165
+ async function updateMarketplace(name: string): Promise<boolean> {
166
+ console.error(`Updating marketplace: ${name}`);
167
+ const result = await execClaude(['plugin', 'marketplace', 'update', name]);
168
+
169
+ if (result.success) {
170
+ console.log(` Updated: ${name}`);
171
+ return true;
172
+ }
173
+
174
+ console.error(` Failed: ${result.stderr.trim()}`);
175
+ return false;
176
+ }
177
+
93
178
  /**
94
179
  * Uninstall a plugin by calling claude plugin uninstall --scope project.
95
180
  */
@@ -118,79 +203,71 @@ export function registerClaudeCommands(program: Command): void {
118
203
  .command('claude')
119
204
  .description('Claude Code plugin operations');
120
205
 
121
- // install command group
122
- const install = claude.command('install').description('Install marketplaces and plugins');
123
-
124
- install
125
- .command('marketplace')
126
- .description('Add a plugin marketplace')
127
- .argument('<url>', 'Marketplace GitHub URL')
206
+ // install command - auto-detects marketplace vs plugin
207
+ claude
208
+ .command('install')
209
+ .description('Install a marketplace (URL) or plugin (name@marketplace), or all from agentio.json')
210
+ .argument('[target]', 'Marketplace URL or plugin name (e.g., plugin@marketplace)')
128
211
  .option('-d, --dir <path>', 'Directory with agentio.json (default: current directory)')
129
- .action(async (url, options) => {
212
+ .action(async (target, options) => {
130
213
  try {
131
214
  const targetDir = options.dir ? path.resolve(options.dir) : process.cwd();
132
215
 
133
- const success = await installMarketplace(url);
134
- if (success) {
135
- addMarketplace(targetDir, url);
136
- }
137
- } catch (error) {
138
- handleError(error);
139
- }
140
- });
141
-
142
- install
143
- .command('plugin')
144
- .description('Install a plugin')
145
- .argument('<name>', 'Plugin name (e.g., plugin-name@marketplace)')
146
- .option('-d, --dir <path>', 'Directory with agentio.json (default: current directory)')
147
- .action(async (name, options) => {
148
- try {
149
- const targetDir = options.dir ? path.resolve(options.dir) : process.cwd();
216
+ // No argument - install all from agentio.json
217
+ if (!target) {
218
+ const config = loadAgentioJson(targetDir);
150
219
 
151
- const success = await installPluginCmd(name);
152
- if (success) {
153
- addPlugin(targetDir, name);
154
- }
155
- } catch (error) {
156
- handleError(error);
157
- }
158
- });
220
+ if (config.marketplaces.length === 0 && config.plugins.length === 0) {
221
+ console.log('No marketplaces or plugins defined in agentio.json');
222
+ return;
223
+ }
159
224
 
160
- // install with no subcommand - install all from agentio.json
161
- install.action(async (options) => {
162
- try {
163
- const targetDir = options.dir ? path.resolve(options.dir) : process.cwd();
164
- const config = loadAgentioJson(targetDir);
225
+ console.error(`Installing from agentio.json...`);
165
226
 
166
- if (config.marketplaces.length === 0 && config.plugins.length === 0) {
167
- console.log('No marketplaces or plugins defined in agentio.json');
168
- return;
169
- }
227
+ // Install marketplaces first
228
+ if (config.marketplaces.length > 0) {
229
+ console.error(`\nMarketplaces (${config.marketplaces.length}):`);
230
+ for (const url of config.marketplaces) {
231
+ await installMarketplace(url);
232
+ }
233
+ }
170
234
 
171
- console.error(`Installing from agentio.json...`);
235
+ // Then install plugins
236
+ if (config.plugins.length > 0) {
237
+ console.error(`\nPlugins (${config.plugins.length}):`);
238
+ for (const name of config.plugins) {
239
+ await installPluginCmd(name);
240
+ }
241
+ }
172
242
 
173
- // Install marketplaces first
174
- if (config.marketplaces.length > 0) {
175
- console.error(`\nMarketplaces (${config.marketplaces.length}):`);
176
- for (const url of config.marketplaces) {
177
- await installMarketplace(url);
243
+ console.log('\nDone.');
244
+ return;
178
245
  }
179
- }
180
246
 
181
- // Then install plugins
182
- if (config.plugins.length > 0) {
183
- console.error(`\nPlugins (${config.plugins.length}):`);
184
- for (const name of config.plugins) {
185
- await installPluginCmd(name);
247
+ // Auto-detect type based on argument format
248
+ if (isMarketplaceUrl(target)) {
249
+ // It's a marketplace URL
250
+ const success = await installMarketplace(target);
251
+ if (success) {
252
+ addMarketplace(targetDir, target);
253
+ }
254
+ } else if (target.includes('@')) {
255
+ // It's a plugin (contains @)
256
+ const success = await installPluginCmd(target);
257
+ if (success) {
258
+ addPlugin(targetDir, target);
259
+ }
260
+ } else {
261
+ throw new CliError(
262
+ 'INVALID_PARAMS',
263
+ `Cannot determine type for: ${target}`,
264
+ 'Use a URL for marketplaces or name@marketplace for plugins'
265
+ );
186
266
  }
267
+ } catch (error) {
268
+ handleError(error);
187
269
  }
188
-
189
- console.log('\nDone.');
190
- } catch (error) {
191
- handleError(error);
192
- }
193
- });
270
+ });
194
271
 
195
272
  // list command
196
273
  claude
@@ -228,40 +305,80 @@ export function registerClaudeCommands(program: Command): void {
228
305
  }
229
306
  });
230
307
 
231
- // remove command group
232
- const remove = claude.command('remove').description('Remove marketplaces and plugins');
233
-
234
- remove
235
- .command('marketplace')
236
- .description('Remove a marketplace from agentio.json')
237
- .argument('<url>', 'Marketplace URL to remove')
308
+ // update command - updates marketplaces
309
+ claude
310
+ .command('update')
311
+ .description('Update marketplaces (all from agentio.json or a specific URL)')
312
+ .argument('[url]', 'Marketplace URL to update (updates all from agentio.json if not specified)')
238
313
  .option('-d, --dir <path>', 'Directory with agentio.json (default: current directory)')
239
314
  .action(async (url, options) => {
240
315
  try {
241
316
  const targetDir = options.dir ? path.resolve(options.dir) : process.cwd();
242
317
 
243
- const removed = removeMarketplace(targetDir, url);
244
- if (removed) {
245
- console.log(`Removed marketplace: ${url}`);
318
+ if (url) {
319
+ // Update specific marketplace by URL
320
+ const name = await findMarketplaceName(url);
321
+ if (!name) {
322
+ throw new CliError('NOT_FOUND', `Marketplace not found for URL: ${url}`, 'Make sure the marketplace is installed');
323
+ }
324
+ await updateMarketplace(name);
246
325
  } else {
247
- throw new CliError('NOT_FOUND', `Marketplace not found: ${url}`);
326
+ // Update all marketplaces from agentio.json
327
+ const config = loadAgentioJson(targetDir);
328
+
329
+ if (config.marketplaces.length === 0) {
330
+ console.log('No marketplaces defined in agentio.json');
331
+ return;
332
+ }
333
+
334
+ console.error(`Updating marketplaces from agentio.json...`);
335
+
336
+ for (const marketplaceUrl of config.marketplaces) {
337
+ const name = await findMarketplaceName(marketplaceUrl);
338
+ if (name) {
339
+ await updateMarketplace(name);
340
+ } else {
341
+ console.error(` Skipped (not installed): ${marketplaceUrl}`);
342
+ }
343
+ }
344
+
345
+ console.log('\nDone.');
248
346
  }
249
347
  } catch (error) {
250
348
  handleError(error);
251
349
  }
252
350
  });
253
351
 
254
- remove
255
- .command('plugin')
256
- .description('Uninstall a plugin and remove from agentio.json')
257
- .argument('<name>', 'Plugin name to remove')
352
+ // remove command - auto-detects marketplace vs plugin
353
+ claude
354
+ .command('remove')
355
+ .description('Remove a marketplace (URL) or plugin (name@marketplace)')
356
+ .argument('<target>', 'Marketplace URL or plugin name to remove')
258
357
  .option('-d, --dir <path>', 'Directory with agentio.json (default: current directory)')
259
- .action(async (name, options) => {
358
+ .action(async (target, options) => {
260
359
  try {
261
360
  const targetDir = options.dir ? path.resolve(options.dir) : process.cwd();
262
361
 
263
- await uninstallPluginCmd(name);
264
- removePlugin(targetDir, name);
362
+ // Auto-detect type based on argument format
363
+ if (isMarketplaceUrl(target)) {
364
+ // It's a marketplace URL
365
+ const removed = removeMarketplace(targetDir, target);
366
+ if (removed) {
367
+ console.log(`Removed marketplace: ${target}`);
368
+ } else {
369
+ throw new CliError('NOT_FOUND', `Marketplace not found: ${target}`);
370
+ }
371
+ } else if (target.includes('@')) {
372
+ // It's a plugin (contains @)
373
+ await uninstallPluginCmd(target);
374
+ removePlugin(targetDir, target);
375
+ } else {
376
+ throw new CliError(
377
+ 'INVALID_PARAMS',
378
+ `Cannot determine type for: ${target}`,
379
+ 'Use a URL for marketplaces or name@marketplace for plugins'
380
+ );
381
+ }
265
382
  } catch (error) {
266
383
  handleError(error);
267
384
  }