mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-15 15:46:30 +03:00
Add first-class Grok Build harness support
Emit .grok skills, agents, and PostToolUse/Stop hooks; wire the CLI installer and downloads; fix the plugin install path to #plugin; and document Grok in HARNESSES.md and README. AI assistance: written with Grok Build.
This commit is contained in:
+39
-11
@@ -462,11 +462,15 @@ This folder contains skills for all supported tools:
|
||||
.codex/ -> Codex custom agents (Codex skills use .agents/)
|
||||
.agents/ -> Codex CLI
|
||||
.github/ -> GitHub Copilot
|
||||
.grok/ -> Grok Build
|
||||
.kiro/ -> Kiro
|
||||
.opencode/ -> OpenCode
|
||||
.pi/ -> Pi
|
||||
.trae-cn/ -> Trae China
|
||||
.trae/ -> Trae International
|
||||
.rovodev/ -> Rovo Dev
|
||||
.vibe/ -> Mistral Vibe
|
||||
.qoder/ -> Qoder
|
||||
|
||||
To install, copy the relevant folder(s) into your project root.
|
||||
For Codex, repo and user skill installs come from .agents/skills.
|
||||
@@ -590,18 +594,21 @@ async function build() {
|
||||
|
||||
console.log(`📋 Synced skills to: ${syncConfigs.map(p => p.configDir).join(', ')}`);
|
||||
|
||||
// Build the Claude Code plugin subtree at ./plugin/.
|
||||
// The Claude Code marketplace is configured with `source: "./plugin"`, so
|
||||
// the plugin cache only copies this slim directory (~0.3 MB) instead of
|
||||
// the entire monorepo (~291 MB on the previous "./" source). The harness
|
||||
// dirs above stay where they are because `npx skills add pbakaus/impeccable`
|
||||
// reads them directly from the GitHub repo at install time.
|
||||
// Build the shared plugin subtree at ./plugin/.
|
||||
// Claude Code marketplace is configured with `source: "./plugin"`, so the
|
||||
// plugin cache only copies this slim directory (~0.3 MB) instead of the
|
||||
// entire monorepo. Grok Build installs the same subtree via
|
||||
// `grok plugin install pbakaus/impeccable#plugin --trust` (or the
|
||||
// marketplace source). The harness dirs above stay where they are because
|
||||
// `npx skills add pbakaus/impeccable` reads them from the GitHub repo.
|
||||
const pluginRoot = path.join(ROOT_DIR, 'plugin');
|
||||
const pluginManifestDir = path.join(pluginRoot, '.claude-plugin');
|
||||
const grokPluginManifestDir = path.join(pluginRoot, '.grok-plugin');
|
||||
const pluginSkillsDir = path.join(pluginRoot, 'skills');
|
||||
const pluginAgentsDir = path.join(pluginRoot, 'agents');
|
||||
const pluginHooksDir = path.join(pluginRoot, 'hooks');
|
||||
if (fs.existsSync(pluginManifestDir)) fs.rmSync(pluginManifestDir, { recursive: true });
|
||||
if (fs.existsSync(grokPluginManifestDir)) fs.rmSync(grokPluginManifestDir, { recursive: true });
|
||||
if (fs.existsSync(pluginSkillsDir)) fs.rmSync(pluginSkillsDir, { recursive: true });
|
||||
if (fs.existsSync(pluginAgentsDir)) fs.rmSync(pluginAgentsDir, { recursive: true });
|
||||
if (fs.existsSync(pluginHooksDir)) fs.rmSync(pluginHooksDir, { recursive: true });
|
||||
@@ -636,6 +643,26 @@ async function build() {
|
||||
JSON.stringify(pluginManifest, null, 2) + '\n',
|
||||
);
|
||||
|
||||
// Native Grok plugin manifest. Grok also reads `.claude-plugin/`; dual
|
||||
// manifests keep both marketplaces and `grok plugin validate` happy when
|
||||
// Claude compat is disabled.
|
||||
// https://docs.x.ai/build/features/skills-plugins-marketplaces
|
||||
const grokPluginManifest = {
|
||||
name: pluginManifest.name,
|
||||
version: pluginManifest.version,
|
||||
description: pluginManifest.description,
|
||||
author: pluginManifest.author,
|
||||
homepage: pluginManifest.homepage,
|
||||
repository: pluginManifest.repository,
|
||||
license: pluginManifest.license || 'MIT',
|
||||
keywords: ['design', 'frontend', 'ui', 'ux', 'skills', 'hooks'],
|
||||
};
|
||||
fs.mkdirSync(grokPluginManifestDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(grokPluginManifestDir, 'plugin.json'),
|
||||
JSON.stringify(grokPluginManifest, null, 2) + '\n',
|
||||
);
|
||||
|
||||
const claudeSkillsSrc = path.join(DIST_DIR, 'claude-code', '.claude', 'skills', 'impeccable');
|
||||
if (fs.existsSync(claudeSkillsSrc)) {
|
||||
fs.mkdirSync(pluginSkillsDir, { recursive: true });
|
||||
@@ -646,17 +673,18 @@ async function build() {
|
||||
copyDirSync(claudeAgentsSrc, pluginAgentsDir);
|
||||
}
|
||||
|
||||
// Ship the design detector as a plugin-packaged hook. Claude Code
|
||||
// auto-discovers `hooks/hooks.json` at the plugin root, so marketplace /
|
||||
// `/plugin install` users get the PostToolUse hook without it being merged
|
||||
// into their project `.claude/settings.json` (that path is the CLI's job).
|
||||
// Ship the design detector as a plugin-packaged hook. Claude Code and
|
||||
// Grok Build both auto-discover `hooks/hooks.json` at the plugin root
|
||||
// (Grok aliases CLAUDE_PLUGIN_ROOT → GROK_PLUGIN_ROOT), so marketplace /
|
||||
// plugin-install users get PostToolUse + Stop without merging into project
|
||||
// settings (that path remains the CLI's job for project-scoped installs).
|
||||
fs.mkdirSync(pluginHooksDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(pluginHooksDir, 'hooks.json'),
|
||||
JSON.stringify(buildClaudePluginHooksManifest(), null, 2) + '\n',
|
||||
);
|
||||
|
||||
console.log('📦 Built Claude Code plugin subtree at ./plugin/');
|
||||
console.log('📦 Built Claude Code / Grok Build plugin subtree at ./plugin/');
|
||||
} else {
|
||||
console.log('📋 Skipped root harness and plugin sync (--skip-root-sync)');
|
||||
}
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
* - Claude Code: `.claude/settings.json` (${CLAUDE_PROJECT_DIR}-relative)
|
||||
* - Codex: `.codex/hooks.json`
|
||||
* - Cursor: `.cursor/hooks.json`
|
||||
* - Grok Build: `.grok/hooks/impeccable.json`
|
||||
*
|
||||
* 2. Claude Code plugin package (the marketplace / `/plugin install` path):
|
||||
* - `plugin/hooks/hooks.json` (${CLAUDE_PLUGIN_ROOT}-relative)
|
||||
* Also consumed by Grok Build via Claude Code plugin compatibility
|
||||
* (`CLAUDE_PLUGIN_ROOT` is aliased to `GROK_PLUGIN_ROOT`).
|
||||
*
|
||||
* 3. OpenAI plugin package:
|
||||
* - `hooks/hooks.json` (${PLUGIN_ROOT}-relative)
|
||||
@@ -49,6 +52,9 @@ const CODEX_PLUGIN_HOOK = '${PLUGIN_ROOT}/skills/impeccable/scripts/hook.mjs';
|
||||
const CODEX_PROJECT_HOOK = '.agents/skills/impeccable/scripts/hook.mjs';
|
||||
const CURSOR_BEFORE_EDIT_SCRIPT = '.cursor/skills/impeccable/scripts/hook-before-edit.mjs';
|
||||
const GITHUB_PROJECT_HOOK = '$(git rev-parse --show-toplevel)/.github/skills/impeccable/scripts/hook.mjs';
|
||||
// Grok project hooks are relative to the git/workspace root. Claude tool names
|
||||
// in the matcher (Edit|Write|MultiEdit) alias to Grok's search_replace family.
|
||||
const GROK_PROJECT_HOOK = '.grok/skills/impeccable/scripts/hook.mjs';
|
||||
|
||||
export function buildClaudeSettingsManifest() {
|
||||
return {
|
||||
@@ -185,6 +191,32 @@ export function buildGitHubHooksManifest() {
|
||||
};
|
||||
}
|
||||
|
||||
// Grok Build discovers project hooks from `.grok/hooks/*.json` and requires
|
||||
// folder trust (`/hooks-trust` or `--trust`) before they run. Event schema is
|
||||
// Claude-compatible (PostToolUse / Stop / PreToolUse); Claude tool names in
|
||||
// matchers are aliased to Grok tools (Edit|Write|MultiEdit → search_replace).
|
||||
// https://docs.x.ai/build/features/hooks
|
||||
export function buildGrokHooksManifest() {
|
||||
return {
|
||||
hooks: {
|
||||
PostToolUse: [
|
||||
{
|
||||
matcher: 'Edit|Write|MultiEdit',
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: `node "${GROK_PROJECT_HOOK}"`,
|
||||
timeout: TIMEOUT_SECONDS,
|
||||
statusMessage: STATUS_MESSAGE,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
Stop: [stopEntry(`node "${GROK_PROJECT_HOOK}"`)],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function hooksJsonFor(provider) {
|
||||
switch (provider) {
|
||||
case 'claude':
|
||||
@@ -195,6 +227,8 @@ export function hooksJsonFor(provider) {
|
||||
return buildCursorHooksManifest();
|
||||
case 'github':
|
||||
return buildGitHubHooksManifest();
|
||||
case 'grok':
|
||||
return buildGrokHooksManifest();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@ export const transformPi = createTransformer(PROVIDERS.pi);
|
||||
export const transformQoder = createTransformer(PROVIDERS.qoder);
|
||||
export const transformRovoDev = createTransformer(PROVIDERS['rovo-dev']);
|
||||
export const transformVibe = createTransformer(PROVIDERS.vibe);
|
||||
export const transformGrok = createTransformer(PROVIDERS.grok);
|
||||
|
||||
export { createTransformer, PROVIDERS };
|
||||
|
||||
@@ -129,4 +129,21 @@ export const PROVIDERS = {
|
||||
displayName: 'Mistral Vibe',
|
||||
frontmatterFields: ['user-invocable', 'license', 'compatibility', 'metadata', 'allowed-tools'],
|
||||
},
|
||||
grok: {
|
||||
provider: 'grok',
|
||||
providerTags: ['grok'],
|
||||
configDir: '.grok',
|
||||
displayName: 'Grok Build',
|
||||
// Grok's skill frontmatter matches the Agent Skills spec plus Claude-style
|
||||
// extensions (user-invocable, argument-hint, allowed-tools, model, effort).
|
||||
// See https://docs.x.ai/build/features/skills-plugins-marketplaces and
|
||||
// ~/.grok/docs/user-guide/08-skills.md.
|
||||
frontmatterFields: ['user-invocable', 'argument-hint', 'license', 'compatibility', 'metadata', 'allowed-tools'],
|
||||
// Project/user agents are markdown with YAML frontmatter (Claude-compatible).
|
||||
agentFormat: 'claude-md',
|
||||
emitHooks: 'grok',
|
||||
// Grok discovers project hooks from `.grok/hooks/*.json` (not a single
|
||||
// settings.json). Claude tool-name matchers alias to Grok tools.
|
||||
hooksManifestRel: 'hooks/impeccable.json',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -633,6 +633,12 @@ export const PROVIDER_PLACEHOLDERS = {
|
||||
config_file: 'AGENTS.md',
|
||||
ask_instruction: 'ask the user directly to clarify what you cannot infer.',
|
||||
command_prefix: '/'
|
||||
},
|
||||
'grok': {
|
||||
model: 'Grok',
|
||||
config_file: 'AGENTS.md',
|
||||
ask_instruction: 'STOP and call the AskUserQuestion tool to clarify.',
|
||||
command_prefix: '/'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -644,6 +650,7 @@ export const PROVIDER_BLOCK_TAGS = new Set([
|
||||
'cursor',
|
||||
'gemini',
|
||||
'github',
|
||||
'grok',
|
||||
'kiro',
|
||||
'opencode',
|
||||
'pi',
|
||||
|
||||
Reference in New Issue
Block a user