santifer/career-ops · error · Error

repo must be named "career-ops-plugin-<name>" (got "${repoNa

Error message

repo must be named "career-ops-plugin-<name>" (got "${repoName}")

What it means

Thrown by parseRepoArg() when the GitHub repo's name does not match the required career-ops-plugin-<name> convention (NAME_RE: ^career-ops-plugin-([a-z0-9][a-z0-9-]*)$). The naming convention is the first trust signal that a repo is an official/community career-ops plugin before it is cloned.

Source

Thrown at plugin-install.mjs:34

import path from 'node:path';
import { validateManifest } from './plugins/_engine.mjs';
import { hashPluginTree } from './plugins/_lock.mjs';
import { auditPlugin } from './plugin-audit.mjs';

const GITHUB_URL_RE = /^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?(?:\.git)?$/;
const NAME_RE = /^career-ops-plugin-([a-z0-9][a-z0-9-]*)$/;
const SHA_RE = /^[0-9a-f]{40}$/;
const MIN_FILES = ['manifest.json', 'index.mjs', 'README.md', 'LICENSE'];

/** Normalize `owner/repo` | full URL into a validated github URL + the plugin id. */
export function parseRepoArg(arg) {
  let url = arg;
  if (/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(arg)) url = `https://github.com/${arg}`;
  url = url.replace(/\.git$/, '');
  if (!GITHUB_URL_RE.test(url)) throw new Error(`refusing non-GitHub/unsafe repo URL: ${arg} (expected https://github.com/<owner>/<repo>)`);
  const repoName = url.split('/').pop() || '';
  const m = NAME_RE.exec(repoName);
  if (!m) throw new Error(`repo must be named "career-ops-plugin-<name>" (got "${repoName}")`);
  return { url, id: m[1] };
}

/** Clone the EXACT pinned SHA into a fresh temp dir. Returns the temp dir path. */
export function safeClone(url, sha) {
  if (!SHA_RE.test(sha || '')) throw new Error(`a 40-hex commit --sha is required (got ${JSON.stringify(sha)})`);
  const dir = mkdtempSync(path.join(tmpdir(), 'co-plugin-'));
  const git = (...args) => execFileSync('git', ['-c', 'protocol.ext.allow=never', '-c', 'protocol.file.allow=never', ...args], { stdio: ['ignore', 'ignore', 'pipe'], timeout: 120_000 });
  try {
    git('-C', dir, 'init', '-q');
    git('-C', dir, 'remote', 'add', 'origin', '--', url);
    git('-C', dir, 'fetch', '--depth', '1', '--no-tags', '-q', 'origin', sha);
    git('-C', dir, 'checkout', '-q', 'FETCH_HEAD');
    rmSync(path.join(dir, '.git'), { recursive: true, force: true }); // drop VCS metadata (and any hooks)
    return dir;
  } catch (err) {
    rmSync(dir, { recursive: true, force: true });
    throw new Error(`clone of ${url}@${sha.slice(0, 10)} failed — ${err.stderr ? String(err.stderr).slice(0, 200) : err.message}`);

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Install only repos named exactly career-ops-plugin-<name> (lowercase, hyphens, alphanumeric).
  2. If you own the plugin, rename the GitHub repo to match the convention.
  3. Re-check the repo name on GitHub for typos or case.
  4. Use `node plugins.mjs scaffold <name>` to create a properly named local plugin instead.

Example fix

// before
parseRepoArg('acme/my-cool-plugin');
// after
parseRepoArg('acme/career-ops-plugin-my-cool');
Defensive patterns

Strategy: validation

Validate before calling

const NAME_RE = /^career-ops-plugin-([a-z0-9][a-z0-9-]*)$/;
function isPluginRepoName(repoSlug) {
  return NAME_RE.test(repoSlug);
}

Type guard

/** Confirms a repo slug follows the career-ops-plugin-<name> convention. */
function isPluginRepoName(repoSlug) {
  return typeof repoSlug === 'string' &&
    /^career-ops-plugin-([a-z0-9][a-z0-9-]*)$/.test(repoSlug);
}

Prevention

When it happens

Trigger: Installing a repo named my-plugin, plugin-x, or Career-Ops-Plugin-X (uppercase); a repo missing the required prefix; a name with underscores or disallowed characters.

Common situations: Trying to install a generic GitHub repo that is not a career-ops plugin; the plugin author did not follow the naming convention; case sensitivity (the regex is lowercase-only).

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/29e1ae09d700a079. Report an issue: GitHub.