diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4096273 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +# Optional local-only export targets. +# Leave unset in public CI and ordinary GitHub Pages builds. +STACKSCOUT_PRIVATE_EXPORT_DIR= +STACKSCOUT_PRIVATE_EXPORT_FILE= +STACKSCOUT_PRIVATE_STATUS_DIR= diff --git a/.gitignore b/.gitignore index 4bfa471..2a5cba6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ planning-local/ .local-notes/ # Local screenshots and scratch files +.serve.log +.serve.err.log tmp-*.png tmp-*.jpg tmp-*.jpeg diff --git a/README.md b/README.md index 4a807b4..37303ba 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ `stackscout` builds **Stack Scout**, the public-facing tools destination for curated builder tools, services, APIs, MCPs, and CLIs. -This repo remains the GitHub Pages implementation base, but the visible product is no longer a simple internal "Tools Hub" brochure. The private operational console stays separate in `W:\Repos\_local\surfaces\tools-hub-local`. +This repo remains the GitHub Pages implementation base, but the visible product is no longer a simple internal "Tools Hub" brochure. The private operational console stays outside this public repo. ## Public vs private - This repo is public-facing only. -- The local launcher, manager inbox, review evidence, session state, and leak-check operations belong in `tools-hub-local`. +- Local launchers, operations state, and review artefacts belong in the private local surface, not this repo. - Public content must stay safe for GitHub Pages and public browsing. - Do not rely on `.gitignore` alone to protect private data. Public output is generated from an allowlisted shared source layer. @@ -23,7 +23,7 @@ These source files drive: - public manifests in `data/` - generated static pages across the public site -- a private preview export written to `W:\Repos\_local\surfaces\tools-hub-local\data\stackscout-publishing.json` +- an optional private preview export when `STACKSCOUT_PRIVATE_EXPORT_DIR` or `STACKSCOUT_PRIVATE_EXPORT_FILE` is set locally ## Build @@ -68,9 +68,9 @@ Before a public refresh, bump `CACHE_NAME` in `service-worker.js` when generated npm run refresh:site ``` -This runs the site build, runs checks, and writes private refresh status to `W:\Repos\_local\surfaces\tools-hub-local\data\stackscout-refresh-status.json`. +This runs the site build, runs checks, and optionally writes private refresh status when `STACKSCOUT_PRIVATE_STATUS_DIR` is set locally. -For unattended Windows refreshes without visible terminal focus theft, use the local-only launcher at `W:\Repos\_My Tools\LOCAL-ONLY\stackscout-refresh\run-stackscout-refresh.cmd`. +For unattended Windows refreshes without visible terminal focus theft, use a local-only launcher outside this public repo and set the optional private export/status environment variables there. ## Site structure diff --git a/catalog/index.html b/catalog/index.html index 5c4a8e6..7d20915 100644 --- a/catalog/index.html +++ b/catalog/index.html @@ -676,7 +676,7 @@

SyncPad

diff --git a/categories/apis/index.html b/categories/apis/index.html index 145d11d..b12e7da 100644 --- a/categories/apis/index.html +++ b/categories/apis/index.html @@ -175,7 +175,7 @@

Anthropic API

diff --git a/categories/clis/index.html b/categories/clis/index.html index c1f4070..aaabad1 100644 --- a/categories/clis/index.html +++ b/categories/clis/index.html @@ -175,7 +175,7 @@

Wrangler

diff --git a/categories/desktop-apps/index.html b/categories/desktop-apps/index.html index 5391e8e..4df671e 100644 --- a/categories/desktop-apps/index.html +++ b/categories/desktop-apps/index.html @@ -141,7 +141,7 @@

SyncPad

diff --git a/categories/index.html b/categories/index.html index 70bbb47..64bb4cf 100644 --- a/categories/index.html +++ b/categories/index.html @@ -228,7 +228,7 @@

Desktop Apps

diff --git a/categories/mcps/index.html b/categories/mcps/index.html index 8a410de..30e54c8 100644 --- a/categories/mcps/index.html +++ b/categories/mcps/index.html @@ -175,7 +175,7 @@

Playwright MCP

diff --git a/categories/web-apps/index.html b/categories/web-apps/index.html index a756d06..c35f706 100644 --- a/categories/web-apps/index.html +++ b/categories/web-apps/index.html @@ -209,7 +209,7 @@

