docs: add .agents workspace and the Astro refactor plan
Adds the agent-facing workspace and a 20-task plan for migrating the site to Astro. Nothing here implements the refactor; these are briefs, rules and templates that the task agents read. - .agents/ holds context, rules, checklists, skills, specialist agents, component/page/config templates and gate scripts. It is vendor-neutral so MiniMax, Gemini and Codex can all read it; CLAUDE.md just points at AGENTS.md. - .husky/ plus .lintstagedrc.json wire the three gate tiers. gate.sh locks on the shared git-common-dir so parallel worktrees serialise, and guards the assertion count in scripts/verify.mjs against a coverage drop. - plans/astro-refactor/ carries the phase graph, per-task briefs and the model-routing recommendation. These files must be tracked before fanning out: a worktree only checks out tracked files, so an untracked plan is invisible to every agent working in one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env node
|
||||
// Mechanically extract every { en, pt } string pair from a file or directory,
|
||||
// sorted and normalised, so a content migration can be proven lossless:
|
||||
//
|
||||
// node .agents/scripts/extract-strings.mjs app.js > /tmp/before.json
|
||||
// node .agents/scripts/extract-strings.mjs src/content/ > /tmp/after.json
|
||||
// diff /tmp/before.json /tmp/after.json
|
||||
//
|
||||
// A non-empty diff means you altered content. These are hand-written
|
||||
// translations with deliberate tone — copy them, never retype them.
|
||||
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const target = process.argv[2];
|
||||
if (!target) {
|
||||
console.error('usage: extract-strings.mjs <file|dir>');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const walk = (dir) =>
|
||||
readdirSync(dir).flatMap((name) => {
|
||||
const path = join(dir, name);
|
||||
return statSync(path).isDirectory() ? walk(path) : [path];
|
||||
});
|
||||
|
||||
const files = statSync(target).isDirectory() ? walk(target) : [target];
|
||||
|
||||
// Matches `en: '…'` / "en": "…" and the pt counterpart, single or double quoted,
|
||||
// tolerating escaped quotes inside.
|
||||
const PAIR = /["']?\b(en|pt)\b["']?\s*:\s*(['"])((?:\\.|(?!\2)[\s\S])*)\2/g;
|
||||
|
||||
const strings = [];
|
||||
for (const file of files) {
|
||||
const source = readFileSync(file, 'utf8');
|
||||
for (const match of source.matchAll(PAIR)) {
|
||||
strings.push({ lang: match[1], value: match[3] });
|
||||
}
|
||||
}
|
||||
|
||||
// Sort so file ordering and structure changes do not show up as content changes.
|
||||
strings.sort((a, b) => (a.lang + a.value).localeCompare(b.lang + b.value));
|
||||
|
||||
console.log(JSON.stringify(strings, null, 2));
|
||||
console.error(`extracted ${strings.length} strings from ${files.length} file(s)`);
|
||||
Reference in New Issue
Block a user