mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-14 16:28:05 +02:00
merge: incorporate origin/main into community-mode branch
Conflicts resolved: - VERSION: keep 0.14.0.0 (our branch > main's 0.13.3.0) - package.json: same version resolution - CHANGELOG.md: keep both entries, 0.14.0.0 above 0.13.3.0 - .gitignore: merge both sides (our bun.lock + main's env patterns) Main brought in v0.13.3.0 "Lock It Down": pinned dependencies via bun.lock, gstack-slug non-git fallback, setup CI timeout, Windows lockfile fix, design doc discovery fix, autoplan sequential voices, community PR guardrails in CLAUDE.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2097,7 +2097,7 @@ Source: [OpenAI "Designing Delightful Frontends with GPT-5.4"](https://developer
|
||||
|
||||
const GENERATED_HEADER = `<!-- AUTO-GENERATED from {{SOURCE}} — do not edit directly -->\n<!-- Regenerate: bun run gen:skill-docs -->\n`;
|
||||
|
||||
function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath: string; content: string } {
|
||||
function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath: string; content: string; symlinkLoop?: boolean } {
|
||||
const tmplContent = fs.readFileSync(tmplPath, 'utf-8');
|
||||
const relTmplPath = path.relative(ROOT, tmplPath);
|
||||
let outputPath = tmplPath.replace(/\.tmpl$/, '');
|
||||
@@ -2108,11 +2108,27 @@ function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath:
|
||||
let outputDir: string | null = null;
|
||||
|
||||
// For codex host, route output to .agents/skills/{codexSkillName}/SKILL.md
|
||||
let symlinkLoop = false;
|
||||
if (host === 'codex') {
|
||||
const codexName = codexSkillName(skillDir === '.' ? '' : skillDir);
|
||||
outputDir = path.join(ROOT, '.agents', 'skills', codexName);
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
outputPath = path.join(outputDir, 'SKILL.md');
|
||||
|
||||
// Guard against symlink loops: if .agents/skills/gstack → repo root,
|
||||
// writing to .agents/skills/gstack/SKILL.md would overwrite the Claude version.
|
||||
// Skip the write entirely for this skill — the codex content is still generated
|
||||
// for token budget tracking.
|
||||
const claudePath = tmplPath.replace(/\.tmpl$/, '');
|
||||
try {
|
||||
const resolvedClaude = fs.realpathSync(claudePath);
|
||||
const resolvedCodex = fs.realpathSync(path.dirname(outputPath)) + '/' + path.basename(outputPath);
|
||||
if (resolvedClaude === resolvedCodex) {
|
||||
symlinkLoop = true;
|
||||
}
|
||||
} catch {
|
||||
// realpathSync fails if file doesn't exist yet — that's fine, no symlink loop
|
||||
}
|
||||
}
|
||||
|
||||
// Extract skill name from frontmatter for TemplateContext
|
||||
@@ -2166,7 +2182,7 @@ function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath:
|
||||
content = content.replace(/~\/\.claude\/plans/g, '~/.codex/plans');
|
||||
content = content.replace(/~\/\.claude\//g, '~/.codex/');
|
||||
|
||||
if (outputDir) {
|
||||
if (outputDir && !symlinkLoop) {
|
||||
const codexName = codexSkillName(skillDir === '.' ? '' : skillDir);
|
||||
const agentsDir = path.join(outputDir, 'agents');
|
||||
fs.mkdirSync(agentsDir, { recursive: true });
|
||||
@@ -2186,7 +2202,7 @@ function processTemplate(tmplPath: string, host: Host = 'claude'): { outputPath:
|
||||
content = header + content;
|
||||
}
|
||||
|
||||
return { outputPath, content };
|
||||
return { outputPath, content, symlinkLoop };
|
||||
}
|
||||
|
||||
// ─── Main ───────────────────────────────────────────────────
|
||||
@@ -2205,10 +2221,12 @@ for (const tmplPath of findTemplates()) {
|
||||
if (dir === 'codex') continue;
|
||||
}
|
||||
|
||||
const { outputPath, content } = processTemplate(tmplPath, HOST);
|
||||
const { outputPath, content, symlinkLoop } = processTemplate(tmplPath, HOST);
|
||||
const relOutput = path.relative(ROOT, outputPath);
|
||||
|
||||
if (DRY_RUN) {
|
||||
if (symlinkLoop) {
|
||||
console.log(`SKIPPED (symlink loop): ${relOutput}`);
|
||||
} else if (DRY_RUN) {
|
||||
const existing = fs.existsSync(outputPath) ? fs.readFileSync(outputPath, 'utf-8') : '';
|
||||
if (existing !== content) {
|
||||
console.log(`STALE: ${relOutput}`);
|
||||
|
||||
Reference in New Issue
Block a user