Strudel Studio

diff --git a/categories/web-services/index.html b/categories/web-services/index.html index 56ff97d..15036a9 100644 --- a/categories/web-services/index.html +++ b/categories/web-services/index.html @@ -175,7 +175,7 @@

Make

diff --git a/categories/websites/index.html b/categories/websites/index.html index 2107b21..1c17ed0 100644 --- a/categories/websites/index.html +++ b/categories/websites/index.html @@ -175,7 +175,7 @@

Hugging Face

diff --git a/collections/index.html b/collections/index.html index c4ad6eb..c24ecb3 100644 --- a/collections/index.html +++ b/collections/index.html @@ -175,7 +175,7 @@

From the Stack Scout Lab

diff --git a/data/categories-manifest.json b/data/categories-manifest.json index dc7e493..0e2dc9c 100644 --- a/data/categories-manifest.json +++ b/data/categories-manifest.json @@ -1,7 +1,7 @@ { "title": "Stack Scout // Categories", - "generatedAt": "2026-05-25", - "updatedAt": "2026-05-25", + "generatedAt": "2026-06-06", + "updatedAt": "2026-06-06", "categories": [ { "slug": "websites", diff --git a/data/collections-manifest.json b/data/collections-manifest.json index 0e6d713..871d976 100644 --- a/data/collections-manifest.json +++ b/data/collections-manifest.json @@ -1,7 +1,7 @@ { "title": "Stack Scout // Collections", - "generatedAt": "2026-05-25", - "updatedAt": "2026-05-25", + "generatedAt": "2026-06-06", + "updatedAt": "2026-06-06", "collections": [ { "slug": "creator-stack", diff --git a/data/methodology-manifest.json b/data/methodology-manifest.json index 5cf7354..0f23911 100644 --- a/data/methodology-manifest.json +++ b/data/methodology-manifest.json @@ -1,7 +1,7 @@ { "title": "Stack Scout // Methodology", - "generatedAt": "2026-05-25", - "updatedAt": "2026-05-25", + "generatedAt": "2026-06-06", + "updatedAt": "2026-06-06", "thesis": "Stack Scout is a public guide, not a benchmark lab. We badge selectively, update from official sources where possible, and avoid pretending every fast-moving tool deserves a fixed numeric score.", "principles": [ { diff --git a/data/page-registry.json b/data/page-registry.json index e4ba728..b3cacce 100644 --- a/data/page-registry.json +++ b/data/page-registry.json @@ -1,6 +1,6 @@ { "title": "Stack Scout // Page Registry", - "generatedAt": "2026-05-25", + "generatedAt": "2026-06-06", "pages": [ { "key": "home", diff --git a/data/radar-manifest.json b/data/radar-manifest.json index 2c299ca..29ddbed 100644 --- a/data/radar-manifest.json +++ b/data/radar-manifest.json @@ -1,7 +1,7 @@ { "title": "Stack Scout // Radar", - "generatedAt": "2026-05-25", - "updatedAt": "2026-05-25", + "generatedAt": "2026-06-06", + "updatedAt": "2026-06-06", "items": [ { "title": "Azure MCP Server", diff --git a/data/tools-manifest.json b/data/tools-manifest.json index 28fa7b6..8074788 100644 --- a/data/tools-manifest.json +++ b/data/tools-manifest.json @@ -1,7 +1,7 @@ { "title": "Stack Scout // Tools Manifest", - "generatedAt": "2026-05-25", - "updatedAt": "2026-05-25", + "generatedAt": "2026-06-06", + "updatedAt": "2026-06-06", "summary": "Public-safe catalog for Stack Scout.", "counts": { "total": 14, diff --git a/data/updates-manifest.json b/data/updates-manifest.json index 4ae33c6..21ac358 100644 --- a/data/updates-manifest.json +++ b/data/updates-manifest.json @@ -1,7 +1,7 @@ { "title": "Stack Scout // Updates", - "generatedAt": "2026-05-25", - "updatedAt": "2026-05-25", + "generatedAt": "2026-06-06", + "updatedAt": "2026-06-06", "summary": "Public-safe activity stream seeded from official or first-party sources.", "items": [ { diff --git a/index.html b/index.html index ccc9d71..7805e61 100644 --- a/index.html +++ b/index.html @@ -833,7 +833,7 @@

From the Stack Scout Lab

diff --git a/method/index.html b/method/index.html index 8b44939..6a0e17f 100644 --- a/method/index.html +++ b/method/index.html @@ -201,7 +201,7 @@

Verdict language

diff --git a/radar/index.html b/radar/index.html index aad5067..cbc054e 100644 --- a/radar/index.html +++ b/radar/index.html @@ -164,7 +164,7 @@

GitHub skills ecosystem

diff --git a/scripts/build-stackscout.js b/scripts/build-stackscout.js index 250398c..e4a6b8a 100644 --- a/scripts/build-stackscout.js +++ b/scripts/build-stackscout.js @@ -4,10 +4,6 @@ const path = require('path') const ROOT_DIR = path.resolve(__dirname, '..') const SOURCE_DIR = path.join(ROOT_DIR, 'content', 'stackscout') const DATA_DIR = path.join(ROOT_DIR, 'data') -const PRIVATE_PREVIEW_EXPORT_CANDIDATES = [ - 'W:\\Repos\\_local\\surfaces\\tools-hub-local\\data\\stackscout-publishing.json', - '\\\\nas_storage_1\\Workspaces\\Repos\\_local\\surfaces\\tools-hub-local\\data\\stackscout-publishing.json', -] const BUILD_NOW = new Date() const GENERATED_AT = BUILD_NOW.toISOString().slice(0, 10) const GENERATED_AT_ISO = BUILD_NOW.toISOString() @@ -54,17 +50,16 @@ function writeExternalJson(absolutePath, value) { fs.writeFileSync(absolutePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8') } -function resolveWritableExternalPath(candidates) { - for (const candidate of candidates) { - try { - ensureParent(candidate) - return candidate - } catch (error) { - continue - } +function resolvePrivatePreviewExportPath() { + if (process.env.STACKSCOUT_PRIVATE_EXPORT_FILE) { + return path.resolve(process.env.STACKSCOUT_PRIVATE_EXPORT_FILE) } - throw new Error(`Unable to resolve writable external path from candidates: ${candidates.join(', ')}`) + if (process.env.STACKSCOUT_PRIVATE_EXPORT_DIR) { + return path.join(path.resolve(process.env.STACKSCOUT_PRIVATE_EXPORT_DIR), 'stackscout-publishing.json') + } + + return null } function escapeHtml(value) { @@ -1278,9 +1273,23 @@ ${routes.map((route) => ` ${PUBLIC_BASE_URL}${route}`).jo ` } +function updateServiceWorkerCacheName() { + const serviceWorkerPath = 'service-worker.js' + const current = fs.readFileSync(path.join(ROOT_DIR, serviceWorkerPath), 'utf8') + const next = current.replace( + /const CACHE_NAME = ['"]stackscout-\d{4}-\d{2}-\d{2}['"]/, + `const CACHE_NAME = 'stackscout-${GENERATED_AT}'`, + ) + + if (next === current) { + throw new Error('service-worker.js cache name was not updated; expected stackscout-YYYY-MM-DD declaration.') + } + + writeFile(serviceWorkerPath, next) +} + function main() { - const privatePreviewExport = - process.platform === 'win32' ? resolveWritableExternalPath(PRIVATE_PREVIEW_EXPORT_CANDIDATES) : null + const privatePreviewExport = resolvePrivatePreviewExportPath() const site = readJson('site-source.json') const tools = readJson('tools-source.json') const updates = readJson('updates-source.json').sort((a, b) => b.publishedAt.localeCompare(a.publishedAt)) @@ -1339,6 +1348,7 @@ function main() { ] writeFile('sitemap.xml', buildSitemap(sitemapRoutes)) + updateServiceWorkerCacheName() console.log( `Stack Scout build complete. Generated ${tools.length} tool pages, ${categories.length} category pages, and ${updates.length} updates.`, ) diff --git a/scripts/refresh-stackscout.ps1 b/scripts/refresh-stackscout.ps1 index fe3be49..cc072d6 100644 --- a/scripts/refresh-stackscout.ps1 +++ b/scripts/refresh-stackscout.ps1 @@ -6,29 +6,24 @@ param( $ErrorActionPreference = 'Stop' $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path -$privateDataDirCandidates = @( - 'W:\Repos\_local\surfaces\tools-hub-local\data', - '\\nas_storage_1\Workspaces\Repos\_local\surfaces\tools-hub-local\data' -) -$privateDataDir = $null -foreach ($candidate in $privateDataDirCandidates) { - try { - if (-not (Test-Path $candidate)) { - New-Item -ItemType Directory -Path $candidate -Force | Out-Null - } +$privateDataDirInput = if ($env:STACKSCOUT_PRIVATE_STATUS_DIR) { + $env:STACKSCOUT_PRIVATE_STATUS_DIR +} elseif ($env:STACKSCOUT_PRIVATE_EXPORT_DIR) { + $env:STACKSCOUT_PRIVATE_EXPORT_DIR +} else { + $null +} - $privateDataDir = (Resolve-Path $candidate).Path - break - } catch { - continue +$privateDataDir = $null +if ($privateDataDirInput) { + if (-not (Test-Path $privateDataDirInput)) { + New-Item -ItemType Directory -Path $privateDataDirInput -Force | Out-Null } -} -if (-not $privateDataDir) { - throw 'Unable to resolve a writable private data directory for Stack Scout refresh status.' + $privateDataDir = (Resolve-Path $privateDataDirInput).Path } -$statusFile = Join-Path $privateDataDir 'stackscout-refresh-status.json' +$statusFile = if ($privateDataDir) { Join-Path $privateDataDir 'stackscout-refresh-status.json' } else { $null } $toolsManifestFile = Join-Path $repoRoot 'data\tools-manifest.json' $updatesManifestFile = Join-Path $repoRoot 'data\updates-manifest.json' $categoriesManifestFile = Join-Path $repoRoot 'data\categories-manifest.json' @@ -38,10 +33,13 @@ $durationStopwatch = [System.Diagnostics.Stopwatch]::StartNew() function Read-JsonFile { param( - [Parameter(Mandatory = $true)] [string]$Path ) + if (-not $Path) { + return $null + } + if (-not (Test-Path $Path)) { return $null } @@ -82,6 +80,10 @@ function Write-RefreshStatus { $payload[$entry.Key] = $entry.Value } + if (-not $statusFile) { + return + } + $directory = Split-Path -Parent $statusFile if (-not (Test-Path $directory)) { New-Item -ItemType Directory -Path $directory -Force | Out-Null diff --git a/scripts/verify-launch-safety.js b/scripts/verify-launch-safety.js index 282dedd..e7a3b97 100644 --- a/scripts/verify-launch-safety.js +++ b/scripts/verify-launch-safety.js @@ -45,9 +45,10 @@ const REQUIRED_PUBLIC_FILES = [ ] const TEXT_EXTENSIONS = new Set(['.css', '.html', '.js', '.json', '.svg', '.txt', '.xml']) const PRIVATE_PATTERNS = [ - { label: 'Windows drive path', pattern: /\b[A-Z]:[\\/][^\s"'<>)]*/i }, - { label: 'UNC path', pattern: /\\\\[A-Za-z0-9._-]+[\\/][^\s"'<>)]*/i }, - { label: 'local-only surface marker', pattern: /\b(?:tools-hub-local|LOCAL-ONLY|_local)\b/i }, + { label: 'Kol Windows user path', pattern: /\b[A-Z]:[\\/]Users[\\/](?:koltregaskes|kolin)[\\/][^\s"'<>)]*/i }, + { label: 'W drive estate path', pattern: /\bW:[\\/][^\s"'<>)]*/i }, + { label: 'estate UNC path', pattern: /\\\\(?:\?\\)?(?:nas_storage_1|MINI-PC|localhost|127\.0\.0\.1)[\\/][^\s"'<>)]*/i }, + { label: 'local-only surface marker', pattern: /\b(?:tools-hub-local|LOCAL-ONLY|_local)\b/ }, { label: 'private operations wording', pattern: /\b(?:manager inbox|review evidence|session state)\b/i }, ] const REQUIRED_GITIGNORE_PATTERNS = ['.env', '.env.*', '*.local.md', '.local/', 'local-hub/'] diff --git a/service-worker.js b/service-worker.js index bcf97f5..65ca957 100644 --- a/service-worker.js +++ b/service-worker.js @@ -1,4 +1,4 @@ -const CACHE_NAME = 'stackscout-2026-05-25' +const CACHE_NAME = 'stackscout-2026-06-06' const APP_SHELL = [ '', 'index.html', diff --git a/tools/anthropic-api/index.html b/tools/anthropic-api/index.html index 00f4859..de44888 100644 --- a/tools/anthropic-api/index.html +++ b/tools/anthropic-api/index.html @@ -311,7 +311,7 @@

n8n

diff --git a/tools/canvas-planner/index.html b/tools/canvas-planner/index.html index 30981ec..d247d4c 100644 --- a/tools/canvas-planner/index.html +++ b/tools/canvas-planner/index.html @@ -277,7 +277,7 @@

SyncPad

diff --git a/tools/gh-cli/index.html b/tools/gh-cli/index.html index 4ce2c98..f810be3 100644 --- a/tools/gh-cli/index.html +++ b/tools/gh-cli/index.html @@ -277,7 +277,7 @@

Wrangler

diff --git a/tools/github-mcp-server/index.html b/tools/github-mcp-server/index.html index 9729a00..34f46cc 100644 --- a/tools/github-mcp-server/index.html +++ b/tools/github-mcp-server/index.html @@ -311,7 +311,7 @@

Wrangler

diff --git a/tools/hugging-face/index.html b/tools/hugging-face/index.html index 5cec48b..437555c 100644 --- a/tools/hugging-face/index.html +++ b/tools/hugging-face/index.html @@ -311,7 +311,7 @@

GitHub MCP Server

diff --git a/tools/make/index.html b/tools/make/index.html index b6ee64d..157e8cc 100644 --- a/tools/make/index.html +++ b/tools/make/index.html @@ -311,7 +311,7 @@

Anthropic API

diff --git a/tools/n8n/index.html b/tools/n8n/index.html index 6bf8067..e546569 100644 --- a/tools/n8n/index.html +++ b/tools/n8n/index.html @@ -311,7 +311,7 @@

Anthropic API

diff --git a/tools/openai-api/index.html b/tools/openai-api/index.html index e4b7c25..b717fbb 100644 --- a/tools/openai-api/index.html +++ b/tools/openai-api/index.html @@ -345,7 +345,7 @@

Playwright MCP

diff --git a/tools/perplexity/index.html b/tools/perplexity/index.html index 56142c2..cc49f23 100644 --- a/tools/perplexity/index.html +++ b/tools/perplexity/index.html @@ -277,7 +277,7 @@

OpenAI API

diff --git a/tools/playwright-mcp/index.html b/tools/playwright-mcp/index.html index 525d96f..59e65bd 100644 --- a/tools/playwright-mcp/index.html +++ b/tools/playwright-mcp/index.html @@ -311,7 +311,7 @@

Anthropic API

diff --git a/tools/signal-stack/index.html b/tools/signal-stack/index.html index 890b1a4..546b067 100644 --- a/tools/signal-stack/index.html +++ b/tools/signal-stack/index.html @@ -255,7 +255,7 @@

Strudel Studio

diff --git a/tools/strudel-studio/index.html b/tools/strudel-studio/index.html index e3f09bb..0d7dbc5 100644 --- a/tools/strudel-studio/index.html +++ b/tools/strudel-studio/index.html @@ -277,7 +277,7 @@

OpenAI API

diff --git a/tools/syncpad/index.html b/tools/syncpad/index.html index 8506e6e..741d63b 100644 --- a/tools/syncpad/index.html +++ b/tools/syncpad/index.html @@ -221,7 +221,7 @@

Canvas Planner

diff --git a/tools/wrangler/index.html b/tools/wrangler/index.html index 6f22718..32cd6f1 100644 --- a/tools/wrangler/index.html +++ b/tools/wrangler/index.html @@ -277,7 +277,7 @@

Playwright MCP

diff --git a/updates/index.html b/updates/index.html index fdc4b3e..63d8716 100644 --- a/updates/index.html +++ b/updates/index.html @@ -364,7 +364,7 @@

Wrangler 4.58.0 improved command grouping and auth ergonomics