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:
Carson (Claude Assistant)
2026-03-08 22:27:43 +01:00
co-authored by Claude Sonnet 4.5
commit df53e2f1ed
5 changed files with 300 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
BRING_EMAIL=your-email@example.com
BRING_PASSWORD=your-password
BRING_LIST_UUID=your-list-uuid
+4
View File
@@ -0,0 +1,4 @@
node_modules/
package-lock.json
*.log
.env
+146
View File
@@ -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
Executable
+139
View File
@@ -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();
+8
View File
@@ -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"
}
}