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