procesio-cli 0.1.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.
- procesio_cli/__init__.py +18 -0
- procesio_cli/_cli.py +71 -0
- procesio_cli/agents/__init__.py +0 -0
- procesio_cli/agents/_lib/__init__.py +0 -0
- procesio_cli/agents/_lib/actiondef.py +24 -0
- procesio_cli/agents/_lib/errors.py +40 -0
- procesio_cli/agents/_lib/reversibility.py +236 -0
- procesio_cli/agents/_lib/toolrunner.py +141 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PLAYBOOK.md +62 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PROCESIO-INTEROP.md +76 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-TROUBLESHOOTING.md +60 -0
- procesio_cli/agents/connector-builder/README.md +51 -0
- procesio_cli/agents/connector-builder/agent.yaml +61 -0
- procesio_cli/agents/connector-builder/context.py +19 -0
- procesio_cli/agents/connector-builder/handlers/__init__.py +1 -0
- procesio_cli/agents/connector-builder/handlers/checklist.py +71 -0
- procesio_cli/agents/connector-builder/handlers/guidance.py +37 -0
- procesio_cli/agents/connector-builder/handlers/nextstep.py +221 -0
- procesio_cli/agents/connector-builder/handlers/status.py +35 -0
- procesio_cli/agents/connector-builder/knowledge.py +47 -0
- procesio_cli/agents/connector-builder/main.py +93 -0
- procesio_cli/agents/connector-builder/tests/conftest.py +95 -0
- procesio_cli/agents/connector-builder/tests/test_cba_agent.py +148 -0
- procesio_cli/agents/orchestrator/__init__.py +0 -0
- procesio_cli/agents/orchestrator/bootstraplib.py +166 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/00-INDEX.md +60 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/01-PROCESSES.md +230 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/02-FORMS.md +287 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/03-INTEGRATIONS.md +384 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/04-DATABASE.md +293 -0
- procesio_cli/agents/procesio/PROCESIO-API-RELIABILITY-DOCTRINE.md +113 -0
- procesio_cli/agents/procesio/PROCESIO-BEST-PRACTICES.md +316 -0
- procesio_cli/agents/procesio/PROCESIO-BUILD-AND-TEST-PLAYBOOK.md +217 -0
- procesio_cli/agents/procesio/PROCESIO-DATASTORE.md +72 -0
- procesio_cli/agents/procesio/PROCESIO-ENVIRONMENTS.md +62 -0
- procesio_cli/agents/procesio/PROCESIO-SCHEDULING.md +47 -0
- procesio_cli/agents/procesio/PROCESIO-VISUAL-ORGANIZATION.md +175 -0
- procesio_cli/agents/procesio/README.md +97 -0
- procesio_cli/agents/procesio/__init__.py +0 -0
- procesio_cli/agents/procesio/agent.yaml +73 -0
- procesio_cli/agents/procesio/audit.py +226 -0
- procesio_cli/agents/procesio/checklist.py +135 -0
- procesio_cli/agents/procesio/context.py +19 -0
- procesio_cli/agents/procesio/handlers/__init__.py +0 -0
- procesio_cli/agents/procesio/handlers/audit.py +39 -0
- procesio_cli/agents/procesio/handlers/checklist.py +34 -0
- procesio_cli/agents/procesio/handlers/guidance.py +36 -0
- procesio_cli/agents/procesio/handlers/status.py +33 -0
- procesio_cli/agents/procesio/handlers/verify.py +39 -0
- procesio_cli/agents/procesio/knowledge.py +115 -0
- procesio_cli/agents/procesio/main.py +89 -0
- procesio_cli/agents/procesio/tests/__init__.py +0 -0
- procesio_cli/agents/procesio/tests/conftest.py +62 -0
- procesio_cli/agents/procesio/tests/test_audit.py +91 -0
- procesio_cli/agents/procesio/tests/test_checklist.py +28 -0
- procesio_cli/agents/procesio/tests/test_guidance.py +78 -0
- procesio_cli/agents/procesio/tests/test_manifest_sync.py +48 -0
- procesio_cli/agents/procesio/tests/test_verify.py +134 -0
- procesio_cli/agents/procesio/tests/test_workspace_scope.py +97 -0
- procesio_cli/agents/procesio/verifylib.py +222 -0
- procesio_cli/dashboard/README.md +117 -0
- procesio_cli/dashboard/__init__.py +18 -0
- procesio_cli/dashboard/serve.py +98 -0
- procesio_cli/dashboard/server/__init__.py +1 -0
- procesio_cli/dashboard/server/config_schemas.py +92 -0
- procesio_cli/dashboard/server/google.py +103 -0
- procesio_cli/dashboard/server/handler.py +160 -0
- procesio_cli/dashboard/server/inventory.py +213 -0
- procesio_cli/dashboard/server/jobs.py +118 -0
- procesio_cli/dashboard/server/llmtest.py +181 -0
- procesio_cli/dashboard/server/procesio_env.py +94 -0
- procesio_cli/dashboard/server/routes.py +74 -0
- procesio_cli/dashboard/server/runner.py +150 -0
- procesio_cli/dashboard/server/security.py +29 -0
- procesio_cli/dashboard/server/setup.py +211 -0
- procesio_cli/dashboard/server/validate.py +152 -0
- procesio_cli/dashboard/tests/conftest.py +16 -0
- procesio_cli/dashboard/tests/test_dashboard.py +225 -0
- procesio_cli/dashboard/tests/test_procesio_env.py +159 -0
- procesio_cli/dashboard/web/actions.js +274 -0
- procesio_cli/dashboard/web/api.js +79 -0
- procesio_cli/dashboard/web/app.js +385 -0
- procesio_cli/dashboard/web/copilot.js +72 -0
- procesio_cli/dashboard/web/google.js +121 -0
- procesio_cli/dashboard/web/index.html +67 -0
- procesio_cli/dashboard/web/procesio_env.js +187 -0
- procesio_cli/dashboard/web/styles.css +207 -0
- procesio_cli/registry.py +370 -0
- procesio_cli/scripts/build-router.py +249 -0
- procesio_cli/scripts/build-tool-skill.py +202 -0
- procesio_cli/scripts/check-requirements.py +131 -0
- procesio_cli/scripts/check-skill-governance.py +174 -0
- procesio_cli/scripts/evaluate-skill-routing.py +217 -0
- procesio_cli/scripts/evaluate-skills.py +316 -0
- procesio_cli/scripts/get-skill.py +122 -0
- procesio_cli/scripts/list-skills.py +68 -0
- procesio_cli/scripts/list-tools.py +73 -0
- procesio_cli/scripts/run-agent.py +59 -0
- procesio_cli/scripts/run-tool.py +123 -0
- procesio_cli/scripts/secret_scan.py +584 -0
- procesio_cli/scripts/set-credential.py +126 -0
- procesio_cli/scripts/validate-skills.py +530 -0
- procesio_cli/skills/PORTFOLIO.md +134 -0
- procesio_cli/skills/__init__.py +6 -0
- procesio_cli/skills/agent-skill-engineer/SKILL.md +226 -0
- procesio_cli/skills/agent-skill-engineer/evals/evals.json +422 -0
- procesio_cli/skills/agent-skill-engineer/references/authoring-standard.md +178 -0
- procesio_cli/skills/agent-skill-engineer/references/decision-framework.md +107 -0
- procesio_cli/skills/agent-skill-engineer/references/domain-evidence-protocol.md +248 -0
- procesio_cli/skills/agent-skill-engineer/references/evaluation-standard.md +191 -0
- procesio_cli/skills/agent-skill-engineer/references/expert-lenses.md +167 -0
- procesio_cli/skills/agent-skill-engineer/references/field-gate-standard.md +231 -0
- procesio_cli/skills/agent-skill-engineer/references/optimization-protocol.md +233 -0
- procesio_cli/skills/agent-skill-engineer/references/optimizer-contract.md +268 -0
- procesio_cli/skills/agent-skill-engineer/references/portability-standard.md +99 -0
- procesio_cli/skills/agent-skill-engineer/references/research-basis.md +128 -0
- procesio_cli/skills/agent-skill-engineer/references/review-standard.md +143 -0
- procesio_cli/skills/agent-skill-engineer/scripts/audit_skill.py +959 -0
- procesio_cli/skills/agent-skill-engineer/scripts/optimize_skill.py +891 -0
- procesio_cli/skills/agent-skill-engineer/scripts/scaffold_skill.py +397 -0
- procesio_cli/skills/evals/baseline-catalog.json +14 -0
- procesio_cli/skills/evals/baseline-routing-v2.json +38 -0
- procesio_cli/skills/evals/baseline.json +37 -0
- procesio_cli/skills/evals/behavioral.json +367 -0
- procesio_cli/skills/evals/field-boundary-development-v1.json +126 -0
- procesio_cli/skills/evals/field-closeout-correction-development-v1.json +23 -0
- procesio_cli/skills/evals/field-closeout-development-v1.json +89 -0
- procesio_cli/skills/evals/portfolio-pressure-training-v1.json +83 -0
- procesio_cli/skills/evals/routing.json +363 -0
- procesio_cli/skills/evals/validation-baseline.json +4 -0
- procesio_cli/skills/procesio-cli/SKILL.md +134 -0
- procesio_cli/skills/procesio-cli/evals/evals.json +111 -0
- procesio_cli/skills/procesio-cli/evals/workflows.json +16 -0
- procesio_cli/skills/procesio-cli/references/connector-lifecycle.md +47 -0
- procesio_cli/skills/procesio-cli/references/credentials-admin.md +113 -0
- procesio_cli/skills/procesio-cli/references/data-verification.md +71 -0
- procesio_cli/skills/procesio-cli/references/documents-files.md +44 -0
- procesio_cli/skills/procesio-cli/references/form-e2e.md +57 -0
- procesio_cli/skills/procesio-cli/references/operation-contract.md +138 -0
- procesio_cli/skills/procesio-cli/references/process-debugging.md +65 -0
- procesio_cli/skills/procesio-cli/references/process-lifecycle.md +88 -0
- procesio_cli/skills/procesio-cli/references/schedules-webhooks.md +58 -0
- procesio_cli/skills/procesio-cli/references/transport-environments.md +53 -0
- procesio_cli/skills/procesio-cli-maintainer/SKILL.md +84 -0
- procesio_cli/skills/procesio-cli-maintainer/evals/evals.json +116 -0
- procesio_cli/skills/procesio-cli-maintainer/references/change-contract.md +34 -0
- procesio_cli/skills/procesio-cli-maintainer/references/mcp-compatibility.md +13 -0
- procesio_cli/skills/procesio-cli-maintainer/references/skill-authoring.md +51 -0
- procesio_cli/skills/procesio-platform-advisor/SKILL.md +82 -0
- procesio_cli/skills/procesio-platform-advisor/evals/evals.json +116 -0
- procesio_cli/skills/procesio-platform-advisor/references/capability-map.md +22 -0
- procesio_cli/skills/procesio-platform-advisor/references/fit-framework.md +35 -0
- procesio_cli/skills/procesio-platform-advisor/references/freshness-policy.md +25 -0
- procesio_cli/skills/procesio-platform-advisor/references/sizing-method.md +70 -0
- procesio_cli/skills/procesio-platform-advisor/scripts/estimate_capacity.py +99 -0
- procesio_cli/skills/procesio-setup/SKILL.md +180 -0
- procesio_cli/skills/sql-server-optimizer/SKILL.md +86 -0
- procesio_cli/skills/sql-server-optimizer/evals/evals.json +116 -0
- procesio_cli/skills/sql-server-optimizer/references/db-context.md +44 -0
- procesio_cli/skills/sql-server-optimizer/references/generic-mode.md +32 -0
- procesio_cli/skills/sql-server-optimizer/references/procesio-mode.md +40 -0
- procesio_cli/skills/sql-server-optimizer/references/sql-rules.md +43 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-indexes.sql +178 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-procs-and-functions.sql +118 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-tables.sql +367 -0
- procesio_cli/skills/tests/__init__.py +0 -0
- procesio_cli/skills/tests/test_agent_skill_engineer.py +240 -0
- procesio_cli/skills/tests/test_auth_failure_stop.py +25 -0
- procesio_cli/skills/tests/test_capacity_estimator.py +85 -0
- procesio_cli/skills/tests/test_field_boundary_learning.py +100 -0
- procesio_cli/skills/tests/test_field_closeout_learning.py +123 -0
- procesio_cli/skills/tests/test_field_gate_standard.py +76 -0
- procesio_cli/skills/tests/test_operational_playbooks.py +51 -0
- procesio_cli/skills/tests/test_portfolio_pressure_training.py +35 -0
- procesio_cli/skills/tests/test_process_lifecycle_single_run.py +25 -0
- procesio_cli/skills/tests/test_skill_evaluator.py +67 -0
- procesio_cli/skills/tests/test_skill_optimizer.py +473 -0
- procesio_cli/skills/tests/test_skill_portfolio_review.py +70 -0
- procesio_cli/skills/tests/test_skill_release_binding.py +204 -0
- procesio_cli/skills/tests/test_skill_routing.py +74 -0
- procesio_cli/skills/tests/test_skill_scaffold_v2.py +116 -0
- procesio_cli/skills/tests/test_validate_skills.py +115 -0
- procesio_cli/tools/__init__.py +0 -0
- procesio_cli/tools/_lib/README.md +119 -0
- procesio_cli/tools/_lib/__init__.py +0 -0
- procesio_cli/tools/_lib/accounting.py +120 -0
- procesio_cli/tools/_lib/contacts.py +544 -0
- procesio_cli/tools/_lib/creds.py +83 -0
- procesio_cli/tools/_lib/creds_backends.py +578 -0
- procesio_cli/tools/_lib/dbprofiles.py +147 -0
- procesio_cli/tools/_lib/dbquery.py +286 -0
- procesio_cli/tools/_lib/dbschema.py +225 -0
- procesio_cli/tools/_lib/io.py +54 -0
- procesio_cli/tools/_lib/manifest.py +297 -0
- procesio_cli/tools/_lib/skill_release.py +127 -0
- procesio_cli/tools/_lib/skill_resources.py +118 -0
- procesio_cli/tools/_lib/userdata.py +228 -0
- procesio_cli/tools/_lib/webserialize/__init__.py +16 -0
- procesio_cli/tools/_lib/webserialize/admin.py +61 -0
- procesio_cli/tools/_lib/webserialize/client.py +264 -0
- procesio_cli/tools/_lib/webserialize/config.py +96 -0
- procesio_cli/tools/_lib/webserialize/errors.py +27 -0
- procesio_cli/tools/_lib/webserialize/framing.py +45 -0
- procesio_cli/tools/_lib/webserialize/registry.py +47 -0
- procesio_cli/tools/_lib/webserialize/runtime.py +61 -0
- procesio_cli/tools/_lib/webserialize/server.py +510 -0
- procesio_cli/tools/_lib/websession.py +194 -0
- procesio_cli/tools/connector-builder/CONNECTOR-BUILDER-API-NOTES.md +91 -0
- procesio_cli/tools/connector-builder/README.md +101 -0
- procesio_cli/tools/connector-builder/SKILL.md +248 -0
- procesio_cli/tools/connector-builder/__init__.py +7 -0
- procesio_cli/tools/connector-builder/actiondef.py +24 -0
- procesio_cli/tools/connector-builder/client.py +251 -0
- procesio_cli/tools/connector-builder/errors.py +44 -0
- procesio_cli/tools/connector-builder/gen_manifest.py +142 -0
- procesio_cli/tools/connector-builder/handlers/__init__.py +1 -0
- procesio_cli/tools/connector-builder/handlers/admin.py +182 -0
- procesio_cli/tools/connector-builder/handlers/auth.py +74 -0
- procesio_cli/tools/connector-builder/handlers/builds.py +396 -0
- procesio_cli/tools/connector-builder/handlers/common.py +104 -0
- procesio_cli/tools/connector-builder/handlers/files.py +111 -0
- procesio_cli/tools/connector-builder/handlers/raw.py +38 -0
- procesio_cli/tools/connector-builder/handlers/telemetry.py +39 -0
- procesio_cli/tools/connector-builder/main.py +94 -0
- procesio_cli/tools/connector-builder/tests/conftest.py +146 -0
- procesio_cli/tools/connector-builder/tests/test_cb_client.py +86 -0
- procesio_cli/tools/connector-builder/tests/test_cb_dispatch.py +105 -0
- procesio_cli/tools/connector-builder/tests/test_cb_manifest_sync.py +76 -0
- procesio_cli/tools/connector-builder/tests/test_cb_wait.py +123 -0
- procesio_cli/tools/connector-builder/tool.yaml +671 -0
- procesio_cli/tools/framework-map/.gitattributes +2 -0
- procesio_cli/tools/framework-map/README.md +98 -0
- procesio_cli/tools/framework-map/SKILL.md +30 -0
- procesio_cli/tools/framework-map/app.js +188 -0
- procesio_cli/tools/framework-map/fm_builder.py +476 -0
- procesio_cli/tools/framework-map/fm_catalog.py +19 -0
- procesio_cli/tools/framework-map/fm_ro_catalog.py +94 -0
- procesio_cli/tools/framework-map/fm_strings.py +101 -0
- procesio_cli/tools/framework-map/main.py +95 -0
- procesio_cli/tools/framework-map/styles.css +214 -0
- procesio_cli/tools/framework-map/tests/conftest.py +33 -0
- procesio_cli/tools/framework-map/tests/test_fm_build.py +126 -0
- procesio_cli/tools/framework-map/tests/test_fm_manifest_sync.py +63 -0
- procesio_cli/tools/framework-map/tests/test_fm_store_tree.py +75 -0
- procesio_cli/tools/framework-map/tool.yaml +61 -0
- procesio_cli/tools/mysql/MOVED-TO-CENTRAL.md +29 -0
- procesio_cli/tools/mysql/README.md +196 -0
- procesio_cli/tools/mysql/SKILL.md +37 -0
- procesio_cli/tools/mysql/__init__.py +0 -0
- procesio_cli/tools/mysql/connection.py +191 -0
- procesio_cli/tools/mysql/main.py +487 -0
- procesio_cli/tools/mysql/profiles.template.json +1 -0
- procesio_cli/tools/mysql/schema.py +434 -0
- procesio_cli/tools/mysql/tests/__init__.py +0 -0
- procesio_cli/tools/mysql/tests/conftest.py +21 -0
- procesio_cli/tools/mysql/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/mysql/tests/test_mysql.py +201 -0
- procesio_cli/tools/mysql/tests/test_mysql_schema.py +337 -0
- procesio_cli/tools/mysql/tool.yaml +112 -0
- procesio_cli/tools/procesio/DTO-SUBTOOLS-NOTE.md +360 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/00-INDEX.md +67 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/01-ANATOMY.md +209 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/02-CODE-INJECTION.md +309 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/03-DOM-CONTRACT.md +370 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/04-INTERACTION-RECIPES.md +600 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/05-STEPPER-AND-MOTION.md +426 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/06-PROCESS-INTEGRATION.md +347 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/07-DEPLOY-WORKFLOW.md +294 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/08-PITFALLS.md +569 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/09-NAVIGATION-AND-HOSTING.md +308 -0
- procesio_cli/tools/procesio/PHASE4-E2E-NOTES.md +594 -0
- procesio_cli/tools/procesio/PROCESIO-API-CORRECTIONS.md +295 -0
- procesio_cli/tools/procesio/PROCESIO-API-NOTES.md +6976 -0
- procesio_cli/tools/procesio/PROCESIO-AUTH-NOTES.md +226 -0
- procesio_cli/tools/procesio/PROCESIO-CARD-BUILD-NOTES.md +183 -0
- procesio_cli/tools/procesio/PROCESIO-CONNECTORS-NOTES.md +141 -0
- procesio_cli/tools/procesio/PROCESIO-CUSTOM-ACTION-NOTES.md +179 -0
- procesio_cli/tools/procesio/PROCESIO-DOCS-FIX-REPORT.md +123 -0
- procesio_cli/tools/procesio/PROCESIO-ENVIRONMENTS-NOTES.md +94 -0
- procesio_cli/tools/procesio/PROCESIO-FE-VALIDATION-NOTES.md +107 -0
- procesio_cli/tools/procesio/PROCESIO-FOREACH-NOTES.md +199 -0
- procesio_cli/tools/procesio/PROCESIO-FORM-API-HANG-NOTE.md +101 -0
- procesio_cli/tools/procesio/PROCESIO-FORM-SUBMISSION-NOTES.md +172 -0
- procesio_cli/tools/procesio/PROCESIO-HTML-TO-DOCX-NOTES.md +110 -0
- procesio_cli/tools/procesio/PROCESIO-INSTANCE-HISTORY-NOTES.md +242 -0
- procesio_cli/tools/procesio/PROCESIO-METERING-NOTES.md +201 -0
- procesio_cli/tools/procesio/PROCESIO-NODE-CODE-NOTES.md +279 -0
- procesio_cli/tools/procesio/PROCESIO-NODE-MODULE-WHITELIST.md +113 -0
- procesio_cli/tools/procesio/PROCESIO-PYTHON-ACTION-NOTES.md +165 -0
- procesio_cli/tools/procesio/PROCESIO-QUERY-STORE-NOTES.md +826 -0
- procesio_cli/tools/procesio/PROCESIO-RECONCILIATION-PATTERNS.md +104 -0
- procesio_cli/tools/procesio/PROCESIO-RESOURCE-MODEL-NOTES.md +1100 -0
- procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-CRED-OPTIONALITY.md +45 -0
- procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-NOTES.md +252 -0
- procesio_cli/tools/procesio/PROCESIO-SQL-ACTIONS-NOTES.md +284 -0
- procesio_cli/tools/procesio/PROCESIO-USAGE-GUIDE.md +219 -0
- procesio_cli/tools/procesio/README.md +341 -0
- procesio_cli/tools/procesio/SCHEDULE-INPUT-SECURITY-NOTES.md +63 -0
- procesio_cli/tools/procesio/SKILL.md +694 -0
- procesio_cli/tools/procesio/__init__.py +1 -0
- procesio_cli/tools/procesio/actiondef.py +27 -0
- procesio_cli/tools/procesio/assets/html2docx/html2docx.js +2155 -0
- procesio_cli/tools/procesio/auth.py +354 -0
- procesio_cli/tools/procesio/autogen.py +121 -0
- procesio_cli/tools/procesio/client.py +355 -0
- procesio_cli/tools/procesio/config.py +96 -0
- procesio_cli/tools/procesio/data/endpoints.json +2901 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/01-authentication.md +180 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/02-conventions.md +116 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/ANONYMOUS-SURFACE.md +51 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/README.md +104 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/SYMBOL-INDEX.md +444 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/03-auth-users-workspaces.md +731 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/04-processes.md +913 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/05-actions.md +605 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/06-files.md +237 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/07-forms-documents.md +985 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/08-credentials.md +532 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/09-datastore-datatypes.md +830 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/10-custom-urls.md +319 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/11-subscriptions-resources-analytics.md +455 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/12-webhooks-notifications-misc.md +519 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/13-realtime-signalr.md +153 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/99-internal-and-other-services.md +52 -0
- procesio_cli/tools/procesio/docs_info/FormBuilder mock.ts +5725 -0
- procesio_cli/tools/procesio/docs_info/Procesio Platform Actions.json +30219 -0
- procesio_cli/tools/procesio/docs_info/form-config-enums.ts +92 -0
- procesio_cli/tools/procesio/docs_info/form-theme.ts +1603 -0
- procesio_cli/tools/procesio/docs_info/process-validation-reference-1.md +2739 -0
- procesio_cli/tools/procesio/dto/__init__.py +13 -0
- procesio_cli/tools/procesio/dto/credential/builder.py +179 -0
- procesio_cli/tools/procesio/dto/credential/config.schema.json +19 -0
- procesio_cli/tools/procesio/dto/credential/description.md +193 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.config.json +11 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.dto.json +28 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.template.json +44 -0
- procesio_cli/tools/procesio/dto/credential/template.dto.json +16 -0
- procesio_cli/tools/procesio/dto/data/_refresh.py +147 -0
- procesio_cli/tools/procesio/dto/data/action_catalog.json +30825 -0
- procesio_cli/tools/procesio/dto/data/action_families.json +55 -0
- procesio_cli/tools/procesio/dto/data/platform_types.json +182 -0
- procesio_cli/tools/procesio/dto/datatype/builder.py +223 -0
- procesio_cli/tools/procesio/dto/datatype/config.schema.json +47 -0
- procesio_cli/tools/procesio/dto/datatype/description.md +85 -0
- procesio_cli/tools/procesio/dto/datatype/fixtures/customer.config.json +41 -0
- procesio_cli/tools/procesio/dto/datatype/fixtures/customer.dto.json +164 -0
- procesio_cli/tools/procesio/dto/datatype/template.dto.json +37 -0
- procesio_cli/tools/procesio/dto/document/builder.py +193 -0
- procesio_cli/tools/procesio/dto/document/config.schema.json +33 -0
- procesio_cli/tools/procesio/dto/document/description.md +124 -0
- procesio_cli/tools/procesio/dto/document/fixtures/invoice.config.json +16 -0
- procesio_cli/tools/procesio/dto/document/fixtures/invoice.dto.json +32 -0
- procesio_cli/tools/procesio/dto/document/template.dto.json +14 -0
- procesio_cli/tools/procesio/dto/form/FORM-STYLING-NOTES.md +142 -0
- procesio_cli/tools/procesio/dto/form/GOLDEN-DRIFT-NOTES.md +72 -0
- procesio_cli/tools/procesio/dto/form/RUN-PROCESS-RESULT-NOTES.md +80 -0
- procesio_cli/tools/procesio/dto/form/addelement.py +277 -0
- procesio_cli/tools/procesio/dto/form/builder.py +1009 -0
- procesio_cli/tools/procesio/dto/form/capture_goldens.py +106 -0
- procesio_cli/tools/procesio/dto/form/code_cipher.py +67 -0
- procesio_cli/tools/procesio/dto/form/config.schema.json +76 -0
- procesio_cli/tools/procesio/dto/form/data_shell.json +1 -0
- procesio_cli/tools/procesio/dto/form/description.md +346 -0
- procesio_cli/tools/procesio/dto/form/elements/approval.json +259 -0
- procesio_cli/tools/procesio/dto/form/elements/assignee.json +137 -0
- procesio_cli/tools/procesio/dto/form/elements/button.json +125 -0
- procesio_cli/tools/procesio/dto/form/elements/chart.json +341 -0
- procesio_cli/tools/procesio/dto/form/elements/chat.json +199 -0
- procesio_cli/tools/procesio/dto/form/elements/checkbox.json +153 -0
- procesio_cli/tools/procesio/dto/form/elements/column.json +77 -0
- procesio_cli/tools/procesio/dto/form/elements/columns.json +78 -0
- procesio_cli/tools/procesio/dto/form/elements/datetime-input.json +304 -0
- procesio_cli/tools/procesio/dto/form/elements/divider.json +54 -0
- procesio_cli/tools/procesio/dto/form/elements/dropdown.json +130 -0
- procesio_cli/tools/procesio/dto/form/elements/dynamic-table-row.json +253 -0
- procesio_cli/tools/procesio/dto/form/elements/file-upload.json +196 -0
- procesio_cli/tools/procesio/dto/form/elements/file-viewer.json +169 -0
- procesio_cli/tools/procesio/dto/form/elements/heading.json +79 -0
- procesio_cli/tools/procesio/dto/form/elements/icon.json +107 -0
- procesio_cli/tools/procesio/dto/form/elements/image.json +174 -0
- procesio_cli/tools/procesio/dto/form/elements/input.json +206 -0
- procesio_cli/tools/procesio/dto/form/elements/list.json +202 -0
- procesio_cli/tools/procesio/dto/form/elements/number-input.json +202 -0
- procesio_cli/tools/procesio/dto/form/elements/paragraph.json +79 -0
- procesio_cli/tools/procesio/dto/form/elements/radiobox.json +174 -0
- procesio_cli/tools/procesio/dto/form/elements/section.json +114 -0
- procesio_cli/tools/procesio/dto/form/elements/select.json +217 -0
- procesio_cli/tools/procesio/dto/form/elements/side-panel.json +146 -0
- procesio_cli/tools/procesio/dto/form/elements/signature-pad.json +141 -0
- procesio_cli/tools/procesio/dto/form/elements/static-table-row.json +84 -0
- procesio_cli/tools/procesio/dto/form/elements/step.json +105 -0
- procesio_cli/tools/procesio/dto/form/elements/stepper.json +126 -0
- procesio_cli/tools/procesio/dto/form/elements/tab.json +77 -0
- procesio_cli/tools/procesio/dto/form/elements/table.json +279 -0
- procesio_cli/tools/procesio/dto/form/elements/tabs.json +114 -0
- procesio_cli/tools/procesio/dto/form/elements/textarea.json +213 -0
- procesio_cli/tools/procesio/dto/form/enum_map.json +68 -0
- procesio_cli/tools/procesio/dto/form/fieldpath.py +284 -0
- procesio_cli/tools/procesio/dto/form/fixtures/reg.config.json +32 -0
- procesio_cli/tools/procesio/dto/form/fixtures/reg.elements.json +638 -0
- procesio_cli/tools/procesio/dto/form/sync_from_mock.py +386 -0
- procesio_cli/tools/procesio/dto/framework.py +125 -0
- procesio_cli/tools/procesio/dto/process/builder.py +1991 -0
- procesio_cli/tools/procesio/dto/process/config.schema.json +159 -0
- procesio_cli/tools/procesio/dto/process/description.md +218 -0
- procesio_cli/tools/procesio/dto/process/fixtures/greet.config.json +32 -0
- procesio_cli/tools/procesio/dto/process/fixtures/greet.dto.json +317 -0
- procesio_cli/tools/procesio/dto/process/naming.py +305 -0
- procesio_cli/tools/procesio/dto/process/normalize.py +74 -0
- procesio_cli/tools/procesio/dto/process/template.dto.json +32 -0
- procesio_cli/tools/procesio/dto/refdata.py +87 -0
- procesio_cli/tools/procesio/dto/registry.py +27 -0
- procesio_cli/tools/procesio/dto/webhook/builder.py +142 -0
- procesio_cli/tools/procesio/dto/webhook/config.schema.json +25 -0
- procesio_cli/tools/procesio/dto/webhook/description.md +92 -0
- procesio_cli/tools/procesio/dto/webhook/fixtures/order.config.json +10 -0
- procesio_cli/tools/procesio/dto/webhook/fixtures/order.dto.json +25 -0
- procesio_cli/tools/procesio/dto/webhook/template.dto.json +14 -0
- procesio_cli/tools/procesio/environments.py +264 -0
- procesio_cli/tools/procesio/errors.py +166 -0
- procesio_cli/tools/procesio/flowmodel/README.md +37 -0
- procesio_cli/tools/procesio/flowmodel/__init__.py +14 -0
- procesio_cli/tools/procesio/flowmodel/digest.py +223 -0
- procesio_cli/tools/procesio/flowmodel/families.py +34 -0
- procesio_cli/tools/procesio/flowmodel/fevalidation.py +1098 -0
- procesio_cli/tools/procesio/flowmodel/formdigest.py +232 -0
- procesio_cli/tools/procesio/flowmodel/inspect.py +78 -0
- procesio_cli/tools/procesio/flowmodel/model.py +82 -0
- procesio_cli/tools/procesio/flowmodel/nodeparam.py +871 -0
- procesio_cli/tools/procesio/flowmodel/reader.py +191 -0
- procesio_cli/tools/procesio/flowmodel/sqlparam.py +410 -0
- procesio_cli/tools/procesio/form_code_keys.py +50 -0
- procesio_cli/tools/procesio/formlint.py +292 -0
- procesio_cli/tools/procesio/gen_manifest.py +118 -0
- procesio_cli/tools/procesio/handlers/__init__.py +1 -0
- procesio_cli/tools/procesio/handlers/auth_actions.py +103 -0
- procesio_cli/tools/procesio/handlers/catalog.py +147 -0
- procesio_cli/tools/procesio/handlers/common.py +90 -0
- procesio_cli/tools/procesio/handlers/customaction_ops.py +146 -0
- procesio_cli/tools/procesio/handlers/datastore_ops.py +482 -0
- procesio_cli/tools/procesio/handlers/datatype_ops.py +275 -0
- procesio_cli/tools/procesio/handlers/dto_actions.py +258 -0
- procesio_cli/tools/procesio/handlers/environment_admin.py +146 -0
- procesio_cli/tools/procesio/handlers/fevalidate.py +236 -0
- procesio_cli/tools/procesio/handlers/files.py +141 -0
- procesio_cli/tools/procesio/handlers/flowgraph.py +229 -0
- procesio_cli/tools/procesio/handlers/flowlint.py +269 -0
- procesio_cli/tools/procesio/handlers/form_chains.py +336 -0
- procesio_cli/tools/procesio/handlers/form_code.py +277 -0
- procesio_cli/tools/procesio/handlers/form_element.py +199 -0
- procesio_cli/tools/procesio/handlers/form_events.py +441 -0
- procesio_cli/tools/procesio/handlers/form_update.py +270 -0
- procesio_cli/tools/procesio/handlers/formprocess.py +211 -0
- procesio_cli/tools/procesio/handlers/generic.py +135 -0
- procesio_cli/tools/procesio/handlers/layout.py +137 -0
- procesio_cli/tools/procesio/handlers/nodeparams.py +1018 -0
- procesio_cli/tools/procesio/handlers/process_layout.py +189 -0
- procesio_cli/tools/procesio/handlers/process_naming.py +213 -0
- procesio_cli/tools/procesio/handlers/processes.py +416 -0
- procesio_cli/tools/procesio/handlers/profile_admin.py +184 -0
- procesio_cli/tools/procesio/handlers/resource_ops.py +447 -0
- procesio_cli/tools/procesio/handlers/schedules.py +320 -0
- procesio_cli/tools/procesio/handlers/sqlactions.py +232 -0
- procesio_cli/tools/procesio/handlers/transport.py +463 -0
- procesio_cli/tools/procesio/handlers/usageguide.py +222 -0
- procesio_cli/tools/procesio/layout/README.md +196 -0
- procesio_cli/tools/procesio/layout/__init__.py +11 -0
- procesio_cli/tools/procesio/layout/adapter.py +736 -0
- procesio_cli/tools/procesio/layout/cycles.py +186 -0
- procesio_cli/tools/procesio/layout/dispatch.py +39 -0
- procesio_cli/tools/procesio/layout/elk/elk_runner.mjs +50 -0
- procesio_cli/tools/procesio/layout/elk/package-lock.json +21 -0
- procesio_cli/tools/procesio/layout/elk/package.json +11 -0
- procesio_cli/tools/procesio/layout/elk_engine.py +568 -0
- procesio_cli/tools/procesio/layout/engine.py +1311 -0
- procesio_cli/tools/procesio/layout/report.py +187 -0
- procesio_cli/tools/procesio/main.py +179 -0
- procesio_cli/tools/procesio/profiles.py +193 -0
- procesio_cli/tools/procesio/reliability.py +193 -0
- procesio_cli/tools/procesio/sqlformat.py +167 -0
- procesio_cli/tools/procesio/swagger.py +51 -0
- procesio_cli/tools/procesio/tests/__init__.py +0 -0
- procesio_cli/tools/procesio/tests/conftest.py +129 -0
- procesio_cli/tools/procesio/tests/test_auth.py +200 -0
- procesio_cli/tools/procesio/tests/test_autogen.py +78 -0
- procesio_cli/tools/procesio/tests/test_builder_attrpath_sqlbind.py +180 -0
- procesio_cli/tools/procesio/tests/test_builder_custom_action_catalog.py +72 -0
- procesio_cli/tools/procesio/tests/test_client.py +114 -0
- procesio_cli/tools/procesio/tests/test_client_reliability.py +139 -0
- procesio_cli/tools/procesio/tests/test_customaction.py +103 -0
- procesio_cli/tools/procesio/tests/test_customaction_test.py +80 -0
- procesio_cli/tools/procesio/tests/test_cycles.py +69 -0
- procesio_cli/tools/procesio/tests/test_datastore.py +378 -0
- procesio_cli/tools/procesio/tests/test_datastore_node.py +94 -0
- procesio_cli/tools/procesio/tests/test_dispatch.py +170 -0
- procesio_cli/tools/procesio/tests/test_document_page_size.py +19 -0
- procesio_cli/tools/procesio/tests/test_dto.py +734 -0
- procesio_cli/tools/procesio/tests/test_edit_dryrun_preview.py +96 -0
- procesio_cli/tools/procesio/tests/test_elk_layout.py +221 -0
- procesio_cli/tools/procesio/tests/test_environments.py +214 -0
- procesio_cli/tools/procesio/tests/test_fevalidate_gate.py +104 -0
- procesio_cli/tools/procesio/tests/test_fevalidation.py +317 -0
- procesio_cli/tools/procesio/tests/test_fieldpath_variables.py +87 -0
- procesio_cli/tools/procesio/tests/test_files.py +72 -0
- procesio_cli/tools/procesio/tests/test_flow_digest.py +202 -0
- procesio_cli/tools/procesio/tests/test_flowlint.py +114 -0
- procesio_cli/tools/procesio/tests/test_flowmodel.py +158 -0
- procesio_cli/tools/procesio/tests/test_form_add_element.py +256 -0
- procesio_cli/tools/procesio/tests/test_form_approval_gaps.py +319 -0
- procesio_cli/tools/procesio/tests/test_form_auto_names.py +60 -0
- procesio_cli/tools/procesio/tests/test_form_chains.py +217 -0
- procesio_cli/tools/procesio/tests/test_form_code_actions.py +188 -0
- procesio_cli/tools/procesio/tests/test_form_code_keys.py +92 -0
- procesio_cli/tools/procesio/tests/test_form_code_style.py +178 -0
- procesio_cli/tools/procesio/tests/test_form_delete_element.py +99 -0
- procesio_cli/tools/procesio/tests/test_form_element_config.py +141 -0
- procesio_cli/tools/procesio/tests/test_form_event_maps.py +135 -0
- procesio_cli/tools/procesio/tests/test_form_event_ops.py +376 -0
- procesio_cli/tools/procesio/tests/test_form_event_replace.py +86 -0
- procesio_cli/tools/procesio/tests/test_form_events.py +92 -0
- procesio_cli/tools/procesio/tests/test_form_parity.py +171 -0
- procesio_cli/tools/procesio/tests/test_form_select_source.py +74 -0
- procesio_cli/tools/procesio/tests/test_form_sync.py +35 -0
- procesio_cli/tools/procesio/tests/test_form_update.py +133 -0
- procesio_cli/tools/procesio/tests/test_formlint.py +155 -0
- procesio_cli/tools/procesio/tests/test_formlint_css_scope.py +38 -0
- procesio_cli/tools/procesio/tests/test_formlint_field_names.py +83 -0
- procesio_cli/tools/procesio/tests/test_guards.py +110 -0
- procesio_cli/tools/procesio/tests/test_hidden_setting_types.py +39 -0
- procesio_cli/tools/procesio/tests/test_html2docx_asset.py +132 -0
- procesio_cli/tools/procesio/tests/test_key_handle_terminology.py +45 -0
- procesio_cli/tools/procesio/tests/test_layout.py +574 -0
- procesio_cli/tools/procesio/tests/test_layout_report.py +88 -0
- procesio_cli/tools/procesio/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/procesio/tests/test_nodedelete.py +151 -0
- procesio_cli/tools/procesio/tests/test_nodeparam.py +826 -0
- procesio_cli/tools/procesio/tests/test_normalize.py +70 -0
- procesio_cli/tools/procesio/tests/test_payload_docs.py +86 -0
- procesio_cli/tools/procesio/tests/test_process_action_naming.py +170 -0
- procesio_cli/tools/procesio/tests/test_process_call_api_seed.py +46 -0
- procesio_cli/tools/procesio/tests/test_process_ext.py +373 -0
- procesio_cli/tools/procesio/tests/test_process_invariants.py +183 -0
- procesio_cli/tools/procesio/tests/test_process_layout.py +117 -0
- procesio_cli/tools/procesio/tests/test_process_naming.py +188 -0
- procesio_cli/tools/procesio/tests/test_process_query_store.py +172 -0
- procesio_cli/tools/procesio/tests/test_processinfo_variable.py +56 -0
- procesio_cli/tools/procesio/tests/test_profiles.py +127 -0
- procesio_cli/tools/procesio/tests/test_put_projects.py +41 -0
- procesio_cli/tools/procesio/tests/test_reliability.py +107 -0
- procesio_cli/tools/procesio/tests/test_removal_gate.py +95 -0
- procesio_cli/tools/procesio/tests/test_resource_ops.py +173 -0
- procesio_cli/tools/procesio/tests/test_save_validity.py +207 -0
- procesio_cli/tools/procesio/tests/test_schedule_cron_shape.py +111 -0
- procesio_cli/tools/procesio/tests/test_schedules.py +309 -0
- procesio_cli/tools/procesio/tests/test_sqlconvert.py +234 -0
- procesio_cli/tools/procesio/tests/test_sqlformat.py +83 -0
- procesio_cli/tools/procesio/tests/test_sqlparam.py +187 -0
- procesio_cli/tools/procesio/tests/test_superseded_actions.py +54 -0
- procesio_cli/tools/procesio/tests/test_surgical_concurrency.py +156 -0
- procesio_cli/tools/procesio/tests/test_tier4.py +123 -0
- procesio_cli/tools/procesio/tests/test_transport.py +147 -0
- procesio_cli/tools/procesio/tests/test_transport_data_stores.py +168 -0
- procesio_cli/tools/procesio/tests/test_transport_mapper_repair.py +150 -0
- procesio_cli/tools/procesio/tests/test_transport_webhook_strip.py +253 -0
- procesio_cli/tools/procesio/tests/test_usage_guide.py +96 -0
- procesio_cli/tools/procesio/tool.yaml +12486 -0
- procesio_cli/tools/sqlserver/MOVED-TO-CENTRAL.md +29 -0
- procesio_cli/tools/sqlserver/README.md +192 -0
- procesio_cli/tools/sqlserver/SKILL.md +37 -0
- procesio_cli/tools/sqlserver/SQLSERVER-SCHEMA-NOTES.md +284 -0
- procesio_cli/tools/sqlserver/__init__.py +0 -0
- procesio_cli/tools/sqlserver/connection.py +242 -0
- procesio_cli/tools/sqlserver/main.py +521 -0
- procesio_cli/tools/sqlserver/profiles.template.json +1 -0
- procesio_cli/tools/sqlserver/schema.py +600 -0
- procesio_cli/tools/sqlserver/tests/__init__.py +0 -0
- procesio_cli/tools/sqlserver/tests/conftest.py +21 -0
- procesio_cli/tools/sqlserver/tests/test_connection.py +75 -0
- procesio_cli/tools/sqlserver/tests/test_dbprofiles.py +92 -0
- procesio_cli/tools/sqlserver/tests/test_dbquery.py +220 -0
- procesio_cli/tools/sqlserver/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/sqlserver/tests/test_sqlserver.py +326 -0
- procesio_cli/tools/sqlserver/tests/test_sqlserver_schema.py +488 -0
- procesio_cli/tools/sqlserver/tool.yaml +123 -0
- procesio_cli/tools/web/README.md +381 -0
- procesio_cli/tools/web/SKILL.md +37 -0
- procesio_cli/tools/web/WEB-AUTOMATION-NOTES.md +277 -0
- procesio_cli/tools/web/__init__.py +21 -0
- procesio_cli/tools/web/actiondef.py +24 -0
- procesio_cli/tools/web/api.py +444 -0
- procesio_cli/tools/web/brokered.py +107 -0
- procesio_cli/tools/web/driver.py +839 -0
- procesio_cli/tools/web/drivercontract.py +44 -0
- procesio_cli/tools/web/errors.py +54 -0
- procesio_cli/tools/web/handlers/__init__.py +1 -0
- procesio_cli/tools/web/handlers/archive.py +367 -0
- procesio_cli/tools/web/handlers/browse.py +182 -0
- procesio_cli/tools/web/handlers/contacts.py +74 -0
- procesio_cli/tools/web/handlers/sessmgmt.py +120 -0
- procesio_cli/tools/web/main.py +128 -0
- procesio_cli/tools/web/sessions.py +253 -0
- procesio_cli/tools/web/steps.py +162 -0
- procesio_cli/tools/web/tests/__init__.py +0 -0
- procesio_cli/tools/web/tests/conftest.py +197 -0
- procesio_cli/tools/web/tests/test_api.py +330 -0
- procesio_cli/tools/web/tests/test_archive.py +190 -0
- procesio_cli/tools/web/tests/test_brokered.py +138 -0
- procesio_cli/tools/web/tests/test_dispatch.py +132 -0
- procesio_cli/tools/web/tests/test_errors.py +60 -0
- procesio_cli/tools/web/tests/test_manifest_sync.py +76 -0
- procesio_cli/tools/web/tests/test_page_contacts.py +68 -0
- procesio_cli/tools/web/tests/test_sessions.py +145 -0
- procesio_cli/tools/web/tests/test_steps.py +210 -0
- procesio_cli/tools/web/tool.yaml +184 -0
- procesio_cli/tools/xlsx/README.md +34 -0
- procesio_cli/tools/xlsx/SKILL.md +28 -0
- procesio_cli/tools/xlsx/__init__.py +0 -0
- procesio_cli/tools/xlsx/main.py +129 -0
- procesio_cli/tools/xlsx/reader.py +91 -0
- procesio_cli/tools/xlsx/tests/__init__.py +0 -0
- procesio_cli/tools/xlsx/tests/conftest.py +39 -0
- procesio_cli/tools/xlsx/tests/test_xlsx.py +103 -0
- procesio_cli/tools/xlsx/tool.yaml +30 -0
- procesio_cli/webplatform/aat_mcp/AAT-MCP-NOTES.md +244 -0
- procesio_cli/webplatform/aat_mcp/README.md +78 -0
- procesio_cli/webplatform/aat_mcp/bridge.py +705 -0
- procesio_cli/webplatform/aat_mcp/chat_server.py +194 -0
- procesio_cli/webplatform/aat_mcp/chat_surface.snapshot.json +668 -0
- procesio_cli/webplatform/aat_mcp/chat_surface.yaml +372 -0
- procesio_cli/webplatform/aat_mcp/gate.py +68 -0
- procesio_cli/webplatform/aat_mcp/http_server.py +139 -0
- procesio_cli/webplatform/aat_mcp/protocol.py +175 -0
- procesio_cli/webplatform/aat_mcp/server.py +306 -0
- procesio_cli/webplatform/aat_mcp/tests/conftest.py +8 -0
- procesio_cli/webplatform/aat_mcp/tests/test_aat_mcp.py +255 -0
- procesio_cli/webplatform/aat_mcp/tests/test_chat_surface.py +247 -0
- procesio_cli/webplatform/aat_mcp/tests/test_discovery.py +489 -0
- procesio_cli/webplatform/aat_mcp/tests/test_multiuser.py +86 -0
- procesio_cli/webplatform/aat_mcp/tests/test_progressive_discovery.py +89 -0
- procesio_cli/webplatform/aat_mcp/tests/test_protocol.py +180 -0
- procesio_cli/webplatform/aat_mcp/tests/test_skill_discovery.py +23 -0
- procesio_cli-0.1.1.dist-info/METADATA +467 -0
- procesio_cli-0.1.1.dist-info/RECORD +647 -0
- procesio_cli-0.1.1.dist-info/WHEEL +4 -0
- procesio_cli-0.1.1.dist-info/entry_points.txt +7 -0
- procesio_cli-0.1.1.dist-info/licenses/LICENSE +201 -0
- procesio_cli-0.1.1.dist-info/licenses/NOTICE +5 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Store a credential in this machine's OS credential store.
|
|
2
|
+
|
|
3
|
+
Which store that is follows the platform: Windows Credential Manager, the macOS
|
|
4
|
+
login Keychain, or the Linux desktop keyring (GNOME Keyring / KWallet). On a
|
|
5
|
+
headless machine, where no OS keyring exists, set AAT_CREDS_BACKEND=encrypted-file
|
|
6
|
+
(passphrase in AAT_SECRETS_PASSPHRASE) and this script writes there instead. The
|
|
7
|
+
identity is the same everywhere: agents-and-tools:<tool>:<secret>.
|
|
8
|
+
|
|
9
|
+
Secrets are prompted via getpass — never echoed, never written to disk in plaintext.
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
python scripts/set-credential.py <tool> <secret> # set or overwrite
|
|
13
|
+
python scripts/set-credential.py <tool> <secret> --delete # remove
|
|
14
|
+
python scripts/set-credential.py <tool> <secret> --check # just check presence
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
python scripts/set-credential.py ryver api-token
|
|
18
|
+
python scripts/set-credential.py ryver api-token --delete
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
# Auto-switch to the project's .venv Python if we were launched with a different one.
|
|
26
|
+
_PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
27
|
+
# .venv layout is per-OS: Scripts/python.exe on Windows, bin/python everywhere
|
|
28
|
+
# else. Hardcoding the Windows path made this a silent no-op on macOS/Linux,
|
|
29
|
+
# where the script then ran on whatever interpreter happened to invoke it.
|
|
30
|
+
_VENV_PY = (_PROJECT_ROOT / ".venv" / "Scripts" / "python.exe" if sys.platform == "win32"
|
|
31
|
+
else _PROJECT_ROOT / ".venv" / "bin" / "python")
|
|
32
|
+
if _VENV_PY.exists() and Path(sys.executable).resolve() != _VENV_PY.resolve():
|
|
33
|
+
import subprocess
|
|
34
|
+
sys.exit(subprocess.run([str(_VENV_PY), __file__, *sys.argv[1:]]).returncode)
|
|
35
|
+
|
|
36
|
+
import argparse # noqa: E402
|
|
37
|
+
import getpass # noqa: E402
|
|
38
|
+
|
|
39
|
+
sys.path.insert(0, str(_PROJECT_ROOT))
|
|
40
|
+
|
|
41
|
+
from tools._lib import creds # noqa: E402
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def main() -> int:
|
|
45
|
+
p = argparse.ArgumentParser(
|
|
46
|
+
description="Store a credential in this machine's OS credential store "
|
|
47
|
+
"(Windows Credential Manager / macOS Keychain / Linux keyring).",
|
|
48
|
+
epilog="Target name in the store: agents-and-tools:<tool>:<secret>",
|
|
49
|
+
)
|
|
50
|
+
p.add_argument("tool", help="Tool name (matches name in tool.yaml)")
|
|
51
|
+
p.add_argument("secret", help="Secret name (matches a secrets[].name entry)")
|
|
52
|
+
g = p.add_mutually_exclusive_group()
|
|
53
|
+
g.add_argument("--delete", action="store_true", help="Delete instead of set")
|
|
54
|
+
g.add_argument("--check", action="store_true", help="Check presence; don't prompt")
|
|
55
|
+
g.add_argument("--from-file", help="Read the value from this file (full content, trimmed)")
|
|
56
|
+
g.add_argument("--stdin", action="store_true", help="Read the value from stdin (until EOF)")
|
|
57
|
+
args = p.parse_args()
|
|
58
|
+
|
|
59
|
+
target = f"agents-and-tools:{args.tool} / {args.secret}"
|
|
60
|
+
|
|
61
|
+
if args.check:
|
|
62
|
+
present = creds.has(args.tool, args.secret)
|
|
63
|
+
print(f"{target}: {'present' if present else 'missing'}")
|
|
64
|
+
return 0 if present else 1
|
|
65
|
+
|
|
66
|
+
if args.delete:
|
|
67
|
+
if not creds.has(args.tool, args.secret):
|
|
68
|
+
print(f"not found: {target}")
|
|
69
|
+
return 1
|
|
70
|
+
creds.delete(args.tool, args.secret)
|
|
71
|
+
print(f"deleted: {target}")
|
|
72
|
+
return 0
|
|
73
|
+
|
|
74
|
+
if creds.has(args.tool, args.secret):
|
|
75
|
+
# Skip the interactive overwrite prompt when reading non-interactively.
|
|
76
|
+
if not (args.from_file or args.stdin):
|
|
77
|
+
answer = input(f"{target} already exists. Overwrite? [y/N] ").strip().lower()
|
|
78
|
+
if answer != "y":
|
|
79
|
+
print("cancelled.")
|
|
80
|
+
return 0
|
|
81
|
+
|
|
82
|
+
interactive = not (args.from_file or args.stdin)
|
|
83
|
+
if args.from_file:
|
|
84
|
+
# utf-8-sig strips a BOM that Notepad/editors may prepend.
|
|
85
|
+
raw = Path(args.from_file).read_text(encoding="utf-8-sig")
|
|
86
|
+
if not raw.strip():
|
|
87
|
+
print(f"file is empty: {args.from_file}", file=sys.stderr)
|
|
88
|
+
return 1
|
|
89
|
+
elif args.stdin:
|
|
90
|
+
raw = sys.stdin.read()
|
|
91
|
+
if not raw.strip():
|
|
92
|
+
print("stdin is empty, aborting.", file=sys.stderr)
|
|
93
|
+
return 1
|
|
94
|
+
else:
|
|
95
|
+
raw = getpass.getpass(f"Enter value for {target}: ")
|
|
96
|
+
if not raw:
|
|
97
|
+
print("empty value, aborting.", file=sys.stderr)
|
|
98
|
+
return 1
|
|
99
|
+
|
|
100
|
+
# Clean pasted/typed input: drop surrounding whitespace and any non-printable
|
|
101
|
+
# control characters (e.g. a stray \x16 from a terminal paste).
|
|
102
|
+
value = "".join(c for c in raw if c.isprintable()).strip()
|
|
103
|
+
had_control = any((ord(c) < 32 or ord(c) == 127) for c in raw.strip())
|
|
104
|
+
|
|
105
|
+
if had_control:
|
|
106
|
+
print("warning: removed non-printable characters from the pasted value "
|
|
107
|
+
"(terminal paste corruption). Verify it stored correctly.",
|
|
108
|
+
file=sys.stderr)
|
|
109
|
+
# A 1-3 char "secret" is almost always a truncated paste (a known issue with
|
|
110
|
+
# getpass + paste on Windows terminals). Refuse and point to the safe path.
|
|
111
|
+
if len(value) < 4 and interactive:
|
|
112
|
+
print(f"refusing: got only {len(value)} usable char(s) — this looks like a "
|
|
113
|
+
"truncated paste (getpass mangles pasted secrets on some terminals).\n"
|
|
114
|
+
"Save the secret to a file and use --from-file instead:\n"
|
|
115
|
+
f" python scripts/set-credential.py {args.tool} {args.secret} "
|
|
116
|
+
"--from-file <path>", file=sys.stderr)
|
|
117
|
+
return 1
|
|
118
|
+
|
|
119
|
+
creds.set_secret(args.tool, args.secret, value)
|
|
120
|
+
print(f"stored: {target} ({len(value)} bytes) in the "
|
|
121
|
+
f"{getattr(creds._b(), 'name', 'default')} credential store")
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if __name__ == "__main__":
|
|
126
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Validate registered Agent Skills and their repository integrations.
|
|
3
|
+
|
|
4
|
+
WHAT THIS ENFORCES
|
|
5
|
+
------------------
|
|
6
|
+
Two guards in one script, dispatched by where a SKILL.md lives.
|
|
7
|
+
|
|
8
|
+
`skills/*/SKILL.md` (hand-written skills) get the full check: portable
|
|
9
|
+
frontmatter, a name that matches its folder, a bounded body, resolvable bundled
|
|
10
|
+
resources, shallow resource layout, and command examples that name real tools,
|
|
11
|
+
agents, actions, and arguments. A `--baseline` file may temporarily waive known
|
|
12
|
+
findings; it never hides new ones.
|
|
13
|
+
|
|
14
|
+
`tools/*/SKILL.md` and `agents/*/SKILL.md` (GENERATED manuals) get only the
|
|
15
|
+
loadability check: the frontmatter must parse and carry a name and description
|
|
16
|
+
within the length caps. The stricter skill rules are deliberately NOT applied to
|
|
17
|
+
them, because a generated manual legitimately declares a manifest name that can
|
|
18
|
+
differ from its folder and a body far longer than a hand-written skill. This is
|
|
19
|
+
the read-back the generated-manual pipeline never had: the generator built its
|
|
20
|
+
YAML frontmatter by string interpolation, so any manifest description carrying a
|
|
21
|
+
colon-space ended the key and produced a document no loader could read. It held
|
|
22
|
+
for roughly half the manuals in the tree and hit the best-described tools first.
|
|
23
|
+
|
|
24
|
+
HOOK CONTRACT
|
|
25
|
+
-------------
|
|
26
|
+
`scripts/hook-lib.sh :: hook_run_skill_validate` calls this as
|
|
27
|
+
`validate-skills.py --staged` and keys on the exit code: 0 pass, 1 block, 2
|
|
28
|
+
could-not-run (nothing checked, so nothing cleared). The staged set is
|
|
29
|
+
VCS-aware, exactly like `scripts/secret_scan.py`: git's staged set where there
|
|
30
|
+
is a `.git`, SVN's locally-modified set otherwise, because this repo is
|
|
31
|
+
published through both.
|
|
32
|
+
|
|
33
|
+
Spec: https://agentskills.io/specification
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
python scripts/validate-skills.py # the whole tree
|
|
37
|
+
python scripts/validate-skills.py --staged # only changed SKILL.md (hook path)
|
|
38
|
+
python scripts/validate-skills.py path/to/SKILL.md ...
|
|
39
|
+
python scripts/validate-skills.py --json # machine-readable report
|
|
40
|
+
|
|
41
|
+
Exit codes:
|
|
42
|
+
0 clean
|
|
43
|
+
1 at least one blocking finding
|
|
44
|
+
2 could not run (bad usage, unreadable tree, unenumerable staged set)
|
|
45
|
+
"""
|
|
46
|
+
from __future__ import annotations
|
|
47
|
+
|
|
48
|
+
import argparse
|
|
49
|
+
import fnmatch
|
|
50
|
+
import json
|
|
51
|
+
import re
|
|
52
|
+
import subprocess
|
|
53
|
+
import sys
|
|
54
|
+
from dataclasses import asdict, dataclass
|
|
55
|
+
from datetime import date
|
|
56
|
+
from pathlib import Path, PurePosixPath
|
|
57
|
+
from typing import Any, Iterable
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
import yaml
|
|
61
|
+
except ImportError: # pragma: no cover - environment problem, not a finding
|
|
62
|
+
print("validate-skills: PyYAML is not installed - nothing checked.", file=sys.stderr)
|
|
63
|
+
raise SystemExit(2)
|
|
64
|
+
|
|
65
|
+
REPO = Path(__file__).resolve().parents[1]
|
|
66
|
+
DEFAULT_SKILLS = REPO / "skills"
|
|
67
|
+
_NAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
|
|
68
|
+
_MARKDOWN_LINK_RE = re.compile(r"\[[^\]]*\]\(([^)]+)\)")
|
|
69
|
+
_CODE_PATH_RE = re.compile(
|
|
70
|
+
r"`((?:(?:references|scripts|assets)/)?[^`\n]+\.(?:md|sql|py|sh|ps1|js|ts|json|yaml|yml|html))`",
|
|
71
|
+
re.IGNORECASE,
|
|
72
|
+
)
|
|
73
|
+
_RUN_RE = re.compile(
|
|
74
|
+
r"python\s+scripts/run-(tool|agent)\.py\s+([a-z0-9-]+)(?:\s+([a-z0-9-]+))?",
|
|
75
|
+
re.IGNORECASE,
|
|
76
|
+
)
|
|
77
|
+
_OPTION_RE = re.compile(r"--([a-z0-9][a-z0-9-]*)", re.IGNORECASE)
|
|
78
|
+
_ALLOWED_FRONTMATTER = {
|
|
79
|
+
"name", "description", "version", "compatibility", "license", "allowed-tools",
|
|
80
|
+
"disable-model-invocation", "argument-hint", "routing", "metadata", "owner",
|
|
81
|
+
"last_verified", "baseline_version", "eval_suite", "source_policy", "tier",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
MAX_NAME = 64
|
|
85
|
+
MAX_DESCRIPTION = 1024
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass(frozen=True, order=True)
|
|
89
|
+
class Finding:
|
|
90
|
+
severity: str
|
|
91
|
+
code: str
|
|
92
|
+
skill: str
|
|
93
|
+
path: str
|
|
94
|
+
message: str
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _split_frontmatter(path: Path) -> tuple[dict[str, Any], str]:
|
|
98
|
+
text = path.read_text(encoding="utf-8")
|
|
99
|
+
lines = text.splitlines()
|
|
100
|
+
if not lines or lines[0].strip() != "---":
|
|
101
|
+
raise ValueError("missing opening ---")
|
|
102
|
+
for index in range(1, len(lines)):
|
|
103
|
+
if lines[index].strip() == "---":
|
|
104
|
+
raw = yaml.safe_load("\n".join(lines[1:index]))
|
|
105
|
+
if not isinstance(raw, dict):
|
|
106
|
+
raise ValueError("frontmatter must be a mapping")
|
|
107
|
+
return raw, "\n".join(lines[index + 1:])
|
|
108
|
+
raise ValueError("missing closing ---")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _load_capabilities(repo: Path) -> dict[str, dict[str, dict[str, set[str]]]]:
|
|
112
|
+
result: dict[str, dict[str, dict[str, set[str]]]] = {"tool": {}, "agent": {}}
|
|
113
|
+
for kind, folder, manifest_name in (
|
|
114
|
+
("tool", "tools", "tool.yaml"), ("agent", "agents", "agent.yaml")
|
|
115
|
+
):
|
|
116
|
+
root = repo / folder
|
|
117
|
+
if not root.exists():
|
|
118
|
+
continue
|
|
119
|
+
for path in sorted(root.glob(f"*/{manifest_name}")):
|
|
120
|
+
if path.parent.name.startswith("_"):
|
|
121
|
+
continue
|
|
122
|
+
try:
|
|
123
|
+
raw = yaml.safe_load(path.read_text(encoding="utf-8"))
|
|
124
|
+
except (OSError, yaml.YAMLError):
|
|
125
|
+
continue
|
|
126
|
+
if not isinstance(raw, dict) or not raw.get("name"):
|
|
127
|
+
continue
|
|
128
|
+
actions: dict[str, set[str]] = {}
|
|
129
|
+
for action in raw.get("actions") or []:
|
|
130
|
+
if not isinstance(action, dict) or not action.get("name"):
|
|
131
|
+
continue
|
|
132
|
+
actions[str(action["name"])] = {
|
|
133
|
+
str(arg["name"])
|
|
134
|
+
for arg in (action.get("args") or [])
|
|
135
|
+
if isinstance(arg, dict) and arg.get("name")
|
|
136
|
+
}
|
|
137
|
+
result[kind][str(raw["name"])] = actions
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _finding(code: str, skill: str, path: Path | str, message: str,
|
|
142
|
+
severity: str = "error") -> Finding:
|
|
143
|
+
return Finding(severity=severity, code=code, skill=skill,
|
|
144
|
+
path=str(path).replace("\\", "/"), message=message)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _candidate_paths(raw: str, source: Path, root: Path) -> list[Path]:
|
|
148
|
+
cleaned = raw.strip().split("#", 1)[0].split("?", 1)[0]
|
|
149
|
+
posix = PurePosixPath(cleaned)
|
|
150
|
+
if not cleaned or cleaned.startswith(("#", "http://", "https://", "mailto:")):
|
|
151
|
+
return []
|
|
152
|
+
if posix.is_absolute() or ".." in posix.parts:
|
|
153
|
+
return [root.parent / "__unsafe_reference__"]
|
|
154
|
+
path = Path(*posix.parts)
|
|
155
|
+
candidates = [source.parent / path]
|
|
156
|
+
if len(posix.parts) == 1:
|
|
157
|
+
candidates += [root / path, root / "references" / path,
|
|
158
|
+
root / "scripts" / path, root / "assets" / path]
|
|
159
|
+
else:
|
|
160
|
+
candidates.append(root / path)
|
|
161
|
+
unique: list[Path] = []
|
|
162
|
+
seen: set[str] = set()
|
|
163
|
+
for candidate in candidates:
|
|
164
|
+
key = str(candidate)
|
|
165
|
+
if key not in seen:
|
|
166
|
+
seen.add(key)
|
|
167
|
+
unique.append(candidate)
|
|
168
|
+
return unique
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _scan_references(skill: str, root: Path, source: Path, text: str) -> list[Finding]:
|
|
172
|
+
findings: list[Finding] = []
|
|
173
|
+
refs = [match.group(1).strip().split()[0] for match in _MARKDOWN_LINK_RE.finditer(text)]
|
|
174
|
+
refs += [match.group(1).strip() for match in _CODE_PATH_RE.finditer(text)]
|
|
175
|
+
for raw in sorted(set(refs)):
|
|
176
|
+
if raw.startswith(("http://", "https://", "mailto:", "#")):
|
|
177
|
+
continue
|
|
178
|
+
candidates = _candidate_paths(raw, source, root)
|
|
179
|
+
if not candidates:
|
|
180
|
+
continue
|
|
181
|
+
if any("__unsafe_reference__" in candidate.parts for candidate in candidates):
|
|
182
|
+
findings.append(_finding("unsafe-reference", skill, source.relative_to(root),
|
|
183
|
+
f"reference escapes the skill root: {raw}"))
|
|
184
|
+
continue
|
|
185
|
+
if not any(candidate.exists() for candidate in candidates):
|
|
186
|
+
findings.append(_finding("missing-reference", skill, source.relative_to(root),
|
|
187
|
+
f"referenced resource does not exist: {raw}"))
|
|
188
|
+
return findings
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _scan_commands(skill: str, root: Path, source: Path, text: str,
|
|
192
|
+
capabilities: dict[str, dict[str, dict[str, set[str]]]]) -> list[Finding]:
|
|
193
|
+
findings: list[Finding] = []
|
|
194
|
+
for match in _RUN_RE.finditer(text):
|
|
195
|
+
kind, name, action = (value.lower() if value else value for value in match.groups())
|
|
196
|
+
catalog = capabilities[kind]
|
|
197
|
+
if name not in catalog:
|
|
198
|
+
findings.append(_finding("unknown-capability", skill, source.relative_to(root),
|
|
199
|
+
f"unknown {kind}: {name}"))
|
|
200
|
+
continue
|
|
201
|
+
if not action or action.startswith("<"):
|
|
202
|
+
continue
|
|
203
|
+
actions = catalog[name]
|
|
204
|
+
if actions and action not in actions:
|
|
205
|
+
findings.append(_finding("unknown-action", skill, source.relative_to(root),
|
|
206
|
+
f"unknown {kind} action: {name} {action}"))
|
|
207
|
+
continue
|
|
208
|
+
if action in actions:
|
|
209
|
+
invocation = match.string[match.start():match.string.find("\n", match.start())]
|
|
210
|
+
for option in _OPTION_RE.findall(invocation):
|
|
211
|
+
if option not in actions[action] and option not in {"help"}:
|
|
212
|
+
findings.append(_finding(
|
|
213
|
+
"unknown-argument", skill, source.relative_to(root),
|
|
214
|
+
f"unknown argument --{option} for {name} {action}", severity="warning"
|
|
215
|
+
))
|
|
216
|
+
return findings
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def validate_skill(skill_md: Path, repo: Path,
|
|
220
|
+
capabilities: dict[str, dict[str, dict[str, set[str]]]]) -> list[Finding]:
|
|
221
|
+
root = skill_md.parent
|
|
222
|
+
folder_name = root.name
|
|
223
|
+
findings: list[Finding] = []
|
|
224
|
+
try:
|
|
225
|
+
frontmatter, body = _split_frontmatter(skill_md)
|
|
226
|
+
except (OSError, UnicodeError, yaml.YAMLError, ValueError) as exc:
|
|
227
|
+
return [_finding("invalid-frontmatter", folder_name, "SKILL.md", str(exc))]
|
|
228
|
+
|
|
229
|
+
name = str(frontmatter.get("name") or "").strip()
|
|
230
|
+
description = " ".join(str(frontmatter.get("description") or "").split())
|
|
231
|
+
skill = name or folder_name
|
|
232
|
+
if not name:
|
|
233
|
+
findings.append(_finding("missing-name", skill, "SKILL.md", "frontmatter name is required"))
|
|
234
|
+
elif not _NAME_RE.fullmatch(name) or len(name) > MAX_NAME:
|
|
235
|
+
findings.append(_finding("invalid-name", skill, "SKILL.md",
|
|
236
|
+
"name must be <=64 lowercase letters, digits, and hyphens"))
|
|
237
|
+
if name and name != folder_name:
|
|
238
|
+
findings.append(_finding("folder-name-mismatch", skill, "SKILL.md",
|
|
239
|
+
f"folder {folder_name!r} does not match name {name!r}"))
|
|
240
|
+
if not description:
|
|
241
|
+
findings.append(_finding("missing-description", skill, "SKILL.md",
|
|
242
|
+
"frontmatter description is required"))
|
|
243
|
+
elif len(description) > MAX_DESCRIPTION:
|
|
244
|
+
findings.append(_finding("description-too-long", skill, "SKILL.md",
|
|
245
|
+
f"description is {len(description)} characters; maximum is 1024"))
|
|
246
|
+
body_lines = len(body.splitlines())
|
|
247
|
+
if body_lines > 500:
|
|
248
|
+
findings.append(_finding("body-too-long", skill, "SKILL.md",
|
|
249
|
+
f"SKILL.md body has {body_lines} lines; maximum is 500"))
|
|
250
|
+
|
|
251
|
+
for key in sorted(set(frontmatter) - _ALLOWED_FRONTMATTER):
|
|
252
|
+
findings.append(_finding("unknown-frontmatter-key", skill, "SKILL.md",
|
|
253
|
+
f"unrecognized frontmatter key: {key}", severity="warning"))
|
|
254
|
+
|
|
255
|
+
if frontmatter.get("last_verified"):
|
|
256
|
+
try:
|
|
257
|
+
date.fromisoformat(str(frontmatter["last_verified"]))
|
|
258
|
+
except ValueError:
|
|
259
|
+
findings.append(_finding("invalid-last-verified", skill, "SKILL.md",
|
|
260
|
+
"last_verified must be YYYY-MM-DD"))
|
|
261
|
+
if frontmatter.get("eval_suite"):
|
|
262
|
+
target = root / str(frontmatter["eval_suite"])
|
|
263
|
+
if not target.is_file():
|
|
264
|
+
findings.append(_finding("missing-eval-suite", skill, "SKILL.md",
|
|
265
|
+
f"eval_suite does not exist: {frontmatter['eval_suite']}"))
|
|
266
|
+
|
|
267
|
+
# Python imports performed by tests can create bytecode caches inside a
|
|
268
|
+
# skill's scripts directory. They are transient interpreter artifacts, not
|
|
269
|
+
# bundled skill resources, and must not turn test order into validation state.
|
|
270
|
+
files = (
|
|
271
|
+
path
|
|
272
|
+
for path in root.rglob("*")
|
|
273
|
+
if path.is_file()
|
|
274
|
+
and path.suffix.lower() != ".pyc"
|
|
275
|
+
and "__pycache__" not in path.relative_to(root).parts
|
|
276
|
+
)
|
|
277
|
+
for file in sorted(files):
|
|
278
|
+
rel = file.relative_to(root)
|
|
279
|
+
if rel.parts[0] in {"references", "scripts", "assets"} and len(rel.parts) > 2:
|
|
280
|
+
findings.append(_finding("nested-resource", skill, rel,
|
|
281
|
+
"bundled resources must stay one level below their category"))
|
|
282
|
+
if rel.parts[0] == "scripts" and file.suffix.lower() in {".html", ".svg", ".png", ".jpg", ".jpeg"}:
|
|
283
|
+
findings.append(_finding("asset-in-scripts", skill, rel,
|
|
284
|
+
"output templates and media belong under assets/"))
|
|
285
|
+
if rel.parts[0] == "references" and "scripts" in rel.parts[1:-1]:
|
|
286
|
+
findings.append(_finding("script-in-references", skill, rel,
|
|
287
|
+
"executable helpers belong directly under scripts/"))
|
|
288
|
+
if file.suffix.lower() in {".md", ".markdown"}:
|
|
289
|
+
try:
|
|
290
|
+
text = file.read_text(encoding="utf-8")
|
|
291
|
+
except (OSError, UnicodeError) as exc:
|
|
292
|
+
findings.append(_finding("unreadable-resource", skill, rel, str(exc)))
|
|
293
|
+
continue
|
|
294
|
+
findings.extend(_scan_references(skill, root, file, text))
|
|
295
|
+
findings.extend(_scan_commands(skill, root, file, text, capabilities))
|
|
296
|
+
|
|
297
|
+
return sorted(set(findings))
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def loadability(skill_md: Path, repo: Path) -> list[Finding]:
|
|
301
|
+
"""The generated-manual guard: the file must parse and carry a name and
|
|
302
|
+
description within the caps. Deliberately no folder/name, body-length, or
|
|
303
|
+
resource rules - a generated manual legitimately breaks those."""
|
|
304
|
+
try:
|
|
305
|
+
rel = skill_md.relative_to(repo).as_posix()
|
|
306
|
+
except ValueError:
|
|
307
|
+
rel = skill_md.name
|
|
308
|
+
folder = skill_md.parent.name
|
|
309
|
+
try:
|
|
310
|
+
frontmatter, _ = _split_frontmatter(skill_md)
|
|
311
|
+
except (OSError, UnicodeError, yaml.YAMLError, ValueError) as exc:
|
|
312
|
+
return [_finding("invalid-frontmatter", folder, rel, str(exc))]
|
|
313
|
+
findings: list[Finding] = []
|
|
314
|
+
name = str(frontmatter.get("name") or "").strip()
|
|
315
|
+
description = str(frontmatter.get("description") or "").strip()
|
|
316
|
+
skill = name or folder
|
|
317
|
+
if not name:
|
|
318
|
+
findings.append(_finding("missing-name", skill, rel, "frontmatter name is required"))
|
|
319
|
+
elif len(name) > MAX_NAME:
|
|
320
|
+
findings.append(_finding("invalid-name", skill, rel,
|
|
321
|
+
f"name is {len(name)} chars, limit {MAX_NAME}"))
|
|
322
|
+
if not description:
|
|
323
|
+
findings.append(_finding("missing-description", skill, rel,
|
|
324
|
+
"frontmatter description is required"))
|
|
325
|
+
elif len(description) > MAX_DESCRIPTION:
|
|
326
|
+
over = len(description) - MAX_DESCRIPTION
|
|
327
|
+
findings.append(_finding("description-too-long", skill, rel,
|
|
328
|
+
f"description is {len(description)} chars, limit "
|
|
329
|
+
f"{MAX_DESCRIPTION} (over by {over})"))
|
|
330
|
+
return findings
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def validate_repo(skills_root: Path = DEFAULT_SKILLS, repo: Path = REPO) -> list[Finding]:
|
|
334
|
+
capabilities = _load_capabilities(repo)
|
|
335
|
+
findings: list[Finding] = []
|
|
336
|
+
for skill_md in sorted(skills_root.glob("*/SKILL.md")):
|
|
337
|
+
if skill_md.parent.name.startswith("_") or skill_md.parent.name == "tests":
|
|
338
|
+
continue
|
|
339
|
+
findings.extend(validate_skill(skill_md, repo, capabilities))
|
|
340
|
+
if not list(skills_root.glob("*/SKILL.md")):
|
|
341
|
+
findings.append(_finding("no-skills", "<repository>", skills_root,
|
|
342
|
+
"no skills/*/SKILL.md files found"))
|
|
343
|
+
return sorted(set(findings))
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _is_skill_path(path: Path, repo: Path) -> bool:
|
|
347
|
+
try:
|
|
348
|
+
parts = path.resolve().relative_to(repo.resolve()).parts
|
|
349
|
+
except ValueError:
|
|
350
|
+
return "skills" in path.parts
|
|
351
|
+
return bool(parts) and parts[0] == "skills"
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def governed_folders(skills_root: Path) -> set[str]:
|
|
355
|
+
"""Skill folders that opt into the governance/eval discipline, by the
|
|
356
|
+
`source_policy` marker. The full rubric (folder/name, body length, resolvable
|
|
357
|
+
and shallow resources, resolvable command examples) is authored for these.
|
|
358
|
+
Imported/portable skills and generated tool manuals omit the marker and get
|
|
359
|
+
only the loadability guard, so a legitimate imported skill - one that runs
|
|
360
|
+
long, references a repo file outside its own folder, or ships nested fonts -
|
|
361
|
+
is not blocked by a rubric it was never written to. In procesio-cli, whose
|
|
362
|
+
`skills/` IS the portfolio, every skill is governed and the two coincide."""
|
|
363
|
+
names: set[str] = set()
|
|
364
|
+
for skill_md in sorted(skills_root.glob("*/SKILL.md")):
|
|
365
|
+
if skill_md.parent.name.startswith("_") or skill_md.parent.name == "tests":
|
|
366
|
+
continue
|
|
367
|
+
try:
|
|
368
|
+
frontmatter, _ = _split_frontmatter(skill_md)
|
|
369
|
+
except (OSError, UnicodeError, yaml.YAMLError, ValueError):
|
|
370
|
+
continue
|
|
371
|
+
if frontmatter.get("source_policy"):
|
|
372
|
+
names.add(skill_md.parent.name)
|
|
373
|
+
return names
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def check_path(path: Path, repo: Path,
|
|
377
|
+
capabilities: dict[str, dict[str, dict[str, set[str]]]],
|
|
378
|
+
governed: set[str]) -> list[Finding]:
|
|
379
|
+
"""Dispatch one SKILL.md: the full skill rubric for a governed skill, the
|
|
380
|
+
loadability guard for an imported skill or a generated manual (tools/, agents/)."""
|
|
381
|
+
if _is_skill_path(path, repo) and path.parent.name in governed:
|
|
382
|
+
return validate_skill(path, repo, capabilities)
|
|
383
|
+
return loadability(path, repo)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _all_manual_files(repo: Path) -> list[Path]:
|
|
387
|
+
manuals: list[Path] = []
|
|
388
|
+
for folder in ("tools", "agents"):
|
|
389
|
+
root = repo / folder
|
|
390
|
+
if root.exists():
|
|
391
|
+
manuals += [p for p in sorted(root.glob("*/SKILL.md"))
|
|
392
|
+
if not p.parent.name.startswith("_")]
|
|
393
|
+
return manuals
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _changed_skill_files(repo: Path) -> list[Path]:
|
|
397
|
+
"""The SKILL.md files this commit would carry, VCS-aware (git staged set, or
|
|
398
|
+
SVN locally-modified set). A path that no longer exists (a delete) is skipped."""
|
|
399
|
+
is_git = (repo / ".git").exists()
|
|
400
|
+
if is_git:
|
|
401
|
+
cmd = ["git", "-C", str(repo), "diff", "--cached", "--name-only", "--diff-filter=ACM"]
|
|
402
|
+
else:
|
|
403
|
+
cmd = ["svn", "status", str(repo)]
|
|
404
|
+
try:
|
|
405
|
+
raw = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8",
|
|
406
|
+
timeout=120).stdout
|
|
407
|
+
except Exception as exc: # noqa: BLE001 - cannot enumerate means cannot clear
|
|
408
|
+
print(f"validate-skills: could not list changed files ({exc}).", file=sys.stderr)
|
|
409
|
+
raise SystemExit(2)
|
|
410
|
+
|
|
411
|
+
out: list[str] = []
|
|
412
|
+
for line in raw.splitlines():
|
|
413
|
+
line = line.rstrip()
|
|
414
|
+
if not line:
|
|
415
|
+
continue
|
|
416
|
+
if is_git:
|
|
417
|
+
path = line
|
|
418
|
+
else:
|
|
419
|
+
if line[:1] not in ("A", "M"):
|
|
420
|
+
continue
|
|
421
|
+
path = line[1:].strip()
|
|
422
|
+
if path.replace("\\", "/").endswith("SKILL.md"):
|
|
423
|
+
out.append(path)
|
|
424
|
+
|
|
425
|
+
files: list[Path] = []
|
|
426
|
+
for p in out:
|
|
427
|
+
candidate = Path(p)
|
|
428
|
+
if not candidate.is_absolute():
|
|
429
|
+
candidate = repo / p
|
|
430
|
+
if candidate.exists():
|
|
431
|
+
files.append(candidate)
|
|
432
|
+
return sorted(set(files))
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def _load_waivers(path: Path | None) -> list[dict[str, str]]:
|
|
436
|
+
if path is None:
|
|
437
|
+
return []
|
|
438
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
439
|
+
rows = raw.get("allow", []) if isinstance(raw, dict) else raw
|
|
440
|
+
if not isinstance(rows, list):
|
|
441
|
+
raise ValueError(f"{path}: expected an allow list")
|
|
442
|
+
return [{key: str(value) for key, value in row.items()} for row in rows]
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _waived(finding: Finding, waivers: Iterable[dict[str, str]]) -> bool:
|
|
446
|
+
payload = asdict(finding)
|
|
447
|
+
for waiver in waivers:
|
|
448
|
+
if all(fnmatch.fnmatch(payload.get(key, ""), pattern) for key, pattern in waiver.items()):
|
|
449
|
+
return True
|
|
450
|
+
return False
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def _parser() -> argparse.ArgumentParser:
|
|
454
|
+
parser = argparse.ArgumentParser(description="Validate registered Agent Skills.")
|
|
455
|
+
parser.add_argument("paths", nargs="*", help="SKILL.md files (default: the whole tree)")
|
|
456
|
+
parser.add_argument("--staged", action="store_true",
|
|
457
|
+
help="check only the SKILL.md files this commit would carry")
|
|
458
|
+
parser.add_argument("--skills-root", type=Path, default=DEFAULT_SKILLS)
|
|
459
|
+
parser.add_argument("--repo", type=Path, default=REPO)
|
|
460
|
+
parser.add_argument("--baseline", type=Path,
|
|
461
|
+
help="JSON waiver file for known findings; new findings still fail")
|
|
462
|
+
parser.add_argument("--strict-warnings", action="store_true")
|
|
463
|
+
parser.add_argument("--json", action="store_true")
|
|
464
|
+
return parser
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def main(argv: list[str] | None = None) -> int:
|
|
468
|
+
args = _parser().parse_args(argv)
|
|
469
|
+
repo = args.repo
|
|
470
|
+
capabilities = _load_capabilities(repo)
|
|
471
|
+
governed = governed_folders(args.skills_root)
|
|
472
|
+
|
|
473
|
+
if args.paths:
|
|
474
|
+
files = [Path(p) if Path(p).is_absolute() else repo / p for p in args.paths]
|
|
475
|
+
findings = [f for path in files for f in check_path(path, repo, capabilities, governed)]
|
|
476
|
+
elif args.staged:
|
|
477
|
+
files = _changed_skill_files(repo)
|
|
478
|
+
if not files:
|
|
479
|
+
print("validate-skills: no SKILL.md in scope - nothing to check.")
|
|
480
|
+
return 0
|
|
481
|
+
findings = [f for path in files for f in check_path(path, repo, capabilities, governed)]
|
|
482
|
+
else:
|
|
483
|
+
findings = []
|
|
484
|
+
skill_files = sorted(args.skills_root.glob("*/SKILL.md"))
|
|
485
|
+
for skill_md in skill_files:
|
|
486
|
+
if skill_md.parent.name.startswith("_") or skill_md.parent.name == "tests":
|
|
487
|
+
continue
|
|
488
|
+
findings.extend(check_path(skill_md, repo, capabilities, governed))
|
|
489
|
+
if not skill_files:
|
|
490
|
+
findings.append(_finding("no-skills", "<repository>", args.skills_root,
|
|
491
|
+
"no skills/*/SKILL.md files found"))
|
|
492
|
+
for manual in _all_manual_files(repo):
|
|
493
|
+
findings.extend(loadability(manual, repo))
|
|
494
|
+
|
|
495
|
+
findings = sorted(set(findings))
|
|
496
|
+
try:
|
|
497
|
+
waivers = _load_waivers(args.baseline)
|
|
498
|
+
except (OSError, ValueError, json.JSONDecodeError) as exc:
|
|
499
|
+
print(f"validate-skills: could not read baseline ({exc}).", file=sys.stderr)
|
|
500
|
+
return 2
|
|
501
|
+
|
|
502
|
+
rows = []
|
|
503
|
+
blocking = []
|
|
504
|
+
for finding in findings:
|
|
505
|
+
waived = _waived(finding, waivers)
|
|
506
|
+
row = {**asdict(finding), "waived": waived}
|
|
507
|
+
rows.append(row)
|
|
508
|
+
if not waived and (finding.severity == "error" or args.strict_warnings):
|
|
509
|
+
blocking.append(row)
|
|
510
|
+
|
|
511
|
+
if args.json:
|
|
512
|
+
report = {
|
|
513
|
+
"schema_version": 1,
|
|
514
|
+
"finding_count": len(rows),
|
|
515
|
+
"blocking_count": len(blocking),
|
|
516
|
+
"findings": rows,
|
|
517
|
+
}
|
|
518
|
+
print(json.dumps(report, indent=2, sort_keys=True))
|
|
519
|
+
elif not rows:
|
|
520
|
+
print("validate-skills: clean")
|
|
521
|
+
else:
|
|
522
|
+
for row in rows:
|
|
523
|
+
marker = "WAIVED" if row["waived"] else row["severity"].upper()
|
|
524
|
+
print(f"{marker}: {row['skill']}:{row['path']}: {row['code']}: {row['message']}")
|
|
525
|
+
print(f"{len(rows)} finding(s), {len(blocking)} blocking")
|
|
526
|
+
return 1 if blocking else 0
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
if __name__ == "__main__":
|
|
530
|
+
raise SystemExit(main())
|