Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 142 additions & 31 deletions SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
name: kronan-cli
description: >
Search Krónan for groceries and prices, get SKU numbers, add to or change
shopping cart, and view past order history using the official Krónan Public API.
Leita að verði á matvörum í Krónunni, vinna með innkaupakörfu (bæta í eða breyta),
og skoða gamlar pantanir.
version: 0.2.0
Comprehensive CLI for Krónan.is grocery store using the official Public API.
Search products, browse categories, manage shopping cart and lists, view orders,
track purchase statistics, and manage shopping notes. Full CRUD operations
for all API features. Designed for both humans and AI agents.
version: 0.3.0
requires:
binaries:
- gh # GitHub CLI — required for install
Expand Down Expand Up @@ -88,6 +88,13 @@ kronan product <sku>
kronan product 02500188 --json
```

### Browse by category

```bash
kronan categories # List all categories
kronan category 01-01-02-epli # Browse products in category
```

### Cart management

```bash
Expand All @@ -96,19 +103,50 @@ kronan cart add <sku> [quantity] # Add item to cart
kronan cart clear # Clear all items from cart
```

### Order history
### Order history and modifications

```bash
kronan orders # Recent orders
kronan orders --json # JSON output for parsing
kronan order <token> # Specific order details (use order token, not ID)

# Modify orders (before fulfillment)
kronan order delete-lines <token> <lineId1> [lineId2...]
kronan order lower-quantity <token> <lineIds...> --quantity N
kronan order toggle-substitution <token> <lineIds...>
```

### Product lists (full CRUD)

```bash
kronan lists # List all product lists
kronan lists create <name> [--description "..."] # Create new list
kronan lists view <token> # View list details
kronan lists delete <token> [--force] # Delete a list
kronan lists add <list-token> <sku> [qty] # Add item to list
kronan lists remove <list-token> <sku> # Remove item from list
kronan lists clear <token> [--force] # Clear all items
```

### Product lists
### Shopping notes (Skundalisti)

```bash
kronan lists # View saved product lists
kronan lists --json
kronan notes # View shopping note
kronan notes add [--text "..."] [--sku SKU] [--quantity N]
kronan notes update <line-token> [--text "..."] [--quantity N]
kronan notes remove <line-token> # Remove item
kronan notes toggle <line-token> # Mark complete/incomplete
kronan notes clear [--force] # Clear all items
kronan notes archived # View completed items
```

### Purchase statistics

```bash
kronan stats [--limit N] [--offset N] # View purchase history
kronan stats --include-ignored # Include hidden products
kronan stats ignore <id> # Hide product from stats
kronan stats unignore <id> # Unhide product
```

### User identity
Expand All @@ -122,44 +160,98 @@ kronan me --json

All commands support `--json` for structured output. This makes kronan-cli suitable as a tool for AI agents managing grocery shopping.

**Important:** Commands that change state (`cart add`, `cart clear`) can modify the user's real shopping cart. Agents **must ask for explicit user confirmation** before running any state-changing command.
**Important:** Commands that change state can modify the user's real data. Agents **must ask for explicit user confirmation** before running any state-changing command.

Read-only commands (`search`, `product`, `orders`, `order`, `cart` (view), `lists`, `me`, `status`) are safe to run without confirmation.
State-changing commands:
- `cart add`, `cart clear`
- `order delete-lines`, `order lower-quantity`, `order toggle-substitution`
- `lists create`, `lists delete`, `lists add`, `lists remove`, `lists clear`
- `notes add`, `notes update`, `notes remove`, `notes toggle`, `notes clear`
- `stats ignore`, `stats unignore`

Example agent workflow:
Read-only commands are safe to run without confirmation:
- `search`, `product`, `categories`, `category`
- `orders`, `order` (view)
- `cart` (view)
- `lists` (view)
- `notes` (view), `notes archived`
- `stats` (view)
- `me`, `status`

```bash
# 1. Check what the user usually buys
kronan orders --json
### Example agent workflows

**Build a weekly cart from frequently purchased items:**

# 2. Search for a specific product
kronan search "nymjolk" --json --limit 5
```bash
# 1. Get purchase statistics to find frequently bought items
kronan stats --limit 50 --json

# 3. Add items to cart
kronan cart add 100224198 6
kronan cart add 02200946 1
# 2. Add top items to cart at their typical quantities
kronan cart add 100224198 6 # Nýmjólk x6
kronan cart add 02200946 1 # Heimilisbrauð

# 4. Review the cart
# 3. Review the cart
kronan cart --json
```

### Typical weekly shopping pattern
**Create a shopping list for a recipe:**

```bash
# 1. Create a new list
kronan lists create "Pizza Night" --description "Ingredients for homemade pizza"

# 2. Search for products and add to list
kronan search "mozzarella" --json
kronan lists add <list-token> 100246180 2

kronan search "pizzasósa" --json
kronan lists add <list-token> 100221958 1

# 3. View the completed list
kronan lists view <list-token>
```

**Manage shopping with notes (Skundalisti):**

```bash
# 1. Add items to shopping note
kronan notes add --text "Mjólk"
kronan notes add --sku 100224198 --quantity 2

An agent can analyze order history to find frequently purchased items and auto-populate the cart:
# 2. Mark items as you shop
kronan notes toggle <line-token>

1. Fetch orders with `kronan orders --json`
2. Count product frequency across orders (by SKU)
3. Add the top items at their typical quantities with `kronan cart add <sku> <qty>`
4. Present the cart for user review with `kronan cart`
# 3. View remaining items
kronan notes

# 4. View completed items
kronan notes archived
```

**Analyze and optimize purchases:**

