mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-17 16:46:31 +03:00
Add browser questionnaire foundation
Prepared with AI assistance.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { cp, copyFile, mkdir, rm } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import sharp from 'sharp';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const buildDir = path.join(root, 'build-picker');
|
||||
const outputDir = path.join(root, 'skill/scripts/picker');
|
||||
const faviconSource = path.join(root, 'site/public/favicon.svg');
|
||||
const faviconOutput = path.join(outputDir, 'favicon.svg');
|
||||
const heroSource = path.join(
|
||||
root,
|
||||
'site/public/assets/neo-kinpaku/candidates/finalists/m-01-v2-01-light.png',
|
||||
);
|
||||
const heroOutput = path.join(outputDir, 'assets/hero-light.jpg');
|
||||
|
||||
await rm(buildDir, { recursive: true, force: true });
|
||||
execFileSync(
|
||||
'bun',
|
||||
['x', 'astro', 'build', '--config', 'picker/astro.config.mjs'],
|
||||
{ cwd: root, stdio: 'inherit' },
|
||||
);
|
||||
|
||||
await rm(outputDir, { recursive: true, force: true });
|
||||
await cp(buildDir, outputDir, { recursive: true });
|
||||
await mkdir(path.dirname(heroOutput), { recursive: true });
|
||||
await copyFile(faviconSource, faviconOutput);
|
||||
await sharp(heroSource)
|
||||
.resize({ width: 1536, withoutEnlargement: true })
|
||||
.jpeg({ quality: 80, mozjpeg: true })
|
||||
.toFile(heroOutput);
|
||||
await rm(buildDir, { recursive: true, force: true });
|
||||
|
||||
console.log(`Built ${path.relative(root, outputDir)}/`);
|
||||
@@ -109,9 +109,11 @@ function readSkillScripts(scriptsDir) {
|
||||
if (PER_PROJECT_SCRIPT_ARTIFACTS.has(entry.name)) continue;
|
||||
|
||||
const relPath = path.relative(scriptsDir, entryPath).split(path.sep).join('/');
|
||||
const isBinary = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.woff', '.woff2']
|
||||
.includes(path.extname(entry.name).toLowerCase());
|
||||
scripts.push({
|
||||
name: relPath,
|
||||
content: fs.readFileSync(entryPath, 'utf-8'),
|
||||
content: fs.readFileSync(entryPath, isBinary ? undefined : 'utf-8'),
|
||||
filePath: entryPath,
|
||||
});
|
||||
}
|
||||
@@ -699,7 +701,8 @@ export function replacePlaceholders(content, provider, commandNames = [], allSki
|
||||
* their command prefix from lib/provider.mjs, whose declaration is replaced
|
||||
* here by an exact string match.
|
||||
*/
|
||||
export function replaceScriptProviderMarker(content, provider, buildProvider = provider) {
|
||||
export function replaceScriptProviderMarker(content, provider) {
|
||||
if (Buffer.isBuffer(content)) return content;
|
||||
const placeholders = PROVIDER_PLACEHOLDERS[provider] || PROVIDER_PLACEHOLDERS.cursor;
|
||||
const commandPrefix = placeholders.command_prefix || '/';
|
||||
const prefixMarker = "export const IMPECCABLE_COMMAND_PREFIX = '/'; // @impeccable-provider-command-prefix";
|
||||
|
||||
+32
-10
@@ -8,7 +8,8 @@
|
||||
// For the skill component, also reruns `bun run build:release` and refuses if the
|
||||
// regenerated harness directories drift from what is committed.
|
||||
|
||||
import { readFileSync, writeFileSync, unlinkSync, existsSync } from 'node:fs';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { readFileSync, readdirSync, writeFileSync, unlinkSync, existsSync } from 'node:fs';
|
||||
import { execSync } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
@@ -88,6 +89,24 @@ function runMutating(cmd) {
|
||||
execSync(cmd, { cwd: repoRoot, stdio: 'inherit' });
|
||||
}
|
||||
|
||||
function directoryHash(directory) {
|
||||
if (!existsSync(directory)) return null;
|
||||
const hash = createHash('sha256');
|
||||
|
||||
function addDirectory(current) {
|
||||
for (const entry of readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
||||
const entryPath = path.join(current, entry.name);
|
||||
const relative = path.relative(directory, entryPath).split(path.sep).join('/');
|
||||
hash.update(relative);
|
||||
if (entry.isDirectory()) addDirectory(entryPath);
|
||||
else if (entry.isFile()) hash.update(readFileSync(entryPath));
|
||||
}
|
||||
}
|
||||
|
||||
addDirectory(directory);
|
||||
return hash.digest('hex');
|
||||
}
|
||||
|
||||
step(`Reading version from ${cfg.manifest}`);
|
||||
const manifest = JSON.parse(readFileSync(path.join(repoRoot, cfg.manifest), 'utf8'));
|
||||
const version = manifest.version;
|
||||
@@ -112,16 +131,19 @@ ok('clean');
|
||||
|
||||
if (cfg.buildCmd) {
|
||||
step(`Rebuilding outputs (${cfg.buildCmd})`);
|
||||
if (dryRun) {
|
||||
console.log(` [dry-run] ${cfg.buildCmd}`);
|
||||
} else {
|
||||
execSync(cfg.buildCmd, { cwd: repoRoot, stdio: 'inherit' });
|
||||
const postBuild = run('git status --porcelain');
|
||||
if (postBuild) {
|
||||
fail(`Build produced uncommitted changes. Run \`${cfg.buildCmd}\`, commit the result, then re-run.\n${postBuild}`);
|
||||
}
|
||||
ok('build outputs match source');
|
||||
const pickerOutput = component === 'skill'
|
||||
? path.join(repoRoot, 'skill/scripts/picker')
|
||||
: null;
|
||||
const pickerHashBefore = pickerOutput ? directoryHash(pickerOutput) : null;
|
||||
execSync(cfg.buildCmd, { cwd: repoRoot, stdio: 'inherit' });
|
||||
if (pickerHashBefore && directoryHash(pickerOutput) !== pickerHashBefore) {
|
||||
fail(`Picker build output was stale. Run \`bun run build:picker\`, then re-run the release check.`);
|
||||
}
|
||||
const postBuild = run('git status --porcelain');
|
||||
if (postBuild) {
|
||||
fail(`Build produced uncommitted changes. Run \`${cfg.buildCmd}\`, commit the result, then re-run.\n${postBuild}`);
|
||||
}
|
||||
ok('build outputs match source');
|
||||
}
|
||||
|
||||
step('Checking HEAD is pushed to origin');
|
||||
|
||||
@@ -25,8 +25,10 @@ export const SUITES = {
|
||||
description: 'Build, provider transforms, CLI helpers, context, and storage unit tests.',
|
||||
triggers: [
|
||||
...COMMON_INFRA_PATTERNS,
|
||||
/^picker\//,
|
||||
/^scripts\/(?!benchmark-detector|build-browser-detector|build-extension)/,
|
||||
/^skill\/(SKILL\.src\.md|agents\/|reference\/|scripts\/(cleanup-deprecated|comp-diff|comp-spec|build-phase|font-match|data\/font-index|concept-seed|generate-image|context|context-signals|critique-storage|design-parser|doctor|hook|impeccable-paths|is-generated|lib\/(artifact-schema|png|raster|image-metrics|font-fingerprint|font-index|hero-checks|composition-catalog|concept-catalog|provider|staleness|staleness-deep|staleness-notice|surface-briefs|target-slug|template-extensions)|pin|surface-brief))/,
|
||||
/^skill\/(SKILL\.src\.md|agents\/|reference\/|scripts\/(cleanup-deprecated|comp-diff|comp-spec|build-phase|font-match|data\/font-index|concept-seed|generate-image|context|context-signals|critique-storage|design-parser|doctor|hook|impeccable-paths|is-generated|lib\/(artifact-schema|png|raster|image-metrics|font-fingerprint|font-index|hero-checks|composition-catalog|concept-catalog|provider|staleness|staleness-deep|staleness-notice|surface-briefs|target-slug|template-extensions)|picker|pin|surface-brief))/,
|
||||
/^site\/(pages|content|components|layouts)\//,
|
||||
/^README(\.npm)?\.md$/,
|
||||
/^cli\/bin\//,
|
||||
],
|
||||
@@ -70,6 +72,7 @@ export const SUITES = {
|
||||
'tests/hook.test.mjs',
|
||||
'tests/impeccable-paths.test.mjs',
|
||||
'tests/openai-plugin.test.mjs',
|
||||
'tests/picker-server.test.mjs',
|
||||
'tests/pin.test.mjs',
|
||||
'tests/release.test.mjs',
|
||||
'tests/doctor.test.mjs',
|
||||
|
||||
Reference in New Issue
Block a user