mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
C4: no external render points at a section file that is not installed
design-html's detector install offer and plan-eng-review's self-check pointed
at $GSTACK_ROOT/<skill>/sections/<file> on every external host, where neither
skill is carved and no section tree exists. design-html now inlines the offer
off Claude, and a skill's pointer to its own {{SECTION}}-inlined section
names the inlined text.
This commit is contained in:
1 parent
d3640f28f8
commit
ea0b4841ba
3 files changed
+29
-6
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { outsideVoiceFailurePolicy, outsideVoiceFor, outsideVoiceInvocation, outsideVoicePreflight, outsideVoiceProvenance } from './outside-voice';
|
||||
import { type TemplateContext, quoteSafePath, toShellPath } from './types';
|
||||
import { binaryAssignment } from './runtime-root';
|
||||
import { usesLazySections } from './sections';
|
||||
import { AI_SLOP_BLACKLIST, OPENAI_HARD_REJECTIONS, OPENAI_LITMUS_CHECKS, CC_BACKGROUND_DEFAULT_SINCE } from './constants';
|
||||
import { OVERUSED_FONTS_DISPLAY, BANNED_FONTS, FONTS_BODY_UI_OK, FONTS_MONO_OK, FONTS_VERIFIED_FREE, HANDOFF_COMMANDS, selectCatalog, catalogEntries, renderCatalog, detectorSlopEntries, judgmentTellEntries } from '../../lib/design-catalog';
|
||||
import { SENTINEL, DETECT_EXIT_ECHO, DETECT_LIMITS } from '../../lib/design-detect-contract';
|
||||
@@ -951,7 +952,8 @@ Exit 2 → one surgical fix pass over the non-advisory rules in the \`${SENTINEL
|
||||
// only), so it carries the brief inline; every other skill keeps its skeleton
|
||||
// small and reads sections/detector-install-offer.md only when the probe printed
|
||||
// the offer (a carved section costs nothing until it is read).
|
||||
const offer = ctx.skillName === 'design-review'
|
||||
// External hosts do not carve design-html, so the section file is never installed there: inline it.
|
||||
const offer = ctx.skillName === 'design-review' || !usesLazySections(ctx.host, ctx.skillName)
|
||||
? generateDesignDetector(ctx, ['offer'])
|
||||
: `**Install offer (one question, asked once).** If the probe printed \`${SENTINEL.INSTALL_OFFER}\`, Read \`${ctx.paths.skillRoot}/${ctx.skillName}/sections/detector-install-offer.md\` and follow it before any other step; otherwise skip it.`;
|
||||
return `**Design detector (optional, deterministic):** gstack runs impeccable's engine when one is installed under the user's home directory. gstack never runs impeccable's installer, its launcher, or \`npx impeccable\`; the one download it can make is the engine binary itself, only after the user says yes to the offer below, verified against a checksum pinned in gstack.
|
||||
|
||||
@@ -39,14 +39,18 @@ export function usesLazySections(host: Host, skill: string): boolean {
|
||||
/**
|
||||
* External hosts: a template's literal `~/.claude/skills/gstack/<skill>/sections/<file>`
|
||||
* reference arrives here as `$GSTACK_ROOT/<skill>/sections/<file>`, which external
|
||||
* runtime roots do not have. For a carved skill, point it at the installed copy.
|
||||
* runtime roots do not have. For a carved skill, point it at the installed copy;
|
||||
* for this skill's own section inlined by {{SECTION}}, point at the inlined text.
|
||||
*/
|
||||
export function rewriteCarvedSectionRefs(content: string, ctx: TemplateContext): string {
|
||||
if (ctx.host === 'claude') return content;
|
||||
return content.replace(/`\$GSTACK_ROOT\/([a-z0-9-]+)\/sections\/([A-Za-z0-9._-]+)`/g, (match, skill: string, file: string) =>
|
||||
usesLazySections(ctx.host, skill) && fs.existsSync(path.join(ROOT, skill, 'sections', `${file}.tmpl`))
|
||||
? `\`sections/${file}\` relative to the installed \`gstack-${skill}\` SKILL.md directory`
|
||||
: match);
|
||||
return content.replace(/`\$GSTACK_ROOT\/([a-z0-9-]+)\/sections\/([A-Za-z0-9._-]+)`/g, (match, skill: string, file: string) => {
|
||||
if (!fs.existsSync(path.join(ROOT, skill, 'sections', `${file}.tmpl`))) return match;
|
||||
if (usesLazySections(ctx.host, skill)) return `\`sections/${file}\` relative to the installed \`gstack-${skill}\` SKILL.md directory`;
|
||||
const entry = loadManifest(skill).sections.find(e => e.file === file);
|
||||
const inlined = skill === ctx.skillName && entry && fs.readFileSync(path.join(ROOT, skill, 'SKILL.md.tmpl'), 'utf-8').includes(`{{SECTION:${entry.id}}}`);
|
||||
return inlined ? `the \`${file.replace(/\.md$/, '')}\` section inlined in this SKILL.md` : match;
|
||||
});
|
||||
}
|
||||
|
||||
interface SectionEntry {
|
||||
|
||||
Reference in new issue
Block a user