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..6afa345 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..4175a4c 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..81b6211 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..c98a026 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..c9acbbd 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..f862d74 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..cb3bc04 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..cf70f1a 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..999627d 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..ae64571 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..78826b4 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-04", + "updatedAt": "2026-06-04", "categories": [ { "slug": "websites", diff --git a/data/collections-manifest.json b/data/collections-manifest.json index 0e6d713..c6f8013 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-04", + "updatedAt": "2026-06-04", "collections": [ { "slug": "creator-stack", diff --git a/data/methodology-manifest.json b/data/methodology-manifest.json index 5cf7354..f57652d 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-04", + "updatedAt": "2026-06-04", "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..33a29ae 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-04", "pages": [ { "key": "home", diff --git a/data/radar-manifest.json b/data/radar-manifest.json index 2c299ca..7d7a77b 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-04", + "updatedAt": "2026-06-04", "items": [ { "title": "Azure MCP Server", diff --git a/data/tools-manifest.json b/data/tools-manifest.json index 28fa7b6..dee2917 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-04", + "updatedAt": "2026-06-04", "summary": "Public-safe catalog for Stack Scout.", "counts": { "total": 14, diff --git a/data/updates-manifest.json b/data/updates-manifest.json index 4ae33c6..779cf1b 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-04", + "updatedAt": "2026-06-04", "summary": "Public-safe activity stream seeded from official or first-party sources.", "items": [ { diff --git a/index.html b/index.html index ccc9d71..ec7781b 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..aa83e0d 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..4bc907f 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..d390a74 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) { @@ -1279,8 +1274,7 @@ ${routes.map((route) => ` ${PUBLIC_BASE_URL}${route}`).jo } 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)) 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/service-worker.js b/service-worker.js index bcf97f5..9ff0fb2 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-04' const APP_SHELL = [ '', 'index.html', diff --git a/tools/anthropic-api/index.html b/tools/anthropic-api/index.html index 00f4859..9274b4a 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..a333ebe 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..3206296 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..cedcfb4 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..8e6dee2 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..cb9981e 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..2b209f0 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..b5b70ae 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..226bd85 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..d2b6b07 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..df9fb37 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..dbd10d1 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..a5b9964 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..2bf1dc5 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..a824e2d 100644 --- a/updates/index.html +++ b/updates/index.html @@ -364,7 +364,7 @@

Wrangler 4.58.0 improved command grouping and auth ergonomics