Files
pbakaus_impeccable/crates/context/src/pin.rs
T
6ebc24ad66 Add DeepSeek Harness as a supported skills provider (#746)
* Add DeepSeek Harness as a supported skills provider

npx impeccable install now detects ~/.dsh (or $DSH_HOME when it sits
under home) and installs into ~/.dsh/skills, the user-level skill root
DeepSeek Harness scans, with project-level .dsh/skills on the same
layout as other providers. Aliases: dsh, deepseek, deepseek-harness.

Engine: PROVIDER_DIRS / aliases / display / input order / global hint,
$DSH_HOME-aware user skills dir, provider id resolution from the skill
dir, pin harness dirs, bundle path normalization for hashing.

Build: dsh transformer target emitting the frontmatter DeepSeek Harness
reads (user-invocable, license, compatibility, metadata; unknown keys
are ignored there) with no emitHooks (DSH hooks are in-process plugins,
not on-disk manifests) and no agentFormat (no documented on-disk
subagent format); placeholders (AGENTS.md config file, ask_user_question
tool, / command prefix), provider block tags, universal README entry.

Docs: HARNESSES.md row and frontmatter column, CLI-CONTRACT constants,
README/DEVELOP/AGENTS provider lists.

Validation: cargo test --workspace; node scripts/run-tests.mjs core
(138 pass); bun run build (19 providers, dist/dsh artifact verified);
engine smoke against a fake HOME with a local bundle: install
--providers=dsh --scope=global, auto-detected install, and update all
resolve the .dsh provider.

Generated provider output intentionally omitted per repo policy; the
sync workflow regenerates tracked .dsh/skills after merge.

Prepared with AI assistance (DeepSeek Harness coding agent).

* Address review: DSH_HOME-only detection, generated-output pathspecs

- Detect DeepSeek Harness through the resolved $DSH_HOME (fallback
  ~/.dsh) instead of gating on a fixed ~/.dsh path, so a DSH_HOME-only
  setup is offered by a provider-less install; generalize the two
  env-relocated config-dir hints (OpenCode, DSH) into one shared probe.
- Add .dsh to the sync workflow's GENERATED_PATHS and CI's generated
  drift check so the tracked .dsh/skills payload is committed and
  validated.
- Cover both behaviors: new install_detection_tests (DSH_HOME-only,
  default ~/.dsh, refused outside-home override) and a CLI-CONTRACT
  note on the resolved detection path.

Validation: cargo test --workspace; node scripts/run-tests.mjs core
(138 pass); engine smoke: DSH_HOME-only fake HOME installs globally
into the resolved skills dir.

Prepared with AI assistance (DeepSeek Harness coding agent).

* Fix DeepSeek Harness home paths on Windows

Use native relative-path containment, cover case and drive boundaries, and verify relocated global install/update without changing project skills. Add DSH output coverage and correct the install documentation.

AI assistance: Codex, under pbakaus maintainer direction.

* Document the CLI limit on external DSH homes

Clarify that outside-home manual copies are not detected or updated by the CLI.

AI assistance: Codex, under pbakaus maintainer direction.

---------

Co-authored-by: Paul Bakaus <paul.bakaus@gmail.com>
2026-09-06 14:46:05 -07:00

323 lines
14 KiB
Rust

//! JS: pin.mjs -> `impeccable pin <pin|unpin> <command>`
use crate::jsp;
use crate::util::{exists, read_json, safe_read};
use impeccable_common::Io;
/// Bundled copy of `skill/scripts/command-metadata.json` (build-time copy;
/// keep in sync with the public repo).
pub const COMMAND_METADATA_JSON: &str = include_str!("command-metadata.json");
const HARNESS_DIRS: [&str; 18] = [
".claude", ".cursor", ".dsh", ".gemini", ".codex", ".agents", ".agent", ".github", ".grok", ".hermes", ".trae", ".trae-cn",
".pi", ".opencode", ".kiro", ".rovodev", ".vibe", ".qoder",
];
const CODEX_HARNESSES: [&str; 2] = [".codex", ".agents"];
pub const VALID_COMMANDS: [&str; 23] = [
"craft", "init", "extract", "document", "shape", "critique", "audit", "polish", "bolder", "quieter", "distill",
"harden", "onboard", "live", "animate", "colorize", "typeset", "layout", "delight", "overdrive", "clarify",
"adapt", "optimize",
];
const PIN_MARKER: &str = "<!-- impeccable-pinned-skill -->";
const OPENCODE_PIN_MARKER: &str = "<!-- impeccable-pinned-command -->";
fn find_project_root(start: &str) -> String {
let mut dir = jsp::resolve(start, &[]);
while dir != "/" {
if exists(&jsp::join(&[&dir, "package.json"]))
|| exists(&jsp::join(&[&dir, ".git"]))
|| exists(&jsp::join(&[&dir, "skills-lock.json"]))
{
return dir;
}
let parent = jsp::resolve(&dir, &[".."]);
if parent == dir {
break;
}
dir = parent;
}
jsp::resolve(start, &[])
}
fn find_harness_dirs(project_root: &str) -> Vec<String> {
let mut dirs = Vec::new();
for h in HARNESS_DIRS {
let skills = jsp::join(&[project_root, h, "skills"]);
if exists(&jsp::join(&[&skills, "impeccable"])) || exists(&jsp::join(&[&skills, "i-impeccable"])) {
dirs.push(skills);
}
}
dirs
}
fn command_prefix_for(skills_dir: &str) -> &'static str {
let harness = jsp::basename(&jsp::dirname(skills_dir));
if CODEX_HARNESSES.contains(&harness.as_str()) {
"$"
} else {
"/"
}
}
fn generate_pinned_skill(command: &str, metadata: &serde_json::Value, prefix: &str, is_codex: bool) -> String {
let entry = metadata.get(command);
let desc = entry
.and_then(|e| e.get("description"))
.and_then(|d| d.as_str())
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.unwrap_or_else(|| format!("Shortcut for {}impeccable {}.", prefix, command));
let hint = entry
.and_then(|e| e.get("argumentHint"))
.and_then(|d| d.as_str())
.filter(|s| !s.is_empty())
.unwrap_or("[target]");
let provider_fm = if is_codex {
format!("metadata:\n argument-hint: \"{}\"", hint)
} else {
format!("argument-hint: \"{}\"\nuser-invocable: true", hint)
};
format!(
"---\nname: {command}\ndescription: \"{desc}\"\n{provider_fm}\n---\n\n{marker}\n\nThis is a pinned shortcut for `{prefix}impeccable {command}`.\n\nInvoke {prefix}impeccable {command}, passing along any arguments provided here, and follow its instructions.\n",
command = command,
desc = desc,
provider_fm = provider_fm,
marker = PIN_MARKER,
prefix = prefix
)
}
// OpenCode 1.18.10 does not honor `user-invocable: true` on SKILL.md
// frontmatter (see docs/HARNESSES.md), 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` on the OpenCode command schema. The body
// loads the skill, runs the context verb, then reads the sub-command's
// reference file, so `/impeccable-<cmd>` runs the same workflow
// `/impeccable <cmd>` routes to.
//
// JS: pin.mjs#generatePinnedOpencodeCommand. The JS body says
// `node <skill-base-dir>/scripts/context.mjs`; the engine names its own
// command, as everywhere else the launcher replaced a script path.
fn generate_pinned_opencode_command(command: &str, metadata: &serde_json::Value) -> String {
let desc = metadata
.get(command)
.and_then(|e| e.get("description"))
.and_then(|d| d.as_str())
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.unwrap_or_else(|| {
format!("Impeccable sub-command shortcut; runs the {command} workflow via /impeccable.")
});
format!(
"---\ndescription: \"{desc}\"\nagent: build\nsubtask: true\n---\n\n{marker}\n\nLoad the `impeccable` skill via the skill tool (name: \"impeccable\"), then run `<skill-base-dir>/scripts/impeccable context`, 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.\n\n$ARGUMENTS\n",
desc = desc,
marker = OPENCODE_PIN_MARKER,
command = command
)
}
/// JS: pin.mjs#opencodeUserConfigDir. Mirrors the CLI's precedence
/// (`OPENCODE_CONFIG_DIR` -> `XDG_CONFIG_HOME/opencode` -> `~/.config/opencode`).
fn opencode_user_config_dir(io: &Io) -> String {
if let Some(v) = io.env.get("OPENCODE_CONFIG_DIR").filter(|v| !v.is_empty()) {
return v.clone();
}
if let Some(v) = io.env.get("XDG_CONFIG_HOME").filter(|v| !v.is_empty()) {
return jsp::join(&[v, "opencode"]);
}
jsp::join(&[&crate::util::homedir(&io.env), ".config", "opencode"])
}
/// JS: pin.mjs#findOpencodeCommandsDirs. The project-local dir when the
/// project has the skill, plus the user config dir when Impeccable is
/// installed globally. With `for_cleanup`, both are included even when the
/// skill is gone, so unpin can still reach a pin a removed install left
/// behind; removal stays safe because it is marker-guarded.
fn find_opencode_commands_dirs(project_root: &str, io: &Io, for_cleanup: bool) -> Vec<String> {
let mut dirs: Vec<String> = Vec::new();
let mut push = |dir: String| {
let key = jsp::resolve(&dir, &[]);
if !dirs.iter().any(|d| jsp::resolve(d, &[]) == key) {
dirs.push(dir);
}
};
if for_cleanup || exists(&jsp::join(&[project_root, ".opencode", "skills", "impeccable"])) {
push(jsp::join(&[project_root, ".opencode", "commands"]));
}
let user_config = opencode_user_config_dir(io);
if for_cleanup || exists(&jsp::join(&[&user_config, "skills", "impeccable"])) {
push(jsp::join(&[&user_config, "commands"]));
}
dirs
}
/// JS: pin.mjs#writePinnedOpencodeCommand
fn write_pinned_opencode_command(
commands_dir: &str,
command: &str,
metadata: &serde_json::Value,
io: &mut Io,
) -> bool {
let command_file = jsp::join(&[commands_dir, &format!("impeccable-{command}.md")]);
if exists(&command_file) {
let existing = safe_read(&command_file).unwrap_or_default();
if !existing.contains(OPENCODE_PIN_MARKER) {
io.out(&format!(
" SKIP: {} (non-pinned command already exists)\n",
command_file
));
return false;
}
} else {
let _ = std::fs::create_dir_all(commands_dir);
}
let _ = std::fs::write(&command_file, generate_pinned_opencode_command(command, metadata));
io.out(&format!(" + {}\n", command_file));
true
}
/// JS: pin.mjs#removePinnedOpencodeCommand
fn remove_pinned_opencode_command(commands_dir: &str, command: &str, io: &mut Io) -> bool {
let command_file = jsp::join(&[commands_dir, &format!("impeccable-{command}.md")]);
if !exists(&command_file) {
return false;
}
let content = safe_read(&command_file).unwrap_or_default();
if !content.contains(OPENCODE_PIN_MARKER) {
io.out(&format!(" SKIP: {} (not a pinned command)\n", command_file));
return false;
}
let _ = std::fs::remove_file(&command_file);
io.out(&format!(" - {}\n", command_file));
true
}
/// JS `skillsDir.includes(`${sep}.opencode${sep}`)`.
fn is_opencode_skills_dir(skills_dir: &str) -> bool {
skills_dir.contains(&format!("{sep}.opencode{sep}", sep = jsp::SEP))
}
fn load_metadata(io: &Io) -> serde_json::Value {
// Prefer a sibling command-metadata.json in the skill dir when present
// (an installed skill may be newer than the embedded copy).
let cwd = io.cwd.to_string_lossy().into_owned();
let provider = crate::provider::detect(&io.env, &cwd);
if let Some(dir) = &provider.skill_dir {
let p = jsp::join(&[dir, "scripts", "command-metadata.json"]);
if let Some(v) = read_json(&p) {
return v;
}
}
serde_json::from_str(COMMAND_METADATA_JSON).unwrap_or(serde_json::Value::Object(Default::default()))
}
pub fn run(args: &[String], io: &mut Io) -> i32 {
let action = args.first().cloned();
let command = args.get(1).cloned();
let (Some(action), Some(command)) = (action.filter(|a| !a.is_empty()), command.filter(|c| !c.is_empty())) else {
io.out("Usage: impeccable pin <pin|unpin> <command>\n");
io.out(&format!("\nAvailable commands: {}\n", VALID_COMMANDS.join(", ")));
return 1;
};
if action != "pin" && action != "unpin" {
io.err(&format!("Unknown action: {}. Use 'pin' or 'unpin'.\n", action));
return 1;
}
if !VALID_COMMANDS.contains(&command.as_str()) {
io.err(&format!("Unknown command: {}\n", command));
io.err(&format!("Available commands: {}\n", VALID_COMMANDS.join(", ")));
return 1;
}
let cwd = io.cwd.to_string_lossy().into_owned();
let root = find_project_root(&cwd);
if action == "pin" {
let metadata = load_metadata(io);
let harness_dirs = find_harness_dirs(&root);
let opencode_commands_dirs = find_opencode_commands_dirs(&root, io, false);
if harness_dirs.is_empty() && opencode_commands_dirs.is_empty() {
io.out("No harness directories with impeccable installed found.\n");
return 0;
}
let mut 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
// prevents a duplicate `.opencode/skills/<cmd>/SKILL.md` that OpenCode
// would never surface as `/<cmd>`.
for skills_dir in &harness_dirs {
if is_opencode_skills_dir(skills_dir) {
continue;
}
let prefix = command_prefix_for(skills_dir);
let content = generate_pinned_skill(&command, &metadata, prefix, prefix == "$");
let skill_dir = jsp::join(&[skills_dir, &command]);
if exists(&skill_dir) {
let md = jsp::join(&[&skill_dir, "SKILL.md"]);
if exists(&md) {
let existing = safe_read(&md).unwrap_or_default();
if !existing.contains(PIN_MARKER) {
io.out(&format!(" SKIP: {} (non-pinned skill already exists)\n", skill_dir));
continue;
}
}
}
let _ = std::fs::create_dir_all(&skill_dir);
let _ = std::fs::write(jsp::join(&[&skill_dir, "SKILL.md"]), content);
io.out(&format!(" + {}\n", skill_dir));
created += 1;
}
// OpenCode: a slash command bridge, not a skill shortcut. Covers both
// project installs and user-scope (global config) installs.
for commands_dir in &opencode_commands_dirs {
if write_pinned_opencode_command(commands_dir, &command, &metadata, io) {
created += 1;
}
}
if created > 0 {
io.out(&format!("\nPinned '{}' as a standalone shortcut in {} location(s).\n", command, created));
io.out("Use the pinned command directly in each harness.\n");
}
} else {
let harness_dirs = find_harness_dirs(&root);
let mut 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.
for skills_dir in &harness_dirs {
if is_opencode_skills_dir(skills_dir) {
continue;
}
let skill_dir = jsp::join(&[skills_dir, &command]);
if !exists(&skill_dir) {
continue;
}
let md = jsp::join(&[&skill_dir, "SKILL.md"]);
if !exists(&md) {
continue;
}
let content = safe_read(&md).unwrap_or_default();
if !content.contains(PIN_MARKER) {
io.out(&format!(" SKIP: {} (not a pinned skill)\n", skill_dir));
continue;
}
let _ = std::fs::remove_dir_all(&skill_dir);
io.out(&format!(" - {}\n", skill_dir));
removed += 1;
}
// OpenCode: remove the pinned command file if it is one of ours, in
// every scope it could have been written to, even when the skill
// itself is gone, since removal is marker-guarded.
for commands_dir in find_opencode_commands_dirs(&root, io, true) {
if remove_pinned_opencode_command(&commands_dir, &command, io) {
removed += 1;
}
}
if removed > 0 {
io.out(&format!("\nUnpinned '{}' from {} location(s).\n", command, removed));
io.out(&format!("Use Impeccable's '{}' workflow directly to access it.\n", command));
} else {
io.out(&format!("No pinned '{}' shortcut found.\n", command));
}
}
0
}