← 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
M public/index.htmlM server.jsM tools.json
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 →