Fix Codex design binary path resolution

This commit is contained in:
Mammad M. 2026-04-23 10:53:01 +02:00
parent 656df0e37e
commit 0d7c0617be
7 changed files with 64 additions and 11 deletions

View File

@ -42,14 +42,14 @@ const codex: HostConfig = {
],
runtimeRoot: {
globalSymlinks: ['bin', 'browse/dist', 'browse/bin', 'gstack-upgrade', 'ETHOS.md'],
globalSymlinks: ['bin', 'browse/dist', 'browse/bin', 'design/dist', 'gstack-upgrade', 'ETHOS.md'],
globalFiles: {
'review': ['checklist.md', 'TODOS-format.md'],
},
},
sidecar: {
path: '.agents/skills/gstack',
symlinks: ['bin', 'browse', 'review', 'qa', 'ETHOS.md'],
symlinks: ['bin', 'browse', 'design', 'review', 'qa', 'ETHOS.md'],
},
install: {

View File

@ -2,6 +2,12 @@ import type { TemplateContext } from './types';
import { COMMAND_DESCRIPTIONS } from '../../browse/src/commands';
import { SNAPSHOT_FLAGS } from '../../browse/src/snapshot';
function resolveBinaryPath(dirExpr: string, binaryName: string): string {
return dirExpr.startsWith('$')
? `${dirExpr}/${binaryName}`
: `$HOME${dirExpr.replace(/^~/, '')}/${binaryName}`;
}
export function generateCommandReference(_ctx: TemplateContext): string {
// Group commands by category
const groups = new Map<string, Array<{ command: string; description: string; usage?: string }>>();
@ -106,7 +112,7 @@ export function generateBrowseSetup(ctx: TemplateContext): string {
_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
B=""
[ -n "$_ROOT" ] && [ -x "$_ROOT/${ctx.paths.localSkillRoot}/browse/dist/browse" ] && B="$_ROOT/${ctx.paths.localSkillRoot}/browse/dist/browse"
[ -z "$B" ] && B="$HOME${ctx.paths.browseDir.replace(/^~/, '')}/browse"
[ -z "$B" ] && B="${resolveBinaryPath(ctx.paths.browseDir, 'browse')}"
if [ -x "$B" ]; then
echo "READY: $B"
else

View File

@ -1,6 +1,12 @@
import type { TemplateContext } from './types';
import { AI_SLOP_BLACKLIST, OPENAI_HARD_REJECTIONS, OPENAI_LITMUS_CHECKS } from './constants';
function resolveBinaryPath(dirExpr: string, binaryName: string): string {
return dirExpr.startsWith('$')
? `${dirExpr}/${binaryName}`
: `$HOME${dirExpr.replace(/^~/, '')}/${binaryName}`;
}
export function generateDesignReviewLite(ctx: TemplateContext): string {
const litmusList = OPENAI_LITMUS_CHECKS.map((item, i) => `${i + 1}. ${item}`).join(' ');
const rejectionList = OPENAI_HARD_REJECTIONS.map((item, i) => `${i + 1}. ${item}`).join(' ');
@ -792,7 +798,7 @@ export function generateDesignSetup(ctx: TemplateContext): string {
_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
D=""
[ -n "$_ROOT" ] && [ -x "$_ROOT/${ctx.paths.localSkillRoot}/design/dist/design" ] && D="$_ROOT/${ctx.paths.localSkillRoot}/design/dist/design"
[ -z "$D" ] && D="$HOME${ctx.paths.designDir.replace(/^~/, '')}/design"
[ -z "$D" ] && D="${resolveBinaryPath(ctx.paths.designDir, 'design')}"
if [ -x "$D" ]; then
echo "DESIGN_READY: $D"
else
@ -800,7 +806,7 @@ else
fi
B=""
[ -n "$_ROOT" ] && [ -x "$_ROOT/${ctx.paths.localSkillRoot}/browse/dist/browse" ] && B="$_ROOT/${ctx.paths.localSkillRoot}/browse/dist/browse"
[ -z "$B" ] && B="$HOME${ctx.paths.browseDir.replace(/^~/, '')}/browse"
[ -z "$B" ] && B="${resolveBinaryPath(ctx.paths.browseDir, 'browse')}"
if [ -x "$B" ]; then
echo "BROWSE_READY: $B"
else
@ -837,7 +843,7 @@ export function generateDesignMockup(ctx: TemplateContext): string {
_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
D=""
[ -n "$_ROOT" ] && [ -x "$_ROOT/${ctx.paths.localSkillRoot}/design/dist/design" ] && D="$_ROOT/${ctx.paths.localSkillRoot}/design/dist/design"
[ -z "$D" ] && D="$HOME${ctx.paths.designDir.replace(/^~/, '')}/design"
[ -z "$D" ] && D="${resolveBinaryPath(ctx.paths.designDir, 'design')}"
[ -x "$D" ] && echo "DESIGN_READY" || echo "DESIGN_NOT_AVAILABLE"
\`\`\`
@ -1139,4 +1145,3 @@ Flat design can strip away useful visual information that signals interactivity.
Prioritize ruthlessly: things needed in a hurry go close at hand, everything
else a few taps away with an obvious path to get there.`;
}

9
setup
View File

@ -532,7 +532,7 @@ link_codex_skill_dirs() {
# ─── Helper: create .agents/skills/gstack/ sidecar symlinks ──────────
# Codex/Gemini/Cursor read skills from .agents/skills/. We link runtime
# assets (bin/, browse/dist/, review/, qa/, etc.) so skill templates can
# assets (bin/, browse/dist/, design/dist/, review/, qa/, etc.) so skill templates can
# resolve paths like $SKILL_ROOT/review/design-checklist.md.
create_agents_sidecar() {
local repo_root="$1"
@ -540,7 +540,7 @@ create_agents_sidecar() {
mkdir -p "$agents_gstack"
# Sidecar directories that skills reference at runtime
for asset in bin browse review qa; do
for asset in bin browse design review qa; do
local src="$SOURCE_GSTACK_DIR/$asset"
local dst="$agents_gstack/$asset"
if [ -d "$src" ] || [ -f "$src" ]; then
@ -579,7 +579,7 @@ create_codex_runtime_root() {
rm -rf "$codex_gstack"
fi
mkdir -p "$codex_gstack" "$codex_gstack/browse" "$codex_gstack/gstack-upgrade" "$codex_gstack/review"
mkdir -p "$codex_gstack" "$codex_gstack/browse" "$codex_gstack/design" "$codex_gstack/gstack-upgrade" "$codex_gstack/review"
if [ -f "$agents_dir/gstack/SKILL.md" ]; then
ln -snf "$agents_dir/gstack/SKILL.md" "$codex_gstack/SKILL.md"
@ -593,6 +593,9 @@ create_codex_runtime_root() {
if [ -d "$gstack_dir/browse/bin" ]; then
ln -snf "$gstack_dir/browse/bin" "$codex_gstack/browse/bin"
fi
if [ -d "$gstack_dir/design/dist" ]; then
ln -snf "$gstack_dir/design/dist" "$codex_gstack/design/dist"
fi
if [ -f "$agents_dir/gstack-upgrade/SKILL.md" ]; then
ln -snf "$agents_dir/gstack-upgrade/SKILL.md" "$codex_gstack/gstack-upgrade/SKILL.md"
fi

View File

@ -0,0 +1,36 @@
import { describe, expect, test } from 'bun:test';
import type { TemplateContext } from '../scripts/resolvers/types';
import { HOST_PATHS } from '../scripts/resolvers/types';
import { generateBrowseSetup } from '../scripts/resolvers/browse';
import { generateDesignMockup, generateDesignSetup } from '../scripts/resolvers/design';
function makeCodexCtx(): TemplateContext {
return {
skillName: 'test-skill',
tmplPath: 'test.tmpl',
host: 'codex',
paths: HOST_PATHS.codex,
};
}
describe('Codex design/browse path generation', () => {
test('generated Codex browse setup uses GSTACK_BROWSE directly', () => {
const out = generateBrowseSetup(makeCodexCtx());
expect(out).toContain('B="$GSTACK_BROWSE/browse"');
expect(out).not.toContain('$HOME$GSTACK_BROWSE');
});
test('generated Codex design setup uses GSTACK_DESIGN and GSTACK_BROWSE directly', () => {
const out = generateDesignSetup(makeCodexCtx());
expect(out).toContain('D="$GSTACK_DESIGN/design"');
expect(out).toContain('B="$GSTACK_BROWSE/browse"');
expect(out).not.toContain('$HOME$GSTACK_DESIGN');
expect(out).not.toContain('$HOME$GSTACK_BROWSE');
});
test('generated Codex design mockup uses GSTACK_DESIGN directly', () => {
const out = generateDesignMockup(makeCodexCtx());
expect(out).toContain('D="$GSTACK_DESIGN/design"');
expect(out).not.toContain('$HOME$GSTACK_DESIGN');
});
});

View File

@ -2188,12 +2188,13 @@ describe('setup script validation', () => {
});
test('create_agents_sidecar links runtime assets', () => {
// Sidecar must link bin, browse, review, qa
// Sidecar must link bin, browse, design, review, qa
const fnStart = setupContent.indexOf('create_agents_sidecar()');
const fnEnd = setupContent.indexOf('}', setupContent.indexOf('done', fnStart));
const fnBody = setupContent.slice(fnStart, fnEnd);
expect(fnBody).toContain('bin');
expect(fnBody).toContain('browse');
expect(fnBody).toContain('design');
expect(fnBody).toContain('review');
expect(fnBody).toContain('qa');
});
@ -2205,6 +2206,7 @@ describe('setup script validation', () => {
expect(fnBody).toContain('gstack/SKILL.md');
expect(fnBody).toContain('browse/dist');
expect(fnBody).toContain('browse/bin');
expect(fnBody).toContain('design/dist');
expect(fnBody).toContain('gstack-upgrade/SKILL.md');
// Review runtime assets (individual files, not the whole dir)
expect(fnBody).toContain('checklist.md');

View File

@ -350,6 +350,7 @@ describe('host-config-export.ts CLI', () => {
expect(exitCode).toBe(0);
const lines = stdout.split('\n');
expect(lines).toContain('bin');
expect(lines).toContain('design/dist');
expect(lines).toContain('ETHOS.md');
expect(lines).toContain('review/checklist.md');
});