Sync generated provider output

This commit is contained in:
github-actions[bot]
2026-09-01 23:06:17 +00:00
parent 9736a9f6e9
commit 4981192613
17 changed files with 1910 additions and 48 deletions
+119 -3
View File
@@ -14,8 +14,9 @@
*/
import { existsSync, readFileSync, writeFileSync, mkdirSync, rmSync, readdirSync } from 'node:fs';
import { basename, join, resolve, dirname } from 'node:path';
import { basename, join, resolve, dirname, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
import { homedir } from 'node:os';
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -115,21 +116,119 @@ Invoke ${commandPrefix}impeccable ${command}, passing along any arguments provid
`;
}
// OpenCode 1.18.10 does not honor `user-invocable: true` on SKILL.md frontmatter
// (see docs/HARNESSES.md and opencode/packages/core/src/v1/config/command.ts),
// so a pinned skill there shows up in `opencode debug skill` but never in the
// slash menu. The fix is a sibling `commands/impeccable-<cmd>.md` that uses the
// OpenCode command schema (description, agent, subtask). Body loads the skill
// via the skill tool and then the sub-command's reference file directly, so
// /impeccable-<cmd> runs the same workflow /impeccable <cmd> routes to.
const OPENCODE_PIN_MARKER = '<!-- impeccable-pinned-command -->';
function generatePinnedOpencodeCommand(command, metadata) {
const desc = metadata[command]?.description || `Impeccable sub-command shortcut; runs the ${command} workflow via /impeccable.`;
return `---
description: "${desc}"
agent: build
subtask: true
---
${OPENCODE_PIN_MARKER}
Load the \`impeccable\` skill via the skill tool (name: "impeccable"), then run \`node <skill-base-dir>/scripts/context.mjs\`, then load \`<skill-base-dir>/reference/${command}.md\` and follow it. \`<skill-base-dir>\` is the skill's base directory as reported by the skill tool response; substitute the actual absolute path before running or reading anything.
$ARGUMENTS
`;
}
// OpenCode's user-scope config dir. Mirrors the CLI's opencodeGlobalConfigDir
// precedence (OPENCODE_CONFIG_DIR → XDG_CONFIG_HOME/opencode →
// ~/.config/opencode); duplicated here because this script ships inside the
// installed skill and cannot import the CLI.
function opencodeUserConfigDir() {
if (process.env.OPENCODE_CONFIG_DIR) return process.env.OPENCODE_CONFIG_DIR;
if (process.env.XDG_CONFIG_HOME) return join(process.env.XDG_CONFIG_HOME, 'opencode');
return join(homedir(), '.config', 'opencode');
}
/**
* Resolve every commands dir that should receive an OpenCode pin: the
* project-local dir when the project has the skill, plus the user config dir
* when Impeccable is installed globally (#406 layout). A user-scope skill is
* visible from every project, so its pinned commands belong next to it.
* With `forCleanup`, both commands dirs are included even when the skill is
* gone, so unpin can still reach a pin left behind by a removed install;
* removal stays safe because removePinnedOpencodeCommand is marker-guarded.
*/
function findOpencodeCommandsDirs(projectRoot, { forCleanup = false } = {}) {
const dirs = [];
const seen = new Set();
const push = (commandsDir) => {
const key = resolve(commandsDir);
if (!seen.has(key)) {
seen.add(key);
dirs.push(commandsDir);
}
};
if (forCleanup || existsSync(join(projectRoot, '.opencode', 'skills', 'impeccable'))) {
push(join(projectRoot, '.opencode', 'commands'));
}
const userConfig = opencodeUserConfigDir();
if (forCleanup || existsSync(join(userConfig, 'skills', 'impeccable'))) {
push(join(userConfig, 'commands'));
}
return dirs;
}
function writePinnedOpencodeCommand(commandsDir, command, metadata) {
const commandFile = join(commandsDir, `impeccable-${command}.md`);
if (existsSync(commandFile)) {
const existing = readFileSync(commandFile, 'utf-8');
if (!existing.includes(OPENCODE_PIN_MARKER)) {
console.log(` SKIP: ${commandFile} (non-pinned command already exists)`);
return false;
}
} else {
mkdirSync(commandsDir, { recursive: true });
}
writeFileSync(commandFile, generatePinnedOpencodeCommand(command, metadata));
console.log(` + ${commandFile}`);
return true;
}
function removePinnedOpencodeCommand(commandsDir, command) {
const commandFile = join(commandsDir, `impeccable-${command}.md`);
if (!existsSync(commandFile)) return false;
const content = readFileSync(commandFile, 'utf-8');
if (!content.includes(OPENCODE_PIN_MARKER)) {
console.log(` SKIP: ${commandFile} (not a pinned command)`);
return false;
}
rmSync(commandFile, { force: true });
console.log(` - ${commandFile}`);
return true;
}
/**
* Pin a command: create shortcut skill in all harness dirs.
*/
function pin(command, projectRoot) {
const metadata = loadCommandMetadata();
const harnessDirs = findHarnessDirs(projectRoot);
const opencodeCommandsDirs = findOpencodeCommandsDirs(projectRoot);
if (harnessDirs.length === 0) {
if (harnessDirs.length === 0 && opencodeCommandsDirs.length === 0) {
console.log('No harness directories with impeccable installed found.');
return false;
}
let created = 0;
// OpenCode is handled separately below because its shortcut format is a
// slash command, not a SKILL.md. Excluding it from the skill loop here
// prevents a duplicate `.opencode/skills/<cmd>/SKILL.md` that OpenCode
// would never surface as `/<cmd>`.
for (const skillsDir of harnessDirs) {
if (skillsDir.includes(`${sep}.opencode${sep}`)) continue;
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
// Check if skill already exists (and isn't a pin)
@@ -151,6 +250,12 @@ function pin(command, projectRoot) {
created++;
}
// OpenCode: write a slash command bridge, not a skill shortcut. Covers both
// project installs and user-scope (global config) installs.
for (const commandsDir of opencodeCommandsDirs) {
if (writePinnedOpencodeCommand(commandsDir, command, metadata)) created++;
}
if (created > 0) {
console.log(`\nPinned '${command}' as a standalone shortcut in ${created} location(s).`);
console.log('Use the pinned command directly in each harness.');
@@ -160,13 +265,17 @@ function pin(command, projectRoot) {
}
/**
* Unpin a command: remove shortcut skill from all harness dirs.
* Unpin a command: remove shortcut skill in all harness dirs.
*/
function unpin(command, projectRoot) {
const harnessDirs = findHarnessDirs(projectRoot);
let removed = 0;
// OpenCode has its own cleanup path below; skip the skill loop here so a
// stray `.opencode/skills/<cmd>/SKILL.md` written by an older Impeccable
// version is never silently dropped here.
for (const skillsDir of harnessDirs) {
if (skillsDir.includes(`${sep}.opencode${sep}`)) continue;
const skillDir = join(skillsDir, command);
if (!existsSync(skillDir)) continue;
@@ -185,6 +294,13 @@ function unpin(command, projectRoot) {
removed++;
}
// OpenCode: remove the pinned command file if it's one of ours, in every
// scope it could have been written to — even when the skill itself is
// already gone, since removal is marker-guarded.
for (const commandsDir of findOpencodeCommandsDirs(projectRoot, { forCleanup: true })) {
if (removePinnedOpencodeCommand(commandsDir, command)) removed++;
}
if (removed > 0) {
console.log(`\nUnpinned '${command}' from ${removed} location(s).`);
console.log(`Use Impeccable's '${command}' workflow directly to access it.`);