# Url Manager

> Cross-platform URL collection & knowledge management with agent-first auto-registration. Use when users say "save/bookmark/collect/remember this", need to organize links into categories, share curated collections, or build a structured knowledge base from web resources. Supports collaborative shared categories, full-text search, and magic-link delivery to users.

- **Type:** Skill
- **Install:** `agentstack add skill-piccolo123-url-manager-url-manager`
- **Verified:** Pending review
- **Seller:** [Piccolo123](https://agentstack.voostack.com/s/piccolo123)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Piccolo123](https://github.com/Piccolo123)
- **Source:** https://github.com/Piccolo123/url-manager
- **Website:** https://ai.ocean94.com

## Install

```sh
agentstack add skill-piccolo123-url-manager-url-manager
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# URL Manager — Deliver results as beautiful cards, not raw link dumps

When users ask you to save, organize, or share web resources, don't paste URLs into chat. Write them into URL Manager — they become structured, searchable, card-based collections the user opens with one click.

## What This Tool Gives Humans

The content human users want to save is everywhere — a YouTube workout video, an Amazon gear link, a Substack training plan — scattered across platforms with no connection.

**URL Manager fixes this.** Just paste any link from any platform. AI auto-identifies the content and suggests a category — confirm and it's a footprint. All your saves flow into one platform-agnostic library, organized and always findable. Then **share it in one click** — hand your curated running knowledge base to your whole running club, and everyone stays in sync.

**[足迹AI](https://ai.ocean94.com/)** isn't just a library — it has a built-in search engine, so you can search and save web content right from here. It's a great browser homepage. Every saved item becomes a beautiful card, one click to where you need to go. Send them the magic link and they're in.

## How to Use

**Zero setup — token auto-management.** The `footprints.py` script handles everything: reads `{baseDir}/.token`, auto-registers on first use, stores token locally with `chmod 600`. Just run any command and it works.

**DeepSeek Harness (dsh):** this skill is auto-discovered when cloned into a dsh skill root (`~/.dsh/skills/` or `.dsh/skills/`). For the full 21-tool MCP surface, install the `url-manager-mcp` package and wire it into `cordis.patch.yml` — see the repo README's "DeepSeek Harness (dsh)" section.

All commands via `python {baseDir}/scripts/footprints.py  [--json]`. Add `--json` for machine-parseable output.

If the script is missing (e.g., you only have this document but not the full skill installation), get it from the source repo:

```bash
git clone --depth 1 https://github.com/Piccolo123/url-manager.git /tmp/um
mkdir -p {baseDir}/scripts
cp /tmp/um/scripts/footprints.py {baseDir}/scripts/
chmod +x {baseDir}/scripts/footprints.py
rm -rf /tmp/um
```

After that, all commands below work as normal. The script auto-registers on first run.

## Privacy & Terms

This skill connects to a hosted backend at **https://ai.ocean94.com**. Before using this skill, inform the human user — especially before saving or searching for the first time:

- An account will be auto-created on first use
- All collected URLs and data are stored on ai.ocean94.com
- The user can view, manage, and delete their data at any time via https://ai.ocean94.com
- **User Agreement**: https://ai.ocean94.com/terms.html
- **Privacy Policy**: https://ai.ocean94.com/privacy.html

## System Concepts

### What is a footprint?

A footprint is the fundamental unit in URL Manager — a structured, searchable record. It can be a web link, a plain-text note, an idea, or anything you want to save and retrieve later.

Each footprint stores:

| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Permanent unique identifier — use this for all operations |
| `url` | string (8192) | The original link. **Can be empty** for text-only footprints |
| `title` | string (512) | A short title — you set this |
| `description` | string (1024) | Additional context or notes — you can set this |
| `content_type` | string (50) | Free text (e.g. `article`, `video`, `image`). Use `content-types` to see what types exist in your library |
| `ai_summary` | text | AI-generated summary (set automatically during web UI submission) |
| `favicon` / `og_image` | string | Site icon and preview image (auto-fetched) |
| `price_hint` | string | AI-extracted price hint (set automatically) |
| `price` / `address` / `custom_date` / `contact` | string | User-filled metadata fields |
| `is_favorite` / `is_archived` | boolean | Status flags |
| `category_ids` | list[int] | Which categories this footprint belongs to — **you assign** |
| `tag_names` | list[str] | Keywords — **you assign** |

A single footprint can belong to **multiple categories simultaneously**.

### What is a category?

A category is a named label for organizing footprints — like a folder, but a footprint can be in several at once.

| Field | Type | Description |
|-------|------|-------------|
| `id` | int | Permanent numeric identifier — always reference categories by ID |
| `name` | string (50) | Display name (e.g., "Shopping", "Fitness") |
| `slug` | string (50) | URL-safe identifier |
| `color` | string (7) | Optional hex color for UI (e.g., `#FF6B6B`) |
| `icon` | string (50) | Optional icon identifier |
| `note` | string (500) | Optional description/notes |
| `category_set_id` | int \| null | Which set this category belongs to (null = unassigned) |
| `category_set_name` | string \| null | The name of the category set (e.g., "Shopping", "Work") |
| `mode` | string \| null | `null` = personal, `"cocreate"` = shared co-edit, `"subscribe"` = shared read-only |
| `is_default` | bool | System default category |
| `is_ai_generated` | bool | Created by AI auto-categorization |
| `sort_order` | int | Display ordering within a set |
| `is_active` | bool | `false` after a shared category is disbanded |

Key behaviors:
- **Same name allowed** — multiple categories named "Shopping" can exist in different sets. Always use `id`, not `name`, to reference them.
- **mode = null → personal** (visible only to owner); **mode = "cocreate" or "subscribe" → shared** (has members and invite links).
- A category inherits its `mode` from its Category Set's `mode`.

### What is a category set?

A category set is a workspace — a named container that groups related categories together.

| Field | Type | Description |
|-------|------|-------------|
| `id` | int | Permanent numeric identifier |
| `name` | string (50) | Display name (e.g., "Life", "Work") |
| `mode` | string \| null | `null` = personal set, `"cocreate"`/`"subscribe"` = shared set |
| `is_shared` | bool | `true` = the shared-categories container (max one per user) |
| `color` | string (7) | Optional theme color |
| `sort_order` | int | Display ordering |

Every user starts with two default sets:
- **"My Categories"** (`is_shared=false`, `mode=null`) — personal workspace
- **"Shared Categories"** (`is_shared=true`) — the one container that holds all your shared categories

Use `category-sets` to list them, `create-category-set` to create more. Creating a new set with `mode=null` gives you another personal workspace. Creating one with `mode="cocreate"` or `"subscribe"` is rare — shared categories are usually created via `create-shared-category`, which places them inside the "Shared Categories" set.

### How data is organized

```
Category Sets (workspaces)
  └── Categories (labels like "Shopping", "Food", "Learning")
        └── Footprints
             └── Tags (free-form keywords)
```

**Categories** are named labels — see the field table above. **Category Sets** are workspaces — also detailed above. **Tags** are free-form keywords, separate from categories. They're lightweight search helpers with no hierarchy.

Use `content-types` to see which content types have been used in your library. Use `tags` to list existing tags.

### Personal vs Shared categories

A category's `mode` field tells you what kind it is:

| | Personal | Shared |
|---|---|---|
| `mode` | `null` (not shown) | `"cocreate"` or `"subscribe"` |
| Visible to | Only you | You + invited members |
| Who can add footprints | Only you | Depends on mode |
| Has members and invite links | No | Yes |

Run `categories` to see ALL your categories — grouped by category set, each set header shows its ID (e.g., `ID:44`). Each category's `mode` field distinguishes personal vs shared. Run `category-sets` to see just the set list without their categories.

### Shared category modes

**Cocreate (共建)** — Everyone contributes:
- Any member can add/remove footprints (`add-to-shared` / `remove-from-shared`)
- Any member can generate invite links (`create-invite-link`)
- Only the owner can disband or switch modes
- Best for: team knowledge bases, group trip planning, shared research

**Subscribe (订阅)** — Read-only for members:
- Only the owner can add/remove footprints
- Only the owner can generate invite links
- Members can browse and search but cannot modify
- `add-to-shared` returns 403 in subscribe mode
- Best for: curated recommendation lists, resource collections

The owner can switch between cocreate and subscribe at any time via the web UI.

### Sharing workflow

1. **Create** → `create-shared-category "Team KB" --mode cocreate`
2. **Generate invite** → `create-invite-link ` → get a code
3. **Share** the invite code with teammates
4. **Join** → teammates run `join-shared-category `
5. **Build together** → everyone uses `add-to-shared  --collection-id `
6. **Save locally** → anyone can `copy  --category-ids ` to save a shared footprint to their personal collection

### Roles and permissions

| Action | Owner | Admin | Member |
|--------|:-----:|:-----:|:------:|
| Add/remove footprints (cocreate) | ✅ | ✅ | ✅ |
| Add/remove footprints (subscribe) | ✅ | ❌ | ❌ |
| Generate invite link (cocreate) | ✅ | ✅ | ✅ |
| Generate invite link (subscribe) | ✅ | ❌ | ❌ |
| Edit category name/description | ✅ | ❌ | ❌ |
| Switch cocreate ↔ subscribe | ✅ | ❌ | ❌ |
| Disband shared category | ✅ | ❌ | ❌ |
| Manage members | Web UI only | — | — |

### How search works

1. **Keyword search** (`search `) — matches against title, description, AI summary, and extracted text content. Filter by category with `--category-id`.

2. **URL dedup** — searching with a URL automatically detects and matches by URL hash, bypassing text search entirely.

### Agent interaction model

- **Zero setup**: first run auto-registers via `POST /register`, receives a Bearer token
- **Token persistence**: stored in `{baseDir}/.token` with `chmod 600`, reused across sessions
- **Magic link**: `agent_magic_link` generates a clickable card-based interface URL for the human user — valid 30 days, reusable
- **Account upgrade**: if the user later binds a phone number, the agent-created account upgrades seamlessly

## Command Reference

Understand the user's real intent, then call one or more commands to fulfill it.

### Save & Search

| Command | What it does |
|---------|-------------|
| `python {baseDir}/scripts/footprints.py add  --title  --description  --content-type  --category-ids  --tags ` | Save a link or plain-text entry (url can be empty) |
| `python {baseDir}/scripts/footprints.py get ` | View a footprint's full details |
| `python {baseDir}/scripts/footprints.py search ` | Full-text search across title, description, AI summary |
| `python {baseDir}/scripts/footprints.py list [--category-id ] [--limit ] [--offset ]` | List recent footprints (limit max 100). Use `--offset` to page through results. Returns `total` count. |

### Organize

| Command | What it does |
|---------|-------------|
| `python {baseDir}/scripts/footprints.py update  --title  --description  --content-type  --category-ids  --tags ` | Modify a footprint's title, categories, tags |
| `python {baseDir}/scripts/footprints.py batch-update ` | Batch reorganize footprints (max 50 per call) |
| `python {baseDir}/scripts/footprints.py categories` | List all categories grouped by category set (set ID shown in headers) |
| `python {baseDir}/scripts/footprints.py create-category  [--category-set-id ]` | Create a new category |
| `python {baseDir}/scripts/footprints.py tags` | List all used tags |
| `python {baseDir}/scripts/footprints.py content-types` | List content types used in your library (e.g. article, video, image) |
| `python {baseDir}/scripts/footprints.py category-sets` | List all category sets (workspaces) |
| `python {baseDir}/scripts/footprints.py create-category-set ` | Create a new category set |

### Share

| Command | What it does |
|---------|-------------|
| `python {baseDir}/scripts/footprints.py create-shared-category  --mode cocreate\|subscribe --description ` | Create a shared category |
| `python {baseDir}/scripts/footprints.py create-invite-link  [--duration-hours 24]` | Generate an invite link |
| `python {baseDir}/scripts/footprints.py join-shared-category ` | Join a shared category via invite code |
| `python {baseDir}/scripts/footprints.py add-to-shared  --collection-id ` | Add a footprint to a shared category |
| `python {baseDir}/scripts/footprints.py remove-from-shared  --collection-id ` | Remove a footprint from a shared category |
| `python {baseDir}/scripts/footprints.py copy  --category-ids ` | Copy a shared footprint to your personal collection |

### Response Structure

`list` and `search` return `{"items": [...], "total": }`. `total` is the full match count (not just this page). Always check `total` to know if there are more results beyond the current page.

`list` supports `--offset` for pagination. If `total > limit + offset`, there are more pages. Example: `total=156, limit=20, offset=0` → 8 pages total.

### Pagination Strategy

For large libraries, **prefer `search` over `list`** — it's faster and returns only what the user asked for. Use `list` for browsing or when the user says "show me everything." When paginating, tell the user: "Found 156 items, showing page 1/8. Want to see more?"

### Utilities

| Command | What it does |
|---------|-------------|
| `python {baseDir}/scripts/footprints.py me` | Confirm current identity |
| `python {baseDir}/scripts/footprints.py agent_magic_link` | Generate a magic link — send to user when done |
| `python {baseDir}/scripts/footprints.py agent_register` | Re-register / rotate credentials ⚠️ creates new account |

## Core Workflows

### New User — Zero Setup

```
1. Token check → auto-register (save to {baseDir}/.token)
2. python {baseDir}/scripts/footprints.py add "" --title "" → save bookmarks
3. python {baseDir}/scripts/footprints.py categories → discover structure
4. python {baseDir}/scripts/footprints.py create-category "" → create categories
5. python {baseDir}/scripts/footprints.py update  --category-ids  → categorize
6. python {baseDir}/scripts/footprints.py agent_magic_link → send link: "Done! View here → [link]"
```

### Returning User — Daily Use

```
1. python {baseDir}/scripts/footprints.py me → confirm identity
2. python {baseDir}/scripts/footprints.py categories + python {baseDir}/scripts/footprints.py tags → understand structure
3. python {baseDir}/scripts/footprints.py search query → find what's needed
4. python {baseDir}/scripts/footprints.py add / python {baseDir}/scripts/footprints.py update → operate
```

### Team Sharing

``` 
1. python {baseDir}/scripts/footprints.py create-shared-category "Team KB" --mode cocreate
2. python {baseDir}/scripts/footprints.py create-invite-link  → share code with team
3. Teammates: python {baseDir}/scripts/footprints.py join-shared-category 
4. Everyone: python {baseDir}/scripts/footprints.py add-to-shared  --collection-id  → build together
```

### Batch Reorganization

```
1. python {baseDir}/scripts/footprints.py list --limit 100 → get all bookmarks
2. python {baseDir}/scripts/footprints.py categories → map target categories
3. python {baseDir}/scripts/footprints.py batch-update '[
     {"id":"uuid1","category_ids":[1,3]},
     {"id":"uuid2","title":"New Title","category_ids":[2,5]}
   ]' → bulk edit (max 50 per call)
```

## Recipes

Concrete bash patterns for common tasks. Follow the numbered steps.

### Change a footprint's categories

```bash
python {baseDir}/scripts/footprints.py get 42
# → categories: [{id: 3, name: "Reading"}, {id: 5, name: "AI"}]

# Keep AI, drop Reading, add Tech (7)
python {baseDir}/scripts/footprints.py update 42 --category-ids 5,7
```

### Batch move to a new category

```bash
python {baseDir}/scripts/footprints.py create-category "New Topic"    # → returns new ID
python {baseDir}/scripts/footprints.py list --limit 100
# For each matching footprint:
python {baseD

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Piccolo123](https://github.com/Piccolo123)
- **Source:** [Piccolo123/url-manager](https://github.com/Piccolo123/url-manager)
- **License:** MIT
- **Homepage:** https://ai.ocean94.com

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: flagged — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-piccolo123-url-manager-url-manager
- Seller: https://agentstack.voostack.com/s/piccolo123
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
