shopping-list
Conversational shopping list with categories, family sharing, and purchase history. Add items, check them off, organize by category — all through natural language. Use for any shopping list, grocery list, "add to list", "what do we need", or "we need to buy" requests.
安装 / 下载方式
TotalClaw CLI推荐
totalclaw install clawskills:clawskills~ajeenkya-shopping-listcURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Aclawskills~ajeenkya-shopping-list/file -o ajeenkya-shopping-list.md# Shopping List
Manage the household shopping list. Add items with quantities, check them off,
organize by category. Data lives in `skills/shopping-list/data/`.
For full command reference and output formats, read `skills/shopping-list/references/commands.md`.
## Before Every Command
Run these checks before every shopping list operation, in order:
1. If `skills/shopping-list/data/` directory does not exist, create it.
2. If `data/active.json` does not exist, create it with:
```json
{
"items": [],
"categories": ["Produce", "Dairy", "Meat", "Pantry", "Frozen", "Beverages", "Household", "Personal"],
"lastModified": "<current ISO timestamp>"
}
```
3. If `data/active.json` exists but fails to parse as valid JSON, rename it to `data/active.json.corrupt` and create a fresh default file. Tell the user: "Shopping list data was corrupted. Saved backup as active.json.corrupt and started a fresh list."
4. If `data/config.json` does not exist, create it with: `{ "user": null, "snoozes": {} }`
5. If `config.json` has `"user": null`, ask the user: "What's your name? I'll use it to track who added each item." Store their answer (lowercased) in `config.json` before proceeding with the original command.
6. Run the archive process (see Archive section below).
All file paths in this document are relative to `skills/shopping-list/` unless stated otherwise.
## Data Files
### data/active.json
The live shopping list. Contains all items that have not yet been archived.
```json
{
"items": [
{
"id": "F47AC10B-58CC-4372-A567-0E02B2C3D479",
"name": "Whole Milk",
"normalizedName": "whole milk",
"quantity": 2,
"unit": "gallons",
"category": "Dairy",
"checkedOff": false,
"checkedOffDate": null,
"addedBy": "aj",
"addedDate": "2026-02-24T10:00:00Z",
"notes": null
}
],
"categories": ["Produce", "Dairy", "Meat", "Pantry", "Frozen", "Beverages", "Household", "Personal"],
"lastModified": "2026-02-24T10:00:00Z"
}
```
Field rules:
- `id` -- Generate via `uuidgen` in bash. If that command is unavailable, construct an ID from the current ISO timestamp concatenated with 4 random hex characters (e.g. `2026-02-24T10:00:00Z-a3f1`).
- `name` -- The item name as the user provided it, with leading/trailing whitespace trimmed. Preserve original casing for display purposes.
- `normalizedName` -- Always computed as `name.toLowerCase().trim()`. Recompute on every add or edit. This field is used for all matching and deduplication logic.
- `quantity` -- Optional. Defaults to `null` when the user does not specify a quantity. When present, must be a number greater than 0. Fractional values are fine (e.g. 0.5 for half a pound).
- `unit` -- Optional free text (e.g. "gallons", "lbs", "bunch", "bag"). Defaults to `null` when not specified.
- `category` -- One of the values from the `categories` array, or "Uncategorized".
- `checkedOff` -- Boolean. Starts as `false`. Set to `true` when the user checks off the item.
- `checkedOffDate` -- ISO timestamp when the item was checked off. `null` when not checked off.
- `addedBy` -- Lowercase string from `config.json` user field (e.g. "aj" or "shal").
- `addedDate` -- ISO timestamp when the item was first added.
- `notes` -- Optional free text for special instructions ("the organic one", "Costco size"). Defaults to `null`.
- `categories` (top-level array) -- The master list of known categories. Starts with 8 presets. Custom categories are appended when created.
- `lastModified` -- ISO timestamp. Updated on every write to this file.
### data/config.json
Stores session-persistent user configuration.
```json
{ "user": "aj", "snoozes": {} }
```
- `user` -- Set on first interaction, persists across sessions. Lowercase string.
- `snoozes` -- Reserved for Phase 2 restock suggestions. Ignore for now; preserve the field when writing.
### data/history-YYYY-MM.json
Monthly archive of purchased items. One file per calendar month, created on demand.
```json
{
"month": "2026-02",
"archivedItems": [
{ "...same fields as active item...", "archivedDate": "2026-02-25T08:00:00Z" }
]
}
```
The `archivedDate` field is added to each item when it moves from active to history. All original fields from the active item are preserved. A new file is created automatically when the first item is archived in a month that does not yet have a history file.
## Core Operations
### Adding Items
Parse the user's natural language into name, quantity, and unit for each item.
**Category inference:** Silently infer the category from the item name. Common mappings: milk/cheese/yogurt/butter go to Dairy, chicken/beef/fish to Meat, bananas/lettuce/onions to Produce, rice/pasta/flour to Pantry, dish soap/paper towels to Household, shampoo/toothpaste to Personal, beer/wine/juice to Beverages, frozen pizza/ice cream to Frozen. If the mapping is not obvious, assign "Uncategorized". Never prompt the user for a category. If the user wants to change it, they can say "move X to Y category".
**Multi-item input:** The user may add several items at once: "add eggs, bread, and 2 gallons milk" should produce 3 separate items. Parse commas and "and" as item separators. When the parsing is ambiguous (compound item names like "hot dog buns" near separators), ask the user rather than guessing wrong.
**Duplicate handling:** Before creating a new item, check if an item with the same `normalizedName` already exists in the active list.
- If it exists and is not checked off, update the existing item's quantity (add to it if the user gives a new quantity, or leave unchanged if not). Do not create a duplicate.
- If it exists and is checked off, uncheck it (set `checkedOff: false`, `checkedOffDate: null`) and update the quantity if specified.
**New categories:** If the user explicitly specifies a category that is not in the `categories` array, add it to the array and assign the item to it.
Set `addedBy` from `config.json` user value. Set `addedDate` to the current ISO timestamp. Set `checkedOff` to `false` and `checkedOffDate` to `null`. Update `lastModified` after writing.
### Checking Off Items
Match items by `normalizedName` -- case-insensitive, partial match is acceptable (e.g. "milk" matches "whole milk" if it is the only milk item).
- **One match:** Set `checkedOff` to `true` and `checkedOffDate` to the current ISO timestamp. Confirm to the user.
- **Multiple matches:** List the matching items and ask the user which one to check off.
- **No match:** Show the current list so the user can identify the correct item.
Checked-off items remain in `active.json` and display with strikethrough styling until the archive process moves them to history (after 24 hours).
### Removing Items
Permanently delete an item from the active list. No archive record, no history entry. This exists for "I added this by mistake" corrections.
Match by `normalizedName` using the same matching rules as checking off. Confirm removal to the user.
### Editing Items
Match the target item by `normalizedName`, then update the specified fields. Supported editable fields: name, quantity, unit, category, notes.
If the name changes, recompute `normalizedName`. If the category changes to one not in the `categories` array, add it. Update `lastModified` after writing.
### Changing User
When the user says "switch to Shal", "I'm AJ", or similar, update the `user` field in `config.json`. All subsequent item additions will use the new user value for `addedBy`.
## Archive Process
This process runs automatically at the start of every shopping list command, before handling the user's request.
1. Read `data/active.json`.
2. Find all items where `checkedOff` is `true` AND `checkedOffDate` is more than 24 hours ago.
3. If no items qualify, skip the rest of this process.
4. Determine the current month string (e.g. "2026-02"). Read `data/history-2026-02.json`. If it does not exist, create it with `{ "month": "2026-02", "archiv