```bash
# View purchase frequency for all products
kronan stats --json

# Hide irrelevant products from stats
kronan stats ignore <id>
```

## Flags

| Flag | Description |
|------|-------------|
| `--json` | Structured JSON output (for AI agents) |
| `--page <n>` | Page number (search) |
| `--page <n>` | Page number (search, category) |
| `--limit <n>` | Results per page |
| `--offset <n>` | Offset for pagination (orders) |
| `--offset <n>` | Offset for pagination |
| `--include-ignored` | Include ignored products in stats |
| `--force` | Skip confirmation for destructive operations |
| `--text "..."` | Text for shopping note item |
| `--sku SKU` | Product SKU |
| `--quantity N` | Quantity (default: 1) |
| `--description "..."` | Description for product list |

## API Reference

Expand All @@ -175,13 +267,26 @@ Key endpoints:
|----------|--------|------|-------------|
| `/products/search/` | POST | Yes | Product search |
| `/products/{sku}/` | GET | Yes | Product detail |
| `/categories/` | GET | Yes | Category tree |
| `/categories/{slug}/products/` | GET | Yes | Category products |
| `/checkout/` | GET | Yes | View checkout/cart |
| `/checkout/lines/` | POST | Yes | Add/replace checkout lines |
| `/orders/` | GET | Yes | Order history |
| `/orders/{token}/` | GET | Yes | Order details |
| `/orders/{token}/delete-lines/` | POST | Yes | Delete order lines |
| `/orders/{token}/lower-quantity-lines/` | POST | Yes | Lower line quantity |
| `/orders/{token}/lines-toggle-substitution/` | POST | Yes | Toggle substitution |
| `/me/` | GET | Yes | Current identity |
| `/product-lists/` | GET | Yes | Product lists |
| `/shopping-notes/` | GET | Yes | Shopping notes |
| `/product-lists/` | GET/POST | Yes | List/create product lists |
| `/product-lists/{token}/` | GET/PATCH/DELETE | Yes | Product list CRUD |
| `/product-lists/{token}/update-item/` | POST | Yes | Add/update list item |
| `/shopping-notes/` | GET | Yes | View shopping note |
| `/shopping-notes/add-line/` | POST | Yes | Add note line |
| `/shopping-notes/change-line/` | PATCH | Yes | Update note line |
| `/shopping-notes/delete-line/` | DELETE | Yes | Delete note line |
| `/shopping-notes/toggle-complete-on-line/` | PATCH | Yes | Toggle completion |
| `/product-purchase-stats/` | GET | Yes | Purchase statistics |
| `/product-purchase-stats/{id}/set-ignored/` | PATCH | Yes | Ignore/unignore product |

Auth header format: `Authorization: AccessToken {token}`

Expand All @@ -195,3 +300,9 @@ If you were using the previous version with Cognito authentication:
4. Update any scripts using `kronan login` to use `kronan token` instead

Note: Order IDs in the new API are tokens (UUIDs), not numeric IDs.

## Version History

- **v0.3.0** - Added comprehensive commands: categories, order modifications, product lists CRUD, shopping notes, purchase statistics
- **v0.2.0** - Migrated to Krónan Public API with AccessToken authentication
- **v0.1.0** - Initial release with Cognito authentication
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "kronan-cli",
"version": "0.1.0",
"version": "0.3.0",
"module": "src/index.ts",
"type": "module",
"private": true,
Expand Down
18 changes: 14 additions & 4 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,10 +670,20 @@ export async function deleteAllProductListItems(
export async function getShoppingNote(
token: AuthToken,
): Promise<PublicShoppingNote> {
const result = await apiRequest<PublicShoppingNote[]>("/shopping-notes/", {
token,
});
return result[0]!;
const result = await apiRequest<PublicShoppingNote | PublicShoppingNote[]>(
"/shopping-notes/",
{
token,
},
);
// API can return either a single object or an array with one object
if (Array.isArray(result)) {
if (result.length === 0) {
throw new Error("No shopping note found");
}
return result[0]!;
}
return result;
}

/**
Expand Down
66 changes: 66 additions & 0 deletions src/commands/categories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Categories commands
*/

import { getCategories, getCategoryProducts } from "../api.ts";
import { requireAuth } from "../auth.ts";

export async function categoriesCommand(
options: { json?: boolean } = {},
): Promise<void> {
const token = await requireAuth();
const categories = await getCategories(token);

if (options.json) {
console.log(JSON.stringify(categories, null, 2));
return;
}

console.log("Categories:\n");
for (const cat of categories) {
console.log(` ${cat.name}`);
if (cat.children && cat.children.length > 0) {
for (const child1 of cat.children) {
console.log(` └── ${child1.name} (${child1.slug})`);
if (child1.children && child1.children.length > 0) {
for (const child2 of child1.children) {
console.log(` └── ${child2.name} (${child2.slug})`);
}
}
}
}
console.log("");
}
}

export async function categoryProductsCommand(
slug: string,
options: { page?: number; json?: boolean } = {},
): Promise<void> {
const { page = 1, json = false } = options;
const token = await requireAuth();
const result = await getCategoryProducts(token, slug, page);

if (json) {
console.log(JSON.stringify(result, null, 2));
return;
}

console.log(`${result.name} (${result.count} products):\n`);

for (const product of result.products) {
const sale = product.onSale ? " [SALE]" : "";
const shortage = product.temporaryShortage ? " [OUT OF STOCK]" : "";
console.log(` ${product.sku} ${product.name}`);
console.log(
` ${product.price} kr ${product.priceInfo}${sale}${shortage}`,
);
console.log("");
}

if (result.hasNextPage) {
console.log(
` → More results: kronan category ${slug} --page ${result.page + 1}`,
);
}
}
Loading
Loading