keystone-cli 0.2.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/PKG-INFO +3 -2
  2. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/pyproject.toml +7 -4
  3. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/__main__.py +41 -0
  4. keystone_cli-0.3.0/src/keystone_cli/_jsonout.py +161 -0
  5. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/agent_logs.py +27 -11
  6. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/auth/token_store.py +6 -0
  7. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/commands/agent.py +824 -194
  8. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/commands/kb.py +9 -1
  9. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/commands/kb_metadata.py +6 -3
  10. keystone_cli-0.3.0/src/keystone_cli/commands/prompt.py +695 -0
  11. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/commands/tool_credentials.py +9 -1
  12. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/commands/whoami.py +10 -3
  13. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/commands/workspace.py +20 -4
  14. keystone_cli-0.3.0/src/keystone_cli/fetch.py +337 -0
  15. keystone_cli-0.3.0/src/keystone_cli/manifest_ref.py +259 -0
  16. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/runner.py +18 -1
  17. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/skills/keystone-debug/SKILL.md +21 -1
  18. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/blank/README.md.tmpl +15 -0
  19. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/blank/agent.yaml.tmpl +5 -0
  20. keystone_cli-0.3.0/src/keystone_cli/templates/blank/pyproject.toml.tmpl +23 -0
  21. keystone_cli-0.3.0/src/keystone_cli/templates/blank/tests/conftest.py.tmpl +25 -0
  22. keystone_cli-0.3.0/src/keystone_cli/templates/blank/tests/test_graph.py.tmpl +20 -0
  23. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/guardrails/README.md.tmpl +15 -0
  24. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/guardrails/agent.yaml.tmpl +0 -2
  25. keystone_cli-0.3.0/src/keystone_cli/templates/guardrails/pyproject.toml.tmpl +23 -0
  26. keystone_cli-0.3.0/src/keystone_cli/templates/guardrails/tests/conftest.py.tmpl +25 -0
  27. keystone_cli-0.3.0/src/keystone_cli/templates/guardrails/tests/test_graph.py.tmpl +100 -0
  28. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/hitl/README.md.tmpl +17 -2
  29. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/hitl/agent.yaml.tmpl +0 -2
  30. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/hitl/pkg/graph.py.tmpl +6 -2
  31. keystone_cli-0.3.0/src/keystone_cli/templates/hitl/pyproject.toml.tmpl +23 -0
  32. keystone_cli-0.3.0/src/keystone_cli/templates/hitl/tests/conftest.py.tmpl +25 -0
  33. keystone_cli-0.3.0/src/keystone_cli/templates/hitl/tests/test_graph.py.tmpl +84 -0
  34. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/kb-builder/README.md.tmpl +15 -0
  35. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/kb-builder/agent.yaml.tmpl +0 -2
  36. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/kb-builder/pyproject.toml.tmpl +11 -0
  37. keystone_cli-0.3.0/src/keystone_cli/templates/kb-builder/tests/conftest.py.tmpl +25 -0
  38. keystone_cli-0.3.0/src/keystone_cli/templates/kb-builder/tests/test_graph.py.tmpl +137 -0
  39. keystone_cli-0.3.0/src/keystone_cli/templates/llm/README.md.tmpl +72 -0
  40. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm/agent.yaml.tmpl +20 -2
  41. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm/pkg/graph.py.tmpl +19 -1
  42. keystone_cli-0.3.0/src/keystone_cli/templates/llm/pyproject.toml.tmpl +23 -0
  43. keystone_cli-0.3.0/src/keystone_cli/templates/llm/system-prompt.txt.tmpl +1 -0
  44. keystone_cli-0.3.0/src/keystone_cli/templates/llm/tests/conftest.py.tmpl +25 -0
  45. keystone_cli-0.3.0/src/keystone_cli/templates/llm/tests/test_graph.py.tmpl +36 -0
  46. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm-streaming/README.md.tmpl +43 -0
  47. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm-streaming/agent.yaml.tmpl +9 -2
  48. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm-streaming/pkg/graph.py.tmpl +18 -1
  49. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm-streaming/pyproject.toml.tmpl +11 -0
  50. keystone_cli-0.3.0/src/keystone_cli/templates/llm-streaming/system-prompt.txt.tmpl +1 -0
  51. keystone_cli-0.3.0/src/keystone_cli/templates/llm-streaming/tests/conftest.py.tmpl +25 -0
  52. keystone_cli-0.3.0/src/keystone_cli/templates/llm-streaming/tests/test_graph.py.tmpl +38 -0
  53. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory/README.md.tmpl +15 -0
  54. keystone_cli-0.3.0/src/keystone_cli/templates/memory/pyproject.toml.tmpl +23 -0
  55. keystone_cli-0.3.0/src/keystone_cli/templates/memory/tests/conftest.py.tmpl +25 -0
  56. keystone_cli-0.3.0/src/keystone_cli/templates/memory/tests/test_graph.py.tmpl +41 -0
  57. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory-service/README.md.tmpl +15 -0
  58. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory-service/agent.yaml.tmpl +0 -2
  59. keystone_cli-0.3.0/src/keystone_cli/templates/memory-service/pyproject.toml.tmpl +23 -0
  60. keystone_cli-0.3.0/src/keystone_cli/templates/memory-service/tests/conftest.py.tmpl +25 -0
  61. keystone_cli-0.3.0/src/keystone_cli/templates/memory-service/tests/test_graph.py.tmpl +84 -0
  62. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/ocr/README.md.tmpl +32 -0
  63. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/ocr/agent.yaml.tmpl +5 -2
  64. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/ocr/pyproject.toml.tmpl +11 -0
  65. keystone_cli-0.3.0/src/keystone_cli/templates/ocr/tests/conftest.py.tmpl +25 -0
  66. keystone_cli-0.3.0/src/keystone_cli/templates/ocr/tests/test_graph.py.tmpl +117 -0
  67. keystone_cli-0.3.0/src/keystone_cli/templates/rag-qa/README.md.tmpl +66 -0
  68. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/rag-qa/agent.yaml.tmpl +20 -2
  69. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/rag-qa/pkg/graph.py.tmpl +18 -1
  70. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/rag-qa/pyproject.toml.tmpl +11 -0
  71. keystone_cli-0.3.0/src/keystone_cli/templates/rag-qa/system-prompt.txt.tmpl +1 -0
  72. keystone_cli-0.3.0/src/keystone_cli/templates/rag-qa/tests/conftest.py.tmpl +25 -0
  73. keystone_cli-0.3.0/src/keystone_cli/templates/rag-qa/tests/test_graph.py.tmpl +82 -0
  74. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/skill/README.md.tmpl +15 -0
  75. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/skill/agent.yaml.tmpl +0 -2
  76. keystone_cli-0.3.0/src/keystone_cli/templates/skill/pyproject.toml.tmpl +23 -0
  77. keystone_cli-0.3.0/src/keystone_cli/templates/skill/tests/conftest.py.tmpl +25 -0
  78. keystone_cli-0.3.0/src/keystone_cli/templates/skill/tests/test_graph.py.tmpl +61 -0
  79. keystone_cli-0.3.0/src/keystone_cli/templates/tool-agent/README.md.tmpl +74 -0
  80. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/tool-agent/agent.yaml.tmpl +9 -2
  81. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/tool-agent/pkg/graph.py.tmpl +21 -1
  82. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/tool-agent/pyproject.toml.tmpl +11 -0
  83. keystone_cli-0.3.0/src/keystone_cli/templates/tool-agent/system-prompt.txt.tmpl +1 -0
  84. keystone_cli-0.3.0/src/keystone_cli/templates/tool-agent/tests/conftest.py.tmpl +25 -0
  85. keystone_cli-0.3.0/src/keystone_cli/templates/tool-agent/tests/test_graph.py.tmpl +35 -0
  86. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli.egg-info/PKG-INFO +3 -2
  87. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli.egg-info/SOURCES.txt +40 -0
  88. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli.egg-info/requires.txt +2 -1
  89. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_agent.py +26 -0
  90. keystone_cli-0.3.0/tests/test_agent_manifest_cmd.py +204 -0
  91. keystone_cli-0.3.0/tests/test_agent_test_cmd.py +121 -0
  92. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_deploy.py +192 -0
  93. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_doctor.py +81 -0
  94. keystone_cli-0.3.0/tests/test_fetch.py +696 -0
  95. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_help_quality.py +68 -1
  96. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_init.py +17 -0
  97. keystone_cli-0.3.0/tests/test_json_output.py +450 -0
  98. keystone_cli-0.3.0/tests/test_jsonout.py +168 -0
  99. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_logs.py +28 -0
  100. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_offline.py +13 -0
  101. keystone_cli-0.3.0/tests/test_prompt_commands.py +402 -0
  102. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_runs.py +24 -0
  103. keystone_cli-0.3.0/tests/test_template_prompt_guard.py +62 -0
  104. keystone_cli-0.3.0/tests/test_template_unit_tests.py +66 -0
  105. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_traces.py +60 -0
  106. keystone_cli-0.2.2/src/keystone_cli/templates/blank/pyproject.toml.tmpl +0 -12
  107. keystone_cli-0.2.2/src/keystone_cli/templates/guardrails/pyproject.toml.tmpl +0 -12
  108. keystone_cli-0.2.2/src/keystone_cli/templates/hitl/pyproject.toml.tmpl +0 -12
  109. keystone_cli-0.2.2/src/keystone_cli/templates/llm/README.md.tmpl +0 -29
  110. keystone_cli-0.2.2/src/keystone_cli/templates/llm/pyproject.toml.tmpl +0 -12
  111. keystone_cli-0.2.2/src/keystone_cli/templates/memory/pyproject.toml.tmpl +0 -12
  112. keystone_cli-0.2.2/src/keystone_cli/templates/memory-service/pyproject.toml.tmpl +0 -12
  113. keystone_cli-0.2.2/src/keystone_cli/templates/rag-qa/README.md.tmpl +0 -23
  114. keystone_cli-0.2.2/src/keystone_cli/templates/skill/pyproject.toml.tmpl +0 -12
  115. keystone_cli-0.2.2/src/keystone_cli/templates/tool-agent/README.md.tmpl +0 -31
  116. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/setup.cfg +0 -0
  117. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/__init__.py +0 -0
  118. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/_build_env.py +0 -0
  119. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/_stdio.py +0 -0
  120. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/_tls.py +0 -0
  121. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/_version.py +0 -0
  122. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/agent_skills.py +0 -0
  123. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/auth/__init__.py +0 -0
  124. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/auth/device_flow.py +0 -0
  125. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/commands/__init__.py +0 -0
  126. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/commands/dev.py +0 -0
  127. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/commands/login.py +0 -0
  128. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/commands/skills.py +0 -0
  129. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/devloop.py +0 -0
  130. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/platform_client.py +0 -0
  131. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/run_debug.py +0 -0
  132. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/scaffold.py +0 -0
  133. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/blank/env.tmpl +0 -0
  134. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/blank/gitignore.tmpl +0 -0
  135. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/blank/pkg/__init__.py.tmpl +0 -0
  136. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/blank/pkg/graph.py.tmpl +0 -0
  137. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/guardrails/env.tmpl +0 -0
  138. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/guardrails/gitignore.tmpl +0 -0
  139. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/guardrails/pkg/__init__.py.tmpl +0 -0
  140. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/guardrails/pkg/graph.py.tmpl +0 -0
  141. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/hitl/env.tmpl +0 -0
  142. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/hitl/gitignore.tmpl +0 -0
  143. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/hitl/pkg/__init__.py.tmpl +0 -0
  144. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/kb-builder/env.tmpl +0 -0
  145. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/kb-builder/gitignore.tmpl +0 -0
  146. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/kb-builder/pkg/__init__.py.tmpl +0 -0
  147. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/kb-builder/pkg/graph.py.tmpl +0 -0
  148. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm/env.tmpl +0 -0
  149. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm/gitignore.tmpl +0 -0
  150. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm/pkg/__init__.py.tmpl +0 -0
  151. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm-streaming/env.tmpl +0 -0
  152. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm-streaming/gitignore.tmpl +0 -0
  153. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/llm-streaming/pkg/__init__.py.tmpl +0 -0
  154. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory/agent.yaml.tmpl +0 -0
  155. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory/env.tmpl +0 -0
  156. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory/gitignore.tmpl +0 -0
  157. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory/pkg/__init__.py.tmpl +0 -0
  158. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory/pkg/graph.py.tmpl +0 -0
  159. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory-service/env.tmpl +0 -0
  160. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory-service/gitignore.tmpl +0 -0
  161. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory-service/pkg/__init__.py.tmpl +0 -0
  162. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/memory-service/pkg/graph.py.tmpl +0 -0
  163. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/ocr/env.tmpl +0 -0
  164. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/ocr/gitignore.tmpl +0 -0
  165. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/ocr/pkg/__init__.py.tmpl +0 -0
  166. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/ocr/pkg/graph.py.tmpl +0 -0
  167. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/rag-qa/env.tmpl +0 -0
  168. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/rag-qa/gitignore.tmpl +0 -0
  169. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/rag-qa/pkg/__init__.py.tmpl +0 -0
  170. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/skill/env.tmpl +0 -0
  171. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/skill/gitignore.tmpl +0 -0
  172. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/skill/pkg/__init__.py.tmpl +0 -0
  173. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/skill/pkg/graph.py.tmpl +0 -0
  174. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/tool-agent/env.tmpl +0 -0
  175. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/tool-agent/gitignore.tmpl +0 -0
  176. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli/templates/tool-agent/pkg/__init__.py.tmpl +0 -0
  177. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli.egg-info/dependency_links.txt +0 -0
  178. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli.egg-info/entry_points.txt +0 -0
  179. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/src/keystone_cli.egg-info/top_level.txt +0 -0
  180. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_assistant_provenance_and_flags.py +0 -0
  181. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_auth.py +0 -0
  182. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_build_progress.py +0 -0
  183. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_ci_token.py +0 -0
  184. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_config_env_mismatch.py +0 -0
  185. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_dev.py +0 -0
  186. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_encoding.py +0 -0
  187. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_hitl_approver_cli.py +0 -0
  188. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_hitl_decisions_cli.py +0 -0
  189. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_invoke.py +0 -0
  190. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_invoke_stream.py +0 -0
  191. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_kb_commands.py +0 -0
  192. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_kb_metadata_commands.py +0 -0
  193. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_login_command.py +0 -0
  194. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_platform_client.py +0 -0
  195. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_publish.py +0 -0
  196. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_retire_delete.py +0 -0
  197. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_run_debug.py +0 -0
  198. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_skills.py +0 -0
  199. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_status.py +0 -0
  200. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_stdio.py +0 -0
  201. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_threads.py +0 -0
  202. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_tls.py +0 -0
  203. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_tool_credentials.py +0 -0
  204. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_version.py +0 -0
  205. {keystone_cli-0.2.2 → keystone_cli-0.3.0}/tests/test_workspace.py +0 -0
