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:
Paul Bakaus
2026-07-21 18:02:58 -07:00
parent 68f42c4e33
commit 06d21dea7d
18 changed files with 205 additions and 43 deletions
@@ -21,6 +21,7 @@ assignees: ''
- [ ] Gemini CLI
- [ ] Codex CLI
- [ ] VS Code Copilot
- [ ] Grok Build
- [ ] Kiro
- [ ] OpenCode
- [ ] Qoder
+1 -1
View File
@@ -26,7 +26,7 @@ This repo is issue-first for outside contributions. If you are not `pbakaus` or
- [ ] Source files updated in `source/`
- [ ] `bun run build` ran successfully
- [ ] `bun test` passes
- [ ] Tested with at least one provider (Cursor / Claude Code / Gemini CLI / Codex / Copilot / Kiro / OpenCode / Qoder / Mistral Vibe)
- [ ] Tested with at least one provider (Cursor / Claude Code / Gemini CLI / Codex / Copilot / Grok Build / Kiro / OpenCode / Qoder / Mistral Vibe)
- [ ] README / DEVELOP.md updated if needed
- [ ] I reviewed the full diff myself before requesting human review
- [ ] I disclosed any AI assistance in this PR and related commits/comments, or no AI assistance was used
@@ -26,6 +26,7 @@ env:
.cursor
.gemini
.github/skills
.grok
.kiro
.opencode
.pi
+1 -1
View File
@@ -21,7 +21,7 @@ Run `bun run build` after changing anything in `skill/`, transformer code, or us
## Generated Provider Output Policy
The root harness folders (`.agents/skills/`, `.claude/skills/`, `.cursor/skills/`, `.gemini/skills/`, `.github/skills/`, `.kiro/skills/`, `.opencode/skills/`, `.pi/skills/`, `.qoder/skills/`, `.rovodev/skills/`, `.trae*/skills/`, `.vibe/skills/`) and `plugin/` stay tracked so `main` remains installable for direct GitHub, `npx skills`, and submodule users. They are still generated artifacts.
The root harness folders (`.agents/skills/`, `.claude/skills/`, `.cursor/skills/`, `.gemini/skills/`, `.github/skills/`, `.grok/skills/`, `.kiro/skills/`, `.opencode/skills/`, `.pi/skills/`, `.qoder/skills/`, `.rovodev/skills/`, `.trae*/skills/`, `.vibe/skills/`) and `plugin/` stay tracked so `main` remains installable for direct GitHub, `npx skills`, and submodule users. They are still generated artifacts.
Normal development should be source-first: stage changes in `skill/`, `scripts/`, `cli/`, `site/`, `extension/`, `functions/`, and `tests/`; leave generated harness churn unstaged unless the user asked for it. After source changes land on `main`, `.github/workflows/sync-generated-output.yml` runs `bun run build:release` and commits generated provider output directly back to `main`. Treat generated harness diffs as release artifacts and keep them out of feature PRs unless they are the point of the PR.
+17 -6
View File
@@ -103,7 +103,7 @@ From the root of your project, run:
npx impeccable install
```
This shows the harness folders it detected (for example `~/.claude`, `~/.codex`, or project-local `.cursor`), lets you keep the detected set or customize providers, then asks whether to install into the current project or globally. Use `--providers=claude,codex,cursor` and `--scope=project|global` to skip those choices in scripts. On Claude Code, Cursor, and Codex, it also installs the provider-native hook manifest for the current project. Works with Cursor, Claude Code, Gemini CLI, Codex CLI, Grok Build, and every other supported tool. Reload your harness afterward.
This shows the harness folders it detected (for example `~/.claude`, `~/.codex`, `~/.grok`, or project-local `.cursor`), lets you keep the detected set or customize providers, then asks whether to install into the current project or globally. Use `--providers=claude,codex,cursor,grok` and `--scope=project|global` to skip those choices in scripts. On Claude Code, Cursor, Codex, GitHub Copilot, and Grok Build, it also installs the provider-native hook manifest for the current project. Works with Cursor, Claude Code, Gemini CLI, Codex CLI, Grok Build, and every other supported tool. Reload your harness afterward.
To refresh an existing install, run:
@@ -111,7 +111,7 @@ To refresh an existing install, run:
npx impeccable update
```
Codex users should open `/hooks` after install or update and approve the project hook when prompted. Codex tracks trust by hook definition, so updates that change `.codex/hooks.json` can require approval again.
Codex users should open `/hooks` after install or update and approve the project hook when prompted. Codex tracks trust by hook definition, so updates that change `.codex/hooks.json` can require approval again. Grok Build users need project folder trust (`/hooks-trust` or launch with `--trust`) before `.grok/hooks/` scripts run.
### Option 2: Git Submodule
@@ -124,7 +124,7 @@ git add .gitmodules .impeccable .claude .cursor
git commit -m "Add Impeccable skills"
```
Use the providers your project needs, for example `claude`, `cursor`, `gemini`, `codex`, `github`, `opencode`, `pi`, `qoder`, `trae`, `trae-cn`, `rovo-dev`, or `vibe`. The command links individual skill folders from `.impeccable/dist/universal/` and leaves existing real skill directories untouched unless you pass `--force`.
Use the providers your project needs, for example `claude`, `cursor`, `gemini`, `codex`, `github`, `grok`, `opencode`, `pi`, `qoder`, `trae`, `trae-cn`, `rovo-dev`, or `vibe`. The command links individual skill folders from `.impeccable/dist/universal/` and leaves existing real skill directories untouched unless you pass `--force`.
To update later:
@@ -144,10 +144,10 @@ npx impeccable link --source=.impeccable --providers=claude,cursor
**Grok Build:**
```bash
grok plugin install pbakaus/impeccable --trust
grok plugin install pbakaus/impeccable#plugin --trust
```
> Grok Build only. Then run `/impeccable init` in a Grok session.
> Grok Build only. The `#plugin` suffix installs the slim plugin package (skills, agents, and hooks) instead of the full monorepo. Then run `/impeccable init` in a Grok session. Project-scoped installs via `npx impeccable install --providers=grok` also work and write `.grok/skills/` plus `.grok/hooks/impeccable.json`.
### Option 4: Download from Website
@@ -259,6 +259,17 @@ cp -r dist/vibe/.vibe your-project/
cp -r dist/vibe/.vibe/skills/* ~/.vibe/skills/
```
**Grok Build:**
```bash
# Project-specific
cp -r dist/grok/.grok your-project/
# Or global (applies to all projects)
cp -r dist/grok/.grok/skills/* ~/.grok/skills/
```
> Prefer `npx impeccable install --providers=grok` or `grok plugin install pbakaus/impeccable#plugin --trust` so the design hook installs too. Project hooks need `/hooks-trust` (or `--trust`) once per folder.
## Usage
Once installed, every command runs through the single `/impeccable` skill:
@@ -325,7 +336,7 @@ If an ephemeral file (a screenshot, `config.local.json`) was committed before yo
## Design hook
On Claude Code, GitHub Copilot, Codex, and Cursor, `npx impeccable install` and `npx impeccable update` install a provider-native hook manifest along with the skill payload. The hook runs the Impeccable design detector on direct UI file edits and surfaces findings back into the agent flow. Claude Code, GitHub Copilot, and Codex surface findings after the edit. Cursor blocks bad proposed writes before they land.
On Claude Code, GitHub Copilot, Codex, Cursor, and Grok Build, `npx impeccable install` and `npx impeccable update` install a provider-native hook manifest along with the skill payload. The hook runs the Impeccable design detector on direct UI file edits and surfaces findings back into the agent flow. Claude Code, GitHub Copilot, Codex, and Grok Build surface findings after the edit (and run a deeper pass on Stop where supported). Cursor blocks bad proposed writes before they land.
Installed hook surfaces:
+14 -3
View File
@@ -23,7 +23,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
const API_BASE = 'https://impeccable.style';
// Provider folder names in project roots
const PROVIDER_DIRS = ['.claude', '.cursor', '.gemini', '.agents', '.github', '.kiro', '.opencode', '.pi', '.qoder', '.trae', '.trae-cn', '.rovodev', '.vibe'];
const PROVIDER_DIRS = ['.claude', '.cursor', '.gemini', '.agents', '.github', '.grok', '.kiro', '.opencode', '.pi', '.qoder', '.trae', '.trae-cn', '.rovodev', '.vibe'];
const PROVIDER_ALIASES = {
agents: '.agents',
claude: '.claude',
@@ -33,6 +33,9 @@ const PROVIDER_ALIASES = {
cursor: '.cursor',
gemini: '.gemini',
github: '.github',
grok: '.grok',
'grok-build': '.grok',
xai: '.grok',
kiro: '.kiro',
opencode: '.opencode',
pi: '.pi',
@@ -50,6 +53,7 @@ const PROVIDER_DISPLAY = {
'.cursor': { name: 'Cursor', input: 'cursor' },
'.gemini': { name: 'Gemini CLI', input: 'gemini' },
'.github': { name: 'GitHub Copilot', input: 'github' },
'.grok': { name: 'Grok Build', input: 'grok' },
'.kiro': { name: 'Kiro', input: 'kiro' },
'.opencode': { name: 'OpenCode', input: 'opencode' },
'.pi': { name: 'Project Indigo', input: 'pi' },
@@ -59,7 +63,7 @@ const PROVIDER_DISPLAY = {
'.trae-cn': { name: 'Trae CN', input: 'trae-cn' },
'.vibe': { name: 'Mistral Vibe', input: 'vibe' },
};
const PROVIDER_INPUT_ORDER = ['claude', 'codex', 'cursor', 'gemini', 'github', 'kiro', 'opencode', 'pi', 'qoder', 'trae', 'trae-cn', 'rovo-dev', 'vibe'];
const PROVIDER_INPUT_ORDER = ['claude', 'codex', 'cursor', 'gemini', 'github', 'grok', 'kiro', 'opencode', 'pi', 'qoder', 'trae', 'trae-cn', 'rovo-dev', 'vibe'];
// Providers whose GLOBAL (home) skills dir is not `<provider>/skills`.
// Pi discovers global skills from ~/.pi/agent/skills/; project scope
@@ -76,6 +80,7 @@ const GLOBAL_HARNESS_HINTS = [
{ home: '.codex', provider: '.agents' },
{ home: '.cursor', provider: '.cursor' },
{ home: '.gemini', provider: '.gemini' },
{ home: '.grok', provider: '.grok' },
{ home: '.kiro', provider: '.kiro' },
{ home: '.opencode', provider: '.opencode' },
{ home: '.pi', provider: '.pi' },
@@ -120,6 +125,12 @@ const PROVIDER_HOOK_ARTIFACTS = {
'.github': [
{ sourceProvider: '.github', rel: 'hooks/impeccable.json', destProvider: '.github' },
],
// Grok Build discovers project hooks from `.grok/hooks/*.json`. Team-shared
// by default (commit them if the whole team uses Grok); folder trust is still
// required via `/hooks-trust` or `--trust` before they run.
'.grok': [
{ sourceProvider: '.grok', rel: 'hooks/impeccable.json', destProvider: '.grok' },
],
};
function userProviderSkillsDir(home, provider) {
@@ -571,7 +582,7 @@ async function copyOrExtractLocalBundle(sourceValue) {
*/
function normalizeForHash(content) {
return content
.replace(/\.(claude|cursor|agents|github|gemini|codex|kiro|opencode|pi|qoder|trae|trae-cn|rovodev|vibe)\/skills\//g, '.PROVIDER/skills/');
.replace(/\.(claude|cursor|agents|github|gemini|codex|grok|kiro|opencode|pi|qoder|trae|trae-cn|rovodev|vibe)\/skills\//g, '.PROVIDER/skills/');
}
function hashSkillFile(filePath) {
+1
View File
@@ -5,6 +5,7 @@ export const FILE_DOWNLOAD_PROVIDER_CONFIG_DIRS = Object.freeze({
codex: '.codex',
agents: '.agents',
github: '.github',
grok: '.grok',
kiro: '.kiro',
opencode: '.opencode',
pi: '.pi',
+2
View File
@@ -170,6 +170,8 @@ The skill-behavior suite runs three providers (claude-haiku-4-5, gpt-5.4-mini, g
- [Pi Skills](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/skills.md)
- [Qoder Skills](https://docs.qoder.com/extensions/skills)
- [Mistral Vibe Skills](https://docs.mistral.ai/vibe/code/cli/skills)
- [Grok Build Skills, Plugins & Marketplaces](https://docs.x.ai/build/features/skills-plugins-marketplaces)
- [Grok Build Hooks](https://docs.x.ai/build/features/hooks)
## Repository Structure
+24 -18
View File
@@ -3,7 +3,7 @@
Source of truth for what each AI coding harness supports in terms of agent skills.
Used to inform provider configs in `scripts/lib/transformers/providers.js`.
Last verified: 2026-04-28 (subagent landscape spot-checked 2026-06-28; Mistral Vibe row verified 2026-07-16)
Last verified: 2026-04-28 (subagent landscape spot-checked 2026-06-28; Mistral Vibe row verified 2026-07-16; Grok Build row verified 2026-07-21)
> This file is point-in-time. Capabilities move fast; verify live before relying
> on any "only X supports Y" claim. Notably, the subagent table below lists
@@ -25,6 +25,7 @@ Last verified: 2026-04-28 (subagent landscape spot-checked 2026-06-28; Mistral V
| Trae | TBD (no official skills docs found yet) |
| Rovo Dev | https://support.atlassian.com/rovo/docs/extend-rovo-dev-cli-with-agent-skills |
| Mistral Vibe | https://docs.mistral.ai/vibe/code/cli/skills |
| Grok Build | https://docs.x.ai/build/features/skills-plugins-marketplaces |
## Spec Compliance
@@ -36,27 +37,28 @@ Provider-specific extensions beyond the spec: `user-invocable`, `argument-hint`,
Fields marked with * are spec-standard. Others are provider extensions.
| Field | Claude Code | Cursor | Gemini | Codex | Copilot | Kiro | OpenCode | Pi | Qoder | Rovo Dev | Mistral Vibe |
|-------|:-----------:|:------:|:------:|:-----:|:-------:|:----:|:--------:|:--:|:-----:|:--------:|:------------:|
| `name`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `description`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `license`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `compatibility`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `metadata`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `allowed-tools`* | Yes | No | Ignored | No | No | No | Yes | Yes | Yes | Yes | Yes |
| `user-invocable` | Yes | No | No | No | Yes | No | Yes | No | Yes | Yes | Yes |
| `argument-hint` | Yes | No | No | No | Yes | No | Yes | No | Yes | Yes | No |
| `disable-model-invocation` | Yes | Yes | No | No | Yes | No | Yes | Yes | TBD | TBD | No |
| `model` | Yes | No | No | No | No | No | Yes | No | No | No | No |
| `effort` | Yes | No | No | No | No | No | No | No | No | No | No |
| `context` | Yes | No | No | No | No | No | No | No | No | No | No |
| `agent` | Yes | No | No | No | No | No | Yes | No | No | No | No |
| `hooks` | Yes | No | No | Yes | No | No | No | No | No | No | No |
| Field | Claude Code | Cursor | Gemini | Codex | Copilot | Grok | Kiro | OpenCode | Pi | Qoder | Rovo Dev | Mistral Vibe |
|-------|:-----------:|:------:|:------:|:-----:|:-------:|:----:|:----:|:--------:|:--:|:-----:|:--------:|:------------:|
| `name`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `description`* | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `license`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `compatibility`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `metadata`* | Yes | Yes | Ignored | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| `allowed-tools`* | Yes | No | Ignored | No | No | Yes | No | Yes | Yes | Yes | Yes | Yes |
| `user-invocable` | Yes | No | No | No | Yes | Yes | No | Yes | No | Yes | Yes | Yes |
| `argument-hint` | Yes | No | No | No | Yes | Yes | No | Yes | No | Yes | Yes | No |
| `disable-model-invocation` | Yes | Yes | No | No | Yes | Yes | No | Yes | Yes | TBD | TBD | No |
| `model` | Yes | No | No | No | No | Yes | No | Yes | No | No | No | No |
| `effort` | Yes | No | No | No | No | Yes | No | No | No | No | No | No |
| `context` | Yes | No | No | No | No | No | No | No | No | No | No | No |
| `agent` | Yes | No | No | No | No | No | No | Yes | No | No | No | No |
| `hooks` | Yes | No | No | Yes | No | Yes | No | No | No | No | No | No |
Notes:
- Gemini CLI validates only `name` and `description`; other spec fields are parsed but ignored.
- Codex CLI uses a separate `agents/openai.yaml` sidecar for skill metadata (icons, branding, MCP tools, invocation control). Codex also auto-discovers subagents bundled inside an installed skill's `agents/` folder (TOML), which is how Impeccable ships its asset-producer. Standalone custom agents can still live under `.codex/agents/` or `~/.codex/agents/`, but Impeccable no longer installs anything there.
- Codex CLI hooks ship under `[features].hooks = true` (still flagged), require `/hooks` trust ceremony per-update, and are disabled on Windows.
- Grok Build is Claude Code compatible with zero config: it also reads `.claude/skills/`, `.claude/settings.json` hooks, and Claude plugin layouts. Native paths are `.grok/skills/`, `.grok/hooks/*.json`, and `.grok/agents/`. Skill frontmatter supports `when-to-use` in addition to the fields above. Project hooks require `/hooks-trust` (or `--trust`). See https://docs.x.ai/build/features/skills-plugins-marketplaces and https://docs.x.ai/build/features/hooks.
- Kiro recognizes `user-invocable` and `disable-model-invocation` per community reports but does not formally document them.
- Unknown fields are silently ignored by all harnesses.
@@ -67,6 +69,7 @@ Notes:
| Claude Code | Yes (`PostToolUse`) | No | `.claude/settings.json` | Project-local settings entry installed by `npx impeccable skills install/update`. Runs `.claude/skills/impeccable/scripts/hook.mjs`. |
| Codex CLI | Yes (`PostToolUse`) | No | `.codex/hooks.json` | Project-local manifest installed with the `.agents/skills/impeccable` payload. Runs `.agents/skills/impeccable/scripts/hook.mjs` from the git root. Requires normal `/hooks` trust approval. |
| Cursor | Yes (`preToolUse`) | No | `.cursor/hooks.json` | Project-level manifest installed with `.cursor/skills/impeccable`. Runs `hook-before-edit.mjs` to block bad proposed writes before they land. Reloads on save; restart Cursor if hooks do not pick up. |
| Grok Build | Yes (`PostToolUse`) | No | `.grok/hooks/impeccable.json` | Project-local manifest installed with `.grok/skills/impeccable`. Claude-compatible matchers (`Edit\|Write\|MultiEdit`) alias to Grok tools. Also runs a Stop deep pass. Requires `/hooks-trust` or `--trust`. Plugin installs use `plugin/hooks/hooks.json` with `${CLAUDE_PLUGIN_ROOT}` (aliased to `GROK_PLUGIN_ROOT`). |
| All other harnesses | No | No | n/a | No documented hook surface today. Skill and commands still ship. |
## Skill Directory Structure
@@ -86,6 +89,7 @@ Notes:
| Trae International | `.trae/skills/` | TBD |
| Rovo Dev | `.rovodev/skills/` | `~/.rovodev/skills/` (user-level) |
| Mistral Vibe | `.vibe/skills/` (project), `~/.vibe/skills/` (global) | `.agents/skills/` (project), `~/.agents/skills/` (global) |
| Grok Build | `.grok/skills/` (project), `~/.grok/skills/` (global) | `.agents/skills/`, `.claude/skills/`, `.cursor/skills/` (Claude/Cursor compat, configurable) |
All harnesses support the `{skill-name}/SKILL.md` directory structure with optional `reference/`, `scripts/`, and `assets/` subdirectories.
@@ -100,15 +104,17 @@ All harnesses support the `{skill-name}/SKILL.md` directory structure with optio
| Harness | Native directory | File format |
|---------|------------------|-------------|
| Claude Code | `.claude/agents/` (installed plugin) | Markdown with YAML frontmatter |
| Grok Build | `.grok/agents/` (project) and plugin `agents/` | Markdown with YAML frontmatter (Claude-compatible) |
| Codex CLI | `<skill>/agents/` (nested, auto-discovered) | TOML |
Impeccable keeps canonical agent prompts under `skill/agents/` and emits provider-native files only for harnesses with a documented on-disk subagent format. Claude reads its agents from the installed plugin; Codex auto-discovers the TOML bundled inside the installed skill's own `agents/` folder, so the normal skills install carries it with no separate sidecar.
Impeccable keeps canonical agent prompts under `skill/agents/` and emits provider-native files only for harnesses with a documented on-disk subagent format. Claude reads its agents from the installed plugin; Grok reads the same markdown agents from the plugin package and from project `.grok/agents/`; Codex auto-discovers the TOML bundled inside the installed skill's own `agents/` folder, so the normal skills install carries it with no separate sidecar.
**Spawn / permission model** (matters more than directory support when building skills):
| Harness | Who can spawn a subagent |
|---------|--------------------------|
| Claude Code | Programmatically, from within the skill/agent flow. |
| Grok Build | Programmatically via `spawn_subagent` (built-in types plus project/user agents under `.grok/agents/`). |
| Codex CLI | Only if the user has allowed sub-agents / parallel work; otherwise the skill must ask once, then stop (see `skill/reference/critique.md` `<codex>` gate). |
| Cursor | Agent-chosen: auto-delegated by the Agent, or user-invoked via `/name`. Not reliably skill-spawnable. |
| Others | Varies; treat as unavailable unless verified, and degrade loudly. |
+39 -11
View File
@@ -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)');
}
+34
View File
@@ -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;
}
+1
View File
@@ -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 };
+17
View File
@@ -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',
},
};
+7
View File
@@ -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',
+2 -1
View File
@@ -1196,6 +1196,7 @@ const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({
agents: ['.codex/hooks.json'],
cursor: ['.cursor/hooks.json'],
github: ['.github/hooks/impeccable.json'],
grok: ['.grok/hooks/impeccable.json'],
});
function truthyEnv(value) {
@@ -1224,7 +1225,7 @@ function hookEnabledAt(root) {
return enabled;
}
const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents']);
const STOP_REVIEW_PROVIDERS = new Set(['claude-code', 'codex', 'agents', 'grok']);
function automaticHookMode(ctx) {
if (ctx.platform === 'ios' || ctx.platform === 'android' || ctx.platform === 'adaptive') {
+2 -2
View File
@@ -21,8 +21,8 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
// All known harness directories
const HARNESS_DIRS = [
'.claude', '.cursor', '.gemini', '.codex', '.agents',
'.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev',
'.claude', '.cursor', '.gemini', '.codex', '.agents', '.github', '.grok',
'.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev', '.vibe', '.qoder',
];
const CODEX_HARNESSES = new Set(['.codex', '.agents']);
+23
View File
@@ -15,6 +15,7 @@ import {
buildCodexHooksManifest,
buildCursorHooksManifest,
buildGitHubHooksManifest,
buildGrokHooksManifest,
hooksJsonFor,
} from '../scripts/lib/transformers/hooks.js';
@@ -109,11 +110,33 @@ describe('hook manifest builders', () => {
assert.equal(manifest.hooks.preToolUse, undefined);
});
it('builds Grok Build project hooks for the real detector hook', () => {
const manifest = buildGrokHooksManifest();
const group = manifest.hooks.PostToolUse[0];
const handler = group.hooks[0];
// Claude-compatible schema; Claude tool names alias to Grok tools at runtime.
assert.equal(group.matcher, 'Edit|Write|MultiEdit');
assert.equal(handler.type, 'command');
assert.equal(handler.timeout, 5);
assert.equal(handler.statusMessage, 'Checking UI changes');
expectCommand(handler.command, '.grok/skills/impeccable/scripts/hook.mjs');
assert.ok(!handler.command.includes('${CLAUDE_PROJECT_DIR}'));
assert.ok(!handler.command.includes('${GROK_PLUGIN_ROOT}'));
assert.equal(manifest.hooks.SessionStart, undefined);
const stop = manifest.hooks.Stop[0].hooks[0];
assert.equal(stop.timeout, 30);
assert.equal(stop.statusMessage, 'Design deep pass');
expectCommand(stop.command, '.grok/skills/impeccable/scripts/hook.mjs');
});
it('routes supported hook builders and leaves other providers alone', () => {
assert.ok(hooksJsonFor('claude'));
assert.ok(hooksJsonFor('codex'));
assert.ok(hooksJsonFor('cursor'));
assert.ok(hooksJsonFor('github'));
assert.ok(hooksJsonFor('grok'));
assert.equal(hooksJsonFor('gemini'), null);
});
});
+18
View File
@@ -434,6 +434,24 @@ describe('skills link: submodule installs', () => {
rmSync(tmp, { recursive: true, force: true });
}, 15000);
test('maps grok and grok-build provider aliases to .grok', () => {
const tmp = mkdtempSync(join(tmpdir(), 'imp-test-link-grok-'));
execSync('git init', { cwd: tmp });
createFakeLinkSource(tmp, ['.grok']);
run('skills link --source=.impeccable --providers=grok -y', { cwd: tmp });
expect(lstatSync(join(tmp, '.grok', 'skills', 'impeccable')).isSymbolicLink()).toBe(true);
rmSync(tmp, { recursive: true, force: true });
const tmp2 = mkdtempSync(join(tmpdir(), 'imp-test-link-grok-build-'));
execSync('git init', { cwd: tmp2 });
createFakeLinkSource(tmp2, ['.grok']);
run('skills link --source=.impeccable --providers=grok-build -y', { cwd: tmp2 });
expect(lstatSync(join(tmp2, '.grok', 'skills', 'impeccable')).isSymbolicLink()).toBe(true);
rmSync(tmp2, { recursive: true, force: true });
}, 15000);
test('skills update leaves linked installs on the submodule path', () => {
const tmp = mkdtempSync(join(tmpdir(), 'imp-test-link-update-'));
execSync('git init', { cwd: tmp });