{"record":{"id":"68a3941ec39958c2","repo":"jackwener/OpenCLI","slug":"invalid-plugin-source-source-supported-forma","errorCode":null,"errorMessage":"Invalid plugin source: \"${source}\"\nSupported formats:\n  github:user/repo\n  github:user/repo/subplugin\n  https://github.com/user/repo\n  https://<host>/<path>/repo.git\n  ssh://git@<host>/<path>/repo.git\n  git@<host>:user/repo.git\n","messagePattern":"Invalid plugin source: \"(.+?)\"\nSupported formats:\n  github:user/repo\n  github:user/repo/subplugin\n  https://github\\.com/user/repo\n  https://<host>/<path>/repo\\.git\n  ssh://git@<host>/<path>/repo\\.git\n  git@<host>:user/repo\\.git\n","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/plugin.ts","lineNumber":697,"sourceCode":"    writeLock?.(commitHash);\n  });\n}\n\n/**\n * Install a plugin from a source.\n * Supports:\n *   \"github:user/repo\"            — single plugin or full monorepo\n *   \"github:user/repo/subplugin\"  — specific sub-plugin from a monorepo\n *   \"https://github.com/user/repo\"\n *   \"file:///absolute/path\"       — local plugin directory (symlinked)\n *   \"/absolute/path\"              — local plugin directory (symlinked)\n *\n * Returns the installed plugin name(s).\n */\nexport function installPlugin(source: string): string | string[] {\n  const parsed = parseSource(source);\n  if (!parsed) {\n    throw new Error(\n      `Invalid plugin source: \"${source}\"\\n` +\n      `Supported formats:\\n` +\n      `  github:user/repo\\n` +\n      `  github:user/repo/subplugin\\n` +\n      `  https://github.com/user/repo\\n` +\n      `  https://<host>/<path>/repo.git\\n` +\n      `  ssh://git@<host>/<path>/repo.git\\n` +\n      `  git@<host>:user/repo.git\\n` +\n      `  file:///absolute/path\\n` +\n      `  /absolute/path`\n    );\n  }\n\n  const { name: repoName, subPlugin } = parsed;\n\n  if (parsed.type === 'local') {\n    return installLocalPlugin(parsed.localPath!, repoName);\n  }","sourceCodeStart":679,"sourceCodeEnd":715,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/plugin.ts#L679-L715","documentation":"installPlugin first parses the source string with parseSource, which recognizes github: shorthand, GitHub HTTPS URLs, ssh://, SCP-style git@, generic HTTPS git URLs, file:// and absolute local paths. If nothing matches, a plain Error listing all supported formats is thrown so the caller can correct the input.","triggerScenarios":"installPlugin('my-plugin') (bare name), installPlugin('git://host/repo'), installPlugin('github:user') (missing repo segment), relative local paths like './my-plugin', URLs with extra path/query segments, or whitespace/typos in the source string.","commonSituations":"Passing a plugin NAME instead of a source; using a git:// protocol URL (unsupported); relative directory path instead of absolute; trailing slash or extra URL components; scp-style without .git and non-matching host prefix.","solutions":["Use a supported format, e.g. installPlugin('github:user/repo') or an absolute local path.","For local development, pass an absolute path ('/abs/path/to/plugin') or file:///absolute/path — relative paths are rejected.","Add a .git suffix or use https://github.com/user/repo for GitHub repos.","For other git hosts use ssh://git@<host>/<path>/repo.git or git@<host>:user/repo.git.","Trim whitespace and remove trailing slashes/query strings from the source string."],"exampleFix":"// before\ninstallPlugin('my-plugin');            // bare name\ninstallPlugin('./plugins/my-plugin');  // relative path\n// after\ninstallPlugin('github:user/my-plugin');\ninstallPlugin('/abs/path/plugins/my-plugin');","handlingStrategy":"validation","validationCode":"const SOURCE_RE = /^(github:[\\w.-]+\\/[\\w.-]+(\\/[\\w.-]+)?|https?:\\/\\/github\\.com\\/[\\w.-]+\\/[\\w.-]+?(\\.git)?|ssh:\\/\\/[^/]+\\/.+|git@[^:]+:.+|https?:\\/\\/[^/]+\\/.+|file:\\/\\/.+|\\/.+)$/;\nif (!SOURCE_RE.test(source)) throw new Error(`Unsupported plugin source: ${source}`);","typeGuard":null,"tryCatchPattern":"try {\n  installPlugin(source);\n} catch (err) {\n  if (err.message.startsWith('Invalid plugin source')) {\n    // normalize the source string (add scheme/absolute path) and retry\n  } else throw err;\n}","preventionTips":["Always pass one of the documented formats — never a bare plugin name.","Use absolute paths (or file:// URLs) for local plugins; expand ~ yourself.","Trim whitespace and strip trailing slashes from user-supplied sources.","Prefer github:user/repo shorthand for GitHub repos."],"tags":["input-validation","plugin-install","argument-format"],"backgroundTag":"invalid-plugin-source","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}