@@ -1,13 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: keystone-cli
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: Keystone pro-code agent CLI — build, test, deploy and operate agents on the Keystone platform.
5
5
  Requires-Python: >=3.12
6
- Requires-Dist: keystone-agent-sdk<0.14,>=0.13.3
6
+ Requires-Dist: keystone-agent-sdk<0.14,>=0.13.10
7
7
  Requires-Dist: keystone-request-context<0.4,>=0.3
8
8
  Requires-Dist: typer<1,>=0.12
9
9
  Requires-Dist: httpx<1,>=0.27
10
10
  Requires-Dist: truststore<1,>=0.10
11
+ Requires-Dist: pytest<9,>=8
11
12
  Provides-Extra: dev
12
13
  Requires-Dist: pytest<9,>=8; extra == "dev"
13
14
  Requires-Dist: pytest-cov<7,>=6; extra == "dev"
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "keystone-cli"
7
- version = "0.2.2"
7
+ version = "0.3.0"
8
8
  description = "Keystone pro-code agent CLI — build, test, deploy and operate agents on the Keystone platform."
9
9
  requires-python = ">=3.12"
10
10
  dependencies = [
11
11
  # Pinned to a compatible range: on an index (Artifact Registry) an unpinned name resolves to
12
12
  # ANY published version — the wheel's metadata is the only pin that travels with it.
13
- "keystone-agent-sdk>=0.13.3,<0.14",
13
+ "keystone-agent-sdk>=0.13.10,<0.14",
14
14
  # Transitive (the SDK's building-block clients) + used directly by `run` to set a local
15
15
  # caller-identity ContextVar and to patch the clients for --offline. uv sources are NOT
16
16
  # transitive, so declare it directly (cf. services/llm-gateway).
@@ -21,6 +21,9 @@ dependencies = [
21
21
  # FDP-6328 — verify TLS through the OS trust store (keystone_cli/_tls.py), so a corporate CA in
22
22
  # the macOS Keychain / Windows store works without SSL_CERT_FILE. Pure Python, no dependencies.
23
23
  "truststore>=0.10,<1",
24
+ # FDP-6569 — `keystone agent test` runs the project's unit tests in this environment (the one
25
+ # `agent run` uses), so pytest ships with the CLI. anyio, their async plugin, comes with httpx.
26
+ "pytest>=8,<9",
24
27
  ]
25
28
 
26
29
  [project.optional-dependencies]
@@ -45,10 +48,10 @@ where = ["src"]
45
48
 
46
49
  # `agent init` templates ride INSIDE the wheel (offline scaffold — no network fetch).
47
50
  [tool.setuptools.package-data]
48
- keystone_cli = ["templates/*/*.tmpl", "templates/*/pkg/*.tmpl", "skills/*/SKILL.md"]
51
+ keystone_cli = ["templates/*/*.tmpl", "templates/*/pkg/*.tmpl", "templates/*/tests/*.tmpl", "skills/*/SKILL.md"]
49
52
 
50
53
  [tool.ruff]
51
- target-version = "0.2.2"
54
+ target-version = "0.3.0"
52
55
  line-length = 120
53
56
 
54
57
  [tool.ruff.lint]
@@ -11,6 +11,7 @@ from keystone_cli.commands.agent import agent_app
11
11
  from keystone_cli.commands.dev import dev
12
12
  from keystone_cli.commands.kb import kb_app
13
13
  from keystone_cli.commands.login import login
14
+ from keystone_cli.commands.prompt import prompt_app
14
15
  from keystone_cli.commands.skills import skills_app
15
16
  from keystone_cli.commands.tool_credentials import tool_credentials_app
16
17
  from keystone_cli.commands.whoami import whoami
@@ -21,8 +22,47 @@ from keystone_cli.commands.workspace import workspace_app
21
22
  # late for the very invocation QC reported first. No-op on a console that already copes.
22
23
  make_stdio_unicode_safe()
23
24
 
25
+ #: Shown under the command list (FDP-6604): where to start, what changes the CLI, what the exit codes
26
+ #: mean. Every KEYSTONE_* variable the code reads is listed — test_help_quality enforces it.
27
+ EPILOG = """\b
28
+ Get started:
29
+ keystone login
30
+ keystone workspace use <name>
31
+ keystone agent init my-agent --template llm
32
+ keystone agent validate my-agent
33
+ keystone agent test my-agent
34
+ keystone agent deploy my-agent --publish --wait
35
+ keystone agent invoke my-agent --input '{"prompt": "hi"}'
36
+ Fields of agent.yaml: keystone agent manifest. Each command: --help.
37
+
38
+ \b
39
+ Environment:
40
+ KEYSTONE_CONFIG config file (default ~/.keystone/config.json);
41
+ use one file per environment
42
+ KEYSTONE_API_BASE platform API URL (overrides the saved one)
43
+ KEYSTONE_AGENT_API_BASE agent API URL: invoke, runs, hitl, threads
44
+ KEYSTONE_TOKEN access token, used as given and never renewed
45
+ (for CI)
46
+ KEYSTONE_ISSUER login: Keycloak realm URL
47
+ KEYSTONE_CLIENT_ID login: client id
48
+ KEYSTONE_CLIENT_SECRET login --client-credentials: client secret
49
+ KEYSTONE_NO_BROWSER login: do not open a browser
50
+ KEYSTONE_SYSTEM_TRUST 0: do not trust the OS certificate store
51
+
52
+ \b
53
+ Exit codes:
54
+ 0 done
55
+ 1 failed (the message says why)
56
+ 2 usage error: an unknown command or option, a missing
57
+ argument, or a field name that agent manifest does not know
58
+ agent test returns the exit code of pytest. agent fetch returns 1 when
59
+ it stops early, and still writes what it got.
60
+ With --json, a failed command prints {"error": ..., "exit_code": ...}
61
+ on stdout and the message on stderr."""
62
+
24
63
  app = typer.Typer(
25
64
  help="Keystone CLI — build, test, deploy and operate pro-code agents.",
65
+ epilog=EPILOG,
26
66
  no_args_is_help=True,
27
67
  add_completion=False,
28
68
  )
@@ -66,6 +106,7 @@ app.add_typer(workspace_app, name="workspace")
66
106
  app.add_typer(kb_app, name="kb")
67
107
  app.add_typer(tool_credentials_app, name="tool-credentials")
68
108
  app.add_typer(skills_app, name="skills")
109
+ app.add_typer(prompt_app, name="prompt")
69
110
  app.command(name="login")(login)
70
111
  app.command(name="dev")(dev)
71
112
  app.command(name="whoami")(whoami)
@@ -0,0 +1,161 @@
1
+ """``--json`` output shared by the read commands (FDP-6604).
2
+
3
+ A script or a coding agent should not parse a table. Every list / get / status / current command
4
+ prints the API's own answer with ``--json`` — the same body, not a reshaped copy, so it carries
5
+ every field the table leaves out. ``test_help_quality`` enforces that these commands have the flag.
6
+
7
+ :func:`json_errors` keeps stdout parseable while such a command runs: see its docstring.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import contextlib
13
+ import functools
14
+ import io
15
+ import json
16
+ import re
17
+ import sys
18
+ from collections.abc import Callable
19
+ from typing import Any, TextIO, cast
20
+
21
+ import typer
22
+
23
+ #: What every ``--json`` help says about a failure (a command with its own --json text appends it).
24
+ JSON_FAIL_NOTE = (
25
+ 'If the command fails before its answer, it prints {"error": ..., "exit_code": ...}; '
26
+ "a stream ends with that object."
27
+ )
28
+ JSON_HELP = f"Print the raw JSON answer instead of a table, for scripts and agents. {JSON_FAIL_NOTE}"
29
+
30
+ #: The names a command's ``--json`` flag has in this CLI (``prompt`` and ``kb files`` use as_json).
31
+ JSON_PARAMS = ("json_out", "as_json")
32
+
33
+ _ANSI = re.compile(r"\x1b\[[0-9;]*m")
34
+
35
+
36
+ class _JsonRun:
37
+ """One ``--json`` command run: the real stdout, the text it printed elsewhere, what it answered."""
38
+
39
+ def __init__(self, stdout: TextIO) -> None:
40
+ self.stdout = stdout
41
+ self.text: list[str] = []
42
+ self.printed = False
43
+ self.streamed = False
44
+
45
+ def emit(self, text: str) -> None:
46
+ self.printed = True
47
+ self.stdout.write(text + "\n")
48
+ self.stdout.flush()
49
+
50
+ def error_text(self) -> str:
51
+ """From the last ✗ line to the end: the reason it stopped, with the lines that explain it."""
52
+ lines = [_ANSI.sub("", ln).strip() for ln in "".join(self.text).splitlines()]
53
+ lines = [ln for ln in lines if ln]
54
+ last = max((i for i, ln in enumerate(lines) if ln.startswith("✗")), default=len(lines) - 1)
55
+ reason = " ".join(lines[max(last, 0) :])
56
+ return reason.lstrip("✗").strip() or "the command failed"
57
+
58
+ def fail(self, exit_code: int, error: str | None = None) -> None:
59
+ # A one-document answer stays as it is (runs get of a failed run). A stream gets the error
60
+ # as its last line, so a consumer can tell a broken stream from one that ended.
61
+ if not self.printed or self.streamed:
62
+ self.emit(json.dumps({"error": error or self.error_text(), "exit_code": exit_code}, ensure_ascii=False))
63
+
64
+
65
+ class _Recorded(io.TextIOBase):
66
+ """stdout and stderr while a ``--json`` command runs: written to stderr, and kept for the error."""
67
+
68
+ def __init__(self, target: TextIO, run: _JsonRun) -> None:
69
+ self._target = target
70
+ self._run = run
71
+
72
+ def write(self, s: str | bytes) -> int: # ty: click may hand bytes to a stream it cannot classify
73
+ text = s.decode("utf-8", "replace") if isinstance(s, bytes) else s
74
+ self._run.text.append(text)
75
+ self._target.write(text)
76
+ return len(s)
77
+
78
+ def flush(self) -> None:
79
+ self._target.flush()
80
+
81
+ def isatty(self) -> bool:
82
+ return self._target.isatty()
83
+
84
+
85
+ #: The ``--json`` run in progress. A module global, not a ContextVar: one command runs per process,
86
+ #: and a worker thread (fetch-style thread pools) must see it too, or its answer would go to stderr.
87
+ _CURRENT: _JsonRun | None = None
88
+
89
+
90
+ def echo_json(value: Any) -> None:
91
+ """The answer of a ``--json`` command: the only output that reaches stdout while it runs."""
92
+ _write(json.dumps(value, indent=2, ensure_ascii=False, default=str))
93
+
94
+
95
+ def echo_json_line(value: Any) -> None:
96
+ """One JSON object on one line, for a command that streams (``logs --follow --json``)."""
97
+ if _CURRENT is not None:
98
+ _CURRENT.streamed = True
99
+ _write(json.dumps(value, ensure_ascii=False, default=str))
100
+
101
+
102
+ def _write(text: str) -> None:
103
+ if _CURRENT is None:
104
+ typer.echo(text)
105
+ else:
106
+ _CURRENT.emit(text)
107
+
108
+
109
+ def _exception_exit(exc: Exception) -> tuple[int, str]:
110
+ """A click-style exception (BadParameter, UsageError...) carries its exit code and message."""
111
+ code = getattr(exc, "exit_code", None)
112
+ format_message = getattr(exc, "format_message", None)
113
+ message = format_message() if callable(format_message) else f"{type(exc).__name__}: {exc}"
114
+ return (code if isinstance(code, int) and code else 1), message
115
+
116
+
117
+ def json_errors[F: Callable[..., Any]](fn: F) -> F:
118
+ """Keep stdout JSON only while a command runs with ``--json``.
119
+
120
+ * stdout carries only :func:`echo_json` / :func:`echo_json_line`. Everything else the command
121
+ prints — ✗ / ! lines, hints, a confirm prompt — goes to stderr, so it cannot break the JSON.
122
+ A streaming command still streams: each answer line is written at once.
123
+ * If the command stops before its answer, stdout gets
124
+ ``{"error": <from its last ✗ line>, "exit_code": <code>}``: on a non-zero exit, an abort, or
125
+ an exception (raised again, so a traceback still shows on stderr). A command that printed
126
+ its one answer and then failed (``runs get`` of a failed run) keeps it; a stream gets the
127
+ error object as its last line.
128
+
129
+ A usage error (an unknown option, exit 2) happens before the command runs; Typer prints it to
130
+ stderr. ``test_help_quality`` checks that every command with ``--json`` has this decorator.
131
+ """
132
+
133
+ @functools.wraps(fn)
134
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
135
+ global _CURRENT # noqa: PLW0603 — one command per process; reset in `finally`
136
+ if not any(kwargs.get(name) for name in JSON_PARAMS):
137
+ return fn(*args, **kwargs)
138
+ run = _JsonRun(sys.stdout)
139
+ stderr = sys.stderr
140
+ _CURRENT = run
141
+ try:
142
+ with (
143
+ contextlib.redirect_stdout(cast("TextIO", _Recorded(stderr, run))),
144
+ contextlib.redirect_stderr(cast("TextIO", _Recorded(stderr, run))),
145
+ ):
146
+ return fn(*args, **kwargs)
147
+ except typer.Exit as exc:
148
+ if exc.exit_code:
149
+ run.fail(exc.exit_code)
150
+ raise
151
+ except typer.Abort:
152
+ run.fail(1, "aborted")
153
+ raise
154
+ except Exception as exc:
155
+ run.fail(*_exception_exit(exc))
156
+ raise
157
+ finally:
158
+ _CURRENT = None
159
+
160
+ setattr(wrapper, "_json_errors", True) # noqa: B010 — read by test_help_quality
161
+ return cast("F", wrapper)
@@ -147,10 +147,25 @@ def explain(resp: httpx.Response) -> str:
147
147
  return f"{head}: {message}" + (f" — {hint}" if hint else "")
148
148
 
149
149
 
150
+ #: A function that returns valid auth headers per request (a long paging run outlives a 300 s token).
151
+ HeaderSource = Callable[[], dict[str, str]]
152
+ #: Headers, or a source of them.
153
+ Headers = dict[str, str] | HeaderSource
154
+
155
+
156
+ def merge_lines(*batches: list[dict[str, Any]], limit: int) -> tuple[list[dict[str, Any]], bool]:
157
+ """Batches of lines → unique by line id, oldest first, the newest ``limit`` kept.
158
+ Returns ``(lines, cut)``; ``cut`` when the union held more than ``limit``."""
159
+ merged = {str(line.get("id")): line for batch in batches for line in batch}
160
+ epoch = datetime(1970, 1, 1, tzinfo=UTC) # by parsed time: mixed-precision ISO stamps mis-sort as text
161
+ lines = sorted(merged.values(), key=lambda ln: (parse_stamp(ln.get("timestamp")) or epoch, str(ln.get("id"))))
162
+ return (lines[-limit:] if limit else []), len(lines) > limit
163
+
164
+
150
165
  def fetch_history(
151
166
  client: httpx.Client,
152
167
  base: str,
153
- headers: dict[str, str],
168
+ headers: Headers,
154
169
  agent_id: str,
155
170
  *,
156
171
  start: datetime,
@@ -173,7 +188,8 @@ def fetch_history(
173
188
  params = [("from", iso(start)), ("to", iso(end)), ("limit", str(min(MAX_PAGE, limit - len(lines))))]
174
189
  if before:
175
190
  params.append(("before", before))
176
- resp = client.get(url, headers=headers, params=[*params, *filters.params()])
191
+ page_headers = headers if isinstance(headers, dict) else headers()
192
+ resp = client.get(url, headers=page_headers, params=[*params, *filters.params()])
177
193
  if resp.status_code != 200:
178
194
  raise LogsError(explain(resp))
179
195
  body = resp.json() or {}
@@ -194,7 +210,7 @@ def fetch_history(
194
210
  def fetch_run_history(
195
211
  client: httpx.Client,
196
212
  base: str,
197
- headers: dict[str, str],
213
+ headers: Headers,
198
214
  agent_id: str,
199
215
  *,
200
216
  start: datetime,
@@ -235,11 +251,8 @@ def fetch_run_history(
235
251
  filters=dataclasses.replace(filters, run_id=None, correlation_id=trace_id),
236
252
  limit=limit,
237
253
  )
238
- merged = {str(line.get("id")): line for line in [*by_run, *by_trace]}
239
- epoch = datetime(1970, 1, 1, tzinfo=UTC) # by parsed time: mixed-precision ISO stamps mis-sort as text
240
- lines = sorted(merged.values(), key=lambda ln: (parse_stamp(ln.get("timestamp")) or epoch, str(ln.get("id"))))
241
- more = more_run or more_trace or len(lines) > limit
242
- return lines[-limit:], more
254
+ lines, cut = merge_lines(by_run, by_trace, limit=limit)
255
+ return lines, more_run or more_trace or cut
243
256
 
244
257
 
245
258
  def iter_sse(chunks: Iterator[str]) -> Iterator[tuple[str, str]]:
@@ -275,7 +288,7 @@ class FollowState:
275
288
  def follow(
276
289
  client: httpx.Client,
277
290
  base: str,
278
- headers: dict[str, str],
291
+ headers: dict[str, str] | Callable[[], dict[str, str]],
279
292
  agent_id: str,
280
293
  *,
281
294
  state: FollowState,
@@ -297,7 +310,10 @@ def follow(
297
310
  up — a timer on the connection — and the read that was waiting returns at once. The read
298
311
  timeout still bounds the wait for the response headers (the server backfills before sending
299
312
  them). An idle stream is never torn down early, so no extra reconnects (each costs a Splunk
300
- backfill and a rate-limit slot)."""
313
+ backfill and a rate-limit slot).
314
+
315
+ ``headers`` may be a callable, asked again for every connection (FDP-6283): a reconnect comes
316
+ ~15 min after the first connection, long after a 300 s access token has expired."""
301
317
  url = base + STREAM_PATH.format(agent_id=agent_id)
302
318
  while True:
303
319
  params = [("since", iso(state.since)), *filters.params()]
@@ -309,7 +325,7 @@ def follow(
309
325
  reason = _one_stream(
310
326
  client,
311
327
  url,
312
- headers,
328
+ headers if isinstance(headers, dict) else headers(),
313
329
  params,
314
330
  timeout=timeout,
315
331
  close_after=left,
@@ -149,6 +149,12 @@ def config_path() -> Path:
149
149
  return Path(override) if override else Path.home() / ".keystone" / "config.json"
150
150
 
151
151
 
152
+ def platform_base(cfg: AuthConfig, override: str | None = None) -> str:
153
+ """Platform API URL: ``override`` (a command's ``--api-base``), then ``KEYSTONE_API_BASE``, then
154
+ the saved one, then the build default. The one place this order is written."""
155
+ return (override or os.environ.get("KEYSTONE_API_BASE") or cfg.api_base or DEFAULT_API_BASE).rstrip("/")
156
+
157
+
152
158
  def load() -> AuthConfig:
153
159
  path = config_path()
154
160
  if not path.exists():