Skip to content

Adding a Boutique

A boutique is a folder in groups/ with at minimum a CLAUDE.md file. This page covers adding one to a running ique.bot instance.

Quick Version

bash
# 1. Create the folder
mkdir -p groups/my-boutique

# 2. Write the persona
cat > groups/my-boutique/CLAUDE.md << 'EOF'
# My Boutique
You are My Boutique. You help with X.
## Hard Rules
1. Never do Y.
2. Always respond in JSON.
EOF

# 3. Register in the database
node -e "
  const { DatabaseSync } = require('node:sqlite');
  const db = new DatabaseSync('ique/ique.db');
  db.exec(\"INSERT OR IGNORE INTO boutiques (boutique_id, display_name, status, description) VALUES ('my-boutique', 'My Boutique', 'active', 'Helps with X tasks.')\");
  db.close();
"

# 4. Add to start.sh (or run standalone)
# Edit ique/start.sh and add:
#   npx tsx ique/worker.ts &   (with BOUTIQUE_ID=my-boutique)

# 5. Restart
./ique/start.sh

Detailed Steps

1. Create the Group Folder

bash
mkdir -p groups/my-boutique

The folder name becomes the boutique_id. Use lowercase with hyphens.

2. Write CLAUDE.md

This is the system prompt the LLM receives on every task. It should include:

  • Identity — who is this worker?
  • Objectives — what does it do? (3-5 bullet points)
  • Hard rules — what must it never do?
  • Output format — JSON? Markdown? Plain text?
  • Domain constraints — budget limits, exclusions, safety rules

See Boutique Authoring for detailed guidance.

3. Register the Boutique

The router needs to know about your boutique. Add it to the boutiques table.

For fresh installs, add to ique/setup.sql:

sql
INSERT OR IGNORE INTO boutiques (boutique_id, display_name, status, description)
VALUES ('my-boutique', 'My Boutique', 'active',
  'One-sentence description of what this boutique handles.');

For an existing database, run the INSERT directly:

bash
node -e "
  const { DatabaseSync } = require('node:sqlite');
  const db = new DatabaseSync('ique/ique.db');
  db.exec(\"INSERT OR IGNORE INTO boutiques (boutique_id, display_name, status, description) VALUES ('my-boutique', 'My Boutique', 'active', 'Description here.')\");
  db.close();
"

The description field matters — it's what the semantic router (Tier 2) reads when deciding where to send free-form messages.

4. Add Slash Commands (Optional)

In ique/router.ts, add entries to FAST_PASS_ROUTES:

typescript
"/myboutique": "my-boutique",

And keyword fallback entries:

typescript
"my-boutique": ["keyword1", "keyword2", "relevant phrase"],

5. Choose a Worker Type

Generic worker — no custom code. Uses the CLAUDE.md as the system prompt and passes the user's message to the LLM:

bash
BOUTIQUE_ID=my-boutique npx tsx ique/worker.ts

Custom worker — write groups/my-boutique/worker.ts with domain-specific fetch-then-reason logic. Copy groups/radar/worker.ts as a starting template.

6. Add to start.sh

For the generic worker:

bash
BOUTIQUE_ID=my-boutique npx tsx ique/worker.ts &
PIDS+=($!)

For a custom worker:

bash
npx tsx groups/my-boutique/worker.ts &
PIDS+=($!)

7. Restart and Test

bash
# Ctrl+C the running stack
./ique/start.sh

Then message your bot with the slash command or a free-form message that matches your boutique's description.

Verify It's Working

Check the boutiques table:

bash
node -e "
  const { DatabaseSync } = require('node:sqlite');
  const db = new DatabaseSync('ique/ique.db');
  console.log(db.prepare('SELECT boutique_id, status FROM boutiques').all());
  db.close();
"

Check the queue for your boutique's tasks:

bash
node -e "
  const { DatabaseSync } = require('node:sqlite');
  const db = new DatabaseSync('ique/ique.db');
  console.log(db.prepare(\"SELECT task_id, status, assigned_boutique FROM ique_queue WHERE assigned_boutique = 'my-boutique' ORDER BY created_at DESC LIMIT 5\").all());
  db.close();
"