mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-17 02:15:31 +02:00
feat(bin): gstack-design-detect wrapper + design_detector config key
bin/gstack-design-detect.ts finds and runs an impeccable engine the user installed; it never installs, downloads, or executes anything that could download. `probe` reads only: config (design_detector off → DISABLED), IMPECCABLE_BIN (absolute, realpath outside the repo and cwd), a PATH walk (absolute entries outside the repo; a #! shim counts as launcher-present, never READY), the ~/.impeccable/bin/<newest semver>/ cache, and the engine installed beside a skill launcher (scripts/bin/<os>-<arch>/impeccable, the layout a real install produced). It reports IMPECCABLE_SKILL, host-aware IMPECCABLE_HOOK (+ HOOK_OTHER), the ignore lists from .impeccable/config*.json, IMPECCABLE_ENGINE_UNTESTED for versions outside the fixture set, and a hint only when a launcher exists without its engine. `scan` re-probes, refuses URLs and anything outside the repo root or the design-report allow-list (realpath, so symlinks cannot escape), derives `--changed <base>` targets NUL-safely through git and lib/frontend-scope.ts, batches 100 absolute paths per engine call with stdin ignored, a SIGKILL timeout, a 50 MB stdout cap, and sanitized length-capped fields, then prints one normalized JSON document (--format gstack) or the engine's bytes (--format raw); DETECT_TOP (fenced as untrusted content), DETECT_SUMMARY, and DETECT_EXIT go to stderr; exit code passes through with 1 over 2 over 0; exit 3 is a gstack bug. `rules` prints the mapped set. Every run appends a content-free line to the local analytics file. lib/design-detect-contract.ts owns every sentinel string, the limits, and the normalized-finding shape (pure module); test/design-detect-contract.test.ts asserts every sentinel-shaped token the agent can read exists there. lib/frontend-scope.ts mirrors gstack-diff-scope's frontend arm, pinned by a parity test that runs the bash script. bin/gstack-config gains design_detector (auto | off, default auto, invalid values rejected with the file unchanged). test/fixtures/fake-impeccable.ts is the env-driven engine stand-in; test/gstack-design-detect.test.ts covers READY/NOT_CACHED/ NOT_AVAILABLE/DISABLED, env trust (.env never loaded, in-repo IMPECCABLE_BIN ignored), newest-semver cache, hook and ignore detection, refusals, exit passthrough, raw byte-identity, normalization, the display cap, timeout, parse errors, diagnostics, --changed, and analytics. The egress scanner test records the wrapper as a documented non-sink. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
71505f8374
commit
3867dae355
@@ -0,0 +1,31 @@
|
||||
// lib/frontend-scope.ts — which repo paths count as frontend.
|
||||
//
|
||||
// Pure module: no I/O, no imports from scripts/. The patterns mirror the
|
||||
// `m_frontend` arm of bin/gstack-diff-scope (the bash source of truth for
|
||||
// SCOPE_FRONTEND); test/frontend-scope.test.ts pins the two against the same
|
||||
// sample paths so they cannot drift. bin/gstack-design-detect.ts uses this to
|
||||
// derive `scan --changed <base>` targets without consuming a shell-split list.
|
||||
|
||||
const EXTENSIONS = new Set([
|
||||
'.css', '.scss', '.less', '.sass', '.pcss',
|
||||
'.tsx', '.jsx', '.vue', '.svelte', '.astro',
|
||||
'.erb', '.haml', '.slim', '.hbs', '.ejs',
|
||||
'.html',
|
||||
]);
|
||||
|
||||
const BASENAME_PREFIXES = ['tailwind.config.', 'postcss.config.'];
|
||||
|
||||
/** Repo-relative path (forward slashes) → is it a frontend file per gstack-diff-scope? */
|
||||
export function isFrontendPath(relPath: string): boolean {
|
||||
const rel = relPath.replace(/\\/g, '/').replace(/^\.\//, '');
|
||||
const base = rel.slice(rel.lastIndexOf('/') + 1);
|
||||
const dot = base.lastIndexOf('.');
|
||||
const ext = dot >= 0 ? base.slice(dot).toLowerCase() : '';
|
||||
if (EXTENSIONS.has(ext)) return true;
|
||||
if (BASENAME_PREFIXES.some(p => base.startsWith(p))) return true;
|
||||
if (rel.startsWith('app/views/')) return true;
|
||||
if (rel.includes('/components/')) return true;
|
||||
if (rel.startsWith('styles/') || rel.startsWith('css/')) return true;
|
||||
if (rel.startsWith('app/assets/stylesheets/')) return true;
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user