[object Object]

← back to Tools Dw Hub

fleet-revival hub correction (TK-10017): 26-tool manifest on 8101-8124 with per-tool markers + onDemand/cli/static flags; probe rewritten TCP→HTTP marker-match with 401-alive + one-hop redirect follow (kills squatted-port false positives); dropped 2 missing-source entries

1cdc4182e2f7010989ddcacf3274e27fe1b1ffe8 · 2026-07-28 13:22:28 -0700 · Steve

Files touched

Diff

commit 1cdc4182e2f7010989ddcacf3274e27fe1b1ffe8
Author: Steve <steve@designerwallcoverings.com>
Date:   Tue Jul 28 13:22:28 2026 -0700

    fleet-revival hub correction (TK-10017): 26-tool manifest on 8101-8124 with per-tool markers + onDemand/cli/static flags; probe rewritten TCP→HTTP marker-match with 401-alive + one-hop redirect follow (kills squatted-port false positives); dropped 2 missing-source entries
---
 public/index.html |   3 +
 server.js         |  36 +++++---
 tools.json        | 263 ++++++++++++++++++++++++++++++++++++------------------
 3 files changed, 203 insertions(+), 99 deletions(-)

diff --git a/public/index.html b/public/index.html
index 7c99884..86b589d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -108,6 +108,9 @@ function render(){
         <span class="cat">${t.category}</span>
         <span class="when" title="${t.created}">🕓 ${fmtDate(t.created)}</span>
         ${t.port?`<span>:${t.port}</span>`:''}
+        ${t.onDemand?'<span title="kept stopped; start on demand">💤 on-demand</span>':''}
+        ${t.cli?'<span title="command-line tool — no web UI">⌨️ CLI</span>':''}
+        ${t.static?'<span title="static files — serve to view">📄 static</span>':''}
       </div>
       <div class="actions">
         ${go}
diff --git a/server.js b/server.js
index 4529386..394bce4 100644
--- a/server.js
+++ b/server.js
@@ -4,7 +4,6 @@
 const http = require('http');
 const fs = require('fs');
 const path = require('path');
-const net = require('net');
 
 const PORT = process.env.PORT || 0;                       // 0 = OS-assigned free port (local dev)
 const BASIC_AUTH = process.env.BASIC_AUTH || 'admin:DW2024!';
@@ -15,18 +14,31 @@ const ROOT = __dirname;
 
 const manifest = () => JSON.parse(fs.readFileSync(path.join(ROOT, 'tools.json'), 'utf8'));
 
-// --- TCP port probe (up/down) with a short timeout ---
-function probe(port, timeout = 900) {
+// --- HTTP marker probe: 'up' ONLY if the response is really THIS tool ---
+// 200 + body contains the tool's marker string => up (true-positive).
+// 401 => up (the tool's own auth wall answered — alive).
+// TCP-open but wrong body (another app squatting the port) => down. This kills the
+// false-positive class where port 5000 = macOS ControlCenter read as a live tool.
+function probe(port, marker, timeout = 2500, path = '/', hop = 0) {
   return new Promise((resolve) => {
     if (!port) return resolve('n/a');
-    const sock = new net.Socket();
-    let done = false;
-    const finish = (v) => { if (!done) { done = true; sock.destroy(); resolve(v); } };
-    sock.setTimeout(timeout);
-    sock.once('connect', () => finish('up'));
-    sock.once('timeout', () => finish('down'));
-    sock.once('error', () => finish('down'));
-    sock.connect(port, PROBE_HOST);
+    const req = http.get({ host: PROBE_HOST, port, path, timeout }, (res) => {
+      if (res.statusCode === 401) { res.resume(); return resolve('up'); }
+      // one-hop redirect follow (auth tools 302 to /login — still THIS tool if the target marker-matches)
+      if ([301, 302, 303, 307, 308].includes(res.statusCode) && res.headers.location && hop < 1) {
+        res.resume();
+        const loc = res.headers.location.startsWith('http') ? new URL(res.headers.location).pathname : res.headers.location;
+        return probe(port, marker, timeout, loc, hop + 1).then(resolve);
+      }
+      let body = '';
+      res.on('data', (c) => { if (body.length < 65536) body += c; });
+      res.on('end', () => {
+        if (res.statusCode === 200 && (!marker || body.includes(marker))) return resolve('up');
+        resolve('down');
+      });
+    });
+    req.on('timeout', () => { req.destroy(); resolve('down'); });
+    req.on('error', () => resolve('down'));
   });
 }
 
@@ -61,7 +73,7 @@ const server = http.createServer(async (req, res) => {
 
   if (url === '/api/status') {
     const { tools } = manifest();
-    const results = await Promise.all(tools.map(async (t) => ({ slug: t.slug, status: await probe(t.port) })));
+    const results = await Promise.all(tools.map(async (t) => ({ slug: t.slug, status: await probe(t.port, t.marker) })));
     res.writeHead(200, { 'Content-Type': 'application/json' });
     return res.end(JSON.stringify(Object.fromEntries(results.map((r) => [r.slug, r.status]))));
   }
diff --git a/tools.json b/tools.json
index 78a340c..48cb56d 100644
--- a/tools.json
+++ b/tools.json
@@ -1,209 +1,298 @@
 {
   "hub": {
     "title": "Designer Wallcoverings · Tools",
-    "subtitle": "Every DW build tool, one launcher",
+    "subtitle": "Every DW build tool, one launcher — fleet revived 2026-07-28 (TK-10017)",
     "domain": "tools.designerwallcoverings.com"
   },
-  "categories": ["Print & Image", "Recolor", "Room Settings", "Search & Color", "Import", "Tagging", "Vendor Updaters", "Ops"],
+  "categories": ["Print & Image", "Recolor", "Room Settings", "Search & Color", "Import", "Tagging", "Vendor Updaters", "Content", "Ops", "CLI"],
   "tools": [
     {
       "slug": "150dpi",
       "name": "150 DPI Converter + Spoonflower Flow",
-      "desc": "Resize artwork to 24\" wide @ 150 DPI and run the Spoonflower upload automation (the full print-prep + upload flow, incl. art coordinates).",
+      "desc": "Resize artwork to 24\" wide @ 150 DPI (3600px) with tile/half-drop/mirror modes + Spoonflower upload automation. PG-backed image library.",
       "category": "Print & Image",
       "folder": "Designer-Wallcoverings/DW-Programming/150dpi",
-      "port": 3150,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/150dpi && npm start",
+      "port": 8108,
+      "marker": "150 DPI Converter",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/150dpi && PORT=8108 npm start",
       "created": "2026-05-26"
     },
     {
       "slug": "recolor-v14",
-      "name": "Duplicate & Recolor (v1.4 · latest)",
-      "desc": "DesignerWallcoverings.AI recolor tool — color replacement + art-coordinate placement on artwork. Newest build.",
+      "name": "Duplicate & Recolor (v1.4 · canonical)",
+      "desc": "Color replacement / art coordinates on artwork — canonical build, recolor engine restored + verified 2026-07-28.",
       "category": "Recolor",
       "folder": "Designer-Wallcoverings/DW-Programming/DW-Duplicate-Recolor-v1.4",
-      "port": 3000,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/DW-Duplicate-Recolor-v1.4 && npm start",
+      "port": 8102,
+      "marker": "Duplicate & Recolor",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/DW-Duplicate-Recolor-v1.4 && PORT=8102 npm start",
       "created": "2026-05-18"
     },
     {
       "slug": "recolor-v13",
       "name": "Duplicate & Recolor (v1.3)",
-      "desc": "Prior stable recolor build — color replacement on duplicated designs.",
+      "desc": "Prior recolor build — proven startable, kept as rollback.",
       "category": "Recolor",
       "folder": "Designer-Wallcoverings/DW-Programming/DW-Duplicate-Recolor-v1.3",
-      "port": 5000,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/DW-Duplicate-Recolor-v1.3 && npm start",
+      "port": 8103,
+      "marker": "Duplicate & Recolor",
+      "onDemand": true,
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/DW-Duplicate-Recolor-v1.3 && PORT=8103 npm start",
       "created": "2026-03-22"
     },
     {
       "slug": "recolor-v1",
       "name": "Duplicate & Recolor (v1 · original)",
-      "desc": "The original Duplicate & Recolor tool.",
+      "desc": "Original recolor build — proven startable, kept as rollback.",
       "category": "Recolor",
       "folder": "Designer-Wallcoverings/DW-Programming/DW-Duplicate-Recolor",
-      "port": 3001,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/DW-Duplicate-Recolor && npm start",
+      "port": 8104,
+      "marker": "Duplicate & Recolor",
+      "onDemand": true,
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/DW-Duplicate-Recolor && PORT=8104 npm start",
       "created": "2026-03-22"
     },
     {
       "slug": "image-to-room",
-      "name": "Image → Room Setting",
-      "desc": "Drop a pattern image, generate photorealistic room-setting mockups.",
+      "name": "AI Room Designer (Image → Room)",
+      "desc": "Room-setting generator. UI + model rankings live; generation backend gated (HuggingFace retired serverless image-gen — re-platform pending).",
       "category": "Room Settings",
       "folder": "Designer-Wallcoverings/DW-Programming/ImagetoRoomSetting",
-      "port": 3004,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/ImagetoRoomSetting && npm run dev",
+      "port": 8105,
+      "marker": "AI Room Designer",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/ImagetoRoomSetting && npm run dev -- -p 8105",
       "created": "2025-12-08"
     },
     {
       "slug": "room-setting-app",
-      "name": "Room Setting App",
-      "desc": "Standalone room-setting generator app.",
+      "name": "Room Visualizer",
+      "desc": "Photorealistic room renders from product photos via gemini-2.5-flash-image — render path verified end-to-end (Google OAuth sign-in gated).",
       "category": "Room Settings",
       "folder": "Designer-Wallcoverings/DW-Programming/room-setting-app",
-      "port": 3075,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/room-setting-app && npm run dev",
+      "port": 8106,
+      "marker": "Room Visualizer",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/room-setting-app && npm run dev -- -p 8106",
       "created": "2026-06-26"
     },
-    {
-      "slug": "roomsetting-creator",
-      "name": "RoomSetting Creator",
-      "desc": "Room mockup composer (static build).",
-      "category": "Room Settings",
-      "folder": "Designer-Wallcoverings/DW-Programming/RoomSetting Creator",
-      "port": null,
-      "start": "open '~/Projects/Designer-Wallcoverings/DW-Programming/RoomSetting Creator/index.html'",
-      "created": "2026-01-07"
-    },
     {
       "slug": "color-wheel",
       "name": "Search by Color Wheel",
-      "desc": "Browse the catalog by picking a hue off an interactive color wheel.",
+      "desc": "Color-ordered catalog browse + admin color-tag updater over the live store (135 color-tagged products verified).",
       "category": "Search & Color",
       "folder": "Designer-Wallcoverings/DW-Programming/SearchByColorWheel",
-      "port": 9840,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/SearchByColorWheel && npm run dev",
+      "port": 8107,
+      "marker": "Designer Wallcoverings",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/SearchByColorWheel && npm run dev -- -p 8107",
       "created": "2025-12-08"
     },
     {
       "slug": "import-sku",
       "name": "Import New SKU from URL",
-      "desc": "Paste a vendor product URL → scrape → import into Shopify.",
+      "desc": "Vendor URL → scrape → AI extract → Shopify import (40+ vendor scrapers). Scrape+preview verified on live Thibaut SKU.",
       "category": "Import",
       "folder": "Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL",
-      "port": 3002,
+      "port": 8101,
+      "marker": "Vendor",
       "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL && npm run dev",
       "created": "2026-02-15"
     },
     {
       "slug": "tagger-kravet",
       "name": "Kravet Design Tagger",
-      "desc": "Gemini-AI design/style tagging for the Kravet line.",
+      "desc": "Gemini-vision design/style tagging for the Kravet line (ported to gemini-3.5-flash, verified).",
       "category": "Tagging",
       "folder": "Designer-Wallcoverings/DW-Programming/KravetDesignTagger",
-      "port": 3851,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/KravetDesignTagger && npm start",
+      "port": 8109,
+      "marker": "Kravet Design Wallpaper Tagger",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/KravetDesignTagger && PORT=8109 DRY_RUN=1 npm start",
       "created": "2026-05-29"
     },
     {
       "slug": "tagger-thibaut",
       "name": "Thibaut Tagger",
-      "desc": "Gemini-AI design/style tagging for the Thibaut line.",
+      "desc": "Gemini-vision design/style tagging for the Thibaut line (4,956 products; ported to gemini-3.5-flash, verified).",
       "category": "Tagging",
       "folder": "Designer-Wallcoverings/DW-Programming/ThibautTagger",
-      "port": 3850,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/ThibautTagger && npm start",
+      "port": 8110,
+      "marker": "Thibaut Wallpaper Tagger",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/ThibautTagger && PORT=8110 DRY_RUN=1 npm start",
       "created": "2026-05-29"
     },
     {
       "slug": "tagger-pr",
       "name": "Philippe Romano Tagger",
-      "desc": "Gemini-AI design/style tagging for the Philippe Romano line.",
+      "desc": "Gemini-vision tagging for the Philippe Romano line (verified end-to-end).",
       "category": "Tagging",
       "folder": "Designer-Wallcoverings/DW-Programming/PhillipeRomanoTagger",
-      "port": 3160,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/PhillipeRomanoTagger && npm start",
+      "port": 8111,
+      "marker": "Philippe Romano Tagger",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/PhillipeRomanoTagger && PORT=8111 npm start",
       "created": "2026-06-23"
     },
     {
-      "slug": "style-tag-updater",
-      "name": "DW Style Tag Updater",
-      "desc": "Bulk style-tag updater across the catalog (static tool).",
-      "category": "Tagging",
-      "folder": "Designer-Wallcoverings/DW-Programming/DW Style Tag Updater",
-      "port": null,
-      "start": "cd '~/Projects/Designer-Wallcoverings/DW-Programming/DW Style Tag Updater' && npm start",
-      "created": "2025-11-13"
+      "slug": "updater-maya",
+      "name": "Maya Romanoff Updater",
+      "desc": "Recrawl/spec updater for the Maya Romanoff line (250 live products read-verified).",
+      "category": "Vendor Updaters",
+      "folder": "Designer-Wallcoverings/DW-Programming/maya-romanoff-updater",
+      "port": 8112,
+      "marker": "Maya Romanoff",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/maya-romanoff-updater && PORT=8112 npm start",
+      "created": "2026-07-09"
     },
     {
       "slug": "updater-schumacher",
       "name": "Schumacher DWSW Updater",
-      "desc": "Architectural Wallcoverings updater dashboard for the Schumacher DWSW line.",
+      "desc": "Progress dashboard for the Schumacher → Architectural Wallcoverings updater.",
       "category": "Vendor Updaters",
       "folder": "Designer-Wallcoverings/DW-Programming/schumacher-updater-dashboard",
-      "port": 9898,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/schumacher-updater-dashboard && npm start",
+      "port": 8113,
+      "marker": "Schumacher DWSW Updater",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/schumacher-updater-dashboard && PORT=8113 ADMIN_PASSWORD=DW2024! npm start",
       "created": "2026-01-22"
     },
     {
       "slug": "updater-architectural",
       "name": "Architectural Updater",
-      "desc": "Transform vendor products to Architectural Wallcoverings/Fabrics branding.",
+      "desc": "Vendor → Architectural Wallcoverings branding updater (250 vendors via GraphQL verified).",
       "category": "Vendor Updaters",
       "folder": "Designer-Wallcoverings/DW-Programming/architectural-updater",
-      "port": 9897,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/architectural-updater && npm start",
+      "port": 8114,
+      "marker": "Architectural Wallcoverings Updater",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/architectural-updater && ARCH_UPDATER_PORT=8114 ADMIN_PASSWORD=DW2024! npm start",
       "created": "2026-01-26"
     },
-    {
-      "slug": "updater-maya",
-      "name": "Maya Romanoff Updater",
-      "desc": "Variant/spec/metafield updater for the Maya Romanoff line (static tool).",
-      "category": "Vendor Updaters",
-      "folder": "Designer-Wallcoverings/DW-Programming/maya-romanoff-updater",
-      "port": null,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/maya-romanoff-updater && npm start",
-      "created": "2026-07-09"
-    },
     {
       "slug": "vendor-dashboard-v2",
       "name": "Vendor Dashboard V2",
-      "desc": "Vendor-catalog management dashboard, v2.",
+      "desc": "463 vendors from the dw_unified registry+scraper join.",
       "category": "Vendor Updaters",
       "folder": "Designer-Wallcoverings/DW-Programming/VendorDashboard-V2",
-      "port": 9832,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/VendorDashboard-V2 && npm start",
+      "port": 8115,
+      "marker": "Vendor Dashboard V2",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/VendorDashboard-V2 && PORT=8115 npm start",
       "created": "2026-06-09"
     },
     {
-      "slug": "dw-dashboard",
-      "name": "DW Dashboard",
-      "desc": "Catalog/ops overview dashboard (static).",
+      "slug": "calendar",
+      "name": "Shopify Launch Calendar",
+      "desc": "Scheduled product-launch calendar over dw_unified (30 real launches verified).",
+      "category": "Vendor Updaters",
+      "folder": "Designer-Wallcoverings/DW-Programming/ShopifyCalendarNext",
+      "port": 8119,
+      "marker": "Shopify Launch Calendar",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/ShopifyCalendarNext && ADMIN_PASSWORD=DW2024! npm run dev -- -p 8119",
+      "created": "2026-01-30"
+    },
+    {
+      "slug": "master-listings",
+      "name": "Master Product Listings",
+      "desc": "16-vendor product curation dashboard over dw_unified (500 rows verified).",
+      "category": "Vendor Updaters",
+      "folder": "Designer-Wallcoverings/DW-Programming/nextjs-liquid-app",
+      "port": 8120,
+      "marker": "Master Product Listings",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/nextjs-liquid-app && npm run dev -- -p 8120",
+      "created": "2026-01-30"
+    },
+    {
+      "slug": "sku-to-blog",
+      "name": "Shopify Blog Creator (SKU→Blog)",
+      "desc": "Generate blog posts from products. Reads verified; publish path gated (needs DW-Write-Content token + approval).",
+      "category": "Content",
+      "folder": "Designer-Wallcoverings/DW-Programming/shopifyskutoblog",
+      "port": 8121,
+      "marker": "Shopify Blog Creator",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/shopifyskutoblog && PORT=8121 npm start",
+      "created": "2026-01-30"
+    },
+    {
+      "slug": "settlement-review",
+      "name": "Settlement Compliance Review",
+      "desc": "Keyword-scan the catalog for settlement-sensitive designs (194 zuber products scanned in verify).",
       "category": "Ops",
-      "folder": "Designer-Wallcoverings/DW-Programming/DW-Dashboard",
-      "port": null,
-      "start": "open ~/Projects/Designer-Wallcoverings/DW-Programming/DW-Dashboard/index.html",
-      "created": "2025-11-13"
+      "folder": "Designer-Wallcoverings/DW-Programming/SettlementReview",
+      "port": 8122,
+      "marker": "Settlement Compliance Review",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/SettlementReview && PORT=8122 npx tsx server.ts",
+      "created": "2026-01-30"
+    },
+    {
+      "slug": "scraper-api",
+      "name": "DW Scraper API",
+      "desc": "Internal scrape API (degraded-honest mode without Mongo/Redis — full revival is an infra decision).",
+      "category": "Ops",
+      "folder": "Designer-Wallcoverings/DW-Programming/scraper-api",
+      "port": 8123,
+      "marker": "DW Scraper API",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/scraper-api && PORT=8123 npm start",
+      "created": "2026-01-30"
     },
     {
       "slug": "system-dashboard",
       "name": "System Dashboard",
-      "desc": "System/process status dashboard (static).",
+      "desc": "Zero-dependency system/pm2 status board.",
       "category": "Ops",
       "folder": "Designer-Wallcoverings/DW-Programming/system-dashboard",
-      "port": null,
-      "start": "open ~/Projects/Designer-Wallcoverings/DW-Programming/system-dashboard/index.html",
+      "port": 8116,
+      "marker": "DW System Dashboard",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/system-dashboard && PORT=8116 node server.js",
       "created": "2026-01-06"
     },
     {
       "slug": "pm2-dashboard",
       "name": "PM2 Dashboard",
-      "desc": "Live view of PM2-managed processes.",
+      "desc": "Live view of PM2-managed processes (82 verified).",
       "category": "Ops",
       "folder": "Designer-Wallcoverings/DW-Programming/pm2-dashboard",
-      "port": 7401,
-      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/pm2-dashboard && npm start",
+      "port": 8117,
+      "marker": "DW PM2 Dashboard",
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/pm2-dashboard && PORT=8117 ADMIN_PASSWORD=DW2024! npm start",
+      "created": "2026-01-30"
+    },
+    {
+      "slug": "dw-dashboard",
+      "name": "Fortune 2000 Inquiries (static)",
+      "desc": "Static viewer for 356 Fortune-2000 product inquiries (data-fetch fixed 2026-07-28).",
+      "category": "Ops",
+      "folder": "Designer-Wallcoverings/DW-Programming/DW-Dashboard",
+      "port": 8118,
+      "marker": "Fortune 2000 Product Inquiries",
+      "static": true,
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/DW-Dashboard && python3 -m http.server 8118",
+      "created": "2025-11-13"
+    },
+    {
+      "slug": "autopost-blog",
+      "name": "DW Auto-Post Blog (CLI)",
+      "desc": "Instagram→Shopify blog auto-poster. CLI tool — posts on run, launch only deliberately. Needs puppeteer bump for node 26.",
+      "category": "CLI",
+      "folder": "Designer-Wallcoverings/DW-Programming/DWAutoPostBlog",
+      "port": null,
+      "cli": true,
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/DWAutoPostBlog && node instagram-to-shopify.js",
+      "created": "2026-01-30"
+    },
+    {
+      "slug": "legal-team",
+      "name": "DW Legal Team (CLI)",
+      "desc": "Legal-review agent (tsx). Parse-verified; needs Gemini/claude-CLI port before revival.",
+      "category": "CLI",
+      "folder": "Designer-Wallcoverings/DW-Programming/DWLegalTeam",
+      "port": null,
+      "cli": true,
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/DWLegalTeam && npx tsx legal-agent.ts",
+      "created": "2026-01-30"
+    },
+    {
+      "slug": "dedupe-shopify",
+      "name": "Remove Duplicates (CLI · destructive)",
+      "desc": "Duplicate-image finder for Shopify. Types-clean (tsc). DESTRUCTIVE — run only deliberately.",
+      "category": "CLI",
+      "folder": "Designer-Wallcoverings/DW-Programming/RemoveDuplicatesInShopify",
+      "port": null,
+      "cli": true,
+      "start": "cd ~/Projects/Designer-Wallcoverings/DW-Programming/RemoveDuplicatesInShopify && npx tsx find-duplicate-images.ts",
       "created": "2026-01-30"
     }
   ]

← 7e42fdb add SCOPE-tools-public.md: tiered plan for publishing indivi  ·  back to Tools Dw Hub  ·  Cody-gate: surface 2 MISSING-SOURCE tools in manifest with ⚠ f12c756 →