{"record":{"id":"6b51cb10c0945497","repo":"can1357/oh-my-pi","slug":"invalid-package-name-name-6b51cb","errorCode":null,"errorMessage":"Invalid package name: ${name}","messagePattern":"Invalid package name: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/extensibility/plugins/manager.ts","lineNumber":54,"sourceCode":"// =============================================================================\n\n/** Valid npm package name pattern (scoped and unscoped, with optional version) */\nconst VALID_PACKAGE_NAME = /^(@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*(@[a-z0-9-._^~>=<]+)?$/i;\n\n/** Characters that are never valid in any plugin install spec — git or npm. */\nconst SHELL_METACHARS = /[;&|`$(){}<>\\\\\\n\\r\\t]/;\n\n/**\n * Validate package name to prevent command injection. npm specs only — git\n * specs (`github:user/repo`, `https://github.com/...`, ...) MUST go through\n * {@link validateGitSpec} instead because they contain characters npm rejects\n * (`:`, `/`, `#`, `+`, `@` in non-version positions).\n */\nfunction validatePackageName(name: string): void {\n\t// Remove version specifier for validation\n\tconst baseName = extractPackageName(name);\n\tif (!VALID_PACKAGE_NAME.test(baseName)) {\n\t\tthrow new Error(`Invalid package name: ${name}`);\n\t}\n\t// Extra safety: no shell metacharacters\n\tif (/[;&|`$(){}[\\]<>\\\\]/.test(name)) {\n\t\tthrow new Error(`Invalid characters in package name: ${name}`);\n\t}\n}\n\n/**\n * Validate a git install spec — accepts `:`, `/`, `#`, `+`, `.`, `-`, `_`,\n * `~`, `@` (which would all fail {@link validatePackageName}) but rejects\n * shell metacharacters so the spec stays safe when forwarded to bun install.\n * `Bun.spawn` does not invoke a shell, but defense-in-depth keeps things\n * obvious for future readers.\n */\nfunction validateGitSpec(spec: string): void {\n\tif (SHELL_METACHARS.test(spec)) {\n\t\tthrow new Error(`Invalid characters in plugin source: ${spec}`);\n\t}","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/extensibility/plugins/manager.ts#L36-L72","documentation":"manager.ts's validatePackageName strips any version specifier and tests the base name against VALID_PACKAGE_NAME (npm-style scoped/unscoped names). This error is thrown when an install/uninstall spec's base name does not match that grammar — typically because the spec is a git URL, tarball, file path, or simply malformed. Git specs must go through validateGitSpec instead.","triggerScenarios":"install(name) or uninstall(name) called with a git spec (`github:user/repo`, `https://...`), a local path (`./plugin`, `/abs/path`), a malformed npm name (uppercase-with-invalid-chars outside the grammar, spaces, empty string), or a scoped name with invalid characters in scope or package part.","commonSituations":"Passing a GitHub URL to an npm-name-only entry point; Windows-style backslash paths; typos like `my plugin`; attempting to install a tarball or directory via the package-name API instead of the git/file source path.","solutions":["Use a plain npm package name (optionally with version), e.g. `my-plugin` or `my-plugin@^1.2.0`","For GitHub sources use the supported git spec syntax (`github:user/repo`) so validateGitSpec handles it instead","Strip whitespace and remove shell metacharacters; validate locally with the npm name rules (lowercase, `-._~`, single scope)","If installing from a local directory, use the link mechanism (linkPlugin) rather than install"],"exampleFix":"// before\nawait install(\"https://github.com/user/my-plugin\");\n// after\nawait install(\"github:user/my-plugin\"); // git spec path\n// or plain npm\nawait install(\"my-plugin\");","handlingStrategy":"validation","validationCode":"const VALID = /^(@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*(@[a-z0-9-._^~>=<]+)?$/i;\nif (!VALID.test(extractPackageName(spec)) || /[;&|`$(){}[\\]<>\\\\]/.test(spec)) {\n\tthrow new Error(`spec must be a plain npm name: ${spec}`);\n}","typeGuard":"function isNpmSpec(spec: string): boolean {\n\tconst VALID = /^(@[a-z0-9-~][a-z0-9-._~]*\\/)?[a-z0-9-~][a-z0-9-._~]*(@[a-z0-9-._^~>=<]+)?$/i;\n\treturn VALID.test(extractPackageName(spec)) && !/[;&|`$(){}[\\]<>\\\\]/.test(spec);\n}","tryCatchPattern":"try {\n\tawait manager.install(spec);\n} catch (err) {\n\tif (err instanceof Error && err.message.startsWith(\"Invalid package name:\")) {\n\t\t// route git/tarball/path specs through the git spec entrypoint instead\n\t}\n\tthrow err;\n}","preventionTips":["Pass only plain npm names (optionally with semver) to install/uninstall","Route GitHub/git sources through the dedicated git-spec syntax (`github:user/repo`)","Trim whitespace and pre-validate names against npm naming rules in tooling","Never build install specs by concatenating untrusted strings"],"tags":["validation","package-name-invalid","plugins","input-validation"],"backgroundTag":"invalid-package-name","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}