{"record":{"id":"1ddbcd0fcb0a1c10","repo":"github/spec-kit","slug":"commands-dir-is-required-for-the-generic-integra","errorCode":null,"errorMessage":"--commands-dir is required for the generic integration","messagePattern":"--commands-dir is required for the generic integration","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/integrations/generic/__init__.py","lineNumber":83,"sourceCode":"        if commands_dir and (not isinstance(commands_dir, str) or commands_dir.strip()):\n            return commands_dir\n\n        # Fall back to raw_options (--integration-options=\"--commands-dir ...\")\n        raw = opts.get(\"raw_options\")\n        if raw:\n            import shlex\n            tokens = shlex.split(raw)\n            for i, token in enumerate(tokens):\n                if token == \"--commands-dir\" and i + 1 < len(tokens):\n                    candidate = tokens[i + 1]\n                    if candidate.strip():\n                        return candidate\n                if token.startswith(\"--commands-dir=\"):\n                    candidate = token.split(\"=\", 1)[1]\n                    if candidate.strip():\n                        return candidate\n\n        raise ValueError(\n            \"--commands-dir is required for the generic integration\"\n        )\n\n    def commands_dest(self, project_root: Path) -> Path:\n        \"\"\"Not supported for GenericIntegration — use setup() directly.\n\n        GenericIntegration is stateless; the output directory comes from\n        ``parsed_options`` or ``raw_options`` at call time, not from\n        instance state.\n        \"\"\"\n        raise ValueError(\n            \"GenericIntegration.commands_dest() cannot be called directly; \"\n            \"the output directory is resolved from parsed_options in setup()\"\n        )\n\n    def setup(\n        self,\n        project_root: Path,","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/integrations/generic/__init__.py#L65-L101","documentation":"Raised by GenericIntegration._resolve_commands_dir() (src/specify_cli/integrations/generic/__init__.py:83) when neither parsed_options (\"commands_dir\") nor raw_options contains a usable --commands-dir value. The generic integration is stateless — its output directory is not in config — so the caller must supply --commands-dir at call time; token parsing finds no '--commands-dir' or '--commands-dir=' token with a non-empty value and raises ValueError.","triggerScenarios":"Calling GenericIntegration().setup(project_root, manifest) with parsed_options lacking 'commands_dir' and raw_options lacking '--commands-dir <dir>' or '--commands-dir=<dir>'; or passing an empty value ('--commands-dir=' with blank string).","commonSituations":"Using the generic integration programmatically and forgetting the option; passing the flag via a different options string that shlex splits unexpectedly; quoting issues where '--commands-dir=' receives an empty value; CLI invocation missing --integration-options.","solutions":["Pass commands_dir in parsed_options: setup(root, manifest, parsed_options={\"commands_dir\": \"myagent/commands\"}).","Or include --integration-options=\"--commands-dir myagent/commands\" on the specify CLI.","Ensure the value is non-empty after shlex.split if passed via raw_options.","Check for the '=' form being given a blank value."],"exampleFix":"# before\nGenericIntegration().setup(project_root, manifest, parsed_options={})\n\n# after\nGenericIntegration().setup(\n    project_root, manifest, parsed_options={\"commands_dir\": \".myagent/commands\"}\n)","handlingStrategy":"validation","validationCode":"opts = parsed_options or {}\nif not opts.get(\"commands_dir\"):\n    raise SystemExit(\n        \"generic integration requires --commands-dir \"\n        \"(or parsed_options['commands_dir'])\"\n    )","typeGuard":"def has_commands_dir(parsed_options: dict | None, raw_options: str | None) -> bool:\n    if parsed_options and str(parsed_options.get(\"commands_dir\", \"\")).strip():\n        return True\n    if raw_options and \"--commands-dir\" in raw_options:\n        import shlex\n        for i, t in enumerate(shlex.split(raw_options)):\n            if t == \"--commands-dir\" and i + 1 < len(shlex.split(raw_options)):\n                return True\n            if t.startswith(\"--commands-dir=\") and t.split(\"=\", 1)[1].strip():\n                return True\n    return False","tryCatchPattern":"try:\n    integration.setup(root, manifest, parsed_options=opts)\nexcept ValueError as e:\n    if \"--commands-dir is required\" in str(e):\n        opts = {**(opts or {}), \"commands_dir\": \".agent/commands\"}\n        integration.setup(root, manifest, parsed_options=opts)\n    else:\n        raise","preventionTips":["Always pass commands_dir in parsed_options when driving the generic integration programmatically.","On the CLI, use --integration-options=\"--commands-dir <dir>\" with a non-empty value.","Check the option string with shlex.split mentally — quoting errors make it parse empty."],"tags":["options","validation","integration-setup","spec-kit","generic"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}