Initial commit: Bring Shopping List Skill
- CLI tool for managing Bring! shopping lists - Environment variable-based configuration - Commands: add, list, remove, recent - Complete English documentation - Security: credentials via env vars, not hardcoded Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.5
commit
df53e2f1ed
@@ -0,0 +1,3 @@
|
|||||||
|
BRING_EMAIL=your-email@example.com
|
||||||
|
BRING_PASSWORD=your-password
|
||||||
|
BRING_LIST_UUID=your-list-uuid
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
*.log
|
||||||
|
.env
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
# Bring Shopping List Skill
|
||||||
|
|
||||||
|
Manage your Bring! Shopping List via CLI using the unofficial Bring! API.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
chmod +x bring
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Set the following environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export BRING_EMAIL="your-email@example.com"
|
||||||
|
export BRING_PASSWORD="your-password"
|
||||||
|
export BRING_LIST_UUID="your-list-uuid"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 1: Using a `.env` file (recommended)
|
||||||
|
|
||||||
|
Create a `.env` file in your skill directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
BRING_EMAIL=your-email@example.com
|
||||||
|
BRING_PASSWORD=your-password
|
||||||
|
BRING_LIST_UUID=your-list-uuid
|
||||||
|
```
|
||||||
|
|
||||||
|
Then source it before running commands:
|
||||||
|
```bash
|
||||||
|
source .env
|
||||||
|
./bring list
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use with AI agents that support .env files.
|
||||||
|
|
||||||
|
### Option 2: System environment variables
|
||||||
|
|
||||||
|
Add to your `~/.bashrc` or `~/.zshrc`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export BRING_EMAIL="your-email@example.com"
|
||||||
|
export BRING_PASSWORD="your-password"
|
||||||
|
export BRING_LIST_UUID="your-list-uuid"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Finding your List UUID
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const bringApi = require('bring-shopping');
|
||||||
|
const bring = new bringApi({
|
||||||
|
mail: 'your@email.com',
|
||||||
|
password: 'yourpassword'
|
||||||
|
});
|
||||||
|
await bring.login();
|
||||||
|
const lists = await bring.loadLists();
|
||||||
|
console.log(lists.lists); // Shows all lists with their UUIDs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bring add <item> [specification] # Add item to shopping list
|
||||||
|
bring list # Show current items
|
||||||
|
bring remove <item> # Remove item (mark as purchased)
|
||||||
|
bring recent # Show recently purchased items
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add items
|
||||||
|
bring add Apples
|
||||||
|
bring add Milk "1 Liter"
|
||||||
|
bring add "Coffee Beans" "Fair Trade"
|
||||||
|
|
||||||
|
# Show list
|
||||||
|
bring list
|
||||||
|
|
||||||
|
# Remove item (mark as bought)
|
||||||
|
bring remove Apples
|
||||||
|
|
||||||
|
# Show recently purchased items
|
||||||
|
bring recent
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration with AI Agents
|
||||||
|
|
||||||
|
### For nanoclaw/openclaw/Carson
|
||||||
|
|
||||||
|
Add to your `CLAUDE.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Bring! Shopping List
|
||||||
|
|
||||||
|
**IMPORTANT - "Shopping list" always means Bring! App:**
|
||||||
|
|
||||||
|
When the user says "shopping list", they ALWAYS mean the Bring! Shopping List App:
|
||||||
|
- **NEVER** use a custom text file or other system
|
||||||
|
- **ALWAYS** use the Bring skill: `bring`
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
bring add <item> [specification] # Add item
|
||||||
|
bring list # Show list
|
||||||
|
bring remove <item> # Remove item
|
||||||
|
bring recent # Recently purchased
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
- "Add milk to shopping list" → `bring add Milk`
|
||||||
|
- "What's on the shopping list?" → `bring list`
|
||||||
|
- "Remove apples from list" → `bring remove Apples`
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setting environment variables for AI agents
|
||||||
|
|
||||||
|
Create a `.env` file in the skill directory with your credentials, then ensure your AI agent loads it before executing commands.
|
||||||
|
|
||||||
|
For nanoclaw/Carson, you can set the environment variables in the skill activation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export BRING_EMAIL="your-email@example.com"
|
||||||
|
export BRING_PASSWORD="your-password"
|
||||||
|
export BRING_LIST_UUID="your-list-uuid"
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
This skill uses the unofficial `bring-shopping` npm package:
|
||||||
|
https://github.com/foxriver76/node-bring-api
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- **Never commit your `.env` file** - it's in `.gitignore` by default
|
||||||
|
- Environment variables keep credentials out of the code
|
||||||
|
- Each user/agent instance uses their own credentials
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Items sync in real-time across all connected devices
|
||||||
|
- The specification parameter is optional (e.g., "Organic", "500g", etc.)
|
||||||
|
- Use quotes for items or specifications with spaces
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const bringApi = require('bring-shopping');
|
||||||
|
|
||||||
|
// Load credentials from environment variables
|
||||||
|
const CREDENTIALS = {
|
||||||
|
mail: process.env.BRING_EMAIL,
|
||||||
|
password: process.env.BRING_PASSWORD
|
||||||
|
};
|
||||||
|
|
||||||
|
const LIST_UUID = process.env.BRING_LIST_UUID;
|
||||||
|
|
||||||
|
// Validate required environment variables
|
||||||
|
if (!CREDENTIALS.mail || !CREDENTIALS.password || !LIST_UUID) {
|
||||||
|
console.error('Error: Missing required environment variables');
|
||||||
|
console.error('Please set: BRING_EMAIL, BRING_PASSWORD, BRING_LIST_UUID');
|
||||||
|
console.error('See SKILL.md for setup instructions');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
const command = args[0];
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
console.error('Usage: bring <add|list|remove|recent> [args...]');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bring = new bringApi(CREDENTIALS);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await bring.login();
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case 'add':
|
||||||
|
await addItem(bring, args.slice(1));
|
||||||
|
break;
|
||||||
|
case 'list':
|
||||||
|
await listItems(bring);
|
||||||
|
break;
|
||||||
|
case 'remove':
|
||||||
|
await removeItem(bring, args.slice(1));
|
||||||
|
break;
|
||||||
|
case 'recent':
|
||||||
|
await recentItems(bring);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Unknown command: ${command}`);
|
||||||
|
console.error('Available commands: add, list, remove, recent');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an item to the shopping list
|
||||||
|
*/
|
||||||
|
async function addItem(bring, args) {
|
||||||
|
const itemName = args[0];
|
||||||
|
const specification = args.slice(1).join(' ') || '';
|
||||||
|
|
||||||
|
if (!itemName) {
|
||||||
|
console.error('Usage: bring add <item> [specification]');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
await bring.saveItem(LIST_UUID, itemName, specification);
|
||||||
|
|
||||||
|
if (specification) {
|
||||||
|
console.log(`✓ "${itemName}" (${specification}) added to shopping list`);
|
||||||
|
} else {
|
||||||
|
console.log(`✓ "${itemName}" added to shopping list`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all items currently on the shopping list
|
||||||
|
*/
|
||||||
|
async function listItems(bring) {
|
||||||
|
const items = await bring.getItems(LIST_UUID);
|
||||||
|
|
||||||
|
if (items.purchase.length === 0) {
|
||||||
|
console.log('Shopping list is empty');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Shopping list:');
|
||||||
|
console.log('');
|
||||||
|
items.purchase.forEach((item, index) => {
|
||||||
|
if (item.specification) {
|
||||||
|
console.log(`${index + 1}. ${item.name} (${item.specification})`);
|
||||||
|
} else {
|
||||||
|
console.log(`${index + 1}. ${item.name}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item from the shopping list (mark as purchased)
|
||||||
|
*/
|
||||||
|
async function removeItem(bring, args) {
|
||||||
|
const itemName = args.join(' ');
|
||||||
|
|
||||||
|
if (!itemName) {
|
||||||
|
console.error('Usage: bring remove <item>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
await bring.removeItem(LIST_UUID, itemName);
|
||||||
|
console.log(`✓ "${itemName}" removed from shopping list`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show recently purchased items
|
||||||
|
*/
|
||||||
|
async function recentItems(bring) {
|
||||||
|
const items = await bring.getItems(LIST_UUID);
|
||||||
|
|
||||||
|
if (items.recently.length === 0) {
|
||||||
|
console.log('No recently purchased items');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Recently purchased:');
|
||||||
|
console.log('');
|
||||||
|
items.recently.slice(0, 10).forEach((item, index) => {
|
||||||
|
if (item.specification) {
|
||||||
|
console.log(`${index + 1}. ${item.name} (${item.specification})`);
|
||||||
|
} else {
|
||||||
|
console.log(`${index + 1}. ${item.name}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "bring-shopping-skill",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Bring! Shopping List Skill for Carson",
|
||||||
|
"dependencies": {
|
||||||
|
"bring-shopping": "^2.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user