graph-agents-cli 0.3.1__py3-none-any.whl

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 (291) hide show
  1. graph_agents_cli/__init__.py +26 -0
  2. graph_agents_cli/_api_policy.py +2145 -0
  3. graph_agents_cli/_approvals.py +400 -0
  4. graph_agents_cli/_build.py +186 -0
  5. graph_agents_cli/_build_info.json +7 -0
  6. graph_agents_cli/_chat_client.py +462 -0
  7. graph_agents_cli/_click.py +157 -0
  8. graph_agents_cli/_defaults.py +139 -0
  9. graph_agents_cli/_experiments.py +64 -0
  10. graph_agents_cli/_http.py +192 -0
  11. graph_agents_cli/_output.py +83 -0
  12. graph_agents_cli/_project.py +462 -0
  13. graph_agents_cli/_remote.py +220 -0
  14. graph_agents_cli/_response_schema.py +264 -0
  15. graph_agents_cli/_runner.py +319 -0
  16. graph_agents_cli/_skills_check.py +274 -0
  17. graph_agents_cli/_tools.py +189 -0
  18. graph_agents_cli/_trust.py +66 -0
  19. graph_agents_cli/api/__init__.py +15 -0
  20. graph_agents_cli/api/_changes.py +506 -0
  21. graph_agents_cli/api/_files.py +658 -0
  22. graph_agents_cli/api/cmd_api.py +2480 -0
  23. graph_agents_cli/deploy/__init__.py +15 -0
  24. graph_agents_cli/deploy/_config.py +171 -0
  25. graph_agents_cli/deploy/_image.py +128 -0
  26. graph_agents_cli/deploy/_kube.py +286 -0
  27. graph_agents_cli/deploy/_modes.py +234 -0
  28. graph_agents_cli/deploy/_preflight.py +370 -0
  29. graph_agents_cli/deploy/_values.py +168 -0
  30. graph_agents_cli/deploy/cmd_deploy.py +1866 -0
  31. graph_agents_cli/deploy/gitops.py +562 -0
  32. graph_agents_cli/deploy/local_load.py +273 -0
  33. graph_agents_cli/dev/__init__.py +13 -0
  34. graph_agents_cli/dev/cmd_build.py +131 -0
  35. graph_agents_cli/dev/cmd_install.py +78 -0
  36. graph_agents_cli/dev/cmd_lint.py +119 -0
  37. graph_agents_cli/dev/cmd_playground.py +297 -0
  38. graph_agents_cli/dev/policy_check.py +1287 -0
  39. graph_agents_cli/eval/__init__.py +22 -0
  40. graph_agents_cli/eval/_client.py +670 -0
  41. graph_agents_cli/eval/_common.py +177 -0
  42. graph_agents_cli/eval/_judge.py +168 -0
  43. graph_agents_cli/eval/_judge_runner.py +238 -0
  44. graph_agents_cli/eval/_paths.py +212 -0
  45. graph_agents_cli/eval/checks.py +581 -0
  46. graph_agents_cli/eval/cmd_analyze.py +278 -0
  47. graph_agents_cli/eval/cmd_compare.py +284 -0
  48. graph_agents_cli/eval/cmd_eval_group.py +80 -0
  49. graph_agents_cli/eval/cmd_generate.py +558 -0
  50. graph_agents_cli/eval/cmd_grade.py +466 -0
  51. graph_agents_cli/eval/cmd_metric.py +156 -0
  52. graph_agents_cli/eval/cmd_run.py +370 -0
  53. graph_agents_cli/eval/cmd_submit.py +400 -0
  54. graph_agents_cli/eval/config.py +435 -0
  55. graph_agents_cli/eval/dataset.py +350 -0
  56. graph_agents_cli/eval/gate.py +420 -0
  57. graph_agents_cli/eval/transcript.py +192 -0
  58. graph_agents_cli/extension/__init__.py +13 -0
  59. graph_agents_cli/extension/_compat.py +86 -0
  60. graph_agents_cli/extension/_loader.py +293 -0
  61. graph_agents_cli/extension/_manifest.py +135 -0
  62. graph_agents_cli/extension/_overrides.py +195 -0
  63. graph_agents_cli/extension/_paths.py +91 -0
  64. graph_agents_cli/extension/_refs.py +193 -0
  65. graph_agents_cli/extension/_resolver.py +453 -0
  66. graph_agents_cli/extension/_schema.py +106 -0
  67. graph_agents_cli/extension/_spec.py +253 -0
  68. graph_agents_cli/extension/_sync.py +102 -0
  69. graph_agents_cli/extension/_trust.py +58 -0
  70. graph_agents_cli/extension/cmd_extension_add.py +259 -0
  71. graph_agents_cli/extension/cmd_extension_group.py +57 -0
  72. graph_agents_cli/extension/cmd_extension_list.py +56 -0
  73. graph_agents_cli/extension/cmd_extension_remove.py +61 -0
  74. graph_agents_cli/extension/cmd_extension_update.py +195 -0
  75. graph_agents_cli/info/__init__.py +13 -0
  76. graph_agents_cli/info/cmd_info.py +222 -0
  77. graph_agents_cli/infra/__init__.py +15 -0
  78. graph_agents_cli/infra/checks.py +1169 -0
  79. graph_agents_cli/infra/cmd_infra.py +103 -0
  80. graph_agents_cli/main.py +591 -0
  81. graph_agents_cli/peer/__init__.py +15 -0
  82. graph_agents_cli/peer/_generate.py +254 -0
  83. graph_agents_cli/peer/cmd_peer.py +1151 -0
  84. graph_agents_cli/run/__init__.py +13 -0
  85. graph_agents_cli/run/_local_server.py +1157 -0
  86. graph_agents_cli/run/_signals.py +141 -0
  87. graph_agents_cli/run/cmd_approvals.py +530 -0
  88. graph_agents_cli/run/cmd_run.py +1421 -0
  89. graph_agents_cli/scaffold/__init__.py +19 -0
  90. graph_agents_cli/scaffold/agents/README.md +24 -0
  91. graph_agents_cli/scaffold/agents/empty_py/.template/templateconfig.yaml +22 -0
  92. graph_agents_cli/scaffold/agents/langgraph/.env.example +292 -0
  93. graph_agents_cli/scaffold/agents/langgraph/.template/templateconfig.yaml +28 -0
  94. graph_agents_cli/scaffold/agents/langgraph/Dockerfile +59 -0
  95. graph_agents_cli/scaffold/agents/langgraph/Dockerfile.langgraph-server +59 -0
  96. graph_agents_cli/scaffold/agents/langgraph/README.md +571 -0
  97. graph_agents_cli/scaffold/agents/langgraph/api-policy.yaml +60 -0
  98. graph_agents_cli/scaffold/agents/langgraph/app/__init__.py +20 -0
  99. graph_agents_cli/scaffold/agents/langgraph/app/agent.py +174 -0
  100. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/__init__.py +15 -0
  101. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/a2a.py +2162 -0
  102. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/a2a_client.py +1167 -0
  103. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/api_client.py +4220 -0
  104. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/approvals.py +1349 -0
  105. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/auth.py +1986 -0
  106. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/chat.py +2962 -0
  107. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/checkpointer.py +432 -0
  108. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/content.py +569 -0
  109. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/db.py +580 -0
  110. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/limits.py +203 -0
  111. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/metrics.py +231 -0
  112. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/middleware.py +361 -0
  113. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/model.py +611 -0
  114. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/playground.py +230 -0
  115. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/run_locks.py +459 -0
  116. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/structured.py +755 -0
  117. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/telemetry.py +681 -0
  118. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/threads.py +493 -0
  119. graph_agents_cli/scaffold/agents/langgraph/app/app_utils/token_exchange.py +959 -0
  120. graph_agents_cli/scaffold/agents/langgraph/app/fast_api_app.py +770 -0
  121. graph_agents_cli/scaffold/agents/langgraph/app/policies/__init__.py +55 -0
  122. graph_agents_cli/scaffold/agents/langgraph/app/policies/custom.py +97 -0
  123. graph_agents_cli/scaffold/agents/langgraph/app/tools/__init__.py +46 -0
  124. graph_agents_cli/scaffold/agents/langgraph/app/tools/example_api.py +92 -0
  125. graph_agents_cli/scaffold/agents/langgraph/app/tools/weather.py +33 -0
  126. graph_agents_cli/scaffold/agents/langgraph/langgraph.json +14 -0
  127. graph_agents_cli/scaffold/agents/langgraph/pyproject.toml +78 -0
  128. graph_agents_cli/scaffold/agents/langgraph/tests/conftest.py +376 -0
  129. graph_agents_cli/scaffold/agents/langgraph/tests/eval/datasets/basic-dataset.json +53 -0
  130. graph_agents_cli/scaffold/agents/langgraph/tests/eval/eval_config.yaml +32 -0
  131. graph_agents_cli/scaffold/agents/langgraph/tests/integration/approval_graph.py +137 -0
  132. graph_agents_cli/scaffold/agents/langgraph/tests/integration/fake_issuer.py +216 -0
  133. graph_agents_cli/scaffold/agents/langgraph/tests/integration/fake_openai.py +357 -0
  134. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_a2a_outcomes.py +569 -0
  135. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_a2a_relay.py +479 -0
  136. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_api_surface.py +812 -0
  137. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_approvals.py +1367 -0
  138. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_approvals_server.py +794 -0
  139. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_cross_actor.py +497 -0
  140. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_cross_actor_server.py +247 -0
  141. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_history_repair.py +278 -0
  142. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_model_apis.py +242 -0
  143. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_postgres.py +637 -0
  144. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_resilience_postgres.py +770 -0
  145. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_runtime_guardrails.py +854 -0
  146. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_server_e2e.py +340 -0
  147. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_server_runtime.py +989 -0
  148. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_structured_answers.py +584 -0
  149. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_structured_server.py +222 -0
  150. graph_agents_cli/scaffold/agents/langgraph/tests/integration/test_token_exchange_issuer.py +650 -0
  151. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/.results/.placeholder +0 -0
  152. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/README.md +22 -0
  153. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/conftest.py +21 -0
  154. graph_agents_cli/scaffold/agents/langgraph/tests/load_test/load_test.py +81 -0
  155. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_a2a_client.py +824 -0
  156. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_a2a_scoping.py +724 -0
  157. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_client.py +1214 -0
  158. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_client_hardening.py +716 -0
  159. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_api_policy_rpc.py +767 -0
  160. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_approval_ledger.py +1536 -0
  161. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_fake_model.py +115 -0
  162. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_jwt_policy.py +991 -0
  163. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_limits.py +310 -0
  164. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_logging.py +148 -0
  165. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_logging_hardening.py +271 -0
  166. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_policy.py +378 -0
  167. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_resilience.py +610 -0
  168. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_server_auth.py +702 -0
  169. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_structured.py +673 -0
  170. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_telemetry.py +404 -0
  171. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_thread_listing.py +255 -0
  172. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_threads.py +268 -0
  173. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_token_exchange.py +1320 -0
  174. graph_agents_cli/scaffold/agents/langgraph/tests/unit/test_untrusted_content.py +393 -0
  175. graph_agents_cli/scaffold/agents/langgraph/uv-fastapi.lock +2084 -0
  176. graph_agents_cli/scaffold/agents/langgraph/uv-langgraph-server.lock +2106 -0
  177. graph_agents_cli/scaffold/agents/langgraph/{{cookiecutter.agent_guidance_filename}} +129 -0
  178. graph_agents_cli/scaffold/base_templates/_shared/graph-agents-cli-manifest.yaml +36 -0
  179. graph_agents_cli/scaffold/base_templates/python/.dockerignore +32 -0
  180. graph_agents_cli/scaffold/base_templates/python/.github/CODEOWNERS +30 -0
  181. graph_agents_cli/scaffold/base_templates/python/.github/agent.env +7 -0
  182. graph_agents_cli/scaffold/base_templates/python/.github/workflows/pr_checks.yaml +214 -0
  183. graph_agents_cli/scaffold/base_templates/python/.gitignore +209 -0
  184. graph_agents_cli/scaffold/base_templates/python/tests/unit/test_dummy.py +23 -0
  185. graph_agents_cli/scaffold/base_templates/python/{{cookiecutter.agent_guidance_filename}} +35 -0
  186. graph_agents_cli/scaffold/cmd_scaffold_group.py +49 -0
  187. graph_agents_cli/scaffold/commands/__init__.py +13 -0
  188. graph_agents_cli/scaffold/commands/create.py +1424 -0
  189. graph_agents_cli/scaffold/commands/enhance.py +1652 -0
  190. graph_agents_cli/scaffold/commands/upgrade.py +570 -0
  191. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/agent.env +12 -0
  192. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/workflows/promote-to-prod.yaml +371 -0
  193. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/.github/workflows/staging.yaml +450 -0
  194. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-dev.yaml +43 -0
  195. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-prod.yaml +41 -0
  196. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/argocd/application-staging.yaml +43 -0
  197. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/.helmignore +14 -0
  198. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/Chart.yaml +21 -0
  199. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/examples/networkpolicy.yaml +103 -0
  200. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/NOTES.txt +48 -0
  201. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/_helpers.tpl +189 -0
  202. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/certificate.yaml +15 -0
  203. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/configmap.yaml +10 -0
  204. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/deployment.yaml +199 -0
  205. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/hpa.yaml +22 -0
  206. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/httproute.yaml +30 -0
  207. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/ingress.yaml +39 -0
  208. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/networkpolicy.yaml +48 -0
  209. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/pdb.yaml +13 -0
  210. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/postgresql-secret.yaml +37 -0
  211. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/service.yaml +15 -0
  212. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/serviceaccount.yaml +13 -0
  213. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/templates/servicemonitor.yaml +42 -0
  214. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-dev.yaml +22 -0
  215. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-prod.yaml +45 -0
  216. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values-staging.yaml +29 -0
  217. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/deployment/helm/{{cookiecutter.project_name}}/values.yaml +396 -0
  218. graph_agents_cli/scaffold/deployment_targets/kubernetes/python/tests/integration/test_chart.py +269 -0
  219. graph_agents_cli/scaffold/deployment_targets/none/README.md +5 -0
  220. graph_agents_cli/scaffold/deployment_targets/none/python/README.md +6 -0
  221. graph_agents_cli/scaffold/utils/__init__.py +13 -0
  222. graph_agents_cli/scaffold/utils/backup.py +212 -0
  223. graph_agents_cli/scaffold/utils/build_record.py +257 -0
  224. graph_agents_cli/scaffold/utils/cli_options.py +184 -0
  225. graph_agents_cli/scaffold/utils/fs.py +83 -0
  226. graph_agents_cli/scaffold/utils/generate_locks.py +214 -0
  227. graph_agents_cli/scaffold/utils/generation_metadata.py +88 -0
  228. graph_agents_cli/scaffold/utils/keyedit.py +768 -0
  229. graph_agents_cli/scaffold/utils/keymerge.py +537 -0
  230. graph_agents_cli/scaffold/utils/language.py +138 -0
  231. graph_agents_cli/scaffold/utils/lock_utils.py +94 -0
  232. graph_agents_cli/scaffold/utils/logging.py +77 -0
  233. graph_agents_cli/scaffold/utils/manifest.py +292 -0
  234. graph_agents_cli/scaffold/utils/merge.py +970 -0
  235. graph_agents_cli/scaffold/utils/merge3.py +216 -0
  236. graph_agents_cli/scaffold/utils/openapi_seed.py +199 -0
  237. graph_agents_cli/scaffold/utils/remote_template.py +376 -0
  238. graph_agents_cli/scaffold/utils/template.py +1352 -0
  239. graph_agents_cli/scaffold/utils/upgrade.py +894 -0
  240. graph_agents_cli/scaffold/utils/version.py +438 -0
  241. graph_agents_cli/secrets/__init__.py +15 -0
  242. graph_agents_cli/secrets/_apply.py +954 -0
  243. graph_agents_cli/secrets/_required.py +188 -0
  244. graph_agents_cli/secrets/cmd_secrets.py +211 -0
  245. graph_agents_cli/setup/__init__.py +13 -0
  246. graph_agents_cli/setup/_antigravity.py +221 -0
  247. graph_agents_cli/setup/cmd_auth.py +1030 -0
  248. graph_agents_cli/setup/cmd_dev_token.py +513 -0
  249. graph_agents_cli/setup/cmd_setup.py +428 -0
  250. graph_agents_cli/setup/cmd_update.py +140 -0
  251. graph_agents_cli/skills/__init__.py +13 -0
  252. graph_agents_cli/skills/_bundle.py +65 -0
  253. graph_agents_cli/skills/data/README.md +19 -0
  254. graph_agents_cli/skills/data/graph-agents-cli-deploy/SKILL.md +357 -0
  255. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/github-settings.md +113 -0
  256. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/gitops.md +137 -0
  257. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/kubernetes.md +315 -0
  258. graph_agents_cli/skills/data/graph-agents-cli-deploy/references/secrets.md +160 -0
  259. graph_agents_cli/skills/data/graph-agents-cli-eval/SKILL.md +303 -0
  260. graph_agents_cli/skills/data/graph-agents-cli-eval/references/dataset_schema.md +282 -0
  261. graph_agents_cli/skills/data/graph-agents-cli-eval/references/metrics-guide.md +143 -0
  262. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/SKILL.md +659 -0
  263. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/langchain-models.md +124 -0
  264. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/langgraph.md +235 -0
  265. graph_agents_cli/skills/data/graph-agents-cli-langgraph-code/references/template-contract.md +477 -0
  266. graph_agents_cli/skills/data/graph-agents-cli-observability/SKILL.md +231 -0
  267. graph_agents_cli/skills/data/graph-agents-cli-observability/references/langsmith.md +46 -0
  268. graph_agents_cli/skills/data/graph-agents-cli-observability/references/otel.md +59 -0
  269. graph_agents_cli/skills/data/graph-agents-cli-scaffold/SKILL.md +414 -0
  270. graph_agents_cli/skills/data/graph-agents-cli-scaffold/references/flags.md +134 -0
  271. graph_agents_cli/skills/data/graph-agents-cli-workflow/SKILL.md +478 -0
  272. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/brainstorming.md +118 -0
  273. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/commands.md +419 -0
  274. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/extension.md +156 -0
  275. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/internals.md +67 -0
  276. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/spec-template.md +56 -0
  277. graph_agents_cli/skills/data/graph-agents-cli-workflow/references/terminology.md +119 -0
  278. graph_agents_cli/system/__init__.py +15 -0
  279. graph_agents_cli/system/_apply.py +519 -0
  280. graph_agents_cli/system/_checks.py +1023 -0
  281. graph_agents_cli/system/_deploy.py +215 -0
  282. graph_agents_cli/system/_model.py +363 -0
  283. graph_agents_cli/system/_system.py +664 -0
  284. graph_agents_cli/system/_views.py +208 -0
  285. graph_agents_cli/system/cmd_system.py +423 -0
  286. graph_agents_cli-0.3.1.dist-info/METADATA +162 -0
  287. graph_agents_cli-0.3.1.dist-info/RECORD +291 -0
  288. graph_agents_cli-0.3.1.dist-info/WHEEL +4 -0
  289. graph_agents_cli-0.3.1.dist-info/entry_points.txt +2 -0
  290. graph_agents_cli-0.3.1.dist-info/licenses/LICENSE +201 -0
  291. graph_agents_cli-0.3.1.dist-info/licenses/NOTICE +19 -0
