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,264 @@
1
+ # Copyright 2026 graph-agents-cli contributors
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
+ """A project's response schema (structured final answers): the check the CLI applies.
16
+
17
+ A project that declares `<agent directory>/response_schema.json` answers in
18
+ JSON of that shape (the template's `app_utils/structured.py`). The schema may
19
+ use only the JSON Schema subset the runtime checks answers with, and its root
20
+ is an object; `create --response-schema` refuses to seed any other file, and
21
+ `lint` reports one. The rules are the template's own (the SHARED block below,
22
+ byte-identical there), so a schema the CLI accepts starts the agent.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import json
28
+ import math
29
+ import re
30
+ from pathlib import Path
31
+ from typing import Any
32
+
33
+ import click
34
+
35
+ SCHEMA_FILENAME = "response_schema.json"
36
+
37
+
38
+ class ResponseSchemaFileError(click.ClickException):
39
+ """A response schema that cannot be read or uses what the answer check cannot check (exit 3)."""
40
+
41
+ exit_code = 3
42
+
43
+ def __init__(self, path: str | Path, problems: list[str]) -> None:
44
+ self.path = Path(path)
45
+ self.problems = list(problems)
46
+ lines = "\n".join(f" - {p}" for p in self.problems)
47
+ super().__init__(f"Invalid response schema {self.path}:\n{lines}")
48
+
49
+
50
+ # --- BEGIN SHARED RESPONSE SCHEMA RULES ---
51
+ # The JSON Schema subset a response schema may use, and the check of a schema
52
+ # against it. The same block is in graph_agents_cli/_response_schema.py, which
53
+ # `create --response-schema` and `lint` use: keep the two byte-identical
54
+ # (tests/dev/test_response_schema_parity.py), so a schema the CLI accepts
55
+ # starts the agent. No Jinja, no imports but json, math, re and typing.Any.
56
+
57
+ # Keywords that are checked, and annotations that are allowed and ignored.
58
+ VALIDATION_KEYWORDS = frozenset(
59
+ {
60
+ "type",
61
+ "enum",
62
+ "const",
63
+ "properties",
64
+ "required",
65
+ "additionalProperties",
66
+ "minProperties",
67
+ "maxProperties",
68
+ "items",
69
+ "minItems",
70
+ "maxItems",
71
+ "uniqueItems",
72
+ "minLength",
73
+ "maxLength",
74
+ "pattern",
75
+ "minimum",
76
+ "maximum",
77
+ "exclusiveMinimum",
78
+ "exclusiveMaximum",
79
+ "multipleOf",
80
+ "anyOf",
81
+ "oneOf",
82
+ "allOf",
83
+ "not",
84
+ "$ref",
85
+ "$defs",
86
+ "definitions",
87
+ }
88
+ )
89
+ ANNOTATION_KEYWORDS = frozenset(
90
+ {
91
+ "$schema",
92
+ "$id",
93
+ "$comment",
94
+ "title",
95
+ "description",
96
+ "default",
97
+ "examples",
98
+ "deprecated",
99
+ "readOnly",
100
+ "writeOnly",
101
+ "format",
102
+ }
103
+ )
104
+ SUPPORTED_KEYWORDS = VALIDATION_KEYWORDS | ANNOTATION_KEYWORDS
105
+ JSON_TYPES = frozenset({"object", "array", "string", "integer", "number", "boolean", "null"})
106
+ _SCHEMA_LISTS = ("anyOf", "oneOf", "allOf")
107
+ _SCHEMA_MAPS = ("properties", "$defs", "definitions")
108
+ _SCHEMA_VALUES = ("items", "additionalProperties", "not")
109
+ _COUNTS = ("minProperties", "maxProperties", "minItems", "maxItems", "minLength", "maxLength")
110
+ _NUMBERS = ("minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf")
111
+
112
+
113
+ def _pointer(schema: Any, ref: str) -> Any:
114
+ """The subschema a local `$ref` (`#`, `#/$defs/name`, ...) names; KeyError when none."""
115
+ if ref == "#":
116
+ return schema
117
+ if not ref.startswith("#/"):
118
+ raise KeyError(ref)
119
+ node = schema
120
+ for raw in ref[2:].split("/"):
121
+ key = raw.replace("~1", "/").replace("~0", "~")
122
+ if isinstance(node, dict) and key in node:
123
+ node = node[key]
124
+ elif isinstance(node, list) and key.isdigit() and int(key) < len(node):
125
+ node = node[int(key)]
126
+ else:
127
+ raise KeyError(ref)
128
+ return node
129
+
130
+
131
+ def _is_number(value: Any) -> bool:
132
+ return isinstance(value, int | float) and not isinstance(value, bool) and math.isfinite(value)
133
+
134
+
135
+ def schema_problems(schema: Any) -> list[str]:
136
+ """What stops `schema` from being a response schema; empty when it can be one.
137
+
138
+ The root is an object schema (`"type": "object"`); every keyword anywhere
139
+ is one `SUPPORTED_KEYWORDS` lists, with a value of the right kind; every
140
+ `$ref` points inside the document; every `pattern` compiles.
141
+ """
142
+ if not isinstance(schema, dict):
143
+ return ["the schema must be a JSON object"]
144
+ problems: list[str] = []
145
+ if schema.get("type") != "object":
146
+ problems.append('$: the root must be an object schema ("type": "object")')
147
+
148
+ def walk(node: Any, where: str) -> None:
149
+ if isinstance(node, bool):
150
+ return
151
+ if not isinstance(node, dict):
152
+ problems.append(f"{where}: a schema must be an object or a boolean")
153
+ return
154
+ for key, value in node.items():
155
+ at = f"{where}.{key}"
156
+ if key not in SUPPORTED_KEYWORDS:
157
+ problems.append(f"{at}: `{key}` is not a keyword the answer check supports")
158
+ elif key == "type":
159
+ types = value if isinstance(value, list) else [value]
160
+ if not types or not all(t in JSON_TYPES for t in types):
161
+ problems.append(f"{at}: must name JSON types ({', '.join(sorted(JSON_TYPES))})")
162
+ elif key == "required":
163
+ if not isinstance(value, list) or not all(isinstance(v, str) for v in value):
164
+ problems.append(f"{at}: must be a list of property names")
165
+ elif key == "enum":
166
+ if not isinstance(value, list) or not value:
167
+ problems.append(f"{at}: must be a non-empty list")
168
+ elif key in _COUNTS:
169
+ if not isinstance(value, int) or isinstance(value, bool) or value < 0:
170
+ problems.append(f"{at}: must be a whole number >= 0")
171
+ elif key in _NUMBERS:
172
+ if not _is_number(value) or (key == "multipleOf" and value <= 0):
173
+ problems.append(
174
+ f"{at}: must be a number" + (" > 0" if key == "multipleOf" else "")
175
+ )
176
+ elif key == "uniqueItems":
177
+ if not isinstance(value, bool):
178
+ problems.append(f"{at}: must be true or false")
179
+ elif key == "pattern":
180
+ try:
181
+ re.compile(value)
182
+ except (re.error, TypeError) as exc:
183
+ problems.append(f"{at}: not a regular expression ({exc})")
184
+ elif key == "$ref":
185
+ try:
186
+ _pointer(schema, value if isinstance(value, str) else "")
187
+ except KeyError:
188
+ problems.append(f"{at}: {value!r} does not point inside this schema")
189
+ elif key in _SCHEMA_LISTS:
190
+ if not isinstance(value, list) or not value:
191
+ problems.append(f"{at}: must be a non-empty list of schemas")
192
+ else:
193
+ for i, sub in enumerate(value):
194
+ walk(sub, f"{at}[{i}]")
195
+ elif key in _SCHEMA_MAPS:
196
+ if not isinstance(value, dict):
197
+ problems.append(f"{at}: must map names to schemas")
198
+ else:
199
+ for name, sub in value.items():
200
+ walk(sub, f"{at}.{name}")
201
+ elif key in _SCHEMA_VALUES:
202
+ if key == "items" and isinstance(value, list):
203
+ problems.append(
204
+ f"{at}: must be one schema (a list of schemas is not supported)"
205
+ )
206
+ else:
207
+ walk(value, at)
208
+
209
+ walk(schema, "$")
210
+ return problems
211
+
212
+
213
+ # --- END SHARED RESPONSE SCHEMA RULES ---
214
+
215
+
216
+ def read_schema(path: Path) -> tuple[dict[str, Any] | None, list[str]]:
217
+ """``(schema, [])`` for a file that can be the response schema, else ``(None, problems)``."""
218
+ try:
219
+ schema = json.loads(path.read_text(encoding="utf-8"))
220
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
221
+ return None, [f"not a JSON file ({exc})"]
222
+ problems = schema_problems(schema)
223
+ return (None, problems) if problems else (schema, [])
224
+
225
+
226
+ def load_schema(path: str | Path) -> dict[str, Any]:
227
+ """The schema in ``path``; ``ResponseSchemaFileError`` listing every problem."""
228
+ schema, problems = read_schema(Path(path))
229
+ if schema is None:
230
+ raise ResponseSchemaFileError(path, problems)
231
+ return schema
232
+
233
+
234
+ def schema_file(project_root: Path, agent_directory: str) -> Path:
235
+ """Where a project keeps its response schema."""
236
+ return project_root / agent_directory / SCHEMA_FILENAME
237
+
238
+
239
+ def wiring_problems(project_root: Path, agent_directory: str) -> list[str]:
240
+ """What the project's ``agent.py`` lacks to answer in its response schema; empty when wired.
241
+
242
+ It builds the agent with ``response_format=response_format(model, tools)`` (else
243
+ every run ends with ``invalid_structured_response``) and has ``StructuredAnswer()``
244
+ in its middleware (else an answer that does not fit is not sent back to the model,
245
+ and the run ends with that error instead of the answer).
246
+ """
247
+ agent_py = f"{agent_directory}/agent.py"
248
+ try:
249
+ text = (project_root / agent_directory / "agent.py").read_text(encoding="utf-8")
250
+ except OSError:
251
+ return [f"{agent_py} cannot be read"]
252
+ problems = []
253
+ if "response_format(" not in text:
254
+ problems.append(
255
+ f"{agent_py} does not pass response_format=response_format(model, tools) to "
256
+ "create_agent: every run would end with invalid_structured_response"
257
+ )
258
+ if "StructuredAnswer(" not in text:
259
+ problems.append(
260
+ f"{agent_py} has no StructuredAnswer() (last) in middleware(): an answer that does "
261
+ "not fit would not go back to the model, and its run would end with "
262
+ "invalid_structured_response"
263
+ )
264
+ return problems
@@ -0,0 +1,319 @@
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
+ """Subprocess helpers for agents CLI."""
16
+
17
+ import io
18
+ import os
19
+ import shlex
20
+ import subprocess
21
+ import sys
22
+ from collections.abc import Sequence
23
+ from pathlib import Path
24
+
25
+ import click
26
+
27
+ from graph_agents_cli import _tools
28
+
29
+ # Set for an extension's command so any re-entry into `graph-agents-cli <cmd>` runs the
30
+ # built-in instead of recursing back into the override.
31
+ DISABLE_OVERRIDES_ENV = "GRAPH_AGENTS_CLI_DISABLE_OVERRIDES"
32
+ # Exported so an extension's scripts can find sibling resources (templates etc.).
33
+ EXTENSION_DIR_ENV = "GRAPH_AGENTS_CLI_EXTENSION_DIR"
34
+
35
+
36
+ def redact_cmd(args: list[str]) -> str:
37
+ """Mask sensitive information in command arguments and return joined string.
38
+
39
+ Masks arguments like --github-pat and environment variables containing secrets.
40
+ """
41
+ redacted_cmd_list = list(args)
42
+ for i, arg in enumerate(args):
43
+ if arg == "--github-pat" and i + 1 < len(args):
44
+ redacted_cmd_list[i + 1] = "[REDACTED]"
45
+ elif any(
46
+ secret in arg for secret in ["GITHUB_PAT", "GH_TOKEN", "GITHUB_TOKEN", "GITHUB_APP_KEY"]
47
+ ):
48
+ redacted_cmd_list[i] = "[REDACTED]"
49
+
50
+ return shlex.join(redacted_cmd_list)
51
+
52
+
53
+ def run(
54
+ args: list[str],
55
+ *,
56
+ cwd: str | Path | None = None,
57
+ env: dict | None = None,
58
+ capture: bool = False,
59
+ print_cmd: bool = True,
60
+ check: bool = True,
61
+ check_err_msg: str | None = None,
62
+ input_data: bytes | None = None,
63
+ timeout: int | None = None,
64
+ resolve_executable: bool = True,
65
+ ) -> subprocess.CompletedProcess:
66
+ """Run a subprocess, streaming output by default.
67
+
68
+ Args:
69
+ args: Command and arguments.
70
+ cwd: Working directory for the subprocess.
71
+ env: Extra environment variables. Merged with os.environ if provided.
72
+ capture: If True, capture stdout/stderr instead of streaming.
73
+ Defaults to False.
74
+ print_cmd: If True, print the command before executing.
75
+ Defaults to True.
76
+ check: If True, raise ClickException on non-zero exit.
77
+ Defaults to True.
78
+ check_err_msg: Error message prefix for check failures.
79
+ input_data: Bytes to feed to stdin of the subprocess.
80
+ timeout: Timeout in seconds for the subprocess.
81
+ resolve_executable: If True, resolve the executable path using require_tool.
82
+ Defaults to True.
83
+
84
+ Returns:
85
+ CompletedProcess instance.
86
+ """
87
+ cmd_str = redact_cmd(args)
88
+
89
+ if print_cmd:
90
+ click.secho(f" ▸ {cmd_str}", fg="cyan", dim=True)
91
+
92
+ run_env = None
93
+ if env is not None:
94
+ run_env = {**os.environ, **env}
95
+
96
+ # Capture output as UTF-8 text (replacing undecodable bytes) unless we're
97
+ # piping raw bytes to stdin, where child output must stay bytes. Without an
98
+ # explicit encoding, subprocess uses the locale codec + strict errors, which
99
+ # raises UnicodeDecodeError on non-UTF-8 locales (e.g. cp1252 on Windows).
100
+ text_mode = input_data is None
101
+ text_kwargs = {"encoding": "utf-8", "errors": "replace"} if text_mode else {}
102
+
103
+ if capture:
104
+ result = run_resolved(
105
+ args,
106
+ resolve_executable=resolve_executable,
107
+ capture_output=True,
108
+ text=text_mode,
109
+ cwd=str(cwd) if cwd else None,
110
+ input=input_data,
111
+ env=run_env,
112
+ timeout=timeout,
113
+ **text_kwargs,
114
+ )
115
+ else:
116
+ # Under click.testing.CliRunner, sys.stdout is a StringIO-like object
117
+ # that doesn't have a fileno(). Subprocess on Windows requires a fileno.
118
+ # We check carefully to avoid issues with mocks or unusual environments.
119
+ use_fallback = True
120
+ if hasattr(sys.stdout, "fileno") and callable(sys.stdout.fileno):
121
+ try:
122
+ sys.stdout.fileno()
123
+ use_fallback = False
124
+ except (io.UnsupportedOperation, AttributeError, OSError):
125
+ pass
126
+
127
+ if not use_fallback:
128
+ result = run_resolved(
129
+ args,
130
+ resolve_executable=resolve_executable,
131
+ stdout=sys.stdout,
132
+ stderr=sys.stderr,
133
+ cwd=str(cwd) if cwd else None,
134
+ input=input_data,
135
+ env=run_env,
136
+ timeout=timeout,
137
+ )
138
+ else:
139
+ # Fallback to capture and manual emit for environments without a
140
+ # real stdout fileno (e.g. click.testing.CliRunner).
141
+ result = run_resolved(
142
+ args,
143
+ resolve_executable=resolve_executable,
144
+ capture_output=True,
145
+ text=text_mode,
146
+ cwd=str(cwd) if cwd else None,
147
+ input=input_data,
148
+ env=run_env,
149
+ timeout=timeout,
150
+ **text_kwargs,
151
+ )
152
+ # stdout/stderr are bytes when input_data is set (text=False).
153
+ for stream, content in (
154
+ (sys.stdout, result.stdout),
155
+ (sys.stderr, result.stderr),
156
+ ):
157
+ if not content:
158
+ continue
159
+ if isinstance(content, bytes):
160
+ content = content.decode(errors="replace")
161
+ stream.write(content)
162
+
163
+ if check and result.returncode != 0:
164
+ error_msg = check_err_msg or f"Command failed: {cmd_str}"
165
+ # When output was captured it never reached the console, so fold it into
166
+ # the error — otherwise the failure reason is lost. Streamed runs already
167
+ # printed it. stdout/stderr are bytes when input_data is set (text=False).
168
+ detail = ""
169
+ if capture:
170
+ captured = "\n".join(
171
+ part.strip()
172
+ for part in (result.stdout, result.stderr)
173
+ if isinstance(part, str) and part.strip()
174
+ )
175
+ if captured:
176
+ detail = f"\n{captured}"
177
+ raise click.ClickException(f"{error_msg} (exit code {result.returncode}){detail}")
178
+
179
+ return result
180
+
181
+
182
+ def _looks_like_path(token: str) -> bool:
183
+ """A `run:` token the author wrote as a path, e.g. `scripts/run.py`."""
184
+ return "/" in token or os.sep in token
185
+
186
+
187
+ def run_extension_command(
188
+ run_vector: Sequence[str],
189
+ extra_args: list[str],
190
+ *,
191
+ cwd: Path,
192
+ extension_root: Path,
193
+ ) -> int:
194
+ """Run an extension's declared `run:` vector with the user's argv appended.
195
+
196
+ Differs from `run` in ways an override needs: the vector is extension-supplied
197
+ and passed through verbatim (no `require_tool` resolution), the command is
198
+ not echoed, and the child's exit code is returned rather than raised, so the
199
+ override exits like the command it replaced.
200
+
201
+ Tokens that exist as paths under `extension_root` are made absolute, so a
202
+ vendored script is found regardless of cwd.
203
+ """
204
+ # Only tokens written as paths. Resolving every token would rewrite an
205
+ # ordinary argument that happens to name a file in the extension, e.g.
206
+ # `run: ["make", "build"]` in an extension that also ships `build/`.
207
+ resolved_vector = [
208
+ str((extension_root / tok).resolve())
209
+ if _looks_like_path(tok) and (extension_root / tok).exists()
210
+ else tok
211
+ for tok in run_vector
212
+ ]
213
+ result = run(
214
+ resolved_vector + list(extra_args),
215
+ cwd=cwd,
216
+ env={DISABLE_OVERRIDES_ENV: "1", EXTENSION_DIR_ENV: str(extension_root)},
217
+ print_cmd=False,
218
+ check=False,
219
+ resolve_executable=False,
220
+ )
221
+ return result.returncode
222
+
223
+
224
+ def run_resolved(
225
+ args: list[str], *, resolve_executable: bool = True, **kwargs
226
+ ) -> subprocess.CompletedProcess:
227
+ """Wrapper around subprocess.run with optional executable resolution.
228
+
229
+ Args:
230
+ args: Command and arguments as a list of strings.
231
+ resolve_executable: If True, resolve the executable path using require_tool.
232
+ Defaults to True.
233
+ **kwargs: Additional keyword arguments passed to subprocess.run.
234
+
235
+ Raises:
236
+ ToolNotFoundError: If resolve_executable is True and the tool cannot be found.
237
+
238
+ Returns:
239
+ CompletedProcess instance.
240
+ """
241
+ if isinstance(args, str):
242
+ raise ValueError("args must be a list of strings, not a single string.")
243
+
244
+ if resolve_executable and args:
245
+ executable = args[0]
246
+ # Create a shallow copy to avoid modifying the original list passed by reference
247
+ args = args.copy()
248
+ args[0] = _tools.require_tool(executable)
249
+
250
+ return subprocess.run(args, **kwargs)
251
+
252
+
253
+ def popen_resolved(
254
+ args: list[str], *, resolve_executable: bool = True, **kwargs
255
+ ) -> subprocess.Popen:
256
+ """Wrapper around subprocess.Popen with optional executable resolution.
257
+
258
+ Args:
259
+ args: Command and arguments as a list of strings.
260
+ resolve_executable: If True, resolve the executable path using require_tool.
261
+ Defaults to True.
262
+ **kwargs: Additional keyword arguments passed to subprocess.Popen.
263
+
264
+ Raises:
265
+ ToolNotFoundError: If resolve_executable is True and the tool cannot be found.
266
+
267
+ Returns:
268
+ Popen instance.
269
+ """
270
+ if isinstance(args, str):
271
+ raise ValueError("args must be a list of strings, not a single string.")
272
+
273
+ if resolve_executable and args:
274
+ executable = args[0]
275
+ # Create a shallow copy to avoid modifying the original list passed by reference
276
+ args = args.copy()
277
+ args[0] = _tools.require_tool(executable)
278
+
279
+ return subprocess.Popen(args, **kwargs)
280
+
281
+
282
+ def popen_resolved_detached(
283
+ args: list[str], *, resolve_executable: bool = True, **kwargs
284
+ ) -> subprocess.Popen:
285
+ """Wrapper around subprocess.Popen for launching detached background processes.
286
+
287
+ Handles cross-platform differences for process detaching:
288
+ - On POSIX, sets start_new_session=True.
289
+ - On Windows, sets creationflags to DETACHED_PROCESS and CREATE_NEW_PROCESS_GROUP.
290
+ - Ensures stdin is redirected to subprocess.DEVNULL.
291
+
292
+ Args:
293
+ args: Command and arguments as a list of strings.
294
+ resolve_executable: If True, resolve the executable path using require_tool.
295
+ Defaults to True.
296
+ **kwargs: Additional keyword arguments passed to subprocess.Popen.
297
+
298
+ Raises:
299
+ ToolNotFoundError: If resolve_executable is True and the tool cannot be found.
300
+
301
+ Returns:
302
+ Popen instance.
303
+ """
304
+ stdin = kwargs.get("stdin")
305
+ if stdin is not None and stdin is not subprocess.DEVNULL:
306
+ raise ValueError("popen_resolved_detached only supports stdin=DEVNULL.")
307
+ kwargs["stdin"] = subprocess.DEVNULL
308
+
309
+ if os.name == "nt":
310
+ # Windows-specific process creation flags for detaching
311
+ create_new_process_group = getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
312
+ create_no_window = getattr(subprocess, "CREATE_NO_WINDOW", 0)
313
+ flags = create_new_process_group | create_no_window
314
+ kwargs["creationflags"] = kwargs.get("creationflags", 0) | flags
315
+ else:
316
+ # POSIX way of detaching
317
+ kwargs["start_new_session"] = True
318
+
319
+ return popen_resolved(args, resolve_executable=resolve_executable, **kwargs)