From 1f36aa280c1f5884d4f2422796e7506ac9196d89 Mon Sep 17 00:00:00 2001 From: eyalz Date: Fri, 26 Sep 2025 14:02:52 +0300 Subject: [PATCH 1/2] Add interactive file read option to fulfill variables --- prompt/prompt_helper.py | 46 +++++++++++++++++++++++++++++++++++++++-- pyproject.toml | 2 +- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/prompt/prompt_helper.py b/prompt/prompt_helper.py index 45b6cb5..b51c42d 100644 --- a/prompt/prompt_helper.py +++ b/prompt/prompt_helper.py @@ -159,17 +159,18 @@ def validate_string(value): async def collect_var_value_interactive(self, var_name: str) -> str: """ - Interactive variable collection - user chooses between direct value or MCP tool. + Interactive variable collection - user chooses between direct value, MCP tool, or file. :param var_name: Name of the variable to collect :return: String value for the variable """ - # Ask user to choose between direct value or MCP tool + # Ask user to choose between direct value, MCP tool, or file choice = await questionary.select( f"How would you like to provide the value for '{var_name}'?", choices=[ questionary.Choice("Provide value directly", "direct"), questionary.Choice("Use MCP tool to fetch value", "mcp"), + questionary.Choice("Read from file", "file"), ], ).ask_async() @@ -177,6 +178,8 @@ async def collect_var_value_interactive(self, var_name: str) -> str: return await self.collect_var_value(var_name) elif choice == "mcp": return await self._collect_var_value_from_mcp(var_name) + elif choice == "file": + return await self._collect_var_value_from_file(var_name) else: raise ValueError("Invalid choice") @@ -245,6 +248,45 @@ async def _collect_var_value_from_mcp(self, var_name: str) -> str: logging.info(f"Tool result for '{var_name}': {content}") return content + async def _collect_var_value_from_file(self, var_name: str) -> str: + """ + Collect variable value by reading from a file. + + :param var_name: Name of the variable to collect + :return: String content of the selected file + """ + import os + + # Use questionary.path to select a file + file_path = await questionary.path( + f"Select a file for variable '{var_name}':", + default="./", + only_directories=False, + ).ask_async() + + if not file_path: + raise ValueError("No file selected") + + # Ensure the path exists and is a file + if not os.path.exists(file_path): + raise ValueError(f"File does not exist: {file_path}") + + if not os.path.isfile(file_path): + raise ValueError(f"Path is not a file: {file_path}") + + try: + # Try to read the file as UTF-8 + with open(file_path, encoding='utf-8') as f: + content = f.read() + + logging.info(f"Successfully read file '{file_path}' for variable '{var_name}'") + return content + + except UnicodeDecodeError as e: + raise ValueError(f"File '{file_path}' is not a valid UTF-8 text file: {e}") from e + except Exception as e: + raise ValueError(f"Failed to read file '{file_path}': {e}") from e + async def _select_mcp_server(self) -> str | None: """ Present user with list of available MCP servers to choose from. diff --git a/pyproject.toml b/pyproject.toml index 1086232..ab535bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "context-kit" -version = "0.1.1" +version = "0.2.1" description = "A CLI tool and MCP client, used to create spec files for AI coding agents with context baked in, based on reusable spec templates" readme = "README.md" requires-python = ">=3.11" From f8a73be7b7770ce1b52c95f2b3edc3afb5548047 Mon Sep 17 00:00:00 2001 From: eyalz Date: Fri, 26 Sep 2025 14:09:02 +0300 Subject: [PATCH 2/2] Update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5bc7694..a49acbe 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ cxk create-spec template.md # ? How would you like to provide the value for 'ticket_id'? # › Provide value directly # Use MCP tool to fetch value +# Read from file ``` ### Output to File