@@ -0,0 +1,86 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Evaluate an extension's declared graph-agents-cli compatibility range.
16
+
17
+ An extension may declare ``requires.agents_cli`` (e.g. ``">=1.1,<2"``), which is a
18
+ PEP 440 specifier set evaluated against the running CLI version.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import logging
24
+
25
+ from packaging.specifiers import InvalidSpecifier, SpecifierSet
26
+ from packaging.version import InvalidVersion, Version
27
+
28
+
29
+ def parse_range(spec: str) -> SpecifierSet | None:
30
+ """Parse a declared range, or None if it is unusable.
31
+
32
+ The single place a range string is interpreted, so validation and the
33
+ compatibility check cannot disagree about what is parseable.
34
+ """
35
+ if not spec.strip():
36
+ # packaging accepts "" as "any version"; an empty range in a manifest is
37
+ # an authoring mistake, so drop it rather than record a no-op.
38
+ return None
39
+ try:
40
+ return SpecifierSet(spec)
41
+ except InvalidSpecifier:
42
+ return None
43
+
44
+
45
+ def validate_range(spec: str) -> bool:
46
+ """True iff ``spec`` is a usable version range."""
47
+ return parse_range(spec) is not None
48
+
49
+
50
+ def is_compatible(version: str, spec: str | None) -> bool:
51
+ """Return whether ``version`` satisfies the range ``spec``.
52
+
53
+ Assumes ``spec`` already passed ``validate_range``: ``_spec._parse_requires``
54
+ warns and drops a bad range at parse time, so an unparseable one never
55
+ reaches here (and is treated as no constraint if it somehow does).
56
+
57
+ No constraint means compatible. A source build (``DEV_VERSION``, or any
58
+ version packaging can't read) is always compatible: running from a checkout
59
+ must never be blocked by an extension's range.
60
+ """
61
+ if not spec:
62
+ return True
63
+ try:
64
+ running = Version(version)
65
+ except InvalidVersion:
66
+ # The *running* CLI reporting an unreadable version is a broken install,
67
+ # not an extension problem, so say so rather than fail it closed.
68
+ logging.warning(
69
+ "Cannot read the running graph-agents-cli version %r; skipping the "
70
+ "compatibility check against %r.",
71
+ version,
72
+ spec,
73
+ )
74
+ return True
75
+ if running.is_devrelease:
76
+ logging.debug(
77
+ "Running a dev build (%s); not enforcing extension range %r.",
78
+ version,
79
+ spec,
80
+ )
81
+ return True
82
+ parsed = parse_range(spec)
83
+ if parsed is None:
84
+ return True
85
+ # prereleases=True so an rc of an in-range release still counts as in range.
86
+ return parsed.contains(running, prereleases=True)
@@ -0,0 +1,293 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Discover active extensions across scopes into a resolved ExtensionSet."""
16
+
17
+ from __future__ import annotations
18
+
19
+ import logging
20
+ from dataclasses import dataclass, replace
21
+ from pathlib import Path
22
+
23
+ import yaml
24
+
25
+ from graph_agents_cli.__init__ import __version__
26
+ from graph_agents_cli.extension._compat import is_compatible
27
+ from graph_agents_cli.extension._manifest import EXTENSIONS_FILE
28
+ from graph_agents_cli.extension._paths import vendored_extensions_dir
29
+ from graph_agents_cli.extension._resolver import ResolverError, validate_extension_name
30
+ from graph_agents_cli.extension._spec import (
31
+ EXTENSION_FILE,
32
+ ExtensionCommand,
33
+ ExtensionSpec,
34
+ ExtensionSpecError,
35
+ parse_extension_spec,
36
+ )
37
+
38
+ # Commands no extension may override, ever. They are how a user removes or
39
+ # repairs an extension, so letting one own them means a broken extension can own
40
+ # its own uninstall. Refused at `extension add` and ignored here, rather than
41
+ # only when the extension is out of range: an in-range extension owning
42
+ # `extension remove` is the same trap.
43
+ RECOVERY_COMMANDS = frozenset(
44
+ {
45
+ "install",
46
+ "extension",
47
+ "extension.add",
48
+ "extension.list",
49
+ "extension.remove",
50
+ "extension.update",
51
+ }
52
+ )
53
+
54
+
55
+ @dataclass(frozen=True)
56
+ class ResolvedCommand:
57
+ contribution: ExtensionCommand
58
+ extension_name: str
59
+ scope: str
60
+ extension_root: Path
61
+ # Set when the owning extension declared `on_incompatible: error` and the
62
+ # running CLI is outside its range. `main` installs a command that fails
63
+ # with an explanation instead of running the extension's vector — falling back
64
+ # to the built-in would silently do something else (an ADK scaffold in a
65
+ # LangChain project, say).
66
+ blocked_requires: str | None = None
67
+
68
+
69
+ @dataclass(frozen=True)
70
+ class ExtensionSet:
71
+ # Tuples (and frozen specs) because a set is parsed once and then read by
72
+ # main, info and extension list; no consumer may mutate it.
73
+ extensions: tuple[ExtensionSpec, ...]
74
+ commands: dict[str, ResolvedCommand]
75
+ conflicts: tuple[str, ...]
76
+ # Extensions whose declared range excludes `running_version`.
77
+ incompatible: tuple[ExtensionSpec, ...] = ()
78
+ running_version: str = __version__
79
+
80
+ def command_rows(self) -> list[dict]:
81
+ """Contributed commands, one JSON-ready row each, sorted by command name.
82
+
83
+ A blocked row is a command the CLI will refuse to run (its extension is out
84
+ of range in `error` mode), so it is reported but not as active.
85
+ """
86
+ requires_by_name = {
87
+ spec.name: spec.requires_agents_cli
88
+ for spec in self.extensions
89
+ if spec.requires_agents_cli
90
+ }
91
+ return [
92
+ {
93
+ "command": name,
94
+ "extension": rc.extension_name,
95
+ "scope": rc.scope,
96
+ "kind": rc.contribution.kind,
97
+ "run": list(rc.contribution.run),
98
+ "requires": requires_by_name.get(rc.extension_name),
99
+ "blocked": rc.blocked_requires is not None,
100
+ }
101
+ for name, rc in sorted(self.commands.items())
102
+ ]
103
+
104
+ def conflict_rows(self) -> list[str]:
105
+ """Command names claimed by more than one extension at one scope."""
106
+ return list(self.conflicts)
107
+
108
+ def incompatible_rows(self) -> list[dict]:
109
+ """Out-of-range extensions, one JSON-ready row each."""
110
+ return [
111
+ {
112
+ "extension": spec.name,
113
+ "scope": spec.scope,
114
+ "requires": spec.requires_agents_cli,
115
+ "running": self.running_version,
116
+ "mode": "error" if spec.error_on_incompatible else "warn",
117
+ }
118
+ for spec in self.incompatible
119
+ ]
120
+
121
+
122
+ def incompatible_spec(extension_dir: Path) -> ExtensionSpec | None:
123
+ """The spec if it is out of range for this CLI, else None.
124
+
125
+ Raises ExtensionSpecError if the manifest is there but unparseable; an
126
+ absent manifest, or one with no `requires`, returns None.
127
+ """
128
+ spec = load_extension_spec(extension_dir)
129
+ if (
130
+ spec
131
+ and spec.requires_agents_cli
132
+ and not is_compatible(__version__, spec.requires_agents_cli)
133
+ ):
134
+ return spec
135
+ return None
136
+
137
+
138
+ def _load_yaml(path: Path) -> dict:
139
+ try:
140
+ with open(path, encoding="utf-8") as f:
141
+ data = yaml.safe_load(f) or {}
142
+ except (OSError, RecursionError, UnicodeDecodeError, yaml.YAMLError) as e:
143
+ logging.warning("%s: could not read (%s); ignoring.", path, e)
144
+ return {}
145
+ return data if isinstance(data, dict) else {}
146
+
147
+
148
+ def load_extension_spec(root: Path) -> ExtensionSpec | None:
149
+ """Parse an extension's manifest.
150
+
151
+ None when there is no manifest; raises ExtensionSpecError when there is a bad one.
152
+
153
+ Everything on the load path catches it and carries on. `extension add` and
154
+ `extension update` let it surface, so an install fails instead of reporting
155
+ success for an extension that contributes nothing.
156
+ """
157
+ path = root / EXTENSION_FILE
158
+ if not path.exists():
159
+ return None
160
+ try:
161
+ with open(path, encoding="utf-8") as f:
162
+ data = yaml.safe_load(f)
163
+ except (OSError, RecursionError, UnicodeDecodeError, yaml.YAMLError) as e:
164
+ # Unreadable, undecodable and unparseable are all as fatal as each other
165
+ # for `add`, and all as skippable for the load path, so they have to
166
+ # arrive as the same error type. RecursionError covers pathological
167
+ # nesting, which yaml raises straight through.
168
+ raise ExtensionSpecError(f"could not be read or parsed: {e}") from e
169
+ if data is not None and not isinstance(data, dict):
170
+ raise ExtensionSpecError(f"expected a mapping, found {type(data).__name__}")
171
+ return parse_extension_spec(data or {}, root=root, default_name=root.name)
172
+
173
+
174
+ def _loaded_spec(root: Path) -> ExtensionSpec | None:
175
+ """`load_extension_spec`, fail-soft: a bad manifest warns and is skipped."""
176
+ try:
177
+ return load_extension_spec(root)
178
+ except ExtensionSpecError as e:
179
+ logging.warning("%s: %s; ignoring this extension.", root / EXTENSION_FILE, e)
180
+ return None
181
+
182
+
183
+ def _vendored_specs(scope_root: Path) -> list[ExtensionSpec]:
184
+ """Load specs for extensions recorded under `extensions:` and vendored on disk."""
185
+ manifest = scope_root / EXTENSIONS_FILE
186
+ if not manifest.exists():
187
+ return []
188
+ data = _load_yaml(manifest)
189
+ raw = data.get("extensions") or []
190
+ specs: list[ExtensionSpec] = []
191
+ if not isinstance(raw, list):
192
+ return specs
193
+ for item in raw:
194
+ if not isinstance(item, dict) or not isinstance(item.get("name"), str):
195
+ continue
196
+ try:
197
+ safe_name = validate_extension_name(item["name"])
198
+ except ResolverError as e:
199
+ logging.warning("Skipping extension with %s", e)
200
+ continue
201
+ extension_dir = vendored_extensions_dir(scope_root) / safe_name
202
+ try:
203
+ spec = load_extension_spec(extension_dir)
204
+ except ExtensionSpecError as e:
205
+ logging.warning("%s: %s; ignoring this extension.", extension_dir, e)
206
+ continue
207
+
208
+ if spec is None:
209
+ logging.warning(
210
+ "Extension %r is recorded in %s but not vendored at %s; "
211
+ "run `graph-agents-cli install`.",
212
+ item["name"],
213
+ manifest,
214
+ extension_dir,
215
+ )
216
+ continue
217
+ # The manifest id is the extension's stable identity (used by `extension
218
+ # remove`/`update`); keep it even if the upstream yaml `name` was
219
+ # renamed after the extension was pinned.
220
+ specs.append(replace(spec, name=safe_name))
221
+ return specs
222
+
223
+
224
+ def load_extension_set(project_root: Path | None, user_root: Path | None) -> ExtensionSet:
225
+ # Lowest precedence first (user), highest last (project), so project-scope
226
+ # entries win by overwriting user-scope ones in the loop below.
227
+ discovered: list[ExtensionSpec] = []
228
+ for root, scope in ((user_root, "user"), (project_root, "project")):
229
+ if root is None:
230
+ continue
231
+ inline = _loaded_spec(root)
232
+ if inline is not None:
233
+ discovered.append(replace(inline, scope=scope))
234
+ for spec in _vendored_specs(root):
235
+ discovered.append(replace(spec, scope=scope))
236
+
237
+ commands: dict[str, ResolvedCommand] = {}
238
+ # (scope, command_name) -> extension_name that first claimed it at that scope.
239
+ claimed_same_scope: dict[tuple[str, str], str] = {}
240
+ conflicts: list[str] = []
241
+ incompatible: list[ExtensionSpec] = []
242
+
243
+ for spec in discovered:
244
+ scope = spec.scope
245
+ # An extension whose declared `requires.agents_cli` range excludes the
246
+ # running CLI is recorded here. "warn" applies its contributions anyway;
247
+ # "error" keeps the command claimed but marks it blocked, so invoking it
248
+ # fails with an explanation rather than silently running the built-in,
249
+ # which for an override does something else entirely.
250
+ blocked_requires: str | None = None
251
+ if spec.requires_agents_cli and not is_compatible(__version__, spec.requires_agents_cli):
252
+ incompatible.append(spec)
253
+ if spec.error_on_incompatible:
254
+ blocked_requires = spec.requires_agents_cli
255
+ for contribution in spec.commands:
256
+ key = contribution.name
257
+ same_scope_key = (scope, key)
258
+ if same_scope_key in claimed_same_scope:
259
+ conflicts.append(f"{scope}:{key}")
260
+ logging.warning(
261
+ "Command %r claimed by multiple %s-scope extensions "
262
+ "(%s and %s); ignoring the later one.",
263
+ key,
264
+ scope,
265
+ claimed_same_scope[same_scope_key],
266
+ spec.name,
267
+ )
268
+ continue
269
+ claimed_same_scope[same_scope_key] = spec.name
270
+ if key in RECOVERY_COMMANDS:
271
+ # `extension add` refuses these, so reaching here means a
272
+ # hand-edited manifest or one installed by an older CLI.
273
+ logging.warning(
274
+ "Extension %r claims %r, which cannot be overridden (it is how "
275
+ "you repair or remove an extension); using the built-in.",
276
+ spec.name,
277
+ key,
278
+ )
279
+ continue
280
+ commands[key] = ResolvedCommand(
281
+ contribution=contribution,
282
+ extension_name=spec.name,
283
+ scope=scope,
284
+ extension_root=spec.root,
285
+ blocked_requires=blocked_requires,
286
+ )
287
+
288
+ return ExtensionSet(
289
+ extensions=tuple(discovered),
290
+ commands=commands,
291
+ conflicts=tuple(conflicts),
292
+ incompatible=tuple(incompatible),
293
+ )
@@ -0,0 +1,135 @@
1
+ # Copyright 2026 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Read/write the installed-extension list.
16
+
17
+ Its own file rather than a block in graph-agents-cli-manifest.yaml: this is
18
+ read-modify-written on every add/remove/update, and a bug here should not be
19
+ able to reach a project's scaffold state. Same filename at both scopes, under
20
+ the project root or the user config dir, so there is nothing to branch on.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import logging
26
+ from dataclasses import dataclass
27
+ from pathlib import Path
28
+ from typing import Any
29
+
30
+ import yaml
31
+
32
+ EXTENSIONS_FILE = "graph-agents-cli-extensions.yaml"
33
+
34
+
35
+ @dataclass(frozen=True)
36
+ class ExtensionEntry:
37
+ name: str
38
+ source: str
39
+ ref: str
40
+ sha: str
41
+ scope: str
42
+
43
+
44
+ def _read_manifest(path: Path) -> dict[str, Any]:
45
+ if not path.exists():
46
+ return {}
47
+ try:
48
+ with open(path, encoding="utf-8") as f:
49
+ raw = yaml.safe_load(f)
50
+ except yaml.YAMLError as e:
51
+ # add/remove/update and install's sync all read this; a hand-edited
52
+ # file should not end them in a traceback.
53
+ logging.warning("Ignoring unparseable %s: %s", path, e)
54
+ return {}
55
+ if isinstance(raw, dict):
56
+ return raw # type: ignore[return-value]
57
+ return {}
58
+
59
+
60
+ def _write_manifest(path: Path, data: dict[str, Any]) -> None:
61
+ path.parent.mkdir(parents=True, exist_ok=True)
62
+ with open(path, "w", encoding="utf-8") as f:
63
+ yaml.safe_dump(data, f, sort_keys=False)
64
+
65
+
66
+ def read_extension_entries(manifest_path: Path) -> list[ExtensionEntry]:
67
+ """Read extension entries from a manifest file.
68
+
69
+ Returns an empty list when the file is missing, unparseable, or has no
70
+ valid ``extensions:`` list.
71
+ """
72
+ data = _read_manifest(manifest_path)
73
+ raw = data.get("extensions") or []
74
+ entries: list[ExtensionEntry] = []
75
+ if not isinstance(raw, list):
76
+ return entries
77
+ for item in raw:
78
+ if not isinstance(item, dict):
79
+ logging.warning(
80
+ "%s: ignoring malformed `extensions:` entry %r (expected a mapping).",
81
+ manifest_path,
82
+ item,
83
+ )
84
+ continue
85
+ entries.append(
86
+ ExtensionEntry(
87
+ name=str(item.get("name") or ""),
88
+ source=str(item.get("source") or ""),
89
+ ref=str(item.get("ref") or ""),
90
+ sha=str(item.get("sha") or ""),
91
+ scope=str(item.get("scope") or ""),
92
+ )
93
+ )
94
+ return entries
95
+
96
+
97
+ def upsert_extension_entry(manifest_path: Path, entry: ExtensionEntry) -> None:
98
+ """Insert or replace an extension entry in the manifest, preserving all other keys.
99
+
100
+ Creates the manifest file (and any missing parent directories) if absent.
101
+ """
102
+ data = _read_manifest(manifest_path)
103
+ existing: list[Any] = data.get("extensions") or []
104
+ # Drop only the entry being replaced. Anything we don't recognise is kept
105
+ # verbatim — rewriting the manifest must not delete a user's content.
106
+ extensions: list[Any] = [
107
+ p for p in existing if not (isinstance(p, dict) and p.get("name") == entry.name)
108
+ ]
109
+ extensions.append(
110
+ {
111
+ "name": entry.name,
112
+ "source": entry.source,
113
+ "ref": entry.ref,
114
+ "sha": entry.sha,
115
+ "scope": entry.scope,
116
+ }
117
+ )
118
+ data["extensions"] = extensions
119
+ _write_manifest(manifest_path, data)
120
+
121
+
122
+ def remove_extension_entry(manifest_path: Path, name: str) -> bool:
123
+ """Remove an extension entry by name.
124
+
125
+ Returns True if an entry was removed, False if no entry matched.
126
+ """
127
+ data = _read_manifest(manifest_path)
128
+ existing: list[Any] = data.get("extensions") or []
129
+ # Same rule as upsert: unrecognised entries survive, only `name` matches go.
130
+ kept: list[Any] = [p for p in existing if not (isinstance(p, dict) and p.get("name") == name)]
131
+ if len(kept) == len(existing):
132
+ return False
133
+ data["extensions"] = kept
134
+ _write_manifest(manifest_path, data)
135
+ return True