mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-14 17:05:28 +02:00
Merge origin/main (v1.64.1.0) into garrytan/time-attack-fork-review
The code-smell wave refactored the tunnel start into the staged startTunnel helper; the merge adopts that structure and threads this branch's truthful consent strings through its consent parameter (the receipt now names the isPairAgentEnabled gate at both call sites). The eng-review PTY warmup takes both improvements: main's resolveClaudeBinary fallback and this branch's resolveEvalModel kind. Resolver imports union; carve budgets take the larger of both waves' measured values (parity suite green on the merged tree); conflicted generated SKILL.md files regenerated from resolved sources. VERSION stays 1.65.0.0 over main's 1.64.1.0; CHANGELOG stacks 1.65.0.0 > 1.64.1.0 > 1.64.0.0.
This commit is contained in:
+48
-19
@@ -104,8 +104,16 @@ const ALL_SKILLS = (() => {
|
||||
return skills;
|
||||
})();
|
||||
|
||||
const CLAUDE_SKIPPED_SKILL_DIRS = new Set(['claude']);
|
||||
const CLAUDE_GENERATED_SKILLS = ALL_SKILLS.filter(skill => !CLAUDE_SKIPPED_SKILL_DIRS.has(skill.dir));
|
||||
// hosts/claude.ts generation.skipSkills entries would filter here; the set is
|
||||
// currently empty (the /claude outside-voice template was removed).
|
||||
// The claude host deliberately skips some skills (skipSkills — e.g. the
|
||||
// /claude outside-voice skill exists only for non-Claude hosts), so those
|
||||
// dirs have a SKILL.md.tmpl but no generated claude-host SKILL.md on a fresh
|
||||
// checkout. Every generated-file assertion must exclude them or it is red on
|
||||
// every clean clone (it was, invisibly, until the free suite ran in CI).
|
||||
import { getHostConfig as __getHostConfig } from '../hosts/index';
|
||||
const CLAUDE_SKIPPED = new Set(__getHostConfig('claude').generation.skipSkills ?? []);
|
||||
const CLAUDE_GENERATED_SKILLS = ALL_SKILLS.filter(s => !CLAUDE_SKIPPED.has(s.dir));
|
||||
|
||||
describe('gen-skill-docs', () => {
|
||||
test('generated SKILL.md contains all command categories', () => {
|
||||
@@ -218,11 +226,6 @@ describe('gen-skill-docs', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('Claude outside-voice skill is not generated for Claude host', () => {
|
||||
expect(fs.existsSync(path.join(ROOT, 'claude', 'SKILL.md.tmpl'))).toBe(true);
|
||||
expect(fs.existsSync(path.join(ROOT, 'claude', 'SKILL.md'))).toBe(false);
|
||||
});
|
||||
|
||||
test(`every Codex SKILL.md description stays within ${MAX_SKILL_DESCRIPTION_LENGTH} chars`, () => {
|
||||
const agentsDir = path.join(ROOT, '.agents', 'skills');
|
||||
if (!fs.existsSync(agentsDir)) return; // skip if not generated
|
||||
@@ -2254,16 +2257,6 @@ describe('Parameterized host smoke tests', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('generates Claude outside-voice skill for external hosts', () => {
|
||||
const skillMd = path.join(hostDir, 'gstack-claude', 'SKILL.md');
|
||||
expect(fs.existsSync(skillMd)).toBe(true);
|
||||
const content = fs.readFileSync(skillMd, 'utf-8');
|
||||
expect(content).toContain('claude -p');
|
||||
expect(content).toContain('--disable-slash-commands');
|
||||
expect(content).toContain('--allowedTools Read,Grep,Glob');
|
||||
expect(content).toContain('--disallowedTools Bash,Edit,Write');
|
||||
});
|
||||
|
||||
test('--dry-run freshness check passes', () => {
|
||||
const result = Bun.spawnSync(
|
||||
['bun', 'run', 'scripts/gen-skill-docs.ts', '--host', hostConfig.name, '--dry-run'],
|
||||
@@ -2431,9 +2424,9 @@ describe('setup script validation', () => {
|
||||
expect(claudeSection).toContain('link_claude_root_skill_alias "$SOURCE_GSTACK_DIR" "$INSTALL_SKILLS_DIR"');
|
||||
});
|
||||
|
||||
test('setup supports --host auto|claude|codex|kiro|opencode', () => {
|
||||
test('setup supports --host auto|claude|codex|kiro|opencode|cursor|slate', () => {
|
||||
expect(setupContent).toContain('--host');
|
||||
expect(setupContent).toContain('claude|codex|kiro|factory|opencode|auto');
|
||||
expect(setupContent).toContain('claude|codex|kiro|factory|opencode|cursor|slate|auto');
|
||||
});
|
||||
|
||||
test('auto mode detects claude, codex, kiro, and opencode binaries', () => {
|
||||
@@ -3466,3 +3459,39 @@ describe('GSTACK REVIEW REPORT mandatory unresolved-decisions status', () => {
|
||||
expect(src).not.toContain('absorbs CODEX / CROSS-MODEL / UNRESOLVED lines if applicable');
|
||||
});
|
||||
});
|
||||
|
||||
// ─── {{PREAMBLE}} requires an explicit preamble-tier ────────
|
||||
|
||||
describe('PREAMBLE resolution requires declared preamble-tier', () => {
|
||||
test('resolving {{PREAMBLE}} without preamble-tier throws with the template path', async () => {
|
||||
const { generatePreamble } = await import('../scripts/resolvers/preamble');
|
||||
const { HOST_PATHS } = await import('../scripts/resolvers/types');
|
||||
const ctx = {
|
||||
skillName: 'tierless-skill',
|
||||
tmplPath: 'tierless-skill/SKILL.md.tmpl',
|
||||
host: 'claude' as const,
|
||||
paths: HOST_PATHS.claude,
|
||||
// preambleTier deliberately absent — the generator must refuse to default it.
|
||||
};
|
||||
expect(() => generatePreamble(ctx)).toThrow(/tierless-skill\/SKILL\.md\.tmpl/);
|
||||
expect(() => generatePreamble(ctx)).toThrow(/preamble-tier/);
|
||||
});
|
||||
|
||||
test('every template that resolves {{PREAMBLE}} declares preamble-tier in frontmatter', () => {
|
||||
const entries = fs.readdirSync(ROOT, { withFileTypes: true });
|
||||
const offenders: string[] = [];
|
||||
const checkTmpl = (tmplPath: string) => {
|
||||
const tmpl = fs.readFileSync(tmplPath, 'utf-8');
|
||||
if (tmpl.includes('{{PREAMBLE}}') && !/^preamble-tier:\s*\d+$/m.test(tmpl)) {
|
||||
offenders.push(path.relative(ROOT, tmplPath));
|
||||
}
|
||||
};
|
||||
checkTmpl(path.join(ROOT, 'SKILL.md.tmpl'));
|
||||
for (const e of entries) {
|
||||
if (!e.isDirectory() || e.name.startsWith('.') || e.name === 'node_modules') continue;
|
||||
const tmplPath = path.join(ROOT, e.name, 'SKILL.md.tmpl');
|
||||
if (fs.existsSync(tmplPath)) checkTmpl(tmplPath);
|
||||
}
|
||||
expect(offenders).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user