diff --git a/scripts/resolvers/browse.ts b/scripts/resolvers/browse.ts index a0ae37a70..9a1c5883d 100644 --- a/scripts/resolvers/browse.ts +++ b/scripts/resolvers/browse.ts @@ -100,13 +100,17 @@ export function generateSnapshotFlags(_ctx: TemplateContext): string { } export function generateBrowseSetup(ctx: TemplateContext): string { + const globalBrowse = ctx.paths.browseDir.startsWith('~') + ? `$HOME${ctx.paths.browseDir.slice(1)}/browse` + : `${ctx.paths.browseDir}/browse`; + return `## SETUP (run this check BEFORE any browse command) \`\`\`bash _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="${globalBrowse}" if [ -x "$B" ]; then echo "READY: $B" else diff --git a/scripts/resolvers/design.ts b/scripts/resolvers/design.ts index 9f31b3619..016861f05 100644 --- a/scripts/resolvers/design.ts +++ b/scripts/resolvers/design.ts @@ -786,13 +786,20 @@ Source: [OpenAI "Designing Delightful Frontends with GPT-5.4"](https://developer } export function generateDesignSetup(ctx: TemplateContext): string { + const globalDesign = ctx.paths.designDir.startsWith('~') + ? `$HOME${ctx.paths.designDir.slice(1)}/design` + : `${ctx.paths.designDir}/design`; + const globalBrowse = ctx.paths.browseDir.startsWith('~') + ? `$HOME${ctx.paths.browseDir.slice(1)}/browse` + : `${ctx.paths.browseDir}/browse`; + return `## DESIGN SETUP (run this check BEFORE any design mockup command) \`\`\`bash _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="${globalDesign}" if [ -x "$D" ]; then echo "DESIGN_READY: $D" else @@ -800,7 +807,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="${globalBrowse}" if [ -x "$B" ]; then echo "BROWSE_READY: $B" else @@ -831,13 +838,17 @@ data, not project files. They persist across branches, conversations, and worksp } export function generateDesignMockup(ctx: TemplateContext): string { + const globalDesign = ctx.paths.designDir.startsWith('~') + ? `$HOME${ctx.paths.designDir.slice(1)}/design` + : `${ctx.paths.designDir}/design`; + return `## Visual Design Exploration \`\`\`bash _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="${globalDesign}" [ -x "$D" ] && echo "DESIGN_READY" || echo "DESIGN_NOT_AVAILABLE" \`\`\` diff --git a/test/host-config.test.ts b/test/host-config.test.ts index 577057033..fd15f5a96 100644 --- a/test/host-config.test.ts +++ b/test/host-config.test.ts @@ -23,7 +23,9 @@ import { cursor, openclaw, } from '../hosts/index'; -import { HOST_PATHS } from '../scripts/resolvers/types'; +import { HOST_PATHS, type TemplateContext } from '../scripts/resolvers/types'; +import { generateBrowseSetup } from '../scripts/resolvers/browse'; +import { generateDesignSetup, generateDesignMockup } from '../scripts/resolvers/design'; const ROOT = path.resolve(import.meta.dir, '..'); @@ -288,6 +290,37 @@ describe('HOST_PATHS derivation from configs', () => { }); }); +// ─── Env-var path resolver regression ─────────────────────── + +describe('env-var path resolver regression', () => { + const hermesCtx: TemplateContext = { + skillName: 'gstack', + tmplPath: 'gstack/SKILL.md.tmpl', + host: 'hermes', + paths: HOST_PATHS.hermes, + }; + + test('browse setup preserves Hermes GSTACK_BROWSE env var path', () => { + const setup = generateBrowseSetup(hermesCtx); + expect(setup).toContain('B="$GSTACK_BROWSE/browse"'); + expect(setup).not.toContain('$HOME$GSTACK_BROWSE'); + }); + + test('design setup preserves Hermes GSTACK_DESIGN and GSTACK_BROWSE env var paths', () => { + const setup = generateDesignSetup(hermesCtx); + expect(setup).toContain('D="$GSTACK_DESIGN/design"'); + expect(setup).toContain('B="$GSTACK_BROWSE/browse"'); + expect(setup).not.toContain('$HOME$GSTACK_DESIGN'); + expect(setup).not.toContain('$HOME$GSTACK_BROWSE'); + }); + + test('design mockup preserves Hermes GSTACK_DESIGN env var path', () => { + const mockup = generateDesignMockup(hermesCtx); + expect(mockup).toContain('D="$GSTACK_DESIGN/design"'); + expect(mockup).not.toContain('$HOME$GSTACK_DESIGN'); + }); +}); + // ─── host-config-export.ts CLI ────────────────────────────── describe('host-config-export.ts CLI